LV001-Cgroups简介
使用不同的 Namespace,可以实现容器中的进程看不到别的容器的资源,但是有一个问题,容器内的进程仍然可以任意地使用主机的 CPU 、内存等资源,如果某一个容器使用的主机资源过多,可能导致主机的资源竞争,进而影响业务。那如果我们想限制一个容器资源的使用(如 CPU、内存等)应该如何做呢?
一、Cgroups
1. 什么是 Cgroups
Cgroups 是 control groups 的缩写,是 Linux 内核提供的一种可以限制(Limit),统计(Account),隔离(Isolate)进程组(process groups)所使用物理资源的机制。最初由 google 工程师提出,后来被整合进 Linux 的内核。因此,Cgroups 为容器实现虚拟化提供了基本保证,是构建 Docker, LXC 等一系列虚拟化管理工具的基石。
在 2006 年,Google 的工程师( Rohit Seth 和 Paul Menage 为主要发起人) 发起了这个项目,起初项目名称并不是 cgroups,而被称为进程容器(process containers)。在 2007 年 cgroups 代码计划合入 Linux 内核,但是当时在 Linux 内核中,容器(container)这个词被广泛使用,并且拥有不同的含义。为了避免命名混乱和歧义,进程容器被重名为 cgroups,并在 2008 年成功合入 Linux 2.6.24 版本中。cgroups 目前已经成为 systemd、Docker、Linux Containers(LXC) 等技术的基础。
这里还有1份官方文档可以参考:Control Group v2 — The Linux Kernel documentation
2. 为什么容器需要 Cgroups
如果没有 Cgroup,所有容器运行在宿主机上时:
容易出现某个容器独占 CPU;
内存泄漏会导致宿主机 OOM;
I/O 密集型容器可能拖慢整个系统。
通过 Cgroup,Docker 能为每个容器设定“上限”,例如:
- CPU 只能用 20%
- 内存最多 512MB
- 最多允许 100 个进程
- 限制磁盘写入速率 10MB/s
这样,每个容器都像被分配了独立的资源配额,互不干扰。
3. 提供了哪些功能
cgroups 主要提供了如下功能。
- 资源限制: 限制资源的使用量,例如我们可以通过限制某个业务的内存上限,从而保护主机其他业务的安全运行。
- 优先级控制:不同的组可以有不同的资源( CPU 、磁盘 IO 等)使用优先级。
- 审计:计算控制组的资源使用情况。
- 控制:控制进程的挂起或恢复。
二、基本概念
1. task
task 就是系统的一个进程,在 Cgroups 中叫 task 而已。
2. 三个核心概念
cgroups 功能的实现依赖于三个核心概念:子系统、控制组、层级树。
- 子系统(subsystem):是一个内核的组件,一个子系统代表一类资源调度控制器。例如内存子系统可以限制内存的使用量,CPU 子系统可以限制 CPU 的使用时间。
- 控制组(cgroup):表示一组进程和一组带有参数的子系统的关联关系。例如,一个进程使用了 CPU 子系统来限制 CPU 的使用时间,则这个进程和 CPU 子系统的关联关系称为控制组。
- 层级树(hierarchy):是由一系列的控制组按照树状结构排列组成的。这种排列方式可以使得控制组拥有父子关系,子控制组默认拥有父控制组的属性,也就是子控制组会继承于父控制组。比如,系统中定义了一个控制组 c1,限制了 CPU 可以使用 1 核,然后另外一个控制组 c2 想实现既限制 CPU 使用 1 核,同时限制内存使用 2G,那么 c2 就可以直接继承 c1,无须重复定义 CPU 限制。
cgroups 的三个核心概念中,子系统是最核心的概念,因为子系统是真正实现某类资源的限制的基础。
3.1 subsystem
subsystem 就是一个资源调度控制器(Resource Controller)。比如 CPU 子系统可以控制 CPU 时间分配,内存子系统可以限制内存使用量。
每个 subsystem 背后都有相应的内核模块来配合它完成对资源的控制。比如对 cpu 资源的限制是通过进程调度模块根据 cpu 子系统的配置来完成的;对内存资源的限制则是内存模块根据 memory 子系统的配置来完成的,而对网络数据包的控制则需要 Traffic Control 子系统来配合完成。cgroups 为每种可以控制的资源定义了一个子系统。
典型的子系统介绍如下:
(1)cpu: 主要限制进程的 cpu 使用率。
(2)cpuacct: 可以统计 cgroups 中的进程的 cpu 使用报告。
(3)cpuset: 可以为 cgroups 中的进程分配单独的 cpu 节点或者内存节点。
(4)memory: 可以限制进程的 memory 使用量。
(5)blkio: 可以限制进程的块设备 io。
(6)devices: 可以控制进程能够访问某些设备。
(7)net_cls: 可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制。
(8)freezer: 可以挂起或者恢复 cgroups 中的进程。
3.2 cgroup
cgroup 是 Cgroups 中的资源控制的单位。cgroup 表示按照某种资源控制标准划分而成的任务组,可以包含一个或多个 subsystem。如果把一个 task 加入到一个 cgroup 中,那么这个 task 使用的资源将受到该 cgroup 中指定的标准限制。
cgroup 有两个版本:cgroup v1 和 cgroup v2。以下是判断系统当前使用的 cgroup 版本的几种方法:
# 1. mount: 如果输出中包含多个以 cgroup 开头的挂载点(如 cpu、memory 等),则为 cgroup v1。如果输出中只有一个 cgroup2 挂载点,则为 cgroup v2。
mount | grep cgroup
# 2. /proc/cmdline, 如果输出包含 systemd.unified_cgroup_hierarchy = 1 或 unified_cgroup_hierarchy = 1,则为 cgroup v2。果没有该参数或值为 0,则为 cgroup v1。
cat /proc/cmdline两个版本对比如下:
| 对比项 | v1 | v2 |
|---|---|---|
| 控制器结构 | 各自独立 | 统一管理 |
| 层级模型 | 控制器可挂载多次 | 单一树结构 |
| 文件接口 | 分散复杂 | 简洁一致 |
| Docker 默认 | 旧版使用 v1 | 新版全面支持 v2 |
3.3 hierarchy
hierarchy 由一系列 cgroup 以一个树状结构排列而成,每个 hierarchy 通过绑定对应的 subsystem 进行资源调度。hierarchy 中的 cgroup 节点可以包含零或多个子节点,子节点继承父节点的属性。整个系统可以有多个 hierarchy。hierarchy 的树状结构在用户态的表现形态为文件系统的层级结构,用户操作 hierarchy 就像创建文件夹,修改文件一样。
就类似这样:
cgroup
├── docker
│ ├── container_1
│ └── container_2
└── system.slice参考资料
(9 封私信) 手撕 Linux Cgroups —— Cgroups 实战分析以及在 Docker 中的应用 - 知乎