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
A 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 rchitecture: x86_64
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(R) Core(TM) 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,psr
pgrep cat``
前面的 PID 是进程,后面的 PSR 就是 cpu 号数了
[root@ccx ~]# ps mo pid,comm,psr pgrep cat
PID COMMAND PSR
42016 cat -
42018 cat -
42019 cat -
42021 cat -
42022 cat -
[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 -
20746 cat -
20755 cat -
20756 cat -
[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 -
20910 cat -
20911 cat -
评论