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