Linux CGroup记录

文章目录

  • 结构
    • 查看subsystem
    • 创建、管理cgroup
    • subsystem
  • 使用CGroup
    • 使用pids-subsystem限制进程数

将进程分组,对一组进程进行统一的资源监控和限制。逻辑上,CGroup将系统中的进程组织成一颗一颗独立的树结构。树将进程分组,然后使用subsystem对这些组进程资源限制、统计等操作。参考了 Linux Namespace和Cgroup系列文章, Redhat SUBSYSTEMS AND TUNABLE PARAMETERS。本文中的命令仅供参考,详细命令解释见参考。

结构

  • subsystem:一个subsystem就是一个内核模块,他被关联到一颗cgroup树之后,就会在树的每个节点(进程组)上做具体的操作。subsystem经常被称作"resource controller",因为它主要被用来调度或者限制每个进程组的资源。
  • hierarchy:一个hierarchy可以理解为一棵cgroup树,树的每个节点就是一个进程组,每棵树都会与零到多个subsystem关联

查看subsystem

  • /proc/cgroups文件列出了当前系统的subsystem,如
      #subsys_name    hierarchy       num_cgroups     enabled
      cpuset          11              1               1
      cpu             3               64              1
      cpuacct         3               64              1
      blkio           8               64              1
      memory          9               104             1
      devices         5               64              1
      freezer         10              4               1
      net_cls         6               1               1
      perf_event      7               1               1
      net_prio        6               1               1
      hugetlb         4               1               1
      pids            2               68              1
    
    上面第一列含义
    • subsystem名字
    • subsystem关联到的cgroups树id
    • subsystem关联的cgroup树种的进程组个数
    • 是否开启
  • /proc/PID/cgroup文件列出了该进程所属的cgroup信息,如
    14:name=demo:
    13:name=test:
    12:freezer:/user/eric/0
    11:perf_event:/
    10:cpu,cpuacct:/user.slice
    9:rdma:
    8:hugetlb:
    7:memory:/user/eric/0
    6:devices:/user.slice
    5:net_cls,net_prio:/
    4:pids:/user.slice/user-1000.slice/session-10.scope
    3:cpuset:/
    2:blkio:/user.slice
    1:name=systemd:/user.slice/user-1000.slice/session-10.scope
    0::/user.slice/user-1000.slice/session-10.scope
    
    由冒号隔开的三列表示
    • cgroup树的ID, 和/proc/cgroups文件中的ID一一对应。
    • 和cgroup树绑定的所有subsystem,多个subsystem之间用逗号隔开。这里name=systemd表示没有和任何subsystem绑定,只是给他起了个名字叫systemd。
    • 进程在cgroup树中的路径,即进程所属的cgroup,这个路径是相对于挂载点的相对路径。

创建、管理cgroup

下列过程仅描述cgroup相关内容,不涉及subsystem

  • 挂载cgroup树 使用前需挂载cgroup树

    $: mkdir cgroup && cd cgroup
    $: mkdir demo
    # 创建 name=demo 的cgroup树,因为该树不存在,所以会创建新的树,该cgroup会挂载到demo目录
    $: sudo mount -t cgroup -o none,name=demo demo ./demo
    # 系统会生成一些默认文件
    $: ls demo/
    cgroup.clone_children  cgroup.procs  cgroup.sane_behavior  notify_on_release  release_agent  tasks
    

    系统生成的文件含义

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

    # 创建子cgroup1
    cgroup/demo$: sudo mkdir cgroup1
    cgroup/demo$: ls cgroup1
    cgroup.clone_children  cgroup.procs  notify_on_release  tasks
    # 删除目录即可
    cgroup/demo$: sudo rmdir cgroup1/
    

    如果一个cgroup中包含了子cgroup或者含有进程或线程,删除就会失败

  • 添加进程到cgroup

    # 向 cgroup.procs 中写入PID就能够进程添加到cgroup中
    sudo sh -c 'echo PID > cgroup.procs
    
    • 创建的子进程默认与父进程在同一cgroup中
    • 上述方式也可以用来移动进程到另一个cgroup
    • 移动时,进程P要有目标cgroup的权限
    • 移动父进程时,子进程不会受影响
    • 普通账号也可以创建cgroup,但设置或移动进程到cgroup时需要有目标cgroup的权限
  • cgroup.procs 与 tasks
    线程ID只会存在于所在的cgroup的tasks文件中,但若同一进程PID的两个线程属于不同cgroup,那两个cgroup中的cgroup.procs都会包含PID

    • cgroup.procs 包含进程ID
    • tasks 包含线程ID,不常用
  • release_agent 当一个cgroup里没有进程也没有子cgroup时,release_agent将被调用来执行cgroup的清理工作

