S_lion's Studio

docker Cgroup

字数统计: 2.3k阅读时长: 11 min
2021/07/10 Share

什么是Cgroup

容器的资源限制是通过Cgroup实现的,Cgroups 的全称是 Control Group,属于linux内核提供的一个特性,它最主要的作用就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。

此外,Cgroups 还能够对进程进行优先级设置、审计,以及将进程挂起和恢复等操作。

在 Linux 中,Cgroups 给用户暴露出来的操作接口是文件系统,默认linux系统在启动时已经挂载到了 /sys/fs/cgroup 路径下。

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@slions_pc1 ~]# mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,xattr,release_agent=/usr/lib/systemd/systemd-cgroups -agent,name=systemd)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,cpuacct,cpu)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,net_prio,net_cls)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,cpuset)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,devices)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,freezer)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,perf_event)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,hugetlb)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,memory)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,pids)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,blkio)

查看Cgroup

可以看到,在 /sys/fs/cgroup 下面有很多诸如 cpuset、cpu、 memory 这样的子目录,也叫子系统。这些都是我这台机器当前可以被 Cgroups 进行限制的资源种类。

1
2
3
[root@slions_pc1 ~]# ls /sys/fs/cgroup/
blkio cpu cpuacct cpu,cpuacct cpuset devices freezer hugetlb memory net_cls net_cls,net_prio net_prio perf_event pids systemd

而在子系统对应的资源种类下,你就可以看到该类资源具体可以被限制的方法。

Cgroup子系统

对实际资源的分配和管理是由各个Cgroup子系统来完成的。

cpuset子系统

cpuset可以为一组进程分配指定的CPU和内存节点。

主要的接口有:

  • cpuset.cpus: 允许进程使用的CPU列表
  • cpuset.mems: 允许进程使用的内存节点列表

cpu子系统

cpu子系统用于限制进程的CPU占用率。

主要的接口有:

  • cpu.shares:CPU比重分配
  • cpu.cfs_period_us和cpu.cfs_quota_us:CPU带宽限制(微秒)
  • cpu.rt_period_us和cpu.rt.runtime_us:实时进程的CPU带宽限制

cpuacct子系统

cpuacct子系统用来统计各Cgroup的CPU使用情况。

  • cpuacct.stat: 报告这个Cgroup分别在用户态和内核态消耗的CPU时间(USER_HZ)
  • cpuacct.usage: 报告这个Cgroup消耗的总CPU时间
  • cpuacct.usage_percpu: 报告这个Cgroup在各CPU上消耗的CPU时间

memory子系统

memory子系统用来限制Cgroup所使用的内存上限。

  • memory.limit_in_bytes:内存上限
  • memory.memsw.limit_in_bytes:内存+swap上限
  • memory.oom_control:设置为0则当内存使用量超过上限时系统不会杀死进程,而是阻塞进程直到有内存被释放
  • memory.stat: 汇报内存使用情况

blkio子系统

blkio子系统用来限制Cgroup的block I/O带宽。

  • blkio.weight: 设置权重

  • blkio.weight_device:对具体的设备设置权重

  • blkio.throttle.write_bps_device:对具体的设备设置每秒写磁盘的带宽上限

  • blkio.throttle.read_bps_device: 对具体的设备设置每秒读磁盘的带宽上限

device子系统

用来控制Cgroup的进程对那些设备有访问权限。

  • devices.list:只读文件,显示目前允许被访问的设备列表

  • devices.deny:只写文件,允许相应的设备访问权限

  • devices.allow:只写文件,禁止相应的设备访问权限

如何限制资源

介绍了那么多Cgroup的概念,我们下面就拿限制cpu使用率为例,来讲讲如何限制这些资源。

首先本地下载用来设置Cgroup的工具包。

1
2
[root@slions_pc1 ~]# yum install  libcgroup-tools.x86_64 -y

