docker在默认运行容器的情况下,是不会对运行的容器进行资源限制的,在自己的实验环境的话是随便你怎么弄的,不过在生产中是一定会对docker运行的容器进行资源限制的,如果不限制的话在生产中会带来很多弊端的。例如当资源没有做限制时,资源用完了后会导致其他的容器无法运行,在生产中的话是会部署几十个或者几百个容器的,这些容器都是共同使用的宿主机的资源CPU、内存、磁盘等等其他资源,当某一个容器占用宿主机的资源过多时,这时就会导致其他的容器无法正常运行,甚至也会导致一些服务的瘫痪,没有足够的内存运行服务的话,这时就会产生OOM现象,这时就会根据优先机制kill掉宿主机上最高的进程从而来释放空间,只要是宿主机的进程都是会有可能被kill掉的,进程被kill掉的话与之相关的进程的服务就会瘫痪。因此我们都会在创建容器的时候进行必要的资源限制。
1、资源限制的概念
Docker对容器的资源限制类似于我们使用VMware Workstation创建的虚拟机,VMware Workstation创建时是可以对每一台虚拟机指定能使用的最大CPU和内存等,虚拟机在运行的过程中只能使用限定范围内的宿主机资源;Docker也是使用了类似的方法对容器进行资源限制,Docker利用cgroup功能限制每个容器能使用多少宿主机资源(主要是内存和CPU),这些限制条件可以在执行docker run命令时进行配置。
在进行Docker资源限制时,一些功能需要得到宿主机的内核支持,在Docker服务器上执行docker info命令可以查看相关支持,如果内核中禁用了某些功能,会在输出结尾给出警告,比较常见的就是使用Ubuntu主机时会提示“WARNING: No swap limit support”。如果要使用这些功能,就必须在宿主机上取消禁用,例如针对Ubuntu作为Docker宿主机时的不支持swap分区内存限制,我们可以修改/etc/default/grub文件,添加启用功能。重启后再次执行docker info命令已不再显示warning,则说明已经取消了禁用。
2、Docker容器的资源限制
Docker对容器的资源限制主要限制的是内存和CPU
2.1、容器的内存限制
Docker可以强制执行硬性的内存限制,也就是允许容器使用指定的内存大小;也是可以执行非硬性的内存限制,即容器可以使用尽可能多的使用多的内存,除非内核检测到主机上的内存不够用了,一般我们都是使用的硬性内存限制。
在产生了OOM时,Dockerd会尝试调整Docker守护程序上的OOM的优先级来减轻这些风险,以便它比系统上的其他进程更不可能被杀死,但是容器的OOM优先级没有调整的话,这会使单个容器被杀死的可能性比Docker守护进程或其他系统进程被杀死的可能性会更大,不推荐通过在守护进程或容器上手动设置–oom-score-adj为极端负数,或通过在容器上设置–oom-kill-disable来跳过这些安全的措施。
在指定容器内存大小时,大部分的选项取正整数,后面跟上b(字节)、k(千字节)、m(兆字节)和g(千兆字节)作为单位后缀。
OOM 优先级的机制
/proc/PID/oom_score_adj #范围为-1000到1000,值越高越容易被宿主机kill掉,如果将该值设置为-1000,则进程永远不会被宿主机kernel kill。
/proc/PIDJoom_adj #范围为-17到+15,取值越高越容易被干掉,如果是-17,则表示不能被kill,该设置参数的存在是为了和旧版本的Linux内核兼容。
/proc/PID/oom_score #这个值是系统综合进程的内存消耗量、CPU时间(utime+stime)、存活时间(uptime - start time)和oom_adj计算出的进程得分,消耗内存越多得分越高,越容易被宿主机kernel强制杀死。
–oom-score-adj:宿主机内核对进程使用的内存进行评分,评分最高的将被宿主机内核kill掉(取值范围为-1000到1000,越低越不会被kill掉),可以指定一个容器的评分为较低的负数,但会影响内核的正常工作,不推荐手动指定;
–oom-kill-disable:在设置了-m选项时使用,对某个容器关闭oom机制,无论是否出现内存溢出现象,该容器都不会被kill掉。如果不设置-m选项,主机在产生oom时也还是会kill到进程。除了MySQL这一类特别重要的容器,一般也不会指定
2.1.1、内存限制的参数
2.1.1.1、-m或–memory
-m或–memory参数是可以指定容器能使用的最大内存,如果设置了此参数,则运行容器启动时最低内存大小为4M(后期的docker版本最小限制为6M)
2.1.1.2、–memory-swap
--memory-swap可以指定容器能使用的交换分区大小,该参数必须和物理内存限制同时存在才可使用,并且设定值不同也有不同的效果。
2.1.1.3、–memory-swappiness
--memory-swappiness可以设置容器使用交换分区的倾向性,取值范围为0-100,0表示只有在物理内存不足的情况下才会使用交换分区,值越高表示越倾向于使用交换分区,取值为100时表示优先使用交换分区。通常我们不会使用到该参数。
2.1.1.4、–kernel-memory
--kernel-memory可以指定容器可以使用的最大内核内存大小,由于内核内存与用户空间内存是相互隔离的,无法直接与用户空间内存进行交换,因此内核内存不足的容器可能会阻塞宿主机资源,这时就会对主机和其它容易及服务进程产生影响,在生产中不要设置容器的内核内存大小。
2.1.1.5、–memory-reservation
--memory-reservation允许指定小于物理内存值的软限制,当Docker检测到宿主机上的内存不足时会激活该限制。如果使用该参数时,设定值必须低于-m指定的物理内存才能使其优先,并且因为该参数是软限制,因而不能保证容器不超过设置的限制,但容器能使用的最大内存不会超过物理内存设定值。
2.1.2、容器内存限制的验证
下载压力测试的镜像来验证容器的内存限制,并执行docker run lorel/docker-stress-ng -help命令来查看压测镜像的使用帮助,底部还有一个示例。
2.1.2.1、内存大小硬限制
不限制物理内存:
使用刚刚下载的压测镜像来创建一个容器,利用-vm指定2个工作进程,并设置每个工作进程最多允许使用256M的内存,并且宿主机也不限制当前容器的最大内存
限制物理内存:
使用-m或者–memory选项来指定容器的最大使用内存
宿主机验证cgroups:
宿主机对容器的资源限制主要是利用Docker的cgroup功能来实现的,可以在/sys/fs/cgroup/memory/docker/目录下找到对应容器ID,容器ID目录下的memory.limit_in_bytes文件中记录了宿主机对容器的内存资源限制;memory.limit_in_bytes文件中的值是将容器内存转化为字节,所以一般数值会很大;我们是可以使用命令来编辑这个文件的数值,从而修改了物理内存大小的限制
注意: 我们是可以通过文本编辑工具编辑文件里的内存限制值的,但是是在原有的基础上添加内存限制,如果修改值小于原有的内存的话是会报出"write error: Device or resource busy"
2.1.2.2、内存大小软限制
内存的软限制是需要使用到–memory-reservation参数,这个参数设置的作用不是很大,设置了后使用docker stats查看时“MEM USAGE / LIMIT”的值还是在设置的最大内存值附件波动
容器内存设置软限制是,也是可以修改/sys/fs/cgroup/memory/docker/容器ID/memory.soft_limit_in_bytes文件,在这里我们是可以增加或者减少的
2.1.2.3、关闭OOM机制
在docker运行容器时会受到OOM机制的影响的,可以创建一个容器来验证cgroup的功能,会发现默认是开启OOM机制的,这时当宿主机出现OOM时会出现被kill掉的情况,需要关闭OOM机制的话是需要添加–oom-kill-disable参数的。
如果关闭OOM机制的话从而会影响宿主机的正常工作,这个参数基本上是不会去添加的,除非一些特殊的情况就另外看咯
2.1.2.4、交换分区限制
当启动一个容器设置的内存是256m,没有添加–memory-swap参数时,这时验证cgroup时memory.memsw.limit_in_bytes文件的值是默认设置的内存的2倍;当我们希望能使用128m的交换分区,则需要设置–memory-swap参数的值为384m,设置完后再查看memory.memsw.limit_in_bytes文件时,这时就已经是设定的384m。
注意: 这个–memory-swap参数设置的值比设置的的物理内存小的话是会报错的
2.2、容器的CPU限制
在默认的情况下,每个容器对主机CPU周期的访问权限是没有限制的,但是我们是可以设置各种约束来限制给定容器访问宿主机的CPU的周期,大多数的用户使用使用的默认的CFS调度的方式,在Docker 1.13版本以及更高的版本,是还可以配置实时的优先级的。
2.2.1、容器CPU限制的参数
2.2.1.1、–cpus
--cpus参数是在Docker1.13版本及后面的版本才开始使用的,也就是替代了之前版本中的–cpu-period(CPU调度周期)和–cpu-quota(CPU调度限制)参数;使用该参数可以指定容器使用宿主机中的可用CPU资源,假设宿主机中是有4个CPU,启动容器时设置了–cpu=“1.5”,则表示容器最多可以访问宿主机1.5个CPU,并且可以是在4个CPU的每个核心上都使用一点,但总数不能超过1.5个。
2.2.1.2、–cpuset-cpus
--cpuset-cpus参数是用于指定容器运行的CPU编号,也就是我们说的绑核。
2.2.1.3、–cpuset-mem
--cpuset-mem参数是设置使用哪个cpu的内存,这个仅对非统一内存访问(NUMA)的架构有效,这个参数用的少。
2.2.1.4、–cpu-shares
--cpu-shares参数是用于设置cfs中调度的相对最大比例权重,cpu-share的值越高的容器,将会分得更多的时间片(宿主机多核CPU的总数为100%,假设容器A的–cpu-shares设定值为1024,容器B的–cpu-shares设定值为2048,则容器B能获得的宿主机CPU资源最多为容器1的2倍),默认的时间片是1024,最大是262144。
2.2.2、容器CPU限制的验证
2.2.2.1、不限制容器的CPU
这里我的虚拟机cpu核数是2C的,我就直接先不对容器的CPU做限制了,分配2个CPU在加上两个工作进程,启动容器后再验证一下cgroup的一些功能。
2.2.2.2、限制容器的CPU
查看cpu.cfs_quota_us文件时,发现里面的值是100000,这个值是以百分比的形式来呈现的,每个核心CPU是按照1000位单位转换成百分比来进行资源分配的,如果是一个CPU的话就是100000/1000=100%,以此类推就好了。
注意: 给容器分配CPU资源时,要计划好一个容器分多少,是不可以超过宿主机的最大CPU核心数,超过的话是会报错的。
2.2.2.3、容器绑定指定CPU
默认容器是没有与宿主机的CPU核心做绑定的,运行容器时是工作在宿主机上的任意一个CPU核心上;如果想要绑定CPU核心的话是要使用到–cpuset-cpus参数,Linux中是从0开始编号的,之所以1号CPU就是编号0,绑定多个CPU是用逗号隔开,连续的可以使用短横线。
2.2.2.4、基于cpu-share来切分CPU
--cpu-shares这个参数是根据不同容器所占的权重来划分宿主机CPU资源,如果设置的权重越高,容器就越容易获得更多的宿主机CPU资源。这里我就启动两个容器来做测试了一个容器的–cpu-shares值设置为1000,另一个就设置500,这样的话查看CPU利用率的话会明显点。
2.2.2.5、动态修改cpu-share值(修改权重)
这里我就直接动态修改一下test-t2容器的–cpu-shares值了,修改完是立即生效的,这个值是可以动态调大或调小的,因为我这里的宿主机的CPU核心是2C,CPU的%也就是200%了。
这时我又把test-t2容器的–cpu-shares值改回去,验证cgroup数据
<div id="asideoffset"></div>
<div class="clearfix label-list">
<!-- <span>本文包含:</span>-->
<!-- -->
<!-- <a href="" target="_blank"></a>-->
</div>
</div>