docker技术剖析--docker资源限制及应用总结 for centos7.2

http://hongge.blog.51cto.com/php

Docker(linux container)所依赖的底层技术node

1 Namespacelinux

用来作容器的隔离,有了namespace,在docker container里头看来,就是一个完整的linux的世界。在host看来,container里的进程,就是一个普通的host进程,namespace提供这种pid的映射和隔离效果,host承载着container,就比如一个世外桃源。程序员

namespace包括:pid namespace、pid namespace、ipc namespace、mnt namespace、uts namespace、user namespacedocker

例如咱们运行一个容器shell

image

查看容器的进程号centos

image

能够看到该容器的pid是3894,在宿主的/proc目录下存在3894进程的目录安全

经过kill能够结束该容器服务器

image

查看/proc/[pid]/ns文件网络

从3.8版本的内核开始,用户就能够在/proc/[pid]/ns文件下看到指向不一样namespace号的文件,效果以下所示,形如[4026531839]者即为namespace号。

咱们运行一个容器并获取容器的pid

image

获取容器的pid

image

#ls -l /proc/pid/ns <<pid 表示应用容器的PID

image

若是两个进程指向的namespace编号相同,就说明他们在同一个namespace下,不然则在不一样namespace里面。

例如咱们再建立一个容器,网络模式为container (使用 --net=container:NAMEorID 指定)

image

从上面能够看出两个容器的net namespace编号相同,说明他们在同一个net namespace下,共用一个网络。

Docker使用了pid、network、mnt、ipc、uts等命名空间来隔离进程、网络、文件系统等资源。注意,因为Linux并非namespace了全部东西(如cgroups、/sys、SELinux、/dev/sd*、内核模块等),仅靠这几个namespace是没法实现像KVM那样的彻底资源隔离的。

pid namespace:PID namespace隔离很是实用,它对进程PID从新标号,即两个不一样namespace下的进程能够有同一个PID,实现进程隔离,容器只能看到本身的进程,而且每一个容器都有一个pid为1的父进程,kill掉该进程容器内的全部进程都会中止;

net namespace:实现网络隔离,每一个容器均可以设置本身的interface、routers、iptables等;docker默认采用veth的方式将container中的虚拟网卡同host上的一个docker bridge: docker0链接在一块儿;

ipc namespace:container中进程交互仍是采用linux常见的进程间交互方法(interprocess communication - IPC),容器中进程间通讯采用的方法包括常见的信号量、消息队列和共享内存。然而与虚拟机不一样的是,容器内部进程间通讯对宿主机来讲,其实是具备相同PID namespace中的进程间通讯,在同一个IPC namespace下的进程彼此可见,而与其余的IPC namespace下的进程则互相不可见。

mnt namespace:经过隔离文件系统挂载点对隔离文件系统提供支持,不一样mnt namespace中的文件结构发生变化也互不影响。你能够经过/proc/[pid]/mounts查看到全部挂载在当前namespace中的文件系统,还能够经过/proc/[pid]/mountstats看到mount namespace中文件设备的统计信息,包括挂载文件的名字、文件系统类型、挂载位置等等

image

注:43234是容器的进程号

uts namspace:UTS namespace提供了主机名和域名的隔离,这样每一个容器就能够拥有了独立的主机名和域名,在网络上能够被视做一个独立的节点而非宿主机上的一个进程。

user namespace:每一个container能够有不一样的 user 和 group id, 也就是说能够在container内部用container内部的用户执行程序而非Host上的用户。

对于容器所依赖的内核文件系统(这些都是non-namespaced),为了保证安全性,docker将其限制为只读的,例如进入一个容器执行mount命令:

#mount

image

2 Cgroups

在前面了解了Docker背后使用的资源隔离技术namespace,经过系统调用构建一个相对隔离的shell环境,也能够称之为一个简单的“容器”。下面咱们则要开始讲解另外一个强大的内核工具——cgroups。他不只能够限制被namespace隔离起来的资源,还能够为资源设置权重、计算使用量、操控进程启停等等。因此cgroups(Control groups)实现了对资源的配额和度量。

cgroups是什么?

cgroups(Control Groups)最初叫Process Container,由Google工程师(Paul Menage和Rohit Seth)于2006年提出,后来由于Container有多重含义容易引发误解,就在2007年改名为Control Groups,并被整合进Linux内核。顾名思义就是把进程放到一个组里面统一加以控制

groups的做用

