谈谈cgroups机制

cgroup是什么

cgroup(control groups,控制组群) 是 Linux 内核提供的一个用来限制和控制进程组的资源(CPU、内存、磁盘io等)的功能。cgroup 大家最熟知得场景应该就是容器了,LXC 和 docker 都用 cgroup 和命名空间来控制和限制资源,当然我们也可以直接用它来限制应用程序。 cgroup 为每种可以控制的资源定义了一个子系统:

  • cpu 子系统,主要用于限制进程的 cpu 使用率,一般和 cpuacct 一起。
  • cpuacct 子系统,主要用于统计 cgroups 中的进程的 cpu 使用报告。
  • cpuset 子系统,可以为 cgroups 中的进程分配单独的 cpu 节点或者内存节点。
  • memory 子系统,可以限制进程的 memory 使用量。
  • blkio 子系统,可以限制进程的块设备 io。
  • devices 子系统,可以控制进程能够访问某些设备。
  • net_cls 子系统,可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制。
  • freezer 子系统,可以挂起或者恢复 cgroups 中的进程。
  • ns 子系统,可以使不同 cgroups 下面的进程使用不同的 namespace。

查询资源目录

cgroup 子系统挂载目录查询:

$ mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)

cgroup 资源一般都挂载在/sys/fs/cgroup下面:

$ ll /sys/fs/cgroup
total 0
drwxr-xr-x 4 root root 0 Jun 18 01:40 blkio
lrwxrwxrwx 1 root root 11 Jun 18 01:40 cpu -> cpu,cpuacct
lrwxrwxrwx 1 root root 11 Jun 18 01:40 cpuacct -> cpu,cpuacct
drwxr-xr-x 5 root root 0 Aug 6 11:21 cpu,cpuacct
drwxr-xr-x 3 root root 0 Jun 18 01:40 cpuset
drwxr-xr-x 4 root root 0 Jun 18 01:40 devices
drwxr-xr-x 3 root root 0 Jun 18 01:40 freezer
drwxr-xr-x 3 root root 0 Jun 18 01:40 hugetlb
drwxr-xr-x 5 root root 0 Aug 6 11:21 memory
lrwxrwxrwx 1 root root 16 Jun 18 01:40 net_cls -> net_cls,net_prio
drwxr-xr-x 3 root root 0 Jun 18 01:40 net_cls,net_prio
lrwxrwxrwx 1 root root 16 Jun 18 01:40 net_prio -> net_cls,net_prio
drwxr-xr-x 3 root root 0 Jun 18 01:40 perf_event
drwxr-xr-x 4 root root 0 Jun 18 01:40 pids
drwxr-xr-x 4 root root 0 Jun 18 01:40 systemd

用法

案例:基于 cgroups 构建受 CPU 和内存限制应用程序

要构建这么一个收限制的应用程序,本质就是将应用程序的进程组加入 cgroup 中。

创建一个 cgroup

由于 cgroups 是通过 VFS(Virtual File System, 虚拟文件系统)的方式把相关的功能暴露给用户,因此我们可以直接通过对文件的操作来实现 cgroups。

比如我们要创建一个限制 CPU 的 cgroups,名字叫 webtest,我们可以直接用 mkdir 命令进行创建:

  • mkdir的创建文件夹的方法

因此,我们创建的时候用mkdir -p直接在 cpu 目录下面创建文件夹即可:

$ mkdir -p /sys/fs/cgroup/cpu/webtest
```

在系统目录下创建文件夹之后, cgroups 程序会某人为一个cgroup,因此会在新生成的文件夹中自动生成相关设置文件,这样要注意的是,cpu 文件夹是指向 cpu,cpuacct:
```bash
$ ll /sys/fs/cgroup/cpu
lrwxrwxrwx 1 root root 11 Jun 21 2019 /sys/fs/cgroup/cpu -> cpu,cpuacct

