加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

Linux Container概述

(2016-03-12 11:02:37)
标签:

linux

分类: 操作系统

1         VMLinuxContainer的比较:

http://s10/mw690/002RQMzZzy703dIFCwp09&690Container概述" TITLE="Linux Container概述" />

VM一般指系统虚拟化,每个虚拟机的运行环境由VMM(虚拟化管理器)仿真而成,每个VM运行各自的内核;而Container仅仅是应用封装容器,它在应用态封装了一个rootfs,多个Container共享同一个内核VMContainer的总体架构图对比如上所示。从使用者的角度,ContainerVM一样也拥有独立虚拟机这个假象,这正是底下内核的namespacecgroup所努力达到的。不过由于Container的内核是共享的,这个假象虚拟机无法像VM虚拟机一样任意安装操作系统。

在有Container之前,所有应用程序的rootfs都是一样的,即整个操作系统的根目录;所有进程涉及到的网络环境都是一样的,即同样的IP地址,同样的路由,同样的网络接口。namespace的功能,允许多个进程组成一个container拥有独立的运行环境,不同container有不同的rootfs,不同的网络协议栈,不同的IPC这就是namespace,进程运行环境所涉及的方方面面都有相应的namesapce。总的来说,Namespace可以制造一种虚拟机的假象,可以实现独立的mnt;独立的pid空间;独立的网络协议栈;独立的IPC;独立的/proc /sys

Container如果只有Namespace,资源无法根据需求QoS配置,因此这里需要借助Cgroupscgroup用于进程组的资源隔离,我们能想到的方方面面的资源都可以通过cgroup来控制,比如控制一组进程总计使用多少CPU、总计使用多少内存;一组进程只能访问哪些设备;一组进程的IOPS最大只能多少Container天然包含一组进程,Container就是借助cgroup来限制资源使用,使得不同Container用到的资源正确隔离。

 

速度:Container运行开销小;部署速度;启动速度快,不需要走整个系统流程;VM运行开销大,因为多了一层Guest Kernel;启动速度较慢;但Container不能做在线迁移。

安全:Container因为内核共享,无法做到彻底的隔离,也就没有令人放心的安全性。资源切割的角度:个人认为,Container更适用于大公司的内部云平台。

1.1       LXC优势

容器有效地将由单个操作系统管理的资源划分到孤立的组中,以更好地在孤立的组之间平衡有冲突的资源使用需求。与虚拟化相比,它的优势在于:

l  与宿主机使用同一个内核,性能损耗小;

l  不需要指令级模拟。 

l  不需要即时(Just-in-time)编译。 

l  容器可以在CPU核心的本地运行指令,而不需要任何专门的解释机制。 

l  避免了准虚拟化和系统调用替换中的复杂性。 

l  轻量级隔离,在隔离的同时还提供共享机制,以实现容器与宿主机的资源共享

 

1.2       LXC不足:

a)        缺少对磁盘配额(disk quota)的支持

LXC 依赖于Linux Cgroups 实现资源管理的功能,而Cgroups 并没有相应的子系统来提供限制磁盘空间的功能。对于服务器整合而言,限制磁盘空间是个很有用的功能,可以限制单一应用占有过多的磁盘空间,从而导致其他应用失败。LXC 现在的解决办法是在创建容器的时候利用LVM 创建一个LVM 分区来限制容器可使用的磁盘空间。这个办法依赖于LVM,用户在使用LXC 时需要做额外的工具

b)        缺少对写时复制(copy on write)的支持

对于基于容器的虚拟化技术来说,每个容器都是一个操作系统用户态实例,因此每个容器都需要自己的系统函数库文件等必需文件。如果每个容器都存一份拷贝的话,在一个系统上有多个容器的情况下,就会造成磁盘空间的浪费。一个简单的解决办法就是对于容器共用的文件,可以只保存一份拷贝,其他容器都采取硬连接的方式来共享文件。这样减少占用的磁盘空间,减少内存里的inode 缓存等。但是如果没有其他的额外措施,当一个容器修改这种共享文件的话就会影响到其他容器。解决这个问题的最佳方法就是写时复制,当有容器对共享文件写入时,为其单独创建一份拷贝。

c)         进程和容器之间的动态关联还不够完善