通俗的来讲,cgroups能够限制、记录、隔离进程组所使用的物理资源(包括:CPU、memory、IO等),为容器实现虚拟化提供了基本保证,是构建Docker等一系列虚拟化管理工具的基石。Cgroups提供了如下四大功能。

1)资源限制(Resource Limitation):cgroups能够对进程组使用的资源总额进行限制。如设2)定应用运行时使用内存的上限,一旦超过这个配额就发出OOM(Out of Memory)。

优先级分配(Prioritization):经过分配的CPU时间片数量及硬盘IO带宽大小,实际上就至关于控制了进程运行的优先级。

3)资源统计(Accounting): cgroups能够统计系统的资源使用量,如CPU使用时长、内存用量等等,这个功能很是适用于计费。

4)进程控制(Control):cgroups能够对进程组执行挂起、恢复等操做。

下面就介绍cgroup如何作到内存,cpu和io速率的隔离

本文用脚本运行示例进程,来验证Cgroups关于cpu、内存、io这三部分的隔离效果。

测试机器环境

image

启动Cgroups

image

执行mount命令查看cgroup的挂载点

image

从上图能够看到cgroup挂载在/sys/fs/cgroup目录

groups能够限制blkio、cpu、cpuacct、cpuset、devices、freezer、memory、net_cls、ns等系统的资源,如下是主要子系统的说明:

blkio 这个子系统设置限制每一个块设备的输入输出控制。例如:磁盘,光盘以及usb等等。

cpu 这个子系统使用调度程序为cgroup任务提供cpu的访问。

cpuacct 产生cgroup任务的cpu资源报告。

cpuset 若是是多核心的cpu,这个子系统会为cgroup任务分配单独的cpu和内存。

devices 容许或拒绝cgroup任务对设备的访问。

freezer 暂停和恢复cgroup任务。

memory 设置每一个cgroup的内存限制以及产生内存资源报告。

net_cls 标记每一个网络包以供cgroup方便使用,它经过使用等级识别符(classid)标记网络数据包,从而容许 Linux 流量控制程序(TC:Traffic Controller)识别从具体cgroup中生成的数据包。

ns:命名空间子系统

cgroups管理进程cpu资源

咱们先看一个限制cpu资源的例子:

跑一个耗cpu的脚本

运行一个容器,在容器内建立脚本并运行脚本,脚本内容:

image

image

将容器切换到后台运行

在宿主机上top能够看到这个脚本基本占了90%多的cpu资源

image

下面用cgroups控制这个进程的cpu资源

对于centos7来讲,经过systemd-cgls来查看系统cgroups tree:

#systemd-cgls

image

注:4813就是咱们所运行的容器pid

image

image

将cpu.cfs_quota_us设为50000,相对于cpu.cfs_period_us的100000是50%

image

进入容器,再次执行脚本,打开宿主机的另外一个终端执行top命令

而后top的实时统计数据以下,cpu占用率将近50%,看来cgroups关于cpu的控制起了效果

image

CPU资源控制

CPU资源的控制也有两种策略,一种是彻底公平调度 (CFS:Completely Fair Scheduler)策略,提供了限额和按比例分配两种方式进行资源控制;另外一种是实时调度(Real-Time Scheduler)策略,针对实时进程按周期分配固定的运行时间。配置时间都以微秒(μs)为单位,文件名中用us表示。

CFS调度策略下的配置

按权重比例设定CPU的分配

docker提供了–cpu-shares参数,在建立容器时指定容器所使用的CPU份额值。例如:

使用命令docker run -tid –cpu-shares 100 镜像,建立容器,则最终生成的cgroup的cpu份额配置能够下面的文件中找到:

# cat /sys/fs/cgroup/cpu/system.slice/docker-<容器的完整长ID>/cpu.shares

image

image

cpu-shares的值不能保证能够得到1个vcpu或者多少GHz的CPU资源,仅仅只是一个加权值。

该加权值是一个整数(必须大于等于2)表示相对权重,最后除以权重总和算出相对比例,按比例分配CPU时间。

默认状况下,每一个docker容器的cpu份额都是1024。单独一个容器的份额是没有意义的,只有在同时运行多个容器时,容器的cpu加权的效果才能体现出来。例如,两个容器A、B的cpu份额分别为1000和500,在cpu进行时间片分配的时候,容器A比容器B多一倍的机会得到CPU的时间片。若是容器A的进程一直是空闲的,那么容器B是能够获取比容器A更多的CPU时间片的。极端状况下,好比说主机上只运行了一个容器,即便它的cpu份额只有50,它也能够独占整个主机的cpu资源。

