容器资源的限制
默认情况下,容器没有资源限制,可以使用主机内核调度程序允许的尽可能多的给定资源。Docker提供了控制容器可以使用多少内存或CPU的方法,设置docker run命令的运行时配置标志。本节提供有关何时应设置此类限制以及设置这些限制的可能含义的详细信息。 其中许多功能都要求您的内核支持Linux功能。要检查支持,可以使用该 docker info命令。如果内核中禁用了某项功能,您可能会在输出结尾处看到警告,如下所示: WARNING: No swap limit support
了解内存不足的风险
重要的是不要让正在运行的容器占用过多的主机内存。在Linux主机上,如果内核检测到没有足够的内存来执行重要的系统功能,它会抛出一个OOME或者 Out Of Memory Exception,并开始查杀进程以释放内存。任何进程都会被杀死,包括Docker和其他重要的应用程序。如果错误的进程被杀死,这可以有效地降低整个系统。 Docker尝试通过调整Docker守护程序上的OOM优先级来减轻这些风险,以便它比系统上的其他进程更不可能被杀死。容器上的OOM优先级未调整。这使得单个容器被杀死的可能性比Docker守护程序或其他系统进程被杀死的可能性更大。您不应试图通过--oom-score-adj在守护程序或容器上手动设置为极端负数或通过设置容器来绕过这些安全措施--oom-kill-disable。
* docker 通过cgroup 工具来限制容器的资源使用,对于资源最重要的就是cpu 和memory 的使用
cgroup
groups(缩写为控制组)是Linux内核功能,用于限制,计算和隔离进程集合的资源使用情况(CPU,内存,磁盘I / O,网络等)。 Google的工程师(主要是Paul Menage和Rohit Seth)于2006年以“流程容器”的名义开始了这项功能的工作。[1] 在2007年末,命名法改为“控制组”,以避免由Linux内核上下文中“ 容器 ” 一词的多重含义引起的混淆,并且控制组功能在内核版本2.6中合并到Linux内核主线中.24,于2008年1月发布。[2]从那时起,开发人员增加了许多新功能和控制器,例如2014年对kernfs的支持,[3] 防火墙,[4]统一的层次结构。[5]
Cgroups提供: 资源限制 组可以设置为不超过配置的内存限制,其中还包括文件系统缓存[8] [9] 优先级 某些组可能获得更大的CPU利用率[10]或磁盘I / O吞吐量[11] 计费 测量组的资源使用情况,例如,可用于计费目的[12] 控制 冻结进程组,检查点和重新启动[12]
这些具体的资源管理功能称为cgroup子系统,有以下几大子系统实现: blkio:设置限制每个块设备的输入输出控制。例如:磁盘,光盘以及usb等等。 cpu:使用调度程序为cgroup任务提供cpu的访问。 cpuacct:产生cgroup任务的cpu资源报告。 cpuset:如果是多核心的cpu,这个子系统会为cgroup任务分配单独的cpu和内存。 devices:允许或拒绝cgroup任务对设备的访问。 freezer:暂停和恢复cgroup任务。 memory:设置每个cgroup的内存限制以及产生内存资源报告。 net_cls:标记每个网络包以供cgroup方便使用。 ns:命名空间子系统。 perf_event:增加了对每group的监测跟踪的能力,即可以监测属于某个特定的group的所有线程以及运行在特定CPU上的线程。
操作接口
在linux系统中一皆文件,当然对CGroup的接口操作也是通过文件来实现的,我们可以通过mount命令查看其挂载目录:
[root@localhost ~]# mount -t cgroup cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd) cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event) cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids) cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls) cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu) cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset) cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices) cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio) cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb) cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer) cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory) [root@localhost ~]#
以上目录都是可以限制的对象,
[root@localhost ~]# ls /sys/fs/cgroup/memory/docker/ 2bf7818020607cd2cded3006c8bf6e76a4424175ddca76ca9bb372913b43bf24 memory.max_usage_in_bytes aa843c370defc11c303473302caad3367600ad87127bbcd65989ef7cbe4eabad memory.memsw.failcnt cgroup.clone_children memory.memsw.limit_in_bytes cgroup.event_control memory.memsw.max_usage_in_bytes cgroup.procs memory.memsw.usage_in_bytes memory.failcnt memory.move_charge_at_immigrate memory.force_empty memory.numa_stat memory.kmem.failcnt memory.oom_control memory.kmem.limit_in_bytes memory.pressure_level memory.kmem.max_usage_in_bytes memory.soft_limit_in_bytes memory.kmem.slabinfo memory.stat memory.kmem.tcp.failcnt memory.swappiness memory.kmem.tcp.limit_in_bytes memory.usage_in_bytes memory.kmem.tcp.max_usage_in_bytes memory.use_hierarchy memory.kmem.tcp.usage_in_bytes notify_on_release memory.kmem.usage_in_bytes tasks memory.limit_in_bytes [root@localhost ~]#
#标红的为容器ID 可以被限制的对象
测试
[root@localhost ~]# cd /sys/fs/cgroup/cpu [root@localhost cpu]# ls cgroup.clone_children cpuacct.stat cpu.cfs_quota_us cpu.stat system.slice cgroup.event_control cpuacct.usage cpu.rt_period_us docker tasks cgroup.procs cpuacct.usage_percpu cpu.rt_runtime_us notify_on_release user.slice cgroup.sane_behavior cpu.cfs_period_us cpu.shares release_agent [root@localhost cpu]# mkdir limit_cpu_demo 在 /sys/fs/cgroup/cpu 目录下创建文件默认会在文件下生成限制的配置文件 [root@localhost cpu]# ls cgroup.clone_children cpuacct.stat cpu.cfs_quota_us cpu.stat release_agent cgroup.event_control cpuacct.usage cpu.rt_period_us docker system.slice cgroup.procs cpuacct.usage_percpu cpu.rt_runtime_us limit_cpu_demo tasks cgroup.sane_behavior cpu.cfs_period_us cpu.shares notify_on_release user.slice [root@localhost cpu]# cd limit_cpu_demo/ [root@localhost limit_cpu_demo]# ls cgroup.clone_children cpuacct.stat cpu.cfs_period_us cpu.rt_runtime_us notify_on_release cgroup.event_control cpuacct.usage cpu.cfs_quota_us cpu.shares tasks cgroup.procs cpuacct.usage_percpu cpu.rt_period_us cpu.stat [root@localhost limit_cpu_demo]# [root@localhost limit_cpu_demo]# while :; do :; done & #执行一个循环 [1] 17392 #top top - 22:47:15 up 1:21, 1 user, load average: 0.67, 0.21, 0.11 Tasks: 101 total, 2 running, 99 sleeping, 0 stopped, 0 zombie %Cpu(s):100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 1863224 total, 81028 free, 1496044 used, 286152 buff/cache KiB Swap: 4194300 total, 4185844 free, 8456 used. 157824 avail Mem
如上可见,此时该进程使用的CPU1已经是100%,即%Cpu :100.0 us,我们先来查看我们刚才创建的cpu_limit_demo目录下这两个参数默认值是多少:
[root@localhost limit_cpu_demo]# cat cpu.cfs_quota_us
-1
[root@localhost limit_cpu_demo]# cat cpu.cfs_period_us
100000
cpu.cfs_quota_us值为-1代表没有任何限制,cpu.cfs_period_us 则是默认的 100 ms,即100000us,下面将向cpu_limit_demo控制组的cpu.cfs_quota_us文件写入50ms即50000us,这表示在100ms周期内,cpu最多使用%50,同时将该进程的pid号为17406写入对应的tasks文件,表示对那个进程限制:
[root@localhost limit_cpu_demo]# echo 50000 > cpu.cfs_quota_us [root@localhost limit_cpu_demo]# echo 17406 > tasks
可以看到cpu 使用率为50% 左右,说明限制生效了。同样的道理,在/sys/fs/cgroup下的子系统都可以限制不通的资源使用如Block IO、Memory等。
限制容器对内存的访问
Docker可以强制执行硬内存限制,允许容器使用不超过给定数量的用户或系统内存或软限制,这允许容器使用尽可能多的内存,除非满足某些条件,例如内核检测到主机上的低内存或争用。当单独使用或设置了多个选项时,其中一些选项会产生不同的效果。
大部分的选项取正整数,跟着一个后缀b,k, m,g,,表示字节,千字节,兆字节或千兆字节。
选项 | 描述 |
---|---|
-m OR --memory= |
容器可以使用的最大内存量。如果设置此选项,则允许的最小值为4m (4兆字节)。 |
--memory-swap * |
允许此容器交换到磁盘的内存量。查看--memory-swap 详情。 |
--memory-swappiness |
默认情况下,主机内核可以交换容器使用的一定百分比的匿名页面。您可以设置--memory-swappiness 0到100之间的值,以调整此百分比。查看--memory-swappiness 详情。 |
--memory-reservation |
允许您指定小于软盘限制--memory 时软盘限制,当Docker检测到主机上的争用或内存不足时。如果使用--memory-reservation ,则必须将其设置为低于--memory 优先级。因为它是软限制,所以不能保证容器不超过限制。 |
--kernel-memory |
容器可以使用的最大内核内存量。允许的最小值是4m 。由于内核内存无法换出,因此内核内存不足的容器可能会阻塞主机资源,这会对主机和其他容器产生副作用。查看--kernel-memory 详情。 |
--oom-kill-disable |
默认情况下,如果发生内存不足(OOM)错误,内核会终止容器中的进程。若要更改此行为,请使用该--oom-kill-disable 选项。仅在已设置-m/--memory 选项的容器上禁用OOM杀手。如果-m 未设置标志,则主机可能会耗尽内存,内核可能需要终止主机系统的进程才能释放内存。 |
有关cgroup和内存的更多信息,请参阅内存资源控制器的文档。
--memory-swap
--memory-swap是一个修饰符标志,只有在--memory设置时才有意义。使用swap允许容器在容器耗尽可用的所有RAM时向磁盘写入多余的内存需求。经常将内存交换到磁盘的应用程序会降低性能。
默认--memory 和 --memory-swap 的值相同,并且 swap 的值为(--memory-swap - [--memory]) 的值。
假如 --m 为 300m ,--memory-swap 为 1G ,那么 可用memory 为300m ,swap 为 (1g-300M = 700M ),在实际配置中建议配合使用
如果只设置-m 那么默认 swap 和memory 相同,也不会进行计算,比如 -m 200M 那么 swap 默认200M 加起来 400M
官方说明copy
--memory-swap是一个修饰符标志,只有在--memory设置时才有意义。使用swap允许容器在容器耗尽可用的所有RAM时向磁盘写入多余的内存需求。经常将内存交换到磁盘的应用程序会降低性能。 它的设置会产生复杂的影响: 如果--memory-swap设置为正整数,那么这两个--memory和 --memory-swap必须设置。--memory-swap表示可以使用的内存和交换总量,并--memory控制非交换内存使用的数量。因此,如果--memory="300m"和--memory-swap="1g",容器可以使用300米的内存和700米(1g - 300m)交换。 如果--memory-swap设置为0,则忽略该设置,并将该值视为未设置。 如果--memory-swap设置为与值相同的值--memory,并且--memory设置为正整数,则容器无权访问swap。请参见 阻止容器使用交换。 如果--memory-swap未设置并且--memory已--memory设置,则如果主机容器配置了交换内存,则容器可以使用两倍于设置的交换。例如,如果--memory="300m"和--memory-swap未设置,容器可以使用300米的内存和600米的交换。 如果--memory-swap明确设置为-1,则允许容器使用无限制交换,最多可达主机系统上可用的数量。 在容器内部,工具如free报告主机的可用交换,而不是容器内可用的内容。不要依赖输出free或类似工具来确定是否存在交换。 防止容器使用交换 如果--memory和--memory-swap设置为相同的值,则可以防止容器使用任何交换。这是因为--memory-swap可以使用的组合内存和交换量,而--memory只是可以使用的物理内存量。
[root@localhost ~]# docker run -it --name c2 -m 100M --memory-swap 200M centos:latest
--memory-swappiness
值为0将关闭匿名页面交换。 值100将所有匿名页面设置为可交换。 默认情况下,如果未设置--memory-swappiness,则值将从父进程继承。
[root@localhost ~]# docker run -it --name c2 --memory-swappiness=0 centos:latest [root@e9fac4e4aa4d /]#
--oom-kill-disable
默认情况下,如果发生内存不足(OOM)错误,内核会终止容器中的进程。若要更改此行为,请使用该--oom-kill-disable选项。仅在已设置-m/--memory选项的容器上禁用OOM杀手。如果-m未设置标志,则主机可能会耗尽内存,内核可能需要终止主机系统的进程才能释放内存。
[root@localhost ~]# docker run -it --name c2 -m 200m --memory-swappiness=0 --oom-kill-disable centos:latest [root@2d440f001a3f /]# [root@localhost ~]# cat /sys/fs/cgroup/memory/docker/e9fac4e4aa4d1c5deb73881f612f3aa2f0bbbc42c020ce8e184f6e9601dc5d6b/memory.oom_control oom_kill_disable 0 #功能默认关闭,会kill 掉超过限制的容器 under_oom 0 [root@localhost ~]# cat /sys/fs/cgroup/memory/docker/2d440f001a3f04d69acb66c459d9a565fffc329d5e3ee9822c9480ccf58b476b/memory.oom_control oom_kill_disable 1 #功能开启,防止kill 掉容器 under_oom 0 [root@localhost ~]#
CPU
默认情况下,每个容器对主机CPU周期的访问权限是不受限制的。您可以设置各种约束来限制给定容器访问主机的CPU周期。大多数用户使用和配置 默认CFS调度程序。在Docker 1.13及更高版本中,您还可以配置 实时调度程序。 配置默认CFS调度程序 CFS是用于正常Linux进程的Linux内核CPU调度程序。多个运行时标志允许您配置对容器具有的CPU资源的访问量。使用这些设置时,Docker会修改主机上容器的cgroup的设置。
选项 | 描述 |
---|---|
--cpus=<value> |
指定容器可以使用的可用CPU资源量。例如,如果主机有两个CPU并且您已设置--cpus="1.5" ,则容器最多保证一个半CPU。这相当于设置--cpu-period="100000" 和--cpu-quota="150000" 。可在Docker 1.13及更高版本中使用。 |
--cpu-period=<value> |
指定与之一起使用的CPU CFS调度程序周期 --cpu-quota 。默认为100微秒。大多数用户不会更改默认设置。如果您使用Docker 1.13或更高版本,请--cpus 改用。 |
--cpu-quota=<value> |
在容器上施加CPU CFS配额。--cpu-period 限制前容器限制为每秒的微秒数。这样作为有效上限。如果您使用Docker 1.13或更高版本,请--cpus 改用。 |
--cpuset-cpus |
限制容器可以使用的特定CPU或核心。如果您有多个CPU,则容器可以使用的以逗号分隔的列表或连字符分隔的CPU范围。第一个CPU编号为0.有效值可能是0-3 (使用第一个,第二个,第三个和第四个CPU)或1,3 (使用第二个和第四个CPU)。 |
--cpu-shares |
将此标志设置为大于或小于默认值1024的值,以增加或减少容器的重量,并使其可以访问主机的CPU周期的更大或更小比例。仅在CPU周期受限时才会强制执行此操作。当有足够的CPU周期时,所有容器都会根据需要使用尽可能多的CPU。这样,这是一个软限制。--cpu-shares 不会阻止容器以群集模式进行调度。它为可用的CPU周期优先考虑容器CPU资源。它不保证或保留任何特定的CPU访问权限。 |
测试
--cpus=
如果您有1个CPU,则以下每个命令都会保证容器每秒最多占CPU的50%。
Docker 1.13及更高版本:
docker run -it --cpus=".5" ubuntu /bin/bash
[root@829f06a8eba2 /]# while :; do :; done & [1] 14 [root@829f06a8eba2 /]# top top - 07:49:43 up 6:24, 0 users, load average: 0.36, 0.11, 0.12 Tasks: 3 total, 2 running, 1 sleeping, 0 stopped, 0 zombie %Cpu(s): 95.0 us, 5.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 1863224 total, 128896 free, 1400608 used, 333720 buff/cache KiB Swap: 4194300 total, 4167420 free, 26880 used. 248504 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 14 root 20 0 11820 576 200 R 94.7 0.0 0:21.47 bash 1 root 20 0 11820 1888 1512 S 0.0 0.1 0:00.08 bash 15 root 20 0 56192 2004 1468 R 0.0 0.1 0:00.06 top [root@localhost ~]# docker run -it --name c2 -m 200m --memory-swappiness=0 --oom-kill-disable --cpus=".5" centos:latest [root@12959b971167 /]# [root@12959b971167 /]# while :; do :; done & [1] 14 [root@12959b971167 /]# top top - 07:51:43 up 6:26, 0 users, load average: 0.42, 0.24, 0.17 Tasks: 3 total, 2 running, 1 sleeping, 0 stopped, 0 zombie %Cpu(s): 51.2 us, 0.3 sy, 0.0 ni, 48.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 1863224 total, 127564 free, 1402052 used, 333608 buff/cache KiB Swap: 4194300 total, 4167420 free, 26880 used. 247084 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 14 root 20 0 11820 604 208 R 50.2 0.0 0:07.50 bash 15 root 20 0 56192 2004 1468 R 0.3 0.1 0:00.01 top 1 root 20 0 11820 1928 1540 S 0.0 0.1 0:00.08 bash
--cpuset-cpus
限制容器可以使用的特定CPU或核心。如果您有多个CPU,则容器可以使用的以逗号分隔的列表或连字符分隔的CPU范围。第一个CPU编号为0.有效值可能是0-3(使用第一个,第二个,第三个和第四个CPU)或1,3(使用第二个和第四个CPU)。
[root@localhost ~]# docker run -it --name c2 -m 200m --memory-swappiness=0 --oom-kill-disable --cpus=".5" --cpuset-cpus=0 centos:latest
[root@ad48f90b38fa /]#
配置各个容器
使用启动容器时,可以传递多个标志来控制容器的CPU优先级docker run
。有关ulimit
适当值的信息,请参阅操作系统的文档或命令。
nice 值
Nice值是类UNIX操作系统中表示静态优先级的数值。每个进程都有自己的静态优先级,优先级高的进程得以优先运行。 Nice值的范围是-20~+19,拥有Nice值越大的进程的实际优先级越小(即Nice值为+19的进程优先级最小,为-20的进程优先级最大),默认的Nice值是0。由于Nice值是静态优先级,所以一经设定,就不会再被内核修改,直到被重新设定。Nice值只起干预CPU时间分配的作用,实际中的细节,由动态优先级决定。 “Nice值”这个名称来自英文单词nice,意思为友好。Nice值越高,这个进程越“友好”,就会让给其他进程越多的时间。
选项 | 描述 |
---|---|
--cap-add=sys_nice |
授予容器CAP_SYS_NICE 容量,允许容器提升过程nice 值,设置实时调度策略,设置CPU关联性和其他操作。 |
--cpu-rt-runtime=<value> |
容器可以在Docker守护程序的实时调度程序周期内以实时优先级运行的最大微秒数。你还需要--cap-add=sys_nice 旗帜。 |
--ulimit rtprio=<value> |
容器允许的最大实时优先级。你还需要--cap-add=sys_nice值 。 |
[root@localhost ~]# docker run -it --name c2 -m 200m --memory-swappiness=0 --oom-kill-disable --cpus=".5" --cpuset-cpus=0 centos:latest #没有给nice 值
[root@ae4bdc3fb9f9 /]#
[root@localhost ~]# ps -ef |grep docker root 19782 1 0 03:08 ? 00:00:17 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock root 20085 19782 0 03:09 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 8081 -container-ip 172.17.0.2 -container-port 8081 root 20098 6505 0 03:09 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/2bf7818020607cd2cded3006c8bf6e76a4424175ddca76ca9bb372913b43bf24 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc root 24464 19426 0 04:06 pts/3 00:00:00 docker run -it --name c2 -m 200m --memory-swappiness=0 --oom-kill-disable --cpus=.5 --cpuset-cpus=0 centos:latest root 24490 6505 0 04:06 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/ae4bdc3fb9f941fa7824916ef62ec0d2eacf76d5b53120a52392a9d37a9a2c53 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc root 24564 21769 0 04:06 pts/0 00:00:00 grep --color=auto docker [root@localhost ~]# top -p 24490 top - 04:07:08 up 6:41, 3 users, load average: 0.02, 0.10, 0.17 Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie %Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 1863224 total, 140776 free, 1402892 used, 319556 buff/cache KiB Swap: 4194300 total, 4167164 free, 27136 used. 246256 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 24490 root 20 0 10656 3352 2492 S 0.0 0.2 0:00.01 containerd-shim
--cap-add=sys_nice
root@localhost ~]# docker run -it --name c3 -m 200m --memory-swappiness=0 --oom-kill-disable --cpus=".5" --cpuset-cpus=0 --cap-add=sys_nice centos:latest [root@cb39dcbc9606 /]#