docker在默认运行容器的情况下,是不会对运行的容器进行资源限制的,在自己的实验环境的话是随便你怎么弄的,不过在生产中是一定会对docker运行的容器进行资源限制的,如果不限制的话在生产中会带来很多弊端的。例如当资源没有做限制时,资源用完了后会导致其他的容器无法运行,在生产中的话是会部署几十个或者几百个容器的,这些容器都是共同使用的宿主机的资源CPU、内存、磁盘等等其他资源,当某一个容器占用宿主机的资源过多时,这时就会导致其他的容器无法正常运行,甚至也会导致一些服务的瘫痪,没有足够的内存运行服务的话,这时就会产生OOM现象,这时就会根据优先机制kill掉宿主机上最高的进程从而来释放空间,只要是宿主机的进程都是会有可能被kill掉的,进程被kill掉的话与之相关的进程的服务就会瘫痪。因此我们都会在创建容器的时候进行必要的资源限制。

1、资源限制的概念

  Docker对容器的资源限制类似于我们使用VMware Workstation创建的虚拟机,VMware Workstation创建时是可以对每一台虚拟机指定能使用的最大CPU和内存等,虚拟机在运行的过程中只能使用限定范围内的宿主机资源;Docker也是使用了类似的方法对容器进行资源限制,Docker利用cgroup功能限制每个容器能使用多少宿主机资源(主要是内存和CPU),这些限制条件可以在执行docker run命令时进行配置。
Docker限制容器的资源_ubuntu
  在进行Docker资源限制时,一些功能需要得到宿主机的内核支持,在Docker服务器上执行docker info命令可以查看相关支持,如果内核中禁用了某些功能,会在输出结尾给出警告,比较常见的就是使用Ubuntu主机时会提示“WARNING: No swap limit support”。如果要使用这些功能,就必须在宿主机上取消禁用,例如针对Ubuntu作为Docker宿主机时的不支持swap分区内存限制,我们可以修改/etc/default/grub文件,添加启用功能。重启后再次执行docker info命令已不再显示warning,则说明已经取消了禁用。

root@node1:~# docker info
Client:
 Debug Mode: false

Server:
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 19.03.15
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: ea765aba0d05254012b0b9e595e995c09186427f
runc version: dc9208a3303feef5b3839f4323d9beb36df0a9dd
init version: fec3683
Security Options:
apparmor
seccomp
Profile: default
Kernel Version: 4.15.0-188-generic
Operating System: Ubuntu 18.04.6 LTS
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 3.827GiB
Name: node1.stars.org
ID: K3DP:CU5Z:H6RW:CAC5:5W72:DNGR:4ZXA:3D77:ZKDI:BMY5:HO7G:WFNZ
Docker Root Dir: /var/lib/docker
Debug Mode: false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
Product License: Community Engine