cgroups只在容器分配的资源紧缺时,也就是说在须要对容器使用的资源进行限制时,才会生效。所以,没法单纯根据某个容器的cpu份额来肯定有多少cpu资源分配给它,资源分配结果取决于同时运行的其余容器的cpu分配和容器中进程运行状况。

cpu-shares演示案例:

先删除docker主机上运行的容器

image

Docker 经过--cpu-shares 指定CPU份额

运行一个容器指定cpu份额为1024

image

注:

--cpu-shares 指定CPU份额,默认就是1024

--cpuset-cpus 能够绑定CPU。例如,指定容器在--cpuset-cpus 0,1 或--cpuset-cpus 0-3

--cpu是stress命令的选项表示产生n个进程 每一个进程都反复不停的计算随机数的平方根

stress命令是linux下的一个压力测试工具。

在docker宿主机上打开一个terminal执行top

image

而后再启动一个容器, --cpu-shares为512。

image

查看top的现实结果

image

能够看到container1的CPU占比为1024/(1024+512)=2/3,container2的CPU占比为512/(1024+512)=1/3

将container1的cpu.shares改成512,

#echo “512” > /sys/fs/cgroup/cpu/system.slice/docker-<容器的完整长ID>/cpu.shares

image

能够看到两个容器的CPU占比趋于平均

设定CPU使用周期使用时间上限

cgroups 里,能够用 cpu.cfs_period_us 和 cpu.cfs_quota_us 来限制该组中的全部进程在单位时间里可使用的 cpu 时间。cpu.cfs_period_us 就是时间周期,默认为 100000,即百毫秒。cpu.cfs_quota_us 就是在这期间内可以使用的 cpu 时间,默认 -1,即无限制。

cpu.cfs_period_us:设定时间周期(单位为微秒(μs)),必须与cfs_quota_us配合使用。

cpu.cfs_quota_us :设定周期内最多可以使用的时间(单位为微秒(μs))。这里的配置指task对单个cpu的使用上限。

举个例子,若是容器进程须要每1秒使用单个CPU的0.2秒时间,能够将cpu-period设置为1000000(即1秒),cpu-quota设置为200000(0.2秒)。

固然,在多核状况下,若cfs_quota_uscfs_period_us的两倍,就表示在两个核上彻底使用CPU,例如若是容许容器进程须要彻底占用两个CPU,则能够将cpu-period设置为100000(即0.1秒),cpu-quota设置为200000(0.2秒)。

使用示例:

使用命令docker run建立容器

image

在宿主机上执行top

image

从上图能够看到基本占了100%的cpu资源

则最终生成的cgroup的cpu周期配置能够下面的目录中找到:

/sys/fs/cgroup/cpu/system.slice/docker-<容器的完整长ID>/

image

修改容器的cpu.cfs_period_us 和 cpu.cfs_quota_us值

image

执行top查看cpu资源

image

从上图能够看到基本占了50%的cpu资源

RT调度策略下的配置 实时调度策略与公平调度策略中的按周期分配时间的方法相似,也是在周期内分配一个固定的运行时间。

cpu.rt_period_us :设定周期时间。

cpu.rt_runtime_us:设定周期中的运行时间。

image

cpuset - CPU绑定

对多核CPU的服务器,docker还能够控制容器运行限定使用哪些cpu内核和内存节点,即便用–cpuset-cpus和–cpuset-mems参数。对具备NUMA拓扑(具备多CPU、多内存节点)的服务器尤为有用,能够对须要高性能计算的容器进行性能最优的配置。若是服务器只有一个内存节点,则–cpuset-mems的配置基本上不会有明显效果

注:

如今的机器上都是有多个CPU和多个内存块的。之前咱们都是将内存块当作是一大块内存,全部CPU到这个共享内存的访问消息是同样的。可是随着处理器的增长,共享内存可能会致使内存访问冲突愈来愈厉害,且若是内存访问达到瓶颈的时候,性能就不能随之增长。NUMA(Non-Uniform Memory Access)就是这样的环境下引入的一个模型。好比一台机器是有2个处理器,有4个内存块。咱们将1个处理器和两个内存块合起来,称为一个NUMA node,这样这个机器就会有两个NUMA node。在物理分布上,NUMA node的处理器和内存块的物理距离更小,所以访问也更快。好比这台机器会分左右两个处理器(cpu1, cpu2),在每一个处理器两边放两个内存块(memory1.1, memory1.2, memory2.1,memory2.2),这样NUMA node1的cpu1访问memory1.1和memory1.2就比访问memory2.1和memory2.2更快。因此使用NUMA的模式若是能尽可能保证本node内的CPU只访问本node内的内存块,那这样的效率就是最高的。

