docker限制容器的cpu内存使用率

| –kernel-memory | 核心内存限制。格式同上,最小为 4M |

[](()memory & memory-swap

    用户内存限制就是对容器能使用的内存和交换分区的大小作出限制。使用时要遵循两条直观的规则:-m,–memory选项的参数最小为 4 M。–memory-swap不是交换分区,而是内存加交换分区的总大小,所以–memory-swap必须比-m,–memory大。在这两条规则下,一般有四种设置方式。

你可能在进行内存限制的实验时发现docker run命令报错:WARNING: Your kernel does not support swap limit capabilities, memory limited without swap.

这是因为宿主机内核的相关功能没有打开。按照下面的设置就行。

step 1:编辑/etc/default/grub文件,将GRUB_CMDLINE_LINUX一行改为GRUB_CMDLINE_LINUX=“cgroup_enable=memory swapaccount=1”

step 2:更新 GRUB,即执行$ sudo update-grub

step 3: 重启系统。

关于内存和缓存的设置分以下4种情况

    1.不设置

如果不设置-m,–memory和–memory-swap,容器默认可以用完宿舍机的所有内存和 swap 分区。不过注意,如果容器占用宿主机的所有内存和 swap 分区超过一段时间后,会被宿主机系统杀死(如果没有设置–00m-kill-disable=true的话)。

    2.设置-m,–memory,不设置–memory-swap

给-m或–memory设置一个不小于 4M 的值,假设为 a,不设置–memory-swap,或将–memory-swap设置为 0。这种情况下,容器能使用的内存大小为 a,能使用的交换分区大小也为 a。因为 Docker 默认容器交换分区的大小和内存相同。

如果在容器中运行一个一直不停申请内存的程序,你会观察到该程序最终能占用的内存大小为 2a。

比如$ docker run -m 1G ubuntu:16.04,该容器能使用的内存大小为 1G,能使用的 swap 分区大小也为 1G。容器内的进程能申请到的总内存大小为 2G。

    3.设置-m,–memory=a,–memory-swap=b,且b > a

给-m设置一个参数 a,给–memory-swap设置一个参数 b。a 时容器能使用的内存大小,b是容器能使用的 内存大小 + swap 分区大小。所以 b 必须大于 a。b -a 即为容器能使用的 swap 分区大小。

比如$ docker run -m 1G --memory-swap 3G ubuntu:16.04,该容器能使用的内存大小为 1G,能使用的 swap 分区大小为 2G。容器内的进程能申请到的总内存大小为 3G。

    4.设置-m,–memory=a,–memory-swap=-1

给-m参数设置一个正常值,而给–memory-swap设置成 -1。这种情况表示限制容器能使用的内存大小为 a,而不限制容器能使用的 swap 分区大小。

这时候,容器内进程能申请到的内存大小为 a + 宿主机的 swap 大小。

[](()Memory reservation

    这种 memory reservation 机制不知道怎么翻译比较形象。Memory reservation 是一种软性限制,用于节制容器内存使用。给–memory-reservation设置一个比-m小的值后,虽然容器最多可以使用-m使用的内存大小,但在宿主机内存资源紧张时,在系统的下次内存回收时,系统会回收容器的部分内存页,强迫容器的内存占用回到--memory-reservation设置的值大小。 没有设置时(默认情况下)--memory-reservation的值和-m的限定的值相同。将它设置为 0 会设置的比-m的参数大 等同于没有设置。 Memory reservation 是一种软性机制,它不保证任何时刻容器使用的内存不会超过–memory-reservation限定的值,它只是确保容器不会长时间占用超过–memory-reservation限制的内存大小。

例如:

$ docker run -it -m 500M --memory-reservation 200M ubuntu:16.04 /bin/bash

    如果容器使用了大于 200M 但小于 500M 内存时,下次系统的内存回收会尝试将容器的内存锁紧到 200M 以下。

例如:

$ docker run -it --memory-reservation 1G ubuntu:16.04 /bin/bash

    容器可以使用尽可能多的内存。–memory-reservation确保容器不会长时间占用太多内存。

[](()OOM killer

    默认情况下,在出现 out-of-memory(OOM) 错误时,系统会杀死容器内的进程来获取更多空闲内存。这个杀死进程来节省内存的进程,我们姑且叫它 OOM killer。我们可以通过设置–oom-kill-disable选项来禁止 OOM killer 杀死容器内进程。但请确保只有在使用了-m/–memory选项时才使用–oom-kill-disable禁用 OOM killer。如果没有设置-m选项,却禁用了 OOM-killer,可能会造成出现 out-of-memory 错误时,系统通过杀死宿主机进程或获取更改内存。 下面的例子限制了容器的内存为 100M 并禁止了 OOM killer:

$ docker run -it -m 100M --oom-kill-disable ubuntu:16.04 /bin/bash

是正确的使用方法。

    而下面这个容器没设置内存限制,却禁用了 OOM killer 是非常危险的:

$ docker run -it --oom-kill-disable ubuntu:16.04 /bin/bash

    容器没用内存限制,可能或导致系统无内存可用,并尝试时杀死系统进程来获取更多可用内存。 一般一个容器只有一个进程,这个唯一进程被杀死,容器也就被杀死了。我们可以通过–oom-score-adj选项来设置在系统内存不够时,容器被杀死的优先级。负值更教不可能被杀死,而正值更有可能被杀死。

[](()核心内存

    核心内存和用户内存不同的地方在于核心内存不能被交换出。不能交换出去的特性使得容器可以通过消耗太多内存来堵塞一些系统服务。核心内存包括: stack pages(栈页面) slab pages socket memory pressure tcp memory pressure 可以通过设置核心内存限制来约束这些内存。例如,每个进程都要消耗一些栈页面,通过限制核心内存,可以在核心内存使用过多时阻止新进程被创建。 核心内存和用户内存并不是独立的,必须在用户内存限制的上下文中限制核心内存。 假设用户内存的限制值为 U,核心内存的限制值为 K。有三种可能地限制核心内存的方式: U != 0,不限制核心内存。这是默认的标准设置方式 K < U,核心内存时用户内存的子集。这种设置在部署时,每个 cgroup 的内存总量被过度使用。过度使用核心内存限制是绝不推荐的,因为系统还是会用完不能回收的内存。在这种情况下,你可以设置 K,这样 groups 的总数就不会超过总内存了。然后,根据系统服务的质量自有地设置 U。 K > U,因为核心内存的变化也会导致用户计数器的变化,容器核心内存和用户内存都会触发回收行为。这种配置可以让管理员以一种统一的视图看待内存。对想跟踪核心内存使用情况的用户也是有用的。 例如:

$ docker run -it -m 500M --kernel-memory 50M ubuntu:16.04 /bin/bash

    容器中的进程最多能使用 500M 内存,在这 500M 中,最多只有 50M 核心内存。

$ docker run -it --kernel-memory 50M ubuntu:16.04 /bin/bash

    没用设置用户内存限制,所以容器中的进程可以使用尽可能多的内存,但是最多能使用 50M 核心内存。

[](()Swappiness

    默认情况下,容器的内核可以交换出一定比例的匿名页。–memory-swappiness就是用来设置这个比例的。–memory-swappiness可以设置为从 0 到 100。0 表示关闭匿名页面交换。100 表示所有的匿名页都可以交换。默认情况下,如果不适用–memory-swappiness,则该值从父进程继承而来。 例如:

$ docker run -it --memory-swappiness=0 ubuntu:16.04 /bin/bash

    将–memory-swappiness设置为 0 可以保持容器的工作集,避免交换代理的性能损失。

[](()cpu限制

====================================================================

[](()查看cpu方法


    最简单的方法就是直接执行命令:lscpu

有一行内容是 CPU(s),后面就是cpu数量了。

root@ccx ~]# lscpu

CPU op-mode(s): 32-bit, 64-bit

Byte Order: Little Endian

CPU(s): 4

On-line CPU(s) list: 0-3

Thread(s) per core: 1

Core(s) per socket: 2

Socket(s): 2

NUMA node(s): 1

Vendor ID: GenuineIntel

CPU family: 6

Model: 60

Model name: Intel® Core™ i5-4210M CPU @ 2.60GHz

Stepping: 3

CPU MHz: 2601.000

BogoMIPS: 5202.00

Virtualization: VT-x

Hypervisor vendor: VMware

Virtualization type: full

L1d cache: 32K

L1i cache: 32K

L2 cache: 256K

L3 cache: 3072K

NUMA node0 CPU(s): 0-3

    默认情况下,每个进程都会随机分配在不同的cpu上,以达到均衡。

[](()查看进程运行在某个cpu上


    既然在说容器使用率,我们就随便创建一个容器,模拟5个后台进程

[root@ccx ~]# docker run -it --rm --name=test hub.c.163.com/library/centos

[root@4f665e5220a7 /]# cat /dev/zero > /dev/null &

[1] 16

[root@4f665e5220a7 /]# cat /dev/zero > /dev/null &

[2] 17

[root@4f665e5220a7 /]# cat /dev/zero > /dev/null &

[3] 18

[root@4f665e5220a7 /]# cat /dev/zero > /dev/null &

[4] 19

[root@4f665e5220a7 /]# cat /dev/zero > /dev/null &

[5] 20

[root@4f665e5220a7 /]#

    回到主机上,看这几个进程

命令:ps aux | grep -v grep | grep cat

是可以看到这5个cat后台进程的

[root@ccx ~]# ps aux | grep -v grep | grep cat

gdm 15950 0.0 0.1 362972 4552 ? Sl 09:23 0:00 /usr/libexec/gsd-print-notifications

root 42016 79.6 0.0 4324 344 pts/0 R 10:49 0:57 cat /dev/zero

root 42018 81.6 0.0 4324 344 pts/0 R 10:49 0:58 cat /dev/zero

root 42019 78.0 0.0 4324 348 pts/0 R 10:49 0:55 cat /dev/zero

root 42021 77.9 0.0 4324 344 pts/0 R 10:49 0:54 cat /dev/zero

root 42022 81.3 0.0 4324 344 pts/0 R 10:49 0:56 cat /dev/zero

[root@ccx ~]#

    查看这5个cat分别运行在几号cpu上

命令:ps mo pid,comm,psrpgrep cat``

前面的PID是进程,后面的PSR就是cpu号数了

[root@ccx ~]# ps mo pid,comm,psr pgrep cat

PID COMMAND PSR

42016 cat -

    1

42018 cat -

    2

42019 cat -

    3

42021 cat -

    3

42022 cat -

    2

[root@ccx ~]#

[](()限制cpu运行号数


[](()说明

    着的限制cpu并不是限制cpu的使用百分比,而是让容器固定运行在某颗cpu上。

限制cpu运行号数参数:--cpuset-cpus=cpu1,cpu2,cpu3 【特殊使用,也可以区间限制的,比如想限制在0-5和11号cpu上,则--cpuset-cpus=0-5,11 】

[](()限制在单颗cpu上

    如:我现在运行一个centos的容器,让其固定运行在1号cpu上,并且在开4个cat的后台程序

[root@ccx ~]# docker run -it --rm --cpuset-cpus=1 hub.c.163.com/library/centos

[root@0e40ae45da0d /]# cat /dev/zero > /dev/null &

[1] 15

[root@0e40ae45da0d /]# cat /dev/zero > /dev/null &

[2] 16

[root@0e40ae45da0d /]# cat /dev/zero > /dev/null &

[3] 17

[root@0e40ae45da0d /]# cat /dev/zero > /dev/null &

[4] 18

[root@0e40ae45da0d /]#

    容器创建完以后重新打开一个终端,查看容器进程是否都限制在1号cpu上了。

测试没问题。

[root@ccx ~]# ps mo pid,comm,psr pgrep cat

PID COMMAND PSR

20745 cat -

    1

20746 cat -

    1

20755 cat -

    1

20756 cat -

    1

[root@ccx ~]#

[](()限制在多颗cpu上

    如:我现在运行一个centos的容器,让其在0和1号cpu上运行,并且在开4个cat的后台程序

[root@ccx ~]# docker run -it --rm --cpuset-cpus=0,1 hub.c.163.com/library/centos

[root@88a8bb41c82f /]# cat /dev/zero > /dev/null &

[1] 14

[root@88a8bb41c82f /]# cat /dev/zero > /dev/null &

[2] 15

[root@88a8bb41c82f /]# cat /dev/zero > /dev/null &

[3] 16

[root@88a8bb41c82f /]# cat /dev/zero > /dev/null &

[4] 17

[root@88a8bb41c82f /]#

    容器创建完以后重新打开一个终端,查看容器进程是否都限制在1号cpu上了。

结果可以看到在0好1号cpu上漂浮,没问题。

[root@ccx ~]# ps mo pid,comm,psr pgrep cat

PID COMMAND PSR

20909 cat -

    1

20910 cat -

    0

20911 cat -

经验分享 程序员 微信小程序 职场和发展