在基于容器的虚拟化技术中,进程不再是个全局概念,而是从属于某个特定的容器(如果我们采取类似Solaris Containers 的观点,将系统本身看成是一个特殊的容器的话)。理想情况下,进程跟容器之间是动态关联的,进程可以在容器之间迁移。在基于容器的虚拟化技术中,容器既是资源容器,也是隔离的命名空间。因此,进程的迁移既是资源主体的变更,也是命名空间的变更。就LXC 而言,资源管理是通过Cgroups 实现的,进程可以在cgroup 之间有条件地迁移;命名空间的隔离是通过Namespaces 机制实现的,目前内核只支持进程变更有限的几类命名空间。LXC 在处理进程在容器间迁移时,使用setns 系统调用切换进程的命名空间,但是setns 系统调用目前只支持三类命名空间的切换,而LXC 需要所有的命名空间都切换。因此,在现有内核上,使用LXC 的进程迁移功能会失败。

d)         不支持checkpoint

checkpoint 技术可以将容器中所有的进程暂时Frozen,并将容器完整的状态存储到磁盘上。目前LXC 还不支持checkpoint 技术,虽然利用Cgroups Freezer子系统,可以将容器中的进程暂时Frozen,也可以将Frozen 的进程恢复进行,但是并没有存储容器完整的状态,效果类似将容器中的进程暂停和恢复。完整的实现checkpoint,还需要在进程Frozen 时,将容器中所有进程的状态存下来,这个可以通过扫描/proc 文件系统,记录相应的状态信息来实现。

e)         不支持容器的动态迁移

动态迁移是指将正在运行的容器从一个机器迁移到另一个机器。动态迁移不需要
停止容器中的进程,可以用来维护在线系统,减少服务器宕机的可能。另外,动
态迁移可以通过将容器从负载较高的服务器迁移到负载较低的服务器来优化服
务器集群的资源配置。LXC 目前并不支持动态迁移。

2         Linux Container具体介绍

Linux container在内核层面由两个独立的机制保证,一个保证资源的隔离性,名为namespace;一个进行资源的控制,名为cgroup.

容器虚拟化技术是非常快速和高效的。它的原理基于操作系统内核对不同的运行进程提供了不同的系统视图。这种隔离可以用于确保在保证安全和效率的情况下授权访问硬件资源,如CPUIO带宽。

2.1       namespace

Linux Namespaces机制提供一种资源隔离方案。PID,IPC,Network等系统资源不再是全局性的,而是属于某个特定的Namespace。每个namespace下的资源对于其他namespace下的资源都是透明,不可见的。要创建新的Namespace,只需要在调用clone时指定相应的flagLinux Namespaces机制为实现基于容器的虚拟化技术提供了很好的基础,LXCLinux containers)就是利用这一特性实现了资源的隔离。不同container内的进程属于不同的Namespace,彼此透明,互不干扰。因此在操作系统层面上看,就会出现多个相同pid的进程。系统中可以同时存在两个进程号为0,1,2的进程,由于属于不同的namespace,所以它们之间并不冲突。而在用户层面上只能看到属于用户自己namespace下的资源,例如使用ps命令只能列出自己namespace下的进程。这样每个namespace看上去就像一个单独的Linux系统。

http://s13/mw690/002RQMzZzy703dJNbbu5c&690Container概述" TITLE="Linux Container概述" />

Linux现有的namespace5种: uts, pid, ipc, mnt, net 。所有的namespace都和task_struct相关,其中uts, pid, ipc, mntnet都处于task_struct->ns_proxy中,

2.1.1     uts

最简单的主机名,一个namespace结构绑定这样一个字符串,uname的时候去current->nsproxy->uts_namespace下面取字符串就好了

当调用clone时,设定了CLONE_NEWUTS,就会创建一个新的UTS Namespace。一个UTS Namespace就是一组被uname返回的标识符。新的UTS Namespace中的标识符通过复制调用进程所属的Namespace的标识符来初始化。Clone出来的进程可以通过相关系统调用改变这些标识符,比如调用sethostname来改变该Namespacehostname。这一改变对该Namespace内的所有进程可见。CLONE_NEWUTSCLONE_NEWNET一起使用,可以虚拟出一个有独立主机名和网络空间的环境,就跟网络上一台独立的主机一样。

