恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

用systemd和cgroups v2锁死agent资源:CPU、内存、IO限制实战

  • 首页
  • 资讯中心
  • /
  • 用systemd和cgroups v2锁死agent资源:CPU、内存、IO限制实战

相关资讯

Linux下JDK多版本切换实战:从JAVA_HOME到软链接脚本 2026/10/9 2:48:01
网页端录音整理怎么和手机同步?会议APP功能原理解析 2026/10/9 2:43:01
【cesium 的使用场景】 2026/10/9 2:43:01

最新资讯

Spring Bean生命周期与三级缓存:从依赖注入到循环依赖的源码级拆解
Agent-Reach:CLI 工具链的声明式调度中枢
PTA L1-007念数字题解:字符串处理与格式控制全攻略
XAML Studio实战:实时预览与可视化树,让界面调试不再反复编译
SpringBoot+Vue医药管理系统设计与实现:从需求分析到部署答辩全解析
行业大模型落地全链路:从RAG微调到本地部署与交付避坑

今日推荐

AI编程智能体实战:从写代码到指挥代码的架构与落地
多模态大模型全栈能力拆解:从数据对齐到弹性推理
大模型Agent开发入门:从工具调用循环到落地避坑指南

本周热门

MR25H40CDF + PIC18F65K40:工业记录仪高可靠存储实战
基于STM32的数控恒压恒流电源设计:从硬件到PID调参全解析
LT9211 MIPI重定时器原理与双路扇出实战指南

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

用systemd和cgroups v2锁死agent资源:CPU、内存、IO限制实战