因此,我们要看到真实目录是要在cpu,cpuacct/目录下:

$ ll /sys/fs/cgroup/cpu,cpuacct/
total 0
-rw-r--r-- 1 root root 0 Jun 21 2019 cgroup.clone_children
--w--w--w- 1 root root 0 Jun 21 2019 cgroup.event_control
-rw-r--r-- 1 root root 0 Jun 21 2019 cgroup.procs
-r--r--r-- 1 root root 0 Jun 21 2019 cgroup.sane_behavior
-r--r--r-- 1 root root 0 Jun 21 2019 cpuacct.stat
-rw-r--r-- 1 root root 0 Jun 21 2019 cpuacct.usage
-r--r--r-- 1 root root 0 Jun 21 2019 cpuacct.usage_percpu
-rw-r--r-- 1 root root 0 Jun 21 2019 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Jun 21 2019 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Jun 21 2019 cpu.rt_period_us
-rw-r--r-- 1 root root 0 Jun 21 2019 cpu.rt_runtime_us
-rw-r--r-- 1 root root 0 Jun 21 2019 cpu.shares
-r--r--r-- 1 root root 0 Jun 21 2019 cpu.stat
-rw-r--r-- 1 root root 0 Jun 21 2019 notify_on_release
-rw-r--r-- 1 root root 0 Jun 21 2019 release_agent
drwxr-xr-x 58 root root 0 Sep 30 22:03 system.slice
-rw-r--r-- 1 root root 0 Jun 21 2019 tasks
drwxr-xr-x 2 root root 0 Jun 23 2019 user.slice
drwxr-xr-x 2 root root 0 Oct 1 00:12 webtest

除了mkdir命令,我们也可以用cgcreate创建。

  • cgcreate的创建文件夹的方法

除了mkdir这种直接创建文件目录的方法外,官方提供cgcreate命令用于创建 cgroups :

$ cgcreate admin:admin -g cpu:website

cgcreate 命令参数:

$ man cgcreate
cgcreate [-h] [-s] [-t <tuid>:<tgid>] [-a <agid>:<auid>] [-f mode] [-d mode] -g <controllers>:<path>
-t <tuid>:<tgid>
定义拥有cgroup的任务文件文件权限用户及用户组,
-a <agid>:<auid>
定义拥有cgroup的其他文件权限用户及用户组,如设置子系统参数和创建subgroups
-d, --dperm mode
文件目录权限
-f, --fperm mode
文件夹权限
-g <controllers>:<path>
子系统和新建文件路径

设置参数

cgroups 下面每个子系统都有很多参数,其中cpu.cfs_quota_us 是控制CPU 运行时间的,其默认值为100000,我们将其改为 3000,即使用 3% 的CPU:

echo 3000 > /sys/fs/cgroup/cpu/webtest/cpu.cfs_quota_us

将需要控制的进程加入 cgroup 中

我们通过sha1sum /dev/zero来跑满单颗 CPU。查看应用 PID :

将应用程序 PID 写入 webtest 目录的 tasks 中,我们这次的应用程序PID是22413:

$ echo "22413" > /sys/fs/cgroup/cpu/webtest/tasks

可以看到,CPU 使用率已经由百分之百降到百分之三以内:

经过一段时间的观察,不过有意思的是,程序偶尔有超过 3%,这也是为什么 cgroup 并不是严格的 CPU 分片。

使用 containerd/cgroups 库在代码中控制 cgroups

containerd 提供了一个 cgroups 库,用于 cgroups 控制,详见:https://github.com/containerd/cgroups
这里简单介绍下其基本用法:

CPU 事件

shares := uint64(100)
control, err := cgroups.New(cgroups.V1, cgroups.StaticPath("/test"), &specs.LinuxResources{
CPU: &specs.CPU{
Shares: &shares,
},
})

参考文献

shikanon wechat
欢迎您扫一扫,订阅我滴↑↑↑的微信公众号!