2.1.2     pid

pid_namespace是每个namespace有一个单独的pid_map作为bitmap进行pid的分配,因此各个pid namespacepid互不干扰,独立分配。当调用clone时,设定了CLONE_NEWPID,就会创建一个新的PID Namespaceclone出来的新进程将成为Namespace里的第一个进程。一个PID Namespace为进程提供了一个独立的PID环境,PID Namespace内的PID将从1开始,在Namespace内调用forkvforkclone都将产生一个在该Namespace内独立的PID。新创建的Namespace里的第一个进程在该Namespace内的PID将为1,就像一个独立的系统里的init进程一样。该Namespace内的孤儿进程都将以该进程为父进程,当该进程被结束时,该Namespace内所有的进程都会被结束。PID Namespace是层次性,新创建的Namespace将会是创建该Namespace的进程属于的Namespace的子Namespace。子Namespace中的进程对于父Namespace是可见的,一个进程将拥有不止一个PID,而是在所在的Namespace以及所有直系祖先Namespace中都将有一个PID。系统启动时,内核将创建一个默认的PID Namespace,该Namespace是所有以后创建的Namespace的祖先,因此系统所有的进程在该Namespace都是可见的。

2.1.3     ipc

当调用clone时,设定了CLONE_NEWIPC,就会创建一个新的IPC Namespaceclone出来的进程将成为Namespace里的第一个进程。一个IPC Namespace有一组System IPC objects 标识符构成,这标识符有IPC相关的系统调用创建。在一个IPC Namespace里面创建的IPC object对该Namespace内的所有进程可见,但是对其他Namespace不可见,这样就使得不同Namespace之间的进程不能直接通信,就像是在不同的系统里一样。当一个IPC Namespace被销毁,该Namespace内的所有IPC object会被内核自动销毁。

PID NamespaceIPC Namespace可以组合起来一起使用,只需在调用clone时,同时指定CLONE_NEWPIDCLONE_NEWIPC,这样新创建的Namespace既是一个独立的PID空间又是一个独立的IPC空间。不同Namespace的进程彼此不可见,也不能互相通信,这样就实现了进程间的隔离。

 

2.1.4     mnt

每个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 Namespace。每个进程都存在于一个mount Namespace里面,mount Namespace为进程提供了一个文件层次视图。如果不设定这个flag,子进程和父进程将共享一个mount Namespace,其后子进程调用mountumount将会影响到所有该Namespace内的进程。如果子进程在一个独立的mount Namespace里面,就可以调用mountumount建立一份新的文件层次视图。该flag配合pivot_root系统调用,可以为进程创建一个独立的目录空间。

2.1.5     net

当调用clone时,设定了CLONE_NEWNET,就会创建一个新的Network Namespace。一个Network Namespace为进程提供了一个完全独立的网络协议栈的视图。包括网络设备接口,IPv4IPv6协议栈,IP路由表,防火墙规则,sockets等等。一个Network Namespace提供了一份独立的网络环境,就跟一个独立的系统一样。一个物理设备只能存在于一个Network Namespace中,可以从一个Namespace移动另一个Namespace中。虚拟网络设备(virtual network device)提供了一种类似管道的抽象,可以在不同的Namespace之间建立隧道。利用虚拟化网络设备,可以建立到其他Namespace中的物理设备的桥接。当一个Network Namespace被销毁时,物理设备会被自动移回init Network Namespace,即系统最开始的Namespace

2.2       cgroup

cgroup是通过vfs的接口来进行配置的,它本身充当一个resource controller的角色,对一组进程进行资源控制,核心角色便是一组task_struct

2.2.1     cgroups可以做什么

Cgroups最初的目标是为资源管理提供的一个统一的框架,既整合现有的cpuset等子系统,也为未来开发新的子系统提供接口。现在的cgroups适用于多种应用场景,从单个进程的资源控制,到实现操作系统层次的虚拟化(OS Level Virtualization)。Cgroups提供了一下功能:

1.限制进程组可以使用的资源数量(Resource limiting )。比如:memory子系统可以为进程组设定一个memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会出发OOMout of memory)。

2.进程组的优先级控制(Prioritization )。比如:可以使用cpu子系统为某个进程组分配特定cpu share