接下来通过cgcreate命令来创建一个自己的控制组slions。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[root@slions_pc1 cpu]# cgcreate -g cpu:slions
[root@slions_pc1 cpu]# ll
总用量 0
-rw-r--r--. 1 root root 0 7月 9 14:54 cgroup.clone_children
--w--w--w-. 1 root root 0 7月 9 14:54 cgroup.event_control
-rw-r--r--. 1 root root 0 7月 9 14:54 cgroup.procs
-r--r--r--. 1 root root 0 7月 9 14:54 cgroup.sane_behavior
-r--r--r--. 1 root root 0 7月 9 14:54 cpuacct.stat
-rw-r--r--. 1 root root 0 7月 9 14:54 cpuacct.usage
-r--r--r--. 1 root root 0 7月 9 14:54 cpuacct.usage_percpu
-rw-r--r--. 1 root root 0 7月 9 14:54 cpu.cfs_period_us
-rw-r--r--. 1 root root 0 7月 9 14:54 cpu.cfs_quota_us
-rw-r--r--. 1 root root 0 7月 9 14:54 cpu.rt_period_us
-rw-r--r--. 1 root root 0 7月 9 14:54 cpu.rt_runtime_us
-rw-r--r--. 1 root root 0 7月 9 14:54 cpu.shares
-r--r--r--. 1 root root 0 7月 9 14:54 cpu.stat
drwxr-xr-x. 3 root root 0 7月 10 12:34 docker
-rw-r--r--. 1 root root 0 7月 9 14:54 notify_on_release
-rw-r--r--. 1 root root 0 7月 9 14:54 release_agent
drwxr-xr-x. 2 root root 0 7月 10 21:50 slions
drwxr-xr-x. 69 root root 0 7月 10 12:04 system.slice
-rw-r--r--. 1 root root 0 7月 9 14:54 tasks
drwxr-xr-x. 2 root root 0 7月 10 11:52 user.slice
[root@slions_pc1 cpu]# ll slions/
总用量 0
-rw-rw-r--. 1 root root 0 7月 10 21:50 cgroup.clone_children
--w--w----. 1 root root 0 7月 10 21:50 cgroup.event_control
-rw-rw-r--. 1 root root 0 7月 10 21:50 cgroup.procs
-r--r--r--. 1 root root 0 7月 10 21:50 cpuacct.stat
-rw-rw-r--. 1 root root 0 7月 10 21:50 cpuacct.usage
-r--r--r--. 1 root root 0 7月 10 21:50 cpuacct.usage_percpu
-rw-rw-r--. 1 root root 0 7月 10 21:50 cpu.cfs_period_us
-rw-rw-r--. 1 root root 0 7月 10 21:50 cpu.cfs_quota_us
-rw-rw-r--. 1 root root 0 7月 10 21:50 cpu.rt_period_us
-rw-rw-r--. 1 root root 0 7月 10 21:50 cpu.rt_runtime_us
-rw-rw-r--. 1 root root 0 7月 10 21:50 cpu.shares
-r--r--r--. 1 root root 0 7月 10 21:50 cpu.stat
-rw-rw-r--. 1 root root 0 7月 10 21:50 notify_on_release
-rw-rw-r--. 1 root root 0 7月 10 21:50 tasks

会发现/sys/fs/cgroup/cpu目录下已经多了一个slions的目录,并且自动创建了一些文件,其中task文件就是记录进程pid的地方,换句话说,只要是在此task中的进程都会被此控制组所限制。

接着写一个死循环后台跑着,获得进程号为44577,执行top看到cpu使用率为100%

1
2
3
4
5
6
7
8
9
10
11
12
[root@slions_pc1 cpu]# while :;do :;done &
[1] 44577
[root@slions_pc1 ~]# top -p 44577
top - 22:00:31 up 10:35, 3 users, load average: 0.78, 0.27, 0.13
Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 50.1 us, 0.0 sy, 0.0 ni, 49.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 3861520 total, 1600076 free, 391708 used, 1869736 buff/cache
KiB Swap: 1044476 total, 1044476 free, 0 used. 3067128 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
44577 root 20 0 116296 1284 132 R 100 0.0 1:33.98 bash

