「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战」
Cgroups 是什么?
Cgroups 是 control groups 的缩写,是 Linux 内核提供的一种可以限制、记录、隔离进程组(progress groups)所使用的物理资源(如:cpu,memory,IO 等等)的机制。最初由 google 的工程师提出,后面被整合进 Linux 内核。
Cgroups 可以做什么?
Cgroups 最初的目标是为资源管理提供的一个统一的框架,既整合需要的 cpuset 等子系统,也为未来开发新的子系统提供接口。现在的 cgroups 适用多种应用场景,从单个进程的资源控制,到实现操作系统层次的虚拟化(OS Level Virtualization)。
cgroups 子系统(subsystem)
它的实现原理是通过对各类 Linux subsystem 进行参数设定,然后将进程和这些子系统进行绑定。
Linux subsystem 有以下几种:
- blkio
- cpu
- cpuacct 统计cgroup中进程的CPU占用
- cpuset
- devices
- freezer 用户挂起和恢复从group中的进程
- memeory 控制cgroup中进程的内存占用
- net_cls
- net_prio
- ns
通过安装 cgroup 工具
1 | shell复制代码$ apt-get install cgroup-tools |
cgroups 层级结构(hierarchy)
hierarchy
的功能是把一组 cgroup 组织成一个树状结构,让 Cgroup 可以实现继承
一个 cgroup1 限制了其下的进程(P1、P2、P3)的 CPU 使用频率,如果还想对进程P2进行内存的限制,可以在 cgroup1 下创建 cgroup2,使其继承于 cgroup1,可以限制 CPU 使用率,又可以设定内存的限制而不影响其他进程。
内核使用 cgroups 结构体来表示对某一个或某几个 cgroups 子系统的资源限制,它是通过一棵树的形式进行组织,被成为hierarchy
.
cgroups 与进程
hierarchy、subsystem 与cgroup进程组间的关系
hierarchy 只实现了继承关系,真正的资源限制还是要靠 subsystem
通过将 subsystem 附加到 hierarchy上,
将进程组 加入到 hierarchy下(task中),实现资源限制
通过这张图可以看出:
- 一个 subsystem 只能附加到一个 hierarchy 上面
- 一个 hierarchy 可以附加多个 subsystem
- 一个进程可以作为多个 cgroup 的成员,但是这些 cgroup 必须在不同hierarchy 中。
- 一个进程 fork 出子进程时,子进程是和父进程在同一个 cgroup 中的,也可以根据需要将其移动到其他 cgroup 中。
cgroups 文件系统
cgroups 的底层实现被 Linux 内核的 VFS(Virtual File System)进行了隐藏,给用户态暴露了统一的文件系统 API 借口。我们来体验一下这个文件系统的使用方式:
- 首先,要创建并挂载一个hierarchy(cgroup树)
1 | shell复制代码$ mkdir cgroup-test |
这些文件就是这个hierarchy中cgroup根节点的配置项
cgroup.clone_children 会被 cpuset 的 subsystem 读取,如果是1,子 cgroup 会继承父 cgroup 的 cpuset 的配置。
notify_on_release 和 release_agent 用于管理当最后一个进程退出时执行一些操作
tasks 标识该 cgroup 下面的进程 ID,将 cgroup 的进程成员与这个 hierarchy 关联
2.再创建两个子 hierarchy创建刚刚创建好的hierarchy上cgroup根节点中扩展出的两个子cgroup
1 | shell复制代码$ cd cgroup-test |
可以看到,在一个 cgroup 的目录下创建文件夹时,Kernel 会把文件夹标记为这个 cgroup 的子 cgroup,它们会继承父 cgroup 的属性。
- 向cgroup中添加和移动进程
一个进程在一个Cgroups的hierarchy中,只能在一个cgroup节点上存在,系统的所有进程都会默认在根节点上存在,可以将进程移动到其他cgroup节点,只需要将进程ID写到移动到的cgroup节点的tasks文件中即可。
1 | shell复制代码# cgroup-test |
可以看到当前终端的进程在根 cgroup 下,我们现在把他移动到子 cgroup 下
1 | shell复制代码$ cd cgroup-1 |
可以看到终端进程所属的 cgroup 已将变成了 cgroup-1,再看一下父 cgroup 的tasks,已经没有了终端进程的 ID
1 | shell复制代码$ cd cgroup-test |
- 通过 subsystem 限制 cgroup 中进程的资源。
操作系统默认已为每一个 subsystem 创建了一个默认的 hierarchy,在sys/fs/cgroup/
目录下
1 | shell复制代码$ ls /sys/fs/cgroup |
可以看到内存子系统的 hierarchy 也在其中创建一个子cgroup
1 | shell复制代码$ cd /sys/fs/cgroup/memory |
可以对比运行前后的内存剩余量,大概只减少了100MB
1 | shell复制代码# 运行前 |
说明 cgroup 的限制生效了
docker 中是怎样进行 cgroup 限制的
首先运行一个被限制内存的容器
1 | shell复制代码$ sudo docker pull redis:4 |
查看原来的内存子系统绑定的cgroup,会看到里面多了子cgroup, docker
1 | shell复制代码$ ls /sys/fs/cgroup/memory |
可以看到docker
cgroup里面的d79f22eb11d22c56a90f88e0aeb3cfda7cbe9639e2ab0e8532003a695e375e8d
cgroup 正好是我们
刚才创建的容器 ID,那么看一下里面吧
1 | shell复制代码$ cd /sys/fs/cgroup/memory/docker/d79f22eb11d22c56a90f88e0aeb3cfda7cbe9639e2ab0e8532003a695e375e8d |
总结
讲述了 cgroups 的原理,它是通过三个概念(cgroup、subsystem、hierarchy)进行组织和关联的,可以理解为
3层结构,将进程关联在 cgroup 中,然后把 cgroup 与 hierarchy 关联,subsystem 再与 hierarchy 关联,从而在限制进程资源的基础上达到一定
的复用能力。
讲述了 docker 的具体实现方式,在使用 docker 时,也能从心中了然它时怎么做到对容器使用资源的限制的。
本文转载自: 掘金