文章目录
前言
默认情况下容器是没有资源限制的,因为它本身就是一个进程,当一个容器占用太多资源的话,会对其他容器产生影响,所以 ,合理应该分配容器资源是作为管理员必须要关注的问题。
Docker通过 Cgroup 来控制容器使用的资源配额,包括 CPU、内存、磁盘三大方面,基本覆盖了常见的资源配额和使用量控制。
一、Cgroup资源配置
Cgroup是Control Groups 的缩写,是Linux内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(如CPU、内存、磁盘IO等等)的机制,被LXC、docker等很多项目用于实现进程资源控制。Cgroup本身是提供将进程进行分组化管理的功能和接口的基础结构,I/O或内存的分配控制等具体的资源管理功能,这些具体的资源管理功能称为Cgroup子系统,有以下几大子系统实现:
●blkio:设置限制每个块设备的输入输出控制;
●cpu:使用调度程序为 cgroup 任务提供 cpu 的访问;
●memory:设置每个 cgroup 的内存限制以及产生内存资源报告;
●cpuacct:产生 cgroup 任务的 cpu 资源报告;
●cpuset:如果是多核心的 cpu,这个子系统会为 cgroup 任务分配单独的 cpu 和内存;
●devices:允许或拒绝 cgroup 任务对设备的访问;
●freezer:暂停和恢复 cgroup 任务;
●net_cls:标记每个网络包以供 cgroup 方便使用;
●ns:命名空间子系统;
●perf_event:增加了对每个 cgroup 的监测跟踪能力,可以监测属于某个特定的 cgroup 的所有线程及运行在特定 CPU 上的线程;
二、使用stress工具测试CPU和内存
使用Dockerfile来创建一个基于Centos的stress工具镜像
[root@docker ~]# mkdir /opt/stress
[root@docker ~]# vim /opt/stress/Dockerfile
FROM centos:7
MAINTAINER cj"cj@163.com"
RUN yum install wget -y
RUN wget -O /etc/yum.repos.d/erel.repo http://mirrors.aliyun.com/repo/epel-7.repo
RUN yum install stress -y
[root@docker ~]# cd /opt/stress/
[root@docker stress]# docker build -t centos:stress .
[root@localhost stress]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos stress 9afb419e126c 8 seconds ago 426MB
centos 7 7e6257c9f8d8 8 weeks ago 203MB
2.1 CPU弹性的加权值 cpu-shares
●默认情况下,每个 Docker 容器的CPU的份额都是1024,单独一个容器的份额是没有意义的。只有在同时运行多个容器时,容器的 CPU 加权的效果才能体现出来。
例如,两个容器 A、B 的CPU份额分别是 1000 和 500 ,在 CPU 进行时间片分配的时候,容器A比容器B多一倍的机会获得 CPU 的时间片。但分配的结果取决于当时主机和其他容器的运行状态,实际上也无法保证容器 A 一定能获得CPU 时间片。比如容器 A 的进程一直是空闲的,那么容器 B 是可以获取比容器 A 更多的 CPU 时间片。极端情况下,例如主机上只运行了一个容器,即使它的 CPU 份额只有 50,它也可以独占整个主机的 CPU 资源。
●Cgroup 只在容器分配的资源紧缺时,即在需要对容器使用的资源进行限制时,才会生效。因此,无法单纯根据某个容器的 CPU 份额来确定有多少 CPU 资源分配给它,资源分配结果取决于同时运行的其他容器的 CPU 分配和容器中进程运行情况。可以通过 cpu share 可以设置容器使用 CPU 的优先级。
●启动两个容器及运行查看CPU使用百分比
[root@docker stress]# docker run -itd --name cpu512 --cpu-shares 512 centos:stress stress -c 10 '容器产生10个子函数进程,CPU份额设为512'
afd15b26b48865745e0bb923619353e16076bbc5793837d6f97109504b4e458b
[root@docker stress]# docker run -itd --name cpu1024 --cpu-shares 1024 centos:stress stress -c 10 '容器产生10个子函数进程,CPU份额设为1024'
c65e798400f4ae83eca5235b83b8d31c507cbdb92663066b2d95fba313bc33ff
[root@docker stress]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c65e798400f4 centos:stress "stress -c 10" 4 seconds ago Up 3 seconds cpu1024
afd15b26b488 centos:stress "stress -c 10" About a minute ago Up About a minute cpu512
查看两个容器的CPU使用率
[root@docker stress]# docker exec -it c65e798400f4 bash
[root@c65e798400f4 /]# top
top - 07:23:39 up 47 min, 0 users, load average: 14.73, 5.03, 1.84
Tasks: 13 total, 11 running, 2 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
KiB Mem : 1863252 total, 153084 free, 971520 used, 738648 buff/cache
KiB Swap: 2097148 total, 2093556 free, 3592 used. 687656 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
13 root 20 0 7312 96 0 R 29.0 0.0 0:13.63 stress
8 root 20 0 7312 96 0 R 28.7 0.0 0:14.66 stress
11 root 20 0 7312 96 0 R 28.7 0.0 0:14.59 stress
12 root 20 0 7312 96 0 R 28.7 0.0 0:13.95 stress
7 root 20 0 7312 96 0 R 27.3 0.0 0:14.51 stress
15 root 20 0 7312 96 0 R 26.0 0.0 0:14.04 stress
6 root 20 0 7312 96 0 R 25.0 0.0 0:14.20 stress
10 root 20 0 7312 96 0 R 24.3 0.0 0:14.71 stress
14 root 20 0 7312 96 0 R 24.3 0.0 0:13.75 stress
9 root 20 0 7312 96 0 R 24.0 0.0 0:14.61 stress
1 root 20 0 7312 628 532 S 0.0 0.0 0:00.02 stress
16 root 20 0 11828 1904 1496 S 0.0 0.1 0:00.01 bash
29 root 20 0 56192 1956 1440 R 0.0 0.1 0:00.00 top
[root@docker ~]# docker exec -it afd15b26b488 bash
[root@afd15b26b488 /]# top
top - 07:23:46 up 47 min, 0 users, load average: 15.16, 5.28, 1.94
Tasks: 13 total, 11 running, 2 sleeping, 0 stopped, 0 zombie
%Cpu(s): 97.5 us, 2.3 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.2 si, 0.0
KiB Mem : 1863252 total, 139132 free, 977692 used, 746428 buff/cache
KiB Swap: 2097148 total, 2093556 free, 3592 used. 681384 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
12 root 20 0 7312 92 0 R 14.7 0.0 0:34.22 stress
15 root 20 0 7312 92 0 R 14.0 0.0 0:34.63 stress
10 root 20 0 7312 92 0 R 13.3 0.0 0:34.68 stress
13 root 20 0 7312 92 0 R 13.3 0.0 0:33.66 stress
6 root 20 0 7312 92 0 R 13.0 0.0 0:33.68 stress
9 root 20 0 7312 92 0 R 12.7 0.0 0:33.33 stress
7 root 20 0 7312 92 0 R 12.3 0.0 0:35.60 stress
8 root 20 0 7312 92 0 R 12.3 0.0 0:33.61 stress
14 root 20 0 7312 92 0 R 12.0 0.0 0:34.19 stress
11 root 20 0 7312 92 0 R 11.3 0.0 0:33.33 stress
1 root 20 0 7312 624 532 S 0.0 0.0 0:00.02 stress
16 root 20 0 11828 1900 1496 S 0.0 0.1 0:00.02 bash
29 root 20 0 56192 1956 1440 R 0.0 0.1 0:00.02 top
通过进入容器,观察两个容器的 CPU百分比,可以发现比例是 1:2
2.2 CPU周期限制
Docker提供了–cpu-period、–cpu-quota两个参数控制容器可以分配到的CPU时钟周期。
–cpu-period :是用来指定容器对 CPU 的使用要在多长时间内做一次重新分配。
–cpu-quota :是用来指定在这个周期内,最多可以有多少时间用来跑这个容器。
与 --cpu-shares 不同的是。这种配置是指定一个绝对值,容器对 CPU 资源的使用绝对不会超过配置的值。
cpu-period 和 cpu-quota 的单位是微秒;
cpu-period 的最小值是1000微秒,最大值为1秒,默认值为0.1秒。
cpu-quota 的值默认是 -1 ,表示不做控制;
cpu-period 和 cpu-quota 参数一般联合使用。
例如:容器进程需要每1秒使用单个CPU的0.2秒时间,可以将cpu-period设置为100000(即1秒),cpu-quota设置为2000(0.2秒)。
当然,在多核情况下,如果允许容器进程完全占用两个CPU,则可以将cpu-period设置为100000(即0.1秒),cpu-quota设置为200000 (0.2秒),
cpu刷新是0.1秒间隔 使用了0.1秒后还有0.1秒就让下一个cpu来处理
[root@docker stress]# docker run -itd --cpu-period 100000 --cpu-quota 200000 centos:stress
86017f50e0ec1ef86748aee0901fd353943f7f403570ae370d689969f56eda81
[root@docker stress]# docker exec -it 86017f50e0ec bash
[root@86017f50e0ec /]# cd /sys/fs/cgroup/cpu/
[root@86017f50e0ec cpu]# cat cpu.cfs_period_us
100000
[root@86017f50e0ec cpu]# cat cpu.cfs_quota_us
200000
2.3 CPU Core控制
对于多核CPU的服务器,Docker 还可以控制容器运行使用哪些CPU内核,即使用 --cpuset-cpus 参数。这对具有多 CPU 的服务器尤其有用,可以对需要高性能计算的容器进行性能最优配置。
[root@docker stress]# docker run -itd --name cpu1 --cpuset-cpus 0-1 centos:stress '创建的容器只能用0、1两个内核'
004b6786282c779a6994f80eef3f8cad396aaa81db8c44c0996e919e56cf4a43
[root@docker stress]# docker exec -it 004b6786282c bash
'最终生成的cgroup的CPU内核配置如下'
[root@004b6786282c /]# cat /sys/fs/cgroup/cpuset/cpuset.cpus
0-1
通过下面指令可以看到容器中进程与cpu内核的绑定关系,达到绑定cpu内核的目的
[root@docker stress]# docker exec 004b6786282c taskset -c -p 1
pid 1's current affinity list: 0,1
通过stress -c压测来验证
[root@docker stress]# docker exec -it 004b6786282c bash
[root@004b6786282c /]# stress -c 10
stress: info: [47] dispatching hogs: 10 cpu, 0 io, 0 vm, 0 hdd
[root@docker ~]# top '可以看到是cpu0、1在运行'
top - 15:35:54 up 1:00, 5 users, load average: 2.22, 1.60, 1.85
Tasks: 257 total, 11 running, 246 sleeping, 0 stopped, 0 zombie
%Cpu0 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 0.0 us, 0.3 sy, 0.0 ni, 98.7 id, 0.0 wa, 0.0 hi, 1.0 si, 0.0 st
%Cpu3 : 0.0 us, 0.3 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 st
KiB Mem : 1863252 total, 137856 free, 857156 used, 868240 buff/cache
KiB Swap: 2097148 total, 2091508 free, 5640 used. 660820 avail Mem
2.4 CPU配额控制参数的混合使用
通过cpuset-cpus参数指定容器A使用CPU内核0,容器B只是用CPU内核1。
在主机上只有这两个容器使用对应CPU内核的情况,它们各自占用全部的内核资源,cpu-shares没有明显效果。
cpuset-cpus、cpuset-mems参数只在多核、多内存节点上的服务器上有效,并且必须与实际的物理配置匹配,否则也无法达到资源控制的目的。
在系统具有多个CPU内核的情况下,需要通过cpuset-cpus 参数为设置容器CPU内核才能方便地进行测试。
[root@docker ~]# docker run -itd --name cpu3 --cpuset-cpus 1 --cpu-shares 512 centos:stress stress -c 1
[root@d67d39b6f4ec /]# top
top - 01:22:10 up 3:29, 0 users, load average: 0.91, 0.33, 0.16
Tasks: 4 total, 2 running, 2 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 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 : 1863252 total, 143136 free, 1013876 used, 706240 buff/cache
KiB Swap: 2097148 total, 2089892 free, 7256 used. 631588 avail Mem
[root@docker ~]# docker run -itd --name cpu4 --cpuset-cpus 3 --cpu-shares 1024 centos:stress stress -c 1
[root@docker ~]# docker exec -it 63b5631bd570 bash
[root@63b5631bd570 /]# top
top - 01:26:04 up 3:32, 0 users, load average: 1.92, 1.09, 0.49
Tasks: 4 total, 2 running, 2 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 :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 : 1863252 total, 102492 free, 1050524 used, 710236 buff/cache
KiB Swap: 2097148 total, 2089724 free, 7424 used. 594588 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
6 root 20 0 7312 92 0 R 100.0 0.0 2:27.96 stress
1 root 20 0 7312 420 344 S 0.0 0.0 0:00.01 stress
7 root 20 0 11828 1896 1496 S 0.0 0.1 0:00.01 bash
21 root 20 0 56184 1948 1440 R 0.0 0.1 0:00.00 top
总结:上面的centos:stress镜像安装了stress工具,用来测试CPU和内存的负载。通过在两个容器上分别执行stress-c1命令,将会给系统一个随机负载,产生1个进程。这个进程都反复不停的计算由rand()产生随机数的平方根,直到资源耗尽。
观察到宿主机上的CPU使用率,第三个内核的使用率接近100%,并且一批进程的CPU使用率明显存在2:1的使用比例的对比。
2.5 内存限额
与操作系统类似,容器可使用的内存包括两部分:物理内存 和 Swap;
docker 通过下面两组参数来控制容器内存的使用量:
-m 或 --memory:设置内存的使用限额,例如 100M、1024M;
–memory-swap:设置内存 +swap 的使用限额。
例如:执行如下命令允许该容器最多使用 200M的内存,300M 的swap:
[root@docker ~]# docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 280M
'--vm 1:启动1个内存工作线程;'
'--vm-bytes 280M :每个线程分配280M内存;'
'-m:设置内存限制为200M'
'-memory-swap:设置内存加swap的使用'
'与CPU的cgroups配置类似,Docker会自动为容器在目录/sys/fs/cgroup/memory/docker/<容器的完整长ID>中创建相应cgroup配置文件'
让工作线程分配的内存超过300M,分配的内存超过限额,stress线程报错,容器退出。
[root@docker ~]# docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 310M
Status: Downloaded newer image for progrium/stress:latest
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogvm worker 1 [6] forked
stress: dbug: [6] allocating 325058560 bytes ...
stress: dbug: [6] touching bytes in strides of 4096 bytes ...
stress: FAIL: [1] (416) <-- worker 6 got signal 9
stress: WARN: [1] (418) now reaping child worker processes
stress: FAIL: [1] (422) kill error: No such process
stress: FAIL: [1] (452) failed run completed in 0s