CGroup初探

基本概念

namespace:隔离进程组之间的资源
cgroup:对一组进程进行统一的资源监控和限制,进程按组进行管理的机制
cgroup 的构成:

  • subsystem:对应内核模块,用于对进程组进行操作。
  • hierarchy:一棵 cgroup 树,用于进程分组。

其他:

  • 在任意一个 hierarchy 内包含 linux 系统的所有 process。
  • 在同一个 hierarchy 内,process 属于唯一的 process group。但 process 可以存在于不同的 hierarchy 中。
  • hierarchy 会对应若干不同的 subsystem(可以为 0),一个 subsystem 只能关联一个 hierarchy。systemd 没有对应的 subsystem。

process -> process group(cgroup) -> hierarchy
树节点 -> 树

Subsystem

image.png
subsystem name
subsystem 关联 hierarchy id:如果为 0,无绑定/cgroup v2 绑定/未被内核开启
num of cgroups:关联 hierarchy 内进程组个数
enabled:是否开启,通过内核参数 cgroup_disable 调整

详细

cpu (since Linux 2.6.24; CONFIG_CGROUP_SCHED)
用来限制 cgroup 的 CPU 使用率。

cpuacct (since Linux 2.6.24; CONFIG_CGROUP_CPUACCT)
统计 cgroup 的 CPU 的使用率。

cpuset (since Linux 2.6.24; CONFIG_CPUSETS)
绑定 cgroup 到指定 CPUs 和 NUMA 节点。

memory (since Linux 2.6.25; CONFIG_MEMCG)
统计和限制 cgroup 的内存的使用率,包括 process memory, kernel memory, 和 swap。

devices (since Linux 2.6.26; CONFIG_CGROUP_DEVICE)
限制 cgroup 创建(mknod)和访问设备的权限。

freezer (since Linux 2.6.28; CONFIG_CGROUP_FREEZER)
suspend 和 restore 一个 cgroup 中的所有进程。

net_cls (since Linux 2.6.29; CONFIG_CGROUP_NET_CLASSID)
将一个 cgroup 中进程创建的所有网络包加上一个 classid 标记,用于 tc 和 iptables。 只对发出去的网络包生效,对收到的网络包不起作用。

blkio (since Linux 2.6.33; CONFIG_BLK_CGROUP)
限制 cgroup 访问块设备的 IO 速度。

perf_event (since Linux 2.6.39; CONFIG_CGROUP_PERF)
对 cgroup 进行性能监控

net_prio (since Linux 3.3; CONFIG_CGROUP_NET_PRIO)
针对每个网络接口设置 cgroup 的访问优先级。

hugetlb (since Linux 3.5; CONFIG_CGROUP_HUGETLB)
限制 cgroup 的 huge pages 的使用量。

pids (since Linux 4.3; CONFIG_CGROUP_PIDS)
限制一个 cgroup 及其子孙 cgroup 中的总进程数。

使用

1
2
3
4
5
6
7
8
9
10
# 挂载一颗和cpuset subsystem关联的cgroup树到/sys/fs/cgroup/cpuset
mkdir /sys/fs/cgroup/cpuset
mount -t cgroup -o cpuset xxx /sys/fs/cgroup/cpuset

# 不关联任何子系统
mkdir /sys/fs/cgroup/systemd
mount -t cgroup -o none,name=systemd xxx /sys/fs/cgroup/systemd

# 关联所有子系统
mount -t cgroup xxx /sys/fs/cgroup

查看进程对应的 cgroup
proc/[pid]/cgroup
image.png
hierarchy id : subsystems : 进程在 hierarchy 中的相对路径

创建管理 cgroup

