一文搞懂Linux技术-Cgroup

一、Cgroup 基础概念

1.1 Cgroup 定义与作用

Cgroup(Control Groups)即控制组,是 Linux 内核提供的一种用于限制、记录、隔离进程组所使用资源(如 CPU、内存、磁盘 I/O、网络等)的机制 。它允许系统管理员对进程组进行精细化的资源管理,将系统资源按需求分配给不同的任务或服务,有效避免资源竞争,提升系统稳定性和资源利用率。在容器技术中,Cgroup 是实现资源限制的关键技术之一,确保容器在共享物理资源的情况下,不会因资源过度占用影响其他容器或宿主机的正常运行。

1.2 Cgroup 核心组件

  1. Hierarchy(层级结构):Cgroup 以树状层级结构组织,每个子节点是父节点的一个分支,每个节点代表一个控制组。系统中的所有进程都属于某个 Cgroup,且一个进程只能属于一个 Cgroup。不同层级的 Cgroup 可以对资源进行分级管理,上层 Cgroup 可以对下层 Cgroup 的资源使用情况进行限制和统计。

  1. Subsystem(子系统):Cgroup 包含多个子系统,每个子系统负责管理一种特定类型的资源。常见的子系统有cpu(CPU 资源管理)、memory(内存资源管理)、blkio(块设备 I/O 资源管理)、net_cls(网络流量分类)等 。一个子系统可以附加到多个 Hierarchy 上,每个 Hierarchy 也可以附加多个子系统,通过这种灵活的组合方式,实现对进程组复杂的资源管理策略。

  1. Task(任务):在 Cgroup 中,Task 指的是系统中的进程,每个进程都可以被添加到某个 Cgroup 中,从而受到该 Cgroup 资源限制策略的约束。

二、Linux 中如何使用 Cgroup 限制资源隔离

2.1 环境准备

在开始操作前,确保系统已启用 Cgroup 功能,一般现代 Linux 发行版默认已启用。可以通过查看/proc/cgroups文件确认,文件中会列出系统支持的 Cgroup 子系统及相关信息:

cat /proc/cgroups

如果系统未启用 Cgroup,可以通过修改内核启动参数(如在/etc/default/grub中添加cgroup_enable=memory等参数,根据需要开启特定子系统),然后重新生成grub.cfg并重启系统来启用。

2.2 CPU 资源限制示例

  1. 创建 Cgroup 层级与控制组

首先,创建一个新的 Cgroup 层级并挂载cpu子系统。在/sys/fs/cgroup目录下创建一个新目录,例如mycpu,然后将cpu子系统挂载到该目录:

mkdir /sys/fs/cgroup/mycpu
mount -t cgroup -o cpu cpu /sys/fs/cgroup/mycpu

接着,在mycpu层级下创建一个控制组,如testcpu:

mkdir /sys/fs/cgroup/mycpu/testcpu
  1. 设置 CPU 资源限制参数

Cgroup 通过cpu.shares和cpu.cfs_quota_us、cpu.cfs_period_us等参数来控制 CPU 资源。cpu.shares用于设置 CPU 时间分配的相对权重,默认值为 1024,数值越大分配到的 CPU 时间比例越高。例如,将testcpu控制组的cpu.shares设置为 2048:

echo 2048 > /sys/fs/cgroup/mycpu/testcpu/cpu.shares

cpu.cfs_quota_us和cpu.cfs_period_us用于精确控制 CPU 使用时间。cpu.cfs_period_us定义时间周期(单位为微秒),cpu.cfs_quota_us定义在一个周期内允许该 Cgroup 使用 CPU 的时间。假设要限制testcpu控制组在每 100ms(100000 微秒)内最多使用 50ms(50000 微秒)的 CPU 时间,可以这样设置:

echo 100000 > /sys/fs/cgroup/mycpu/testcpu/cpu.cfs_period_us
echo 50000 > /sys/fs/cgroup/mycpu/testcpu/cpu.cfs_quota_us
  1. 将进程添加到 Cgroup 控制组

启动一个占用 CPU 资源的进程,例如使用stress工具模拟 CPU 负载:

stress --cpu 1 &

获取该进程的 PID,假设为12345,将其添加到testcpu控制组:

echo 12345 > /sys/fs/cgroup/mycpu/testcpu/tasks

此时,该进程的 CPU 使用将受到testcpu控制组设置的资源限制。

2.3 内存资源限制示例

  1. 创建 Cgroup 层级与控制组

同样,先创建一个新的 Cgroup 层级并挂载memory子系统:

mkdir /sys/fs/cgroup/mymemory
mount -t cgroup -o memory memory /sys/fs/cgroup/mymemory

在mymemory层级下创建控制组testmemory:

mkdir /sys/fs/cgroup/mymemory/testmemory
  1. 设置内存资源限制参数

通过memory.limit_in_bytes参数设置控制组可使用的最大内存。例如,限制testmemory控制组最多使用 128MB 内存:

echo 134217728 > /sys/fs/cgroup/mymemory/testmemory/memory.limit_in_bytes

还可以通过memory.swappiness参数设置内存交换的倾向程度(取值范围 0 - 100),假设设置为 10:

echo 10 > /sys/fs/cgroup/mymemory/testmemory/memory.swappiness
  1. 将进程添加到 Cgroup 控制组

启动一个占用内存的进程,例如使用stress工具分配内存:

stress --vm 1 --vm-bytes 256M &

获取该进程的 PID,假设为67890,将其添加到testmemory控制组:

echo 67890 > /sys/fs/cgroup/mymemory/testmemory/tasks

当该进程使用的内存达到限制时,系统会根据策略进行处理,如触发 OOM(Out Of Memory)机制。

三、Docker 对 Cgroup 的使用

3.1 Docker 使用 Cgroup 实现资源限制

Docker 在创建容器时,会自动为容器创建对应的 Cgroup 控制组,并根据用户指定的资源限制参数(如--cpu-shares、--memory等)设置 Cgroup 中的相关参数。例如,当使用docker run --cpu-shares 512 --memory 256m ubuntu命令创建容器时:

  1. 创建 Cgroup 层级与控制组:Docker 会在 Cgroup 的默认层级下(如/sys/fs/cgroup/cpu、/sys/fs/cgroup/memory等)为容器创建唯一标识的控制组目录,目录名称通常包含容器的 ID。

  1. 设置 Cgroup 参数:在cpu子系统对应的控制组目录下,Docker 会将cpu.shares设置为 512;在memory子系统对应的控制组目录下,将memory.limit_in_bytes设置为 256MB 对应的字节数(268435456)。

  1. 添加进程到 Cgroup:容器内运行的进程会被添加到相应的 Cgroup 控制组中,从而实现对容器资源使用的限制,保证容器不会过度占用宿主机资源,同时也确保不同容器之间的资源隔离。

四、总结

Cgroup 作为 Linux 内核强大的资源管理机制,通过层级结构、子系统和任务的有机结合,实现了对进程组资源使用的精细化控制。通过详细的 Linux 实操 Demo,我们清晰地看到了如何利用 Cgroup 进行 CPU、内存等资源的限制和隔离。而 Docker 则将 Cgroup 和 Namespace 技术深度整合,在容器创建、运行过程中充分发挥两者的优势,实现了容器的资源限制与多维度隔离,为容器技术在云计算、微服务等领域的广泛应用奠定了坚实基础。深入理解 Cgroup 和 Namespace 的原理及应用,对于 Linux 系统管理、容器化开发与运维具有重要意义。