WARNING: No swap limit support
root@node1:~# vim /etc/default/grub
GRUB_DEFAULT=0
GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=0
GRUB_DISTRIBUTOR=</span>lsb_release <span class="token parameter variable">-i</span> <span class="token parameter variable">-s</span> <span class="token operator"><span class="token file-descriptor important">2</span>&gt;</span> /dev/null <span class="token operator">||</span> <span class="token builtin class-name">echo</span> Debian<span class="token variable">
GRUB_CMDLINE_LINUX_DEFAULT=""
GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0 cgroup_enable=memory swapaccount=1"
root@node1:~# update-grub
root@node1:~# reboot
root@node1:~# docker info

    Docker限制容器的资源_压测镜像_02

    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强制杀死。

    Docker限制容器的资源_压测镜像_03

    –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)

    root@node1:~# docker images 
    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
    alpine              latest              9c6f07244728        2 days ago          5.54MB
    ubuntu              18.04               8d5df41c547b        10 days ago         63.1MB
    centos              centos7.9.2009      eeb6ee3f44bd        11 months ago       204MB
    nginx               1.18.0-alpine       684dbf9f01f3        16 months ago       21.9MB
    nginx               1.18.0              c2c45d506085        16 months ago       133MB
    root@node1:~# docker run -it --rm -m 2m ubuntu:18.04 bash
    docker: Error response from daemon: Minimum memory limit allowed is 4MB.
    See 'docker run --help'.
    root@node1:~# docker run -it --rm -m 4m ubuntu:18.04 bash
    root@03cab5616f4f:/# exit
    exit
    

      Docker限制容器的资源_压测镜像_04

      2.1.1.2、–memory-swap

        --memory-swap可以指定容器能使用的交换分区大小,该参数必须和物理内存限制同时存在才可使用,并且设定值不同也有不同的效果。

      #当设置值大于物理内存时
      [如果想要容器内可以使用交换分区,则设定值要大于物理内存大小,容器中的swap交换分区可用大小为交换分区设定值减去物理内存设定值(所得值大于0),例如容器物理内存限制为512m,要想使用100m的交换分区,则启动容器时交换分区的设定值为612m。]
      root@node1:~# docker run -it --rm --memory-swap 30m ubuntu:18.04
      docker: Error response from daemon: You should always set the Memory limit when using Memoryswap limit, see usage.
      See 'docker run --help'.
      root@node1:~# docker run -it --rm -m 512m --memory-swap 612m ubuntu:18.04
      root@c9ceb920eae6:/# exit
      exit
      

      #当设置值为0或等于物理内存时
      [当值为0时,这时就可以忽略这个参数设置的,也就是不使用交换分区]
      root@node1:~# docker run -it --rm -m 512m --memory-swap 0 ubuntu:18.04
      root@bdc0b5f7d4d9:/# exit
      exit
      root@node1:~# docker run -it --rm -m 512m --memory-swap 512m ubuntu:18.04
      root@aa0aeb40125a:/# exit
      exit

      #当设置值为unset时
      [当宿主机开启了swap功能,并且--memory-swap设定值为unset,即启动容器时未添加--memory-swap参数,则表示容器可使用的交换分区大小为2倍的物理内存大小;我们将容器的物理内存设定为512m,不添加--memory-swap参数时,容器能使用的交换分区大小为1g]
      root@node1:~# docker run -it --rm -m 512m ubuntu:18.04
      root@31e868d0dd49:/# exit
      exit

      #当设置值为-1时
      [当宿主机开启了swap功能,并且容器--memory-swap设定值为-1,表示容器可以使用宿主机的swap最大空间]
      root@node1:~# docker run -it --rm -m 512m --memory-swap -1 ubuntu:18.04
      root@3687aa4f055c:/# exit
      exit

        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命令来查看压测镜像的使用帮助,底部还有一个示例。
        Docker限制容器的资源_物理内存_05
        Docker限制容器的资源_ubuntu_06

        2.1.2.1、内存大小硬限制

        不限制物理内存:
        使用刚刚下载的压测镜像来创建一个容器,利用-vm指定2个工作进程,并设置每个工作进程最多允许使用256M的内存,并且宿主机也不限制当前容器的最大内存
        Docker限制容器的资源_压测镜像_07
        限制物理内存:
        使用-m或者–memory选项来指定容器的最大使用内存
        Docker限制容器的资源_物理内存_08
        宿主机验证cgroups:
        宿主机对容器的资源限制主要是利用Docker的cgroup功能来实现的,可以在/sys/fs/cgroup/memory/docker/目录下找到对应容器ID,容器ID目录下的memory.limit_in_bytes文件中记录了宿主机对容器的内存资源限制;memory.limit_in_bytes文件中的值是将容器内存转化为字节,所以一般数值会很大;我们是可以使用命令来编辑这个文件的数值,从而修改了物理内存大小的限制
        Docker限制容器的资源_物理内存_09
        注意: 我们是可以通过文本编辑工具编辑文件里的内存限制值的,但是是在原有的基础上添加内存限制,如果修改值小于原有的内存的话是会报出"write error: Device or resource busy"
        Docker限制容器的资源_压测镜像_10

        2.1.2.2、内存大小软限制

          内存的软限制是需要使用到–memory-reservation参数,这个参数设置的作用不是很大,设置了后使用docker stats查看时“MEM USAGE / LIMIT”的值还是在设置的最大内存值附件波动
        Docker限制容器的资源_压测镜像_11
        容器内存设置软限制是,也是可以修改/sys/fs/cgroup/memory/docker/容器ID/memory.soft_limit_in_bytes文件,在这里我们是可以增加或者减少的
        Docker限制容器的资源_压测镜像_12

        2.1.2.3、关闭OOM机制

          在docker运行容器时会受到OOM机制的影响的,可以创建一个容器来验证cgroup的功能,会发现默认是开启OOM机制的,这时当宿主机出现OOM时会出现被kill掉的情况,需要关闭OOM机制的话是需要添加–oom-kill-disable参数的。
        Docker限制容器的资源_docker_13
        Docker限制容器的资源_物理内存_14
        如果关闭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。
        Docker限制容器的资源_docker_15
        Docker限制容器的资源_ubuntu_16
        注意: 这个–memory-swap参数设置的值比设置的的物理内存小的话是会报错的
        Docker限制容器的资源_物理内存_17

        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的一些功能。
        Docker限制容器的资源_物理内存_18
        Docker限制容器的资源_Docker的资源限制_19

        2.2.2.2、限制容器的CPU

        Docker限制容器的资源_Docker的资源限制_20
        Docker限制容器的资源_docker_21
        查看cpu.cfs_quota_us文件时,发现里面的值是100000,这个值是以百分比的形式来呈现的,每个核心CPU是按照1000位单位转换成百分比来进行资源分配的,如果是一个CPU的话就是100000/1000=100%,以此类推就好了。
        注意: 给容器分配CPU资源时,要计划好一个容器分多少,是不可以超过宿主机的最大CPU核心数,超过的话是会报错的。
        Docker限制容器的资源_Docker的资源限制_22

        2.2.2.3、容器绑定指定CPU

          默认容器是没有与宿主机的CPU核心做绑定的,运行容器时是工作在宿主机上的任意一个CPU核心上;如果想要绑定CPU核心的话是要使用到–cpuset-cpus参数,Linux中是从0开始编号的,之所以1号CPU就是编号0,绑定多个CPU是用逗号隔开,连续的可以使用短横线。
        Docker限制容器的资源_ubuntu_23

        2.2.2.4、基于cpu-share来切分CPU

          --cpu-shares这个参数是根据不同容器所占的权重来划分宿主机CPU资源,如果设置的权重越高,容器就越容易获得更多的宿主机CPU资源。这里我就启动两个容器来做测试了一个容器的–cpu-shares值设置为1000,另一个就设置500,这样的话查看CPU利用率的话会明显点。
        Docker限制容器的资源_ubuntu_24
        Docker限制容器的资源_物理内存_25

        2.2.2.5、动态修改cpu-share值(修改权重)

          这里我就直接动态修改一下test-t2容器的–cpu-shares值了,修改完是立即生效的,这个值是可以动态调大或调小的,因为我这里的宿主机的CPU核心是2C,CPU的%也就是200%了。
        Docker限制容器的资源_docker_26
        Docker限制容器的资源_ubuntu_27
        这时我又把test-t2容器的–cpu-shares值改回去,验证cgroup数据
        Docker限制容器的资源_物理内存_28
        Docker限制容器的资源_docker_29