登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

Linux cgroup v2 限制服务 CPU 与内存的完整思路

来源:17golang原创

时间:2026-10-07 03:49:20 150浏览 收藏

给 Linux 服务限制 CPU 和内存,先确定服务属于哪一层 cgroup,再分别处理“持续分配”“压力降级”和“硬性终止”。生产环境里我更倾向于让 systemd 管理边界:用 CPUQuota= 限制 CPU 时间,用 MemoryHigh= 给内存回收压力设边界,再用 MemoryMax= 兜住最坏情况。

官方资料:https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html systemd 资源控制:https://cgit.freedesktop.org/systemd/systemd/tree/man/systemd.resource-control.xml

要点速览
  • 先确认 cgroup.controllers 是否提供 cpu 和 memory,控制器按父子层级向下生效。
  • CPUQuota 是带宽上限,MemoryHigh 是压力边界,MemoryMax 才是硬内存上限。
  • 配置后要同时看服务状态、cpu.stat、memory.current 和 memory.events,否则很难区分限流和故障。

先把服务放进可解释的 cgroup 层级

cgroup v2 是统一层级。父级启用控制器后,子级才能继续分配;父级的限制也不能被更深的子级抵消。直接改文件前,先确认挂载类型和控制器可用性:

# 确认目标路径是统一 cgroup v2 文件系统
stat -fc %T /sys/fs/cgroup

# 查看根级可用控制器,缺少 cpu 或 memory 时不要继续写对应文件
cat /sys/fs/cgroup/cgroup.controllers

# 查看根级已经向子层级开放的控制器
cat /sys/fs/cgroup/cgroup.subtree_control

文件不存在不一定是权限问题:控制器可能没有在父级开启,也可能仍由旧的 v1 层级管理。

Linux cgroup v2 服务层级说明图:systemd unit 连接统一层级并向下约束 cpu.max 与 memory.max
图1:cgroup v2 层级说明图,展示服务边界与 CPU、内存控制接口的关系。

把预算拆成 CPU 上限、内存压力和硬边界

CPU 和内存的语义不同,不能只按机器总配置平均切一刀。CPU 看高峰处理速率,内存要给运行时、缓存和突发分配留余量。

目标systemd 配置底层接口判断方式
限制 CPU 带宽CPUQuota=50%cpu.max观察 nr_throttled
降低内存回收冲击MemoryHigh=512Mmemory.high看回收压力与延迟
兜住最坏内存占用MemoryMax=768Mmemory.max检查 oom 事件
相对分配权重CPUWeight=200cpu.weight只比较同层竞争者

CPUQuota=50% 表示最多使用一个 CPU 的一半时间,超过 100% 才允许跨多个 CPU 累计使用。内核的 cpu.max 采用“最大带宽 + 周期”格式,例如 50000 100000。MemoryHigh 主要带来回收和节流压力,MemoryMax 才是硬限制。

用 systemd drop-in 持久配置服务边界

假设服务名为 demo.service,用 drop-in 管理资源参数,便于审查和回滚:

# /etc/systemd/system/demo.service.d/resources.conf
[Service]
# 以单个 CPU 为基准限制总 CPU 时间,避免突发任务长期抢占
CPUQuota=50%
# 先让内存进入回收压力区,给服务留出自我收缩机会
MemoryHigh=512M
# 最坏情况下限制该服务及其子进程的内存总量
MemoryMax=768M
# 只在同一父级的服务之间调整相对 CPU 权重
CPUWeight=200
# 让 systemd 重新读取 drop-in,再重启目标服务
sudo systemctl daemon-reload
sudo systemctl restart demo.service

# 查看最终合并后的资源配置和服务所在层级
systemctl show demo.service -p ControlGroup -p CPUQuotaPerSecUSec -p MemoryHigh -p MemoryMax

数值只是示例,上线前应以压测峰值、延迟预算和降级策略反推;出现超时,要确认缓存、子进程或日志侧车是否也算进了该 cgroup。

限制生效后要看什么

只看 systemctl status 不够。CPU 看累计使用与 throttling 计数;内存区分当前占用、high 压力和 max 事件:

# 取出 systemd 展开的 cgroup 相对路径
cg=$(systemctl show -p ControlGroup --value demo.service)

# 读取 CPU 使用与被节流的累计计数
cat "/sys/fs/cgroup${cg}/cpu.stat"

# 读取当前内存占用及内存事件计数
cat "/sys/fs/cgroup${cg}/memory.current"
cat "/sys/fs/cgroup${cg}/memory.events"

nr_throttled 持续增长,先检查 CPU 配额或线程池;high 增长,说明回收压力已影响服务;oom 或 oom_kill 增长,则回退 MemoryMax、减少缓存或拆分子层级。ControlGroup 是相对 /sys/fs/cgroup 的路径,不是固定值。

Linux cgroup v2 CPUQuota MemoryHigh MemoryMax 与 cpu.stat memory.current memory.events 的关系说明图
图2:预算与观测关系说明图,区分 CPU 节流、内存回收压力和硬上限。

直接操作 cgroup v2 时的边界

手工写 cpu.max、memory.high 或 memory.max 适合容器运行时、批处理沙箱和动态控制器,但要先创建子 cgroup、迁移进程,再按父级允许的控制器配置。不要让 systemd 和手工脚本同时争夺同一个目录。

需要把资源控制权交给服务内部的容器管理器时,才考虑 systemd 的 Delegate=。它代表允许服务在自己的控制组下继续建立子层级,并不意味着服务可以突破父级的资源限制。委派前要明确谁负责创建子组、谁负责清理以及谁读取事件。

常见问题

CPUQuota 和 CPUWeight 应该二选一吗?

不必。Quota 是绝对带宽上限,Weight 是同层竞争时的相对分配;低优先级服务可以两者同时设置,但不要用 Weight 代替硬上限。

MemoryHigh 超出后会立即杀进程吗?

不会。它主要触发回收和节流压力;需要硬性兜底时再设置 MemoryMax,并为 OOM 后的恢复或降级准备方案。

为什么子 cgroup 里没有 cpu.max?

先看父级的 cgroup.controllers 和 cgroup.subtree_control。控制器只有在父级可用并向子层级开启后,子目录才会出现相应接口。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>