3.记录进程组使用的资源数量(Accounting )。比如:可以使用cpuacct子系统记录某个进程组使用的cpu时间

4.进程组隔离(Isolation)。比如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。

5.进程组控制(Control)。比如:使用freezer子系统可以将进程组挂起和恢复。

2.2.2      cgroups的几个核心概念

cgroup核心的有:

1.任务(task)。在cgroups中,任务就是系统的一个进程。

2.控制族群(control group)。控制族群就是一组按照某种标准划分的进程。Cgroups中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用cgroups以控制族群为单位分配的资源,同时受到cgroups以控制族群为单位设定的限制。

3.层级(hierarchy)。控制族群可以组织成hierarchical的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性。

4.子系统(subsytem)。一个子系统就是一个资源控制器,比如cpu子系统就是控制cpu时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。

整体的逻辑可以这样来看:

a)         每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认 cgroup(我们称之为 root cgroup ,此cgroup在创建层级时自动创建,后面在该层级中创建的cgroup都是此cgroup的后代)的初始成员。

b)        一个hierarchy和多个subsystem绑定,该hierarchy只对这几个subsystem对应的resouce进行control

c)         不同的hierarchy绑定的subsystem是互斥的

d)        一个任务可以是多个cgroup的成员,但是这些cgroup必须在不同的层级。

e)         系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在 cgroup 的成员。然后可根据需要将该子任务移动到不同的 cgroup 中,但开始时它总是继承其父任务的cgroup

2.2.3     cgroup 在内核层次和task_struct的交互

一个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     cgroup 当前已有的subsys

l  SUBSYS(cpuset)sched_setaffinity,是对于一组进程设置CPU亲和性。内核在CPU亲和性逻辑跟以前没什么区别,无非是把这些进程只调度到对应CPUrun queue而已。同时cpuset还提供了一个只在对应cpu间进行负载均衡的特性,就是把对应的cpu作为一个sched domain,可以在其中负载均衡。不过要求最好各个cgoup设置互斥的cpu,否则就会取cgroup的最大互斥并集作为sched domain,这样跨cgroupload banlance又会导致其他的复杂因素。

l  SUBSYS(debug)

l  SUBSYS(cpu_cgroup)CPU的使用率进行控制,根本上利用了CFS中已有的task group概念,最近出来的autogroup也是 利用了这样一个机制。内核中的调度针对的是sched_entity这样一个结构,sched_entity本身有一个run queue,同时它也会在别人的run queue上,以此层层嵌套下来,直至最后的可执行单元(task_struct)。于是一个cgroupcpu resource controller

便是这样的一个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  SUBSYS(cpuacct)这个子系统自动生成 cgroup 中任务所使用的 CPU 报告。

l  SUBSYS(mem_cgroup)memory有全局的per zone lru list来进行page relcaim等,有全局的page数组来进行物理内存管理. memory_cgroupcgroup层面上实现了per_cgroup lru list, 以及page_cgroup数组,memory_cgrouplru操作及其他基于page的操作都是以page_cgroup为依据的,而page_cgroup和真正的page之间又会有个映射关系

mem_cgroup对于内存资源的控制,是对每个cgroup的使用内存情况进行记录,主要方式就是在真正分配物理页面的地方,通过current->cgroups->subsys[xx]找到对应的mem_cgroup控制结构,判断可否分配,可以就加上自己的计数,不行或达到某个limit,就会触发基于per-cgroup lrureclaim, 再或者就触发cgoup内部的OOM killer等。

l  SUBSYS(devices)这个子系统可允许或者拒绝 cgroup 中的任务访问设备

l  SUBSYS(freezer) 这个子系统挂起或者恢复 cgroup 中的任务

l  SUBSYS(net_cls) 主要利用了内核网络协议栈traffic control的相关东西,实现了一个cgroup的统一标记id,然后实现了一个叫cgoupfilter,这个filter就是根据当前进程所在的cgroup_subsys决定给sk_buff打上何样的id标记,然后这个标记就会被用于匹配相应的traffic controlqdiscclass来进行具体的流量控制策略。

l  SUBSYS(blkio)

l  SUBSYS(perf)

l  SUBSYS(net_prio)

l  SUBSYS(hugetlb)

l  SUBSYS(bcache)

 


0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有