容器是一种沙盒技术,主要目的是为了将应用运行在其中,与外界隔离。与虚拟机技术不同linux内核工作原理,容器技术并不会为每位容器实例启动一个操作系统,而是在同一台宿主机中共享同一个操作系统。容器本质上讲就是运行在操作系统上的一个进程,只不过加入了对资源的隔离和限制。
Linux内核提供的Namespaces、Cgroup技术构成了容器的基石,Namespaces实现资源隔离,Cgroup完成资源限制。容器的实现原理是把系统中为同一个业务目标服务的相关进程合成一组,置于同一个namespace命名空间中,每位namespace可以拥有自己独立的主机名、进程ID系统、IPC、网络、文件系统、用户等资源linux主机,之后通过Cgroup技术限制进程能使用的CPU、内存等资源。Docker是容器化技术的具体技术实现之一,也是依赖Namespaces、Cgroup来实现容器资源的隔离与限制。
1.1LinuxNameSpaces
LinuxNamespaces是由Linux内核提供的,用于进程间资源隔离的一种技术。namespace将全局的系统资源包装在一个具象视图里,让进程(看上去)拥有独立的全局资源实例。Linux提供了多种类型的namespace用于隔离不同类型的资源。LinuxNamespaces是容器化技术的基石,docker就是基于namespace来实现容器工作空间的隔离。当我们运行一个docker容器时,docker会为该容器创建一组namespace,这种namespace提供了一个隔离层,进程只能听到同一namespace下的资源。如右图所示,宿主机的两个进程(systemd和crond)的namespace是相同的,而docker容器对应的进程的namespace显著是新的一组namespace,也就是说该docker容器听到的是独立于宿主机的资源视图。
1.2LinuxCgroups
Cgroups(controlgroups)是Linux内核的一个功能,拿来限制一个进程组的资源(如CPU、内存、磁盘输入输出等)。在Linux中,Cgroups向用户曝露下来的插口是文件系统,即它以文件和目录的形式组织在操作系统的/sys/fs/cgroup路径下,可使用mount指令显示cgroups各个子系统挂载的路径。
如上图所示,在/sys/fs/cgroup下边有好多例如blkio、menory、cpu、cpuset这样的子目录,也叫cgroups子系统,这种都是当前机器可以被cgroups限制的资源种类。而在每位子系统对应的文件目录内,我们可以看见各种配置文件。诸如在CPU子系统对应的文件目录中就包含有如下几个文件:
tasks文件:配置着受该cgroups控制的进程ID,即属于该进程组的进程ID;
cpu.cfs_period_us和cpu.cfs_quota_us文件:cfs_period_us和cfs_quota_us这两个参数组合使用,可用于限制进程在宽度为cfs_period的周期内只能被分配到总数为cfs_quota的cpu时间,例如cfs_period_us=10000&cfs_quota_us=20000则表示该进程组最多只能使用0.5核cpu,假如cfs_period_us=10000&cfs_quota_us=20000则表示该进程组最多只能使用2核cpu。
cpu.shares:cpu.shares以相对比例限制cgroup的cpu。例如:在cgroup中将cpu.shares设定为2的任务可使用的CPU时间是在cgroup中将cpu.shares设定为1的任务可使用的CPU时间的两倍。
可以通过在/sys/fs/cgroup文件夹创建新目录的形式来创建一个新的CPU控制组
比如:mkdir-p/sys/fs/cgroup/cpu/testGroup/group1
如图所示,在cpu子系统目录下创建完新目录以后还会手动生成对应的配置文件,在该目录下可做类似如下操作来限制对应的进程
这儿值得注意的点是,控制组之间是有层级关系的,上图mkdir-p/sys/fs/cgroup/cpu/testGroup/group1这个命令实际上是创建了2个控制组testGroup和group1,在这两个目录下都有对应的配置文件,而group1控制组会默认承继testGroup的属性。
Cgroups总共有以下几个子系统
blkio:这个子系统为块设备设定输入/输出限制,例如化学设备(c盘,固态硬碟,USB等等)。
cpu:这个子系统使用调度程序提供对CPU的cgroup任务访问。
cpuacct:这个子系统手动生成cgroup中任务所使用的CPU报告。
cpuset:这个子系统为cgroup中的任务分配独立CPU(在多核系统)和显存节点。
devices:这个子系统可容许或则拒绝cgroup中的任务访问设备。
freezer:这个子系统挂起或则恢复cgroup中的任务。
memory:这个子系统设定cgroup中任务使用的显存限制,并手动生成由这些任务使用的显存资源报告。
net_cls:这个子系统使用等级辨识符(classid)标记网路数据包,可容许Linux流量控制程序(tc)辨识从具体cgroup中生成的数据包。
ns:名称空间子系统,提供ns_cgroup_clone函数,支持基于已有的cgroup创建新的cgroup。
1.3RootFS与UnionFS
rootfs即根文件系统,是系统内核启动后挂载的第一个文件系统,包含着操作系统所必需的文件、配置和目录(例如bin、dev、etc、home、lib等目录及文件)。Linux的系统内核和系统文件是分开的,同一台机器上的容器共享同一个宿主机的内核,但每位容器有着属于自己的文件系统。基于mountnamespace的技术我们可以让容器有属于自己的文件系统,为了让容器见到的根目录愈发真实,通常会为容器的根目录挂载一个完整操作系统的文件系统,而这个rootfs文件系统虽然就是容器镜像的具体内容。例如,在基于Ubuntu镜像启动的容器中执行ls/看见的内容就是ubuntu系统根目录的文件。
制做容器镜像虽然就是制做rootfs的过程,因为rootfs离打包的不仅仅是应用,而是整个操作系统的文件和目录,这意味着,应用以及它运行所依赖的环境都被打包封装到了容器镜像上面。这就赋于了容器所谓的一致性:在任何机器上linux教程下载,用户只要解压打包好的容器镜像,这个应用运行所需的完整的执行环境能够再现。
Docker使用了UnionFS联合文件系统的技术,在镜像的设计中引入了层(layer)的概念,支持以增量的形式来制做容器镜像,例如我们可以基于ubuntu镜像来制做nginx镜像,使用nginx镜像来制做基于nginx的业务应用镜像。UnionFS联合文件系统是一种分层、轻量级而且高性能的文件系统linux内核工作原理,它支持对文件系统的更改作为一次递交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。采用UnionFS分层制做容器镜像最大的用处就是共享资源,好多镜像都是从相同的base镜像建立而至,而宿主机的c盘只需保留一份base镜像,大大节约了容器镜像的占用空间。