我们把此进程号写入slions/tasks中

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@slions_pc1 cpu]# echo 44577 > slions/tasks
[root@slions_pc1 cpu]# cat slions/tasks
44577
[root@slions_pc1 cpu]# top -p 44577
top - 22:05:02 up 10:39, 3 users, load average: 0.99, 0.69, 0.35
Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 50.2 us, 0.2 sy, 0.0 ni, 49.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 3861520 total, 1598680 free, 393096 used, 1869744 buff/cache
KiB Swap: 1044476 total, 1044476 free, 0 used. 3065736 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
44577 root 20 0 116296 1284 132 R 99.7 0.0 6:04.97 bash

没有变化,这是因为默认没有任何限制。

1
2
3
4
5
[root@slions_pc1 cpu]# cat slions/cpu.cfs_quota_us
-1
[root@slions_pc1 cpu]# cat slions/cpu.cfs_period_us
100000

接下来,我们可以通过修改这些文件的内容来设置限制。

向 slions组里的 cfs_quota_us 文件写入 30 ms(30000 us),它意味着在每 100 ms 的时间里,被该控制组限制的进程只能使用 30 ms 的 CPU 时间,也就是说这个进程只能使用到 30% 的 CPU 带宽。

1
2
3
4
5
6
7
8
9
10
11
[root@slions_pc1 cpu]# echo 30000 > slions/cpu.cfs_quota_us
[root@slions_pc1 cpu]# top -p 44577
top - 22:18:53 up 10:53, 3 users, load average: 0.34, 0.79, 0.67
Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 15.0 us, 0.0 sy, 0.0 ni, 85.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 3861520 total, 1599448 free, 392324 used, 1869748 buff/cache
KiB Swap: 1044476 total, 1044476 free, 0 used. 3066508 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
44577 root 20 0 116296 1284 132 R 30.0 0.0 18:50.61 bash

我们可以再起一个死循环进程,并且放入slions控制组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@slions_pc1 cpu]# while :;do :;done &
[2] 44658
[root@slions_pc1 cpu]# echo 44658 >> slions/tasks
[root@slions_pc1 cpu]# cat slions/tasks
44577
44658
[root@slions_pc1 cpu]#top
top - 22:22:49 up 10:57, 3 users, load average: 0.08, 0.43, 0.55
Tasks: 129 total, 3 running, 126 sleeping, 0 stopped, 0 zombie
%Cpu(s): 15.3 us, 0.2 sy, 0.0 ni, 84.6 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 3861520 total, 1598784 free, 393092 used, 1869644 buff/cache
KiB Swap: 1044476 total, 1044476 free, 0 used. 3065852 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
44577 root 20 0 116296 1284 132 R 15.3 0.0 19:45.59 bash
44658 root 20 0 116296 1464 312 R 15.3 0.0 0:42.83 bash

可以看到,这两个进程各占了15%,加起来正好是我们的cfs_quota_us 限制。

总结

Linux Cgroups 的设计还是比较易用的,简单粗暴地理解呢,它就是一个子系统目录加上一组资源限制文件的组合。而对于 Docker 等 Linux 容器项目来说,它们只需要在每个子系统下面,为每个容器创建一个控制组(即创建一个新目录),然后在启动容器进程之后,把这个进程的 PID 填写到对应控制组的 tasks 文件中就可以了。

CATALOG
  1. 1. 什么是Cgroup
  2. 2. 查看Cgroup
  3. 3. Cgroup子系统
    1. 3.1. cpuset子系统
    2. 3.2. cpu子系统
    3. 3.3. cpuacct子系统
    4. 3.4. memory子系统
    5. 3.5. blkio子系统
    6. 3.6. device子系统
  4. 4. 如何限制资源
  5. 5. 总结