发布时间:2026/10/9 2:48:01
用systemd和cgroups v2锁死agent资源:CPU、内存、IO限制实战 你有没有遇到过这种情况明明只是部署了一个 agent 服务结果它在高峰期把整台机器的 CPU 吃满内存一路涨到 swap 都救不回来最后把同机上的数据库、网关、日志采集全部拖垮。我在生产环境里踩过这个坑而且不止一次。后来我把所有 agent 的启动方式统一收编到 systemd 下用 cgroups v2 对 CPU、内存、IO 逐项加锁才算真正把资源失控这件事摁死。这篇文章就把整套思路、配置细节、验证方法和排查心得完整写出来给同样被 agent 资源问题折磨过的人一个可以直接抄作业的方案。1. 先想清楚agent 到底该怎么约束1.1 你跑的 agent 属于哪一类agent这个词现在确实太宽泛了。我在实际接触中至少见过三类AI agent 服务比如跑在服务器上的多智能体协作框架、定时触发的推理任务特点是并发高、计算密集、上下文缓存吃内存高峰期 CPU 和内存曲线像过山车。监控采集 agent比如负责抓指标、采集日志、做健康检查的常驻进程特点是平时负载低但遇到大量文件扫描或网络轮询时会把 CPU 和 IO 突然拉高。自动化任务 agent比如定时脚本、消息处理 worker、爬虫任务特点是线程池容易膨胀任务堆积时可能创建出几千个线程。不同 agent 的资源模型不一样但约束手段是同一套在操作系统层面给进程组划一条清晰的资源边界。我的思路很简单——不指望 agent 自己写得足够收敛而是假设它有失控的可能从外部给它套上笼子。1.2 为什么是 systemd 加 cgroups v2而不是改 agent 代码很多人第一反应是优化 agent 代码比如加限流、控制并发数、调整缓存大小。这当然能做但有几个现实问题agent 可能是第三方开源的你改不到核心代码即使能改你不可能预测到所有极端场景而且代码里的自控逻辑一旦写错反而引入新的 bug。相比之下systemd 加 cgroups v2 这套组合有几个别人比不了的优势systemd 是几乎所有现代发行版的 init 系统天然管理服务生命周期。把 agent 写成 systemd unit开机自启、崩溃重启、日志管理全都顺带解决。资源限制直接在 unit 配置里声明不需要额外装工具不需要改业务代码。cgroups v2 是内核级约束agent 内部的垃圾回收、线程池、缓存模块完全感知不到也无法绕过。配置即代码把 unit 文件放进 Git 仓库环境之间迁移非常干净。1.3 cgroups v2 相比 v1 到底改了什么cgroups v2 不是简单换个挂载点它有几处设计变化直接影响使用方式统一层级树v1 时代每个子系统一个挂载点cpu、memory、blkio 各自为政v2 统一挂载在 /sys/fs/cgroup所有控制器都在同一棵树上管理逻辑清爽很多。内部进程约束一个 cgroup 目录要么直接包含进程要么包含子目录不能既跑进程又建子目录。这个约束让资源统计更干净不会出现父子混在一起算不清账的情况。控制器合并原来独立的 cpu 和 cpuacct 合并成 cpu 控制器内存控制器也做了大量重构统计口径更统一。systemd 深度集成每个 unit 服务自动落在 /sys/fs/cgroup/system.slice/xxx.service 目录下你不需要手动创建 cgroup改 unit 配置加参数就行。这里面最需要注意的是内部进程约束。在 v2 里你的 agent 进程如果直接跑在一个 cgroup 目录里你就不能往它下面再挂子目录反过来如果有子目录你就不能直接往里放进程。实际操作中 systemd 会帮你处理好但如果你手动建 cgroup 调试很容易被这个规则绊一下。2. 动手前的地基检查cgroup 版本与 systemd 环境2.1 确认当前系统挂载的是 v2 还是 v1很多教程上来就写配置结果读者配置完发现参数没生效十有八九是系统还跑在 cgroups v1 上。所以第一步永远是确认版本。最简单的命令mount | grep cgroup如果你看到的是cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime)说明已经启用 cgroups v2可以直接继续。如果你看到一堆 cgroup 挂在 /sys/fs/cgroup/cpu、/sys/fs/cgroup/memory 这种子目录那就是 v1。还有一种方式看 /sys/fs/cgroup 目录里有没有 cgroup.controllers 这个文件cat /sys/fs/cgroup/cgroup.controllers有输出类似 cpu memory io 这样的列表就是 v2。这个文件在 v1 里不存在。2.2 还在 v1 的老系统如何切换如果你的系统还在 v1并且内核支持 v2可以通过内核参数切换。编辑 /etc/default/grub在 GRUB_CMDLINE_LINUX 里加上systemd.unified_cgroup_hierarchy1 cgroup_no_v1all然后重新生成引导配置# Debian/Ubuntu update-grub # CentOS/RHEL grub2-mkconfig -o /boot/grub2/grub.cfg重启后确认挂载情况。切换前一定要评估一下机器上是否有老程序硬编码依赖 v1 的路径比如 /sys/fs/cgroup/memory/memory.limit_in_bytes。这类老工具切到 v2 后可能不工作需要一并升级。2.3 systemd 版本和 agent 服务的 unit 归属systemd 对 cgroups v2 的完整支持在 246 版本之后才比较稳建议至少 250 以上。查看版本systemd --version另外你要确认 agent 进程到底落在哪个 cgroup 下面cat /proc/pid/cgroup如果输出是0::/system.slice/agent.service说明它由 systemd 的 system 用户管理资源限制可以直接通过 unit 参数生效。如果输出是 user.slice 之类说明 agent 是用手动 nohup 或者用户会话方式启动的。这种情况下你想用 systemd 限制最好先把启动方式改成 systemd service否则后面配置都是空谈。这也是我坚持先收编再限制的原因——运行方式不规范限制就无从谈起。3. 核心配置实操从 CPU 到内存再到 IO逐层加锁3.1 CPU 限制CPUQuota 与 CPUWeight 怎么选CPU 限制有两个核心参数很多人搞混。CPUQuota硬性配额格式是百分数。CPUQuota100% 代表最多使用 1 个核CPUQuota300% 代表最多使用 3 个核CPUQuota50% 代表最多使用半个核。它是真正意义上的上限不管系统多空闲agent 都不能超过这条线。CPUWeight相对权重范围 1 到 10000默认 100。它管的是系统繁忙时大家怎么分 CPU权重越高竞争时分配到的份额越大。系统空闲时权重高的进程照样能跑满。对 agent 来说我推荐优先用 CPUQuota。原因是防的就是它高峰时吃满 CPU不是想让它在低负载时让位。给 AI agent 类服务我一般这样配CPUQuota300% CPUAffinity0-3CPUQuota 是上限CPUAffinity 是把进程绑定到指定 CPU 核心。绑定核心的好处是减少缓存抖动尤其适合计算密集的 agent坏处是核心不足时可能浪费算力所以要不要绑得看机器情况。你可能会问那 CPUWeight 完全没用吗也不是。如果你的机器上同时跑着多个同等重要的 agent你希望它们大致公平地分享 CPU而不想给某个硬上限那 CPUWeight 更合适。实际项目里我是两者都写CPUQuota 定死天花板CPUWeight 控制竞争优先级。3.2 内存限制MemoryHigh 和 MemoryMax 的配合内存参数里最常用的是 MemoryMax 和 MemoryHigh含义差别很大。MemoryMax 是硬上限比如 MemoryMax2G。agent 使用的内存一旦超过这个值内核就会触发 OOM在对应 cgroup 里挑进程杀掉。这是保命线不能没有。MemoryHigh 是软上限比如 MemoryHigh1.5G。超过之后内核不会立刻杀进程而是加大内存回收力度把这个 cgroup 的页缓存、可回收内存先压出来。如果 agent 自身有缓存清理或 GC 机制它就会在接近 MemoryHigh 的地方开始自我调节。我把这一层比喻成财务预算——超过预算后开始节省开支但不会直接冻结账户。生产环境里我建议两个配合使用MemoryHigh1.5G MemoryMax2G只设 MemoryHigh 不设 MemoryMax 的问题在于如果 agent 存在真正的内存泄漏回收也兜不住最终会把宿主机内存吃光。只设 MemoryMax 不设 MemoryHigh 的问题在于agent 会毫无预警地撞墙被 OOM频繁重启反而更不稳定。另外还要关注 Swap。默认情况下 cgroup 的交换空间没有单独限制agent 可以把 swap 也吃满。可以用 MemorySwapMax 单独限制MemorySwapMax512M这里有一个比较容易踩的认知误区cgroup 的 memory 统计是包含页缓存page cache的。agent 如果大量读文件它的 memory.current 会很高但那些页缓存是可以在内存压力下回收的。所以别一看到内存占用超过 MemoryMax 就以为泄漏了先看 memory.events 文件里的 oom 字段是否在涨。3.3 IO 限制防止 agent 打满磁盘带宽CPU 和内存是大家最先想到的IO 往往被忽略但 agent 打满磁盘的问题一点都不少见。典型场景是多个 agent 实例同时启动各自读取模型文件、写日志、写缓存目录瞬间把磁盘 IO 拉满整个宿主机上的服务全部卡顿。cgroups v2 的 IO 控制通过 systemd 配置时核心是两个方向IOWeight20 IOReadBandwidthMax/dev/sda 100M IOWriteBandwidthMax/dev/sda 50MIOWeight 是权重默认 100范围 1 到 10000。性能敏感型服务可以给到 200 以上不重要的 agent 给 10 到 50。IOReadBandwidthMax 和 IOWriteBandwidthMax 是按设备名限制读写带宽属于硬上限。注意一点设备名要写对。用 lsblk 查看实际设备名有些云主机上是 /dev/vda有些是 /dev/nvme0n1别照抄我这边的 /dev/sda。还有一点需要提醒cgroups v2 的 IO 控制器不是在所有文件系统上都生效而且某些内核配置下 io 控制器可能没有启用。排查方式就是看 /sys/fs/cgroup/cgroup.controllers 里有没有 io 这个关键字。没有的话你的 IOWeight 和带宽限制参数会被 systemd 接受但实际不会起作用。3.4 额外防线TasksMax 与进程数量控制agent 用线程池时特别容易疯狂创建线程。我见过一个 Python agent 在任务积压时线程数涨到 5000 多直接把整个机器拖死。这个用 TasksMax 就能兜底TasksMax512TasksMax 限制的是 cgroup 里的任务数也就是线程和进程的总数。考虑到 agent 本身的线程池、异步任务、子进程512 是个比较稳的起步值如果实际业务并发需求高可以放宽到 1024。除了 TasksMaxsystemd 还有一批安全加固参数虽然不是资源限制但对 agent 这类不可信服务非常有用。我在这类 unit 里一般会带上去PrivateTmptrue ProtectSystemstrict ProtectHometrue NoNewPrivilegestrue这些参数主要解决 agent 的安全问题同时也有副带好处防止 agent 把自己或者临时文件写到系统的不可控位置间接避免 IO 污染。3.5 一份可以直接抄的 agent.service 完整配置把前面的参数合成一个完整 unit 文件。我目前在生产环境用的模板是这样的[Unit] DescriptionMy AI Agent Service Afternetwork-online.target Wantsnetwork-online.target [Service] Typesimple Useragent-runner Groupagent-runner ExecStart/opt/agent/run-agent Restarton-failure RestartSec5 TimeoutStopSec30 # CPU CPUQuota300% CPUWeight20 # Memory MemoryHigh1.5G MemoryMax2G MemorySwapMax512M # IO IOWeight20 IOReadBandwidthMax/dev/sda 100M IOWriteBandwidthMax/dev/sda 50M # Tasks TasksMax512 # Security PrivateTmptrue ProtectSystemstrict ProtectHometrue NoNewPrivilegestrue [Install] WantedBymulti-user.target把这个文件放到 /etc/systemd/system/agent.service然后执行systemctl daemon-reload systemctl enable --now agent注意两个细节。一是 Useragent-runner 必须提前创建好而且建议用独立账号跑 agent不要直接用 root。这样即使 agent 内部出了问题也没有权限去修改 cgroup 限制等于把资源锁和权限锁一起上。二是 CPUAffinity 我没有写进模板因为不是所有机器都适合绑核如果你确定要绑定自己加一行即可。4. 验证、压测与动态调优4.1 实时观测systemd-cgtop 和 systemctl show配置改完了第一件事是确认它真的生效。看状态最常用的两个工具systemd-cgtop它会实时显示每个 slice/service 的 CPU、内存、IO 占用效果类似 top但是站在 cgroup 视角汇总数据。你一眼就能看到 agent.service 当前吃了多少资源以及它离你设置的上限还有多远。另一个命令是查询 unit 实际生效的参数systemctl show agent.service -p CPUQuota -p MemoryMax -p MemoryHigh -p TasksMax -p IOWeight输出里的值会告诉你 systemd 到底给这个服务下了什么配置。注意如果某些参数显示为空说明 systemd 认为这个参数不适用或者值没有配置成功。4.2 用压力工具验证限制是否真的生效配置生效与否不要光看参数要实际压一把。我常用的压测方法是# 在 agent 服务里跑一段压力的临时命令 systemctl stop agent systemd-run --unitagent-pressure-test --service-typesimple \ --propertyCPUQuota200% --propertyMemoryMax512M \ --propertyIOWriteBandwidthMax/dev/sda 10M \ bash -c stress --cpu 4 --timeout 60 dd if/dev/zero of/tmp/testfile bs1M count200 oflagdirect当然更简单的方式是直接重启 agent让它跑真实业务然后盯着 systemd-cgtop 看曲线。如果 CPU 曲线精确卡在配额上说明限制生效如果明显超过配额那就要排查是不是运行方式和预期不一致或者配置没加载。对于内存限制可以这样验证写一个小脚本读取大文件并把内容放进内存观察到达上限后是否被 OOM。4.3 读懂 cgroup 状态文件memory.events 与 cpu.stat这是排查资源问题最硬核的手段也是常规教程基本不会讲的部分。systemd 创建的每个 unit 在 /sys/fs/cgroup/system.slice/agent.service/ 下都有状态文件。重点看两个cat /sys/fs/cgroup/system.slice/agent.service/memory.events输出类似low 0 high 137 max 5 oom 2 oom_kill 1这里的 high 表示 MemoryHigh 被触发的次数max 表示 MemoryMax 被触发的次数oom 和 oom_kill 表示发生过 OOM 及实际杀进程的次数。如果你的 high 增长很快说明 MemoryHigh 设置得太紧如果 max 有增长说明内存已经撞硬墙了如果 oom_kill 有增长说明已经有进程被杀这在生产环境是重大事故信号。CPU 要看 cpu.statcat /sys/fs/cgroup/system.slice/agent.service/cpu.stat重点关注 usage_usec它表示该 cgroup 累计使用 CPU 的微秒数。你可以在一个压测周期前后各读一次差值除以时间就能算出实际 CPU 占用百分比用来验证 CPUQuota 是否真的拦住。4.4 线上动态调整systemctl set-property资源限制不一定要停服才能改。systemd 支持热修改资源参数systemctl set-property agent.service MemoryMax3G systemctl set-property agent.service CPUQuota400%这会即时生效并且会把配置持久化写到 /etc/systemd/system.agent.service.d/ 下。我踩过一个小坑用 set-property 调整之后如果后来直接编辑主 unit 文件两者可能产生覆盖关系行为不好预测。所以我的建议是主 unit 文件保存稳定基线临时调优统一用 set-property或者统一用 drop-in override 文件管理避免两套配置混在一起打架。5. 踩坑记录与排查速查表5.1 限制不生效的五个常见原因我排查过不少配置了没用的案例最常见的原因排序如下系统还跑在 cgroups v1 上。这是最大概率的原因很多人直接跳过环境检查。agent 不是由 systemd 启动的。如果你用 crontab 或者 nohup 起进程systemd unit 里的所有资源参数都和它无关。修改配置后没有 daemon-reload或者没有重启服务。systemd 资源参数在启动服务时固化到 cgroup 里修改后只 reload 不 restart 是不行的。参数写错了位置。比如把 MemoryMax 写到了 [Unit] 段而不是 [Service] 段systemd 解析时会忽略。被 drop-in 配置覆盖。用 systemctl cat agent.service 查看最终生效的配置如果和预期不一致多半是 /etc/systemd/system/agent.service.d/ 下有覆盖文件。5.2 no internal process 约束导致的奇怪现象cgroups v2 的层级规则比较死板一个 cgroup 目录如果已经有进程在里面就不能再创建子目录如果已经有子目录就不能直接往里面放进程。我第一次手动调试时想给 agent 的 cgroup 下创建一个子 cgroup 来隔离子进程结果 mkdir 直接返回 -ENOMEM。后来才明白这是 v2 的设计约束目的是保证资源统计没有歧义。实际使用 systemd 时这条规则一般不会影响你但如果你有在一个服务里再分 cgroup的需求需要换思路用 systemd-run 在现有服务的 pids 层级下再创建 scope而不是手动去改 cgroup 目录。5.3 agent 频繁 OOM 怎么办如果 memory.events 里 oom_kill 一直在涨先别急着加大 MemoryMax。正确排查顺序是看日志journalctl -u agent.service 会输出内核 OOM 的具体记录确认是不是 agent 主进程被杀。看内存构成进入 cgroup 目录执行 cat memory.stat区分 anon匿名页真正的进程数据和 file页缓存可回收。如果 file 占比很高说明不是泄漏而是缓存占用太多可以适当降 MemoryHigh或者调低读取缓存行为。确认 agent 是否存在内存泄漏连续观察 memory.current 和 memory.events 的 high 次数如果每轮任务后内存不下降那就是泄漏这种情况加不加 MemoryMax 都治标不治本该修的还得修。5.4 agent 线程/进程数失控TasksMax 能拦住线程数但如果你没设这个参数systemd 默认值是很大的。我的习惯是任何 agent unit 必须显式设置 TasksMax不能依赖默认。不过要提醒一点进程数限制对你的排查工作也有影响。如果 agent 控制的子进程很多而且你怀疑某个子进程异常可以这样查systemd-cgls -u agent.service for pid in $(cat /sys/fs/cgroup/system.slice/agent.service/cgroup.procs); do echo PID $pid: $(cat /proc/$pid/comm) donecgroup.procs 文件会列出这个 cgroup 里所有进程排查线程和子进程特别方便。5.5 排查速查表症状排查命令常见原因所有限制不生效mount | grep cgroup系统仍在 cgroups v1CPU 上限未拦住cat cpu.stat 前后差值进程不属于该 cgroup内存频繁被杀cat memory.events页面缓存占比过高或内存泄漏IO 限制无效cat cgroup.controllersio 控制器未启用线程数失控wc -l cgroup.procs未设置 TasksMax配置显示生效但行为不对systemctl cat agent.service存在 drop-in 覆盖文件手动建子目录报错mkdir 返回 ENOMEMv2 的 no internal process 约束5.6 关于agent 自己改配置的防御思路cgroups v2 的 cgroup.procs 和 cgroup.subtree_control 等文件写入权限受严格限制。非 root 用户的 agent 无法修改自己所在 cgroup 的资源配置所以让 agent 以独立非 root 用户运行本身就是一道有效的资源安全锁。如果你管理的是不可信 agent我建议再把 cgroup 相关目录的写权限再收一收同时配合 systemd 的 ProtectSystemstrict 和 NoNewPrivilegestrue。这套组合打下来agent 既不能改自己的资源上限也不能通过 sudo 之类的方式提权属于比较稳妥的防护姿势。写在最后的经验我从第一次被 agent 搞挂生产环境到现在已经形成一套固定习惯任何 agent 类服务落地第一步就是写 systemd unitCPU、内存、IO、任务数四项限制全部显式声明不允许裸奔。你可能会觉得加限制会影响 agent 性能实际情况是一个被资源边界约束的 agent 只是不能无限膨胀正常业务流量下它该跑多快还是多快。另外限制参数不是写完就完事的。我每次升级 agent 版本或调整业务规模后都会重新看一眼 systemd-cgtop如果 memory.events 的 high 次数持续上升说明资源水位贴近了提前调参数比线上 OOM 再救火舒服得多。最后分享一个小技巧把 systemd-cgtop 的输出做成定时采集丢到现有监控系统里画曲线资源趋势有了数据支撑调参时就不用靠猜这套流程跑顺之后agent 的资源问题基本可以不用再操心了。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号