subsystem

支持如下12种subsystem

  • 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中的总进程数。

使用CGroup

使用pids-subsystem限制进程数

  • 使用/sys/fs/cgroup/pids/:systemd已经将subsystem挂载,直接使用即可
  • 创建子cgroup
    eric@ubuntu:/sys/fs/cgroup/pids$ sudo mkdir test
    eric@ubuntu:sudo chown -R eric:eric test/
    eric@ubuntu:/sys/fs/cgroup/pids$ cd test && ls
    cgroup.clone_children  cgroup.procs  notify_on_release  pids.current  pids.events  pids.max  tasks
    
    其中:
    • pids.max: 最大进程数,只在进程forkclone时验证
    • pids.current: 当前进程数,不一定比pids.max小
  • 限制进程数:向pids.max文件写入最大进程数即可
    eric@ubuntu:/sys/fs/cgroup/pids/test$ echo 1 > pids.max
    eric@ubuntu:/sys/fs/cgroup/pids/test$ cat pids.max
    1
    eric@ubuntu:/sys/fs/cgroup/pids/test$ cat pids.current
    0
    
    这里限制最大进程数为1,那么后续执行的其它进程都会GG =_=
  • 移动进程到子cgroup:(由于更改了test/的owner,因此无法移动由系统创建的进程)移动当前的bash:
    eric@ubuntu:/sys/fs/cgroup/pids/test$ echo $$ 
    2739
    eric@ubuntu:/sys/fs/cgroup/pids/test$ echo $$ > cgroup.procs
    eric@ubuntu:/sys/fs/cgroup/pids/test$ ll
    # 可以看到GG了
    -bash: fork: retry: Resource temporarily unavailable
    ^C-bash: fork: Interrupted system call
    
  • 后代cgroup的限制:后代cgroup依然可以设置pids.max限制,但是后代cgroup中的进程同时也属于父cgroup,因此同样要受到父进程的pids.max限制
    # 另一个终端(位于根cgroup中,可以操作之前创建的test中内容)
    
    eric@ubuntu:/sys/fs/cgroup/pids/test$ cat pids.max
    1
    # 更改当前cgroup最大进程限制
    eric@ubuntu:/sys/fs/cgroup/pids/test$ echo 2 >pids.max
    # 创建子cgroup subtest
    eric@ubuntu:/sys/fs/cgroup/pids/test$ mkdir subtest
    eric@ubuntu:/sys/fs/cgroup/pids/test$ cd subtest/
    eric@ubuntu:/sys/fs/cgroup/pids/test/subtest$ ls
    # 同样的文件
    cgroup.clone_children  cgroup.procs  notify_on_release  pids.current  pids.events  pids.max  tasks
    # 默认无限制
    eric@ubuntu:/sys/fs/cgroup/pids/test/subtest$ cat pids.max
    max
    # 设置为3
    eric@ubuntu:/sys/fs/cgroup/pids/test/subtest$ echo 3 > pids.max
    # 移动当前进程
    eric@ubuntu:/sys/fs/cgroup/pids/test/subtest$ echo $$ > cgroup.procs
    # 依然GG,因为test的进程限制为 2
    eric@ubuntu:/sys/fs/cgroup/pids/test/subtest$ ll
    -bash: fork: retry: Resource temporarily unavailable
    ^C-bash: fork: Interrupted system call
    
  • 出现pids.current > pids.max的情况
    • 设置pids.max时,设置的值比pids.current小
    • 将其他进程加入到当前cgroup时不检测pids.max

更多subsystem的使用见Red Hat官方文档参考。

你可能感兴趣的:(Linux,容器)