Linux Container概述

标签:
linux |
分类: 操作系统 |
1
http://s10/mw690/002RQMzZzy703dIFCwp09&690Container概述" TITLE="Linux
VM一般指系统虚拟化,每个虚拟机的运行环境由VMM(虚拟化管理器)仿真而成,每个VM运行各自的内核;而Container仅仅是应用封装容器,它在应用态封装了一个rootfs,多个Container共享同一个内核。VM和Container的总体架构图对比如上所示。从使用者的角度,Container和VM一样也拥有独立虚拟机这个假象,这正是底下内核的namespace和cgroup所努力达到的。不过由于Container的内核是共享的,这个假象虚拟机无法像VM虚拟机一样任意安装操作系统。
在有Container之前,所有应用程序的rootfs都是一样的,即整个操作系统的根目录;所有进程涉及到的网络环境都是一样的,即同样的IP地址,同样的路由,同样的网络接口。namespace的功能,允许多个进程组成一个container拥有独立的运行环境,不同container有不同的rootfs,不同的网络协议栈,不同的IPC。这就是namespace,进程运行环境所涉及的方方面面都有相应的namesapce。总的来说,Namespace可以制造一种虚拟机的假象,可以实现独立的mnt;独立的pid空间;独立的网络协议栈;独立的IPC;独立的/proc /sys。
但Container如果只有Namespace,资源无法根据需求QoS配置,因此这里需要借助Cgroups。cgroup用于进程组的资源隔离,我们能想到的方方面面的资源都可以通过cgroup来控制,比如控制一组进程总计使用多少CPU、总计使用多少内存;一组进程只能访问哪些设备;一组进程的IOPS最大只能多少。Container天然包含一组进程,Container就是借助cgroup来限制资源使用,使得不同Container用到的资源正确隔离。
速度:Container运行开销小;部署速度;启动速度快,不需要走整个系统流程;VM运行开销大,因为多了一层Guest Kernel;启动速度较慢;但Container不能做在线迁移。
安全:Container因为内核共享,无法做到彻底的隔离,也就没有令人放心的安全性。资源切割的角度:个人认为,Container更适用于大公司的内部云平台。
1.1
容器有效地将由单个操作系统管理的资源划分到孤立的组中,以更好地在孤立的组之间平衡有冲突的资源使用需求。与虚拟化相比,它的优势在于:
l
l
l
l
l
l
1.2
a)
LXC 依赖于Linux Cgroups 实现资源管理的功能,而Cgroups 并没有相应的子系统来提供限制磁盘空间的功能。对于服务器整合而言,限制磁盘空间是个很有用的功能,可以限制单一应用占有过多的磁盘空间,从而导致其他应用失败。LXC 现在的解决办法是在创建容器的时候利用LVM 创建一个LVM 分区来限制容器可使用的磁盘空间。这个办法依赖于LVM,用户在使用LXC 时需要做额外的工具
b)
对于基于容器的虚拟化技术来说,每个容器都是一个操作系统用户态实例,因此每个容器都需要自己的系统函数库文件等必需文件。如果每个容器都存一份拷贝的话,在一个系统上有多个容器的情况下,就会造成磁盘空间的浪费。一个简单的解决办法就是对于容器共用的文件,可以只保存一份拷贝,其他容器都采取硬连接的方式来共享文件。这样减少占用的磁盘空间,减少内存里的inode 缓存等。但是如果没有其他的额外措施,当一个容器修改这种共享文件的话就会影响到其他容器。解决这个问题的最佳方法就是写时复制,当有容器对共享文件写入时,为其单独创建一份拷贝。
c)
在基于容器的虚拟化技术中,进程不再是个全局概念,而是从属于某个特定的容器(如果我们采取类似Solaris Containers 的观点,将系统本身看成是一个特殊的容器的话)。理想情况下,进程跟容器之间是动态关联的,进程可以在容器之间迁移。在基于容器的虚拟化技术中,容器既是资源容器,也是隔离的命名空间。因此,进程的迁移既是资源主体的变更,也是命名空间的变更。就LXC 而言,资源管理是通过Cgroups 实现的,进程可以在cgroup 之间有条件地迁移;命名空间的隔离是通过Namespaces 机制实现的,目前内核只支持进程变更有限的几类命名空间。LXC 在处理进程在容器间迁移时,使用setns 系统调用切换进程的命名空间,但是setns 系统调用目前只支持三类命名空间的切换,而LXC 需要所有的命名空间都切换。因此,在现有内核上,使用LXC 的进程迁移功能会失败。
d)
checkpoint 技术可以将容器中所有的进程暂时Frozen,并将容器完整的状态存储到磁盘上。目前LXC 还不支持checkpoint 技术,虽然利用Cgroups 的Freezer子系统,可以将容器中的进程暂时Frozen,也可以将Frozen 的进程恢复进行,但是并没有存储容器完整的状态,效果类似将容器中的进程暂停和恢复。完整的实现checkpoint,还需要在进程Frozen 时,将容器中所有进程的状态存下来,这个可以通过扫描/proc 文件系统,记录相应的状态信息来实现。
e)
动态迁移是指将正在运行的容器从一个机器迁移到另一个机器。动态迁移不需要
停止容器中的进程,可以用来维护在线系统,减少服务器宕机的可能。另外,动
态迁移可以通过将容器从负载较高的服务器迁移到负载较低的服务器来优化服
务器集群的资源配置。LXC
目前并不支持动态迁移。
2
Linux container在内核层面由两个独立的机制保证,一个保证资源的隔离性,名为namespace;一个进行资源的控制,名为cgroup.
容器虚拟化技术是非常快速和高效的。它的原理基于操作系统内核对不同的运行进程提供了不同的系统视图。这种隔离可以用于确保在保证安全和效率的情况下授权访问硬件资源,如CPU和IO带宽。
2.1
Linux
Namespaces机制提供一种资源隔离方案。PID,IPC,Network等系统资源不再是全局性的,而是属于某个特定的Namespace。每个namespace下的资源对于其他namespace下的资源都是透明,不可见的。要创建新的Namespace,只需要在调用clone时指定相应的flag。Linux
http://s13/mw690/002RQMzZzy703dJNbbu5c&690Container概述" TITLE="Linux
Linux现有的namespace有5种:
2.1.1
最简单的主机名,一个namespace结构绑定这样一个字符串,uname的时候去current->nsproxy->uts_namespace下面取字符串就好了
当调用clone时,设定了CLONE_NEWUTS,就会创建一个新的UTS
2.1.2
pid_namespace是每个namespace有一个单独的pid_map作为bitmap进行pid的分配,因此各个pid namespace的pid互不干扰,独立分配。当调用clone时,设定了CLONE_NEWPID,就会创建一个新的PID
2.1.3
当调用clone时,设定了CLONE_NEWIPC,就会创建一个新的IPC
PID
2.1.4
每个mnt_namespace有自己独立的vfsmount *root, 即根挂载点是互相独立的,同时由vfsmount->mnt_child串接起来的子mnt链表,以及继续往下都是彼此独立的,产生的外在效果就是某个mnt_namespace中的mount, umount不会对其他namespace产生影响,因为整个mount树是每个namespace各有一份,彼此间无干扰, path lookup也在各自的mount树中进行。这里和chroot之类的又不一样,chroot改变的只是 task_struct相关的fs_struct中的root,影响的是path lookup的起始点,对整个mount树并无关系
当调用clone时,设定了CLONE_NEWNS,就会创建一个新的mount
2.1.5
当调用clone时,设定了CLONE_NEWNET,就会创建一个新的Network
2.2
cgroup是通过vfs的接口来进行配置的,它本身充当一个resource controller的角色,对一组进程进行资源控制,核心角色便是一组task_struct。
2.2.1
Cgroups最初的目标是为资源管理提供的一个统一的框架,既整合现有的cpuset等子系统,也为未来开发新的子系统提供接口。现在的cgroups适用于多种应用场景,从单个进程的资源控制,到实现操作系统层次的虚拟化(OS
1.限制进程组可以使用的资源数量(Resource
2.进程组的优先级控制(Prioritization
3.记录进程组使用的资源数量(Accounting
4.进程组隔离(Isolation)。比如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。
5.进程组控制(Control)。比如:使用freezer子系统可以将进程组挂起和恢复。
2.2.2
cgroup核心的有:
1.任务(task)。在cgroups中,任务就是系统的一个进程。
2.控制族群(control
3.层级(hierarchy)。控制族群可以组织成hierarchical的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性。
4.子系统(subsytem)。一个子系统就是一个资源控制器,比如cpu子系统就是控制cpu时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。
整体的逻辑可以这样来看:
a)
b)
c)
d)
e)
2.2.3
一个cgroup和一个subsystem决定了一个cgroup_subsys_state, 每个进程都以css_set(一个静态数组)的方式保存了所有和它有关的cgroup_subsys_state,同样,每个cgroup也会保存这样一组。cgroup_subsys_state又会作为每个subsystem自己结构体的内部成员包含,这样通过container_of很容易就可以找到subsystem自己的控制结构体,来进行各子系统自己的控制,于是每个task_struct都可以通过css_set找到它所属的subsystem结构体,来进行后面的操作.
而对于每个css_set, 也会把所有使用它的task连起来,这样cgroup导出所有当前绑定进程也有了依据(每个Cgroup链接一组css_set,每个css_set又会串联一组task_struct,挨个遍历)
2.2.4
l
l
l
便是这样的一个sched_entity,所有attach到这个Cgroup的进程都在这个sched_entity下面 ,调度也都在这个sched_entity下面进行。
这样的一个树形架构下,每个sched_entity只负责进行在该sched_entity runquueu上的sched_entity进行CFS,即从红黑树最左端pick一个sched_entity,由该sched_entity进行下一个层次的CFS,而每次最终pick出来的task_struct的运行对所有它的父sched_entity的运行时间都有贡献,如此实现一个全局的CFS。
l
l
mem_cgroup对于内存资源的控制,是对每个cgroup的使用内存情况进行记录,主要方式就是在真正分配物理页面的地方,通过current->cgroups->subsys[xx]找到对应的mem_cgroup控制结构,判断可否分配,可以就加上自己的计数,不行或达到某个limit,就会触发基于per-cgroup lru的reclaim, 再或者就触发cgoup内部的OOM killer等。
l
l
l
l
l
l
l
l