跳过正文
  1. 所有文章/

如何限制不同容器的资源

·2817 字·6 分钟
目录

Linux control groups,也就是 cgroups 控制组机制。cgroups 是 Linux 内核用来把进程分组,并限制、统计、隔离、控制这些进程资源使用的机制。比如限制某组进程最多用多少 CPU、内存、IO、进程数等。Docker、systemd、Kubernetes 等底层都会用到它。

cgroup 是一组进程的集合,内核可以对这组进程施加资源控制。它通过一个伪文件系统暴露接口,叫 cgroupfs。你可以把 cgroup 理解成 /sys/fs/cgroup 下的一棵目录树,每个目录就是一个 cgroup:

/sys/fs/cgroup
├── blkio
├── cpu
├── devices
├── memory
├── pids
├── ...
└── systemd

不同类型的资源控制器
#

资源控制由不同的 controller 完成,例如:

控制器作用
cpu控制 CPU 使用
memory控制内存使用
io / blkio控制磁盘 IO
pids限制进程数量
cpuset绑定 CPU 核和 NUMA 节点
freezer冻结/恢复进程
devices控制设备访问
hugetlb控制 huge pages

cgroups的两个版本:v1 和 v2
#

v1 功能灵活但复杂,v2 使用统一层级、统一规则和更安全的委派模型,是现代 Linux 推荐使用的模型。

特点比较
#

项目cgroups v1cgroups v2
层级结构多个层级单一统一层级
控制器挂载可分别挂载自动出现在统一层级
线程控制支持 tasks默认不支持,后续 thread mode 支持
资源分配规则不统一更统一
内部进程规则宽松有 no internal processes 限制
通知机制release_agentcgroup.events
委派安全较弱更严格
网络控制net_cls / net_prio通过 eBPF、iptables 等方式

v1 比较灵活,但复杂,它的特点:

  • 每个 controller 可以有自己的层级
  • 多个 controller 也可以挂在同一个层级
  • 不同 controller 的行为不统一
  • 支持把同一个进程的不同线程放到不同 cgroup
  • 管理复杂,容易产生语义混乱

v2 是新模型,目标是统一和简化,它的特点:

  • 所有 controller 都放在一个统一层级里
  • 文件系统类型是 cgroup2
  • 控制器行为更一致
  • 资源分配规则更清楚
  • 委派机制更安全
  • 默认不再允许随意按线程分组,后来通过 thread mode 部分支持

场景比较
#

新系统、新容器环境里 cgroup v2 已经更多;但存量生产环境里 cgroup v1 / 混合模式仍然很多。

场景现在常见情况
新版 Linux 发行版多数默认用 cgroup v2
新版 Kubernetes / containerd / Docker基本都支持 cgroup v2
老服务器、老集群很多仍是 cgroup v1
企业存量环境v1、v2、混合模式都可能存在
systemd 较新的系统倾向 v2 统一层级

典型趋势是:

  • Ubuntu 22.04/24.04/26.04:通常默认偏向 cgroup v2
  • Debian 11/12:默认支持/使用 cgroup v2
  • Fedora:很早就默认 cgroup v2
  • RHEL 9:默认 cgroup v2
  • RHEL 8、CentOS 7、老 Ubuntu:很多还是 cgroup v1 或混合模式
  • Kubernetes:cgroup v2 从较新版本开始已经稳定支持,官方也推荐在默认启用 v2 的发行版上使用

限制、统计、隔离资源的核心作用
#

把一组进程的资源使用关进一个“笼子”里,让它们只能用被分配的资源,并且可以被观察、计量和管理。

限制:防止一个进程吃光机器
#

比如某个服务内存泄漏,如果没有限制,它可能把整台机器内存吃光,导致其他服务也挂掉。用 cgroup 可以限制:这个服务最多只能用 2GB 内存 或者 这个服务最多只能用 2 个 CPU 的算力。这样它出问题时,影响被限制在自己的范围内。

核心价值: 防止资源抢占和故障扩散

统计:知道谁用了多少资源
#

cgroup 可以统计一组进程用了多少:

  • CPU 时间
  • 内存
  • IO
  • 进程数
  • 网络相关资源
  • page cache 等

比如你可以知道:

  • 容器 A 当前用了 800MB 内存
  • 容器 B 当前用了 1.5 个 CPU
  • 服务 C 磁盘 IO 很高

核心价值: 可观测、可计费、可排障。

Docker / Kubernetes 的资源监控,本质上大量依赖 cgroup 的统计能力。

隔离:让不同工作负载互不干扰
#

多个服务跑在同一台机器上时,如果没有资源隔离,大家会互相抢资源。

例如:服务 A 是核心在线服务,服务 B 是离线批处理任务。没有隔离时,服务 B 可能占满 CPU,导致服务 A 延迟升高。