使用示例:

image

表示建立的容器只能用0、一、2这三个内核。最终生成的cgroup的cpu内核配置以下:

image

cpuset.cpus:在这个文件中填写cgroup可以使用的CPU编号,如0-2,16表明 0、一、2和16这4个CPU。

cpuset.mems:与CPU相似,表示cgroup可以使用的memory node,格式同上

经过docker exec <容器ID> taskset -c -p 1(容器内部第一个进程编号通常为1),能够看到容器中进程与CPU内核的绑定关系,能够认为达到了绑定CPU内核的目的。

总结:

CPU配额控制参数的混合使用

当上面这些参数中时,cpu-shares控制只发生在容器竞争同一个内核的时间片时,若是经过cpuset-cpus指定容器A使用内核0,容器B只是用内核1,在主机上只有这两个容器使用对应内核的状况,它们各自占用所有的内核资源,cpu-shares没有明显效果。

cpu-period、cpu-quota这两个参数通常联合使用,在单核状况或者经过cpuset-cpus强制容器使用一个cpu内核的状况下,即便cpu-quota超过cpu-period,也不会使容器使用更多的CPU资源。

cpuset-cpus、cpuset-mems只在多核、多内存节点上的服务器上有效,而且必须与实际的物理配置匹配,不然也没法达到资源控制的目的。

在系统具备多个CPU内核的状况下,须要经过cpuset-cpus为容器CPU内核才能比较方便地进行测试。

内存配额控制

和CPU控制同样,docker也提供了若干参数来控制容器的内存使用配额,能够控制容器的swap大小、可用内存大小等各类内存方面的控制。主要有如下参数:

Docker提供参数-m, --memory=""限制容器的内存使用量,若是不设置-m,则默认容器内存是不设限的,容器可使用主机上的全部空闲内存

内存配额控制使用示例

设置容器的内存上限,参考命令以下所示

#docker run -dit --memory 128m 镜像

默认状况下,除了–memory指定的内存大小之外,docker还为容器分配了一样大小的swap分区,也就是说,上面的命令建立出的容器实际上最多可使用256MB内存,而不是128MB内存。若是须要自定义swap分区大小,则能够经过联合使用–memory–swap参数来实现控制。

image

能够发现,使用256MB进行压力测试时,因为超过了内存上限(128MB内存+128MB swap),进程被OOM(out of memory)杀死。

使用250MB进行压力测试时,进程能够正常运行。

image

经过docker stats能够查看到容器的内存已经满负载了。

#docker stats test2

image

对上面的命令建立的容器,能够查看到在cgroups的配置文件中,查看到容器的内存大小为128MB (128×1024×1024=134217728B),内存和swap加起来大小为256MB (256×1024×1024=268435456B)。

#cat /sys/fs/cgroup/memory/system.slice/docker-<容器的完整ID>/memory.limit_in_bytes
134217728

#cat /sys/fs/cgroup/memory/system.slice/docker-<容器的完整ID>/memory.memsw.limit_in_bytes
268435456

image

磁盘IO配额控制

主要包括如下参数:

--device-read-bps:限制此设备上的读速度(bytes per second),单位能够是kb、mb或者gb。--device-read-iops:经过每秒读IO次数来限制指定设备的读速度。

--device-write-bps :限制此设备上的写速度(bytes per second),单位能够是kb、mb或者gb。

--device-write-iops:经过每秒写IO次数来限制指定设备的写速度。

--blkio-weight:容器默认磁盘IO的加权值,有效值范围为10-1000。

--blkio-weight-device: 针对特定设备的IO加权控制。其格式为DEVICE_NAME:WEIGHT

磁盘IO配额控制示例

blkio-weight

使用下面的命令建立两个–blkio-weight值不一样的容器:

在容器中同时执行下面的dd命令,进行测试

image

image

注:oflag=direct规避掉文件系统的cache,把写请求直接封装成io指令发到硬盘

3 Chroot

如何在container里头,看到的文件系统,就是一个完整的linux系统,有/etc、/lib 等,经过chroot实现

4 Veth

container里,执行ifconfig能够看到eth0的网卡,如何通讯呢?实际上是在host上虚拟了一张网卡出来(veth73f7),跟container里的网卡作了桥接,全部从container出来的流量都要过host的虚拟网卡,进container的流量也是如此。

5 Union FS

