Namespace技术为docker容器做了重要的隔离,但是docker容器每个隔离空间之间怎么保持独立而不互相竞争资源呢?这就是cgroups要做的事情了

Linux Cgroups(control groups)提供了对一组进程及其子进程的资源限制、控制和统计的能力,包括cpu、内存、存储和网络等。

cgroups组件

  • cgroup
  • subsystem
  • hierarchy

cgroup

cgroup是对进程分组管理的一种机制,一个cgroup包含一组进程,并可以在这个cgroup上增加linux subsystem的各种配置参数,将一组进程和一组subsystem的系统参数关联起来。

subsystem

是一组资源控制的模块,包括

  • blkio 设置对块设备输入输出的访问控制
  • cpu 设置cgroup 中进程的cpu被调度的策略
  • cpuacct 可以统计cgroup中进程的cpu占用
  • cpuset 在多核机器上设置cgroup中进程可以使用的cpu和内存
  • devices 控制cgroup中进程对设备的访问
  • freezer 用于挂起和恢复cgroup中的进程
  • memory 用于控制cgroup中进程的内存占用
  • net_cls 用于将cgroup中进程产生的网络包分类,以便linux的tc可以根据分类区分来自某个cgroup的包并做限流和监控
  • ns 使cgroup中的进程在新的namespace中fork新进程时,创建出一个新的cgroup,这个cgroup包含新的namespace中的进程

每个subsystem会关联到定义了相应限制的cgroup上,并对这个cgroup中的进行做相应的限制和控制。这些subsystem是逐步合并到内核中的。

如何看内核当前支持哪些subsystem呢?使用apt-get install cgroup-bin,然后通过lssubsys -a查看

hierarchy

把一组cgroup串成一个树状结构,一个这样的树便是一个hierarchy,通过这种树状结构,cgroups可以形成继承关系。

三个组件的关系

  • 系统在创建了新的hierarchy之后,系统中所有的进程都会加入这个hierarchy的cgroup根节点,这个cgroup根节点是hierarchy默认创建的
  • 一个subsystem只能附加到一个hierarchy上面
  • 一个hierarchy可以附加多个subsystem
  • 一个进程可以作为多个cgroup的成员,但是这些cgroup必须在不同的hierarchy中。
  • 一个进程fork出子进程时,子进程是和父进程在同一个cgroup中的,也可以根据需要将其移动到其他cgroup中。

kernel加载Cgroups

kernel通过虚拟树状文件系统配置cgroups,通过层级的目录虚拟出cgroup树。

1. 首先,要创建并挂载一个hierarchy
mkdir cgroup-test
mount -t cgroup -o none,name=cgroup-test cgroup-test ./cgroup-test # 挂载一个hierarchy
ls ./cgroup-test
  • cgroup.clone_children cpuset的subsystem会读取这个配置文件。如果是1,子cgroup才会继承父cgroup的cpuset的配置
  • cgroup.procs 是树中当前节点cgroup中的进程组id
  • notify_on_release和release_agent 会一起使用
  • tasks 标识该cgroup下面的进程id,如果一个进程id写到tasks文件中,便会将相应的进程加入到这个cgroup中。
2. 在刚创建好的hierarchy 上的cgroup根节点中扩展出的两个子cgroup
cd cgroup-test
mkdir cgroup-1
mkdir cgroup-2
tree

可以看到,在一个cgroup的目录下创建文件夹时,kernel会把文件夹标记为这个cgroup的子cgroup,他们会继承父cgroup的属性

3. 在cgroup中添加和移动进程

一个进程在一个cgroups的hierarchy中,只能在一个cgroup节点上存在,系统的所有进程都会默认在根节点上存在,可以将进程移动到其他cgroup节点。 只需要将进程id写到移动到的cgroup节点的tasks文件中即可。

[cgroup-1] sh -c "echo $$ >> tasks" #将我所在的终端进程移动到cgroup-1中
4. 通过subsystem限制cgroup中进程的资源

在hierarchy中创建cgroup,限制如下进程占用的内存

[memory] stress --vm-bytes 200m --vm-keep -m 1
[memory] # 创建一个cgroup
[memory] mkdir test-limit-memory && cd test-limit-memory
[test-limit-memory] # 设置最大cgroup的最大内存占用为100MB
[test-limit-memory] sh -c "echo "100m" > memory.limit_in_bytes"
[test-limit-memory] sh - c "echo $$ > tasks" # 将当前进程移动到这个cgroup中
[test-limit-memory] stress --vm-bytes 200m --vm-keep -m 1 # 再次运行占用内存200MB的stress进程