用 cgroup 可以做到:服务 A 保证较高 CPU 权重,服务 B 只能使用有限 CPU。

核心价值: 让不同服务在同一台机器上共存,但互相影响更小。

最终目的
#

容器不是虚拟机,它共享宿主机内核。所以容器所谓的:

--memory=1g
--cpus=2
--pids-limit=100

底层基本就是通过 cgroup 实现的。也就是说:namespace 负责“看起来隔离”,cgroup 负责“资源上隔离”。

cgroup 的核心目的不是单纯“限制资源”,而是:让一台 Linux 机器可以安全、稳定、可控地运行多个工作负载。

它解决的是这些问题:

  • 谁可以用多少资源?
  • 谁正在用多少资源?
  • 一个服务失控会不会拖垮整台机器?
  • 多个服务之间怎么公平分配资源?
  • 容器的 CPU、内存限制怎么实现?
  • Kubernetes Pod 的资源限制怎么落地?

所以,“限制、统计、隔离资源”的核心作用就是:资源治理。把系统资源从“大家随便抢”,变成“按规则分配、按边界使用、按指标观测”。

CPU、memory、IO 维度的控制思想
#

可以把 cgroup 对 CPU、memory、IO 的控制思想理解成三种不同资源治理模型:

  • CPU:按时间片/权重分配
  • Memory:按容量边界限制
  • IO:按设备吞吐/请求速率调度
维度控制对象核心思想失控后果
CPU计算时间权重分配 + 配额限制服务变慢、延迟升高
Memory内存容量设置边界 + 回收/OOMOOM、系统抖动、进程被杀
IO磁盘访问权重调度 + 带宽/IOPS 限制磁盘延迟升高、服务卡顿

CPU 控制思想:分配“计算时间”
#

CPU 是一种 可抢占、可调度、可压缩 的资源。意思是:一个进程 CPU 不够用,通常不会立刻死,只是变慢。

CPU 控制的核心是: 控制进程组能获得多少 CPU 时间,以及 CPU 紧张时谁优先。

所以 CPU 控制主要关注:

  • 谁优先用 CPU?
  • 谁最多能用多少 CPU?
  • 多个 cgroup 之间如何公平分配?

常见控制方式有两类:权重控制配额控制

权重控制:谁更重要
#

比如:服务 A 权重为 1024,服务 B 权重为 512

当 CPU 紧张时,A 大约能拿到 B 的两倍 CPU 时间。这不是硬限制,而是相对分配。

配额控制:最多只能用多少
#

比如可以限制某个 cgroup:最多使用 2 个 CPU。

底层思想通常是:每 100ms 周期里,最多允许运行 200ms CPU 时间。因为 2 个 CPU 并行运行 100ms,总 CPU 时间就是 200ms。

Memory 控制思想:限制“容量边界”
#

Memory 和 CPU 不一样。内存是 不可随便压缩不可无限等待 的资源。CPU 不够,进程只是慢,内存不够,可能直接:

  • 分配失败
  • 触发 reclaim
  • 触发 swap
  • 触发 OOM kill

所以 memory 控制更像是在划边界:这个 cgroup 最多只能占多少内存。

Memory 控制的核心是: 给进程组划定内存容量边界,并在接近或超过边界时回收、限速或 OOM。

硬限制:不能超过
#

比如某容器最多使用 1GB 内存,超过后可能触发OOM kill。

软限制 / 保护 / 高水位
#

内存控制不只是简单 kill,也有更温和的控制方式,比如:

  • 超过某个值后开始回收
  • 低于某个值时尽量保护

像cgroup里就有这些参数:

文件思想
memory.max硬上限,超过可能 OOM
memory.high高水位,超过后强制回收、限速
memory.low尽量保护这部分内存
memory.min更强的保护

IO 控制思想:控制“访问设备的速度和优先级”
#

IO 主要指磁盘、块设备访问。IO 和 CPU、Memory 又不一样。它的特点是:

  • 共享设备
  • 有队列
  • 有延迟
  • 容易互相拖累

比如一个批处理任务疯狂读写磁盘,可能导致数据库延迟变高。所以 IO 控制关注:

  • 谁的磁盘读写优先级高?
  • 谁最多能读写多少 MB/s?
  • 谁最多能发多少 IOPS?
  • 怎么避免一个 cgroup 把磁盘队列占满?

IO 控制的核心是: 控制进程组访问块设备的优先级、吞吐量和请求速率,避免一个任务拖慢整块磁盘。

权重控制:IO 紧张时谁优先
#

类似 CPU 权重。比如:数据库服务权重高,日志压缩任务权重低。当磁盘繁忙时,数据库优先获得 IO 调度。

限速控制:最多多少带宽 / IOPS
#

可以限制:

  • 最多读 100MB/s
  • 最多写 50MB/s
  • 最多 1000 IOPS