对于这种叠加的文件系统,有一个很好的实现是AUFS,这个能够作到以文件为粒度的copy-on-write,为海量的container的瞬间启动。

6 Iptables, netfilter

主要用来作ip数据包的过滤,好比能够作container之间没法通讯,container能够没法访问host的网络,可是能够经过host的网卡访问外网等这样的网络策略

2、学习Docker也有一段时间了,了解了Docker的基本实现原理,也知道了Docker的使用方法,这里对Docker的一些典型应用场景作一个总结

一、配置简化
这是Docker的主要使用场景。将应用的全部配置工做写入Dockerfile中,建立好镜像,之后就能够无限次使用这个镜像进行应用部署了。这大大简化了应用的部署,不须要为每次部署都进行繁琐的配置工做,实现了一次打包,屡次部署。这大大加快了应用的开发效率,使得程序员能够快速搭建起开发测试环境,不用关注繁琐的配置工做,而是将全部精力都尽量用到开发工做中去。

二、代码流水线管理
代码从开发环境到测试环境再到生产环境,须要通过不少次中间环节,Docker给应用提供了一个从开发到上线均一致的环境,开发测试人员均只需关注应用的代码,使得代码的流水线变得很是简单,这样应用才能持续集成和发布。

三、快速部署
在虚拟机以前,引入新的硬件资源须要消耗几天的时间。Docker的虚拟化技术将这个时间降到了几分钟,Docker只是建立一个容器进程而无需启动操做系统,这个过程只须要秒级的时间。

四、应用隔离
资源隔离对于提供共享hosting服务的公司是个强需求。 若是使用VM,虽然隔离性很是完全,但部署密度相对较低,会形成成本增长。

Docker容器充分利用linux内核的namespace提供资源隔离功能。结合cgroups,能够方便的设置每一个容器的资源配额。既能知足资源隔离的需求,又能方便的为不一样级别的用户设置不一样级别的配额限制。

五、服务器资源整合
正如经过VM来整合多个应用,Docker隔离应用的能力使得Docker一样能够整合服务器资源。因为没有额外的操做系统的内存占用,以及能在多个实例之间共享没有使用的内存,Docker能够比VM提供更好的服务器整合解决方案。

一般数据中心的资源利用率只有30%,经过使用Docker并进行有效的资源分配能够提升资源的利用率。

六、多版本混合部署
随着产品的不断更新换代,一台服务器上部署多个应用或者同一个应用的多个版本在企业内部很是常见。但一台服务器上部署同一个软件的多个版本,文件路径、端口等资源每每会发生冲突,形成多个版本没法共存的问题。

若是用docker,这个问题将很是简单。因为每一个容器都有本身独立的文件系统,因此根本不存在文件路径冲突的问题; 对于端口冲突问题,只须要在启动容器时指定不一样的端口映射便可解决问题。

七、版本升级回滚
一次升级,每每不只仅是应用软件自己的升级,经过还会包含依赖项的升级。 但新旧软件的依赖项极可能是不一样的,甚至是有冲突的,因此在传统的环境下作回滚通常比较困难。

若是使用docker,咱们只须要每次应用软件升级时制做一个新的docker镜像,升级时先停掉旧的容器, 而后把新的容器启动。 须要回滚时,把新的容器停掉,旧的启动便可完成回滚,整个过程各在秒级完成,很是方便。

八、内部开发环境
在容器技术出现以前,公司每每是经过为每一个开发人员提供一台或者多台虚拟机来充当开发测试环境。开发测试环境通常负载较低,大量的系统资源都被浪费在虚拟机自己的进程上了。

Docker容器没有任何CPU和内存上的额外开销,很适合用来提供公司内部的开发测试环境。并且因为Docker镜像能够很方便的在公司内部共享,这对开发环境的规范性也有极大的帮助。

九、PaaS
使用Docker搭建大规模集群,提供PaaS。这一应用是最有前景的一个了,目前已有不少创业公司在使用Docker作PaaS了,例如云雀云平台。用户只需提交代码,全部运维工做均由服务公司来作。并且对用户来讲,整个应用部署上线是一键式的,很是方便。

十、云桌面
在每个容器内部运行一个图形化桌面,用户经过RDP或者VNC协议链接到容器。该方案所提供的虚拟桌面相比于传统的基于硬件虚拟化的桌面方案更轻量级,运行速率大大提高。不过该方案仍处于实验阶段,不知是否可行。能够参考一下Docker-desktop方案。

http://hongge.blog.51cto.com/

相关文章
相关标签/搜索