S_lion's Studio

docker Cgroup

字数统计: 2.3k阅读时长: 11 min
2021/07/10 34 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 文件中就可以了。

0 comments
Anonymous
Markdown is supported

Be the first person to leave a comment!

CATALOG
  1. 1. 什么是Cgroup
  2. 2. 查看Cgroup
  3. 3. Cgroup子系统
  4. 4. 如何限制资源
  5. 5. 总结