cgroup 目录

  • cgroup.clone_children
    这个文件只对 cpuset subsystem 有影响,当该文件的内容为 1 时,新创建的 cgroup 将会继承父 cgroup 的配置,即从父 cgroup 里面拷贝配置文件来初始化新 cgroup,可以参考这里
  • cgroup.procs
    当前 cgroup 中的所有进程 ID,系统不保证 ID 是顺序排列的,且 ID 有可能重复
  • cgroup.sane_behavior
    具体功能不详,可以参考这里这里
  • notify_on_release
    该文件的内容为 1 时,当 cgroup 退出时(不再包含任何进程和子 cgroup),将调用 release_agent 里面配置的命令。新 cgroup 被创建时将默认继承父 cgroup 的这项配置。
  • release_agent
    里面包含了 cgroup 退出(移出)时将会执行的命令,系统调用该命令时会将相应 cgroup 的相对路径当作参数传进去。 注意:这个文件只会存在于 root cgroup 下面,其他 cgroup 里面不会有这个文件。
  • tasks
    当前 cgroup 中的所有线程 ID,系统不保证 ID 是顺序排列的

创建子 cgroup

新建子文件夹

添加进程

  • 在一颗 cgroup 树里面,一个进程必须要属于一个 cgroup。
  • 新创建的子进程将会自动加入父进程所在的 cgroup
  • 从一个 cgroup 移动一个进程到另一个 cgroup 时,只要有目的 cgroup 的写入权限就可以了,系统不会检查源 cgroup 里的权限。
  • 用户只能操作属于自己的进程,不能操作其他用户的进程,root 账号除外

打印当前 shell pid
echo $$

1
sh -c 'echo 1421 > ../cgroup.procs'

将 pid 写入,即是进程加入 cgroup。pid 不可在文件中删除,只可以被转移。因为 在一颗 cgroup 树里面,一个进程必须要属于一个 cgroup

Pid Subsystem

image.png
pids.current: 表示当前 cgroup 及其所有子孙 cgroup 中现有的总的进程数量
pids.max: 当前 cgroup 及其所有子孙 cgroup 中所允许创建的总的最大进程数量
image.png
通过写入 pids.max 限制成功

  • 子孙 cgroup 中的 pids.max 大小不能超过父 cgroup 中的大小
  • 子 cgroup 中的进程数不仅受自己的 pids.max 的限制,还受祖先 cgroup 的限制

pids.current > pids.max 出现的情况:

  • 设置 pids.max 时,将其值设置的已经比 pids.current 小
  • pids.max 只会在当前 cgroup 中的进程 fork、clone 的时候生效,将其他进程加入到当前 cgroup 时,不会检测 pids.max

Memory Subsystem

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cgroup.event_control       #用于eventfd的接口
memory.usage_in_bytes #显示当前已用的内存
memory.limit_in_bytes #设置/显示当前限制的内存额度
memory.failcnt #显示内存使用量达到限制值的次数
memory.max_usage_in_bytes #历史内存最大使用量
memory.soft_limit_in_bytes #设置/显示当前限制的内存软额度
memory.stat #显示当前cgroup的内存使用情况
memory.use_hierarchy #设置/显示是否将子cgroup的内存使用情况统计到当前cgroup里面
memory.force_empty #触发系统立即尽可能的回收当前cgroup中可以回收的内存
memory.pressure_level #设置内存压力的通知事件,配合cgroup.event_control一起使用
memory.swappiness #设置和显示当前的swappiness
memory.move_charge_at_immigrate #设置当进程移动到其他cgroup中时,它所占用的内存是否也随着移动过去
memory.oom_control #设置/显示oom controls相关的配置
memory.numa_stat #显示numa相关的内存

设置了内存限制立即生效 –> 物理内存使用量达到 limit –> memory.failcnt +1 –> 内核会尽量将物理内存中的数据移到 swap 空间上去 –> 设置的 limit 过小,或者 swap 空间不足 –> kill 掉 cgroup 内继续申请内存的进程(默认行为)
详细链接

##

CPU Subsystem

cfs_period_us:时间周期长度
cfs_quota_us:在一个周期长度内所能使用的 CPU 时间数
cpu.shares:

  • 针对所有的 CPU 的相对值,默认值是 1024
  • 仅在 CPU 忙时起作用
  • 无法精确的控制 CPU 使用率,因为 cgroup 会动态变化

limit_in_cores = cfs_period_us / cfs_quota_us

Ref

https://segmentfault.com/a/1190000006917884