服务器入坑指北:CPU 限制该用权重还是上限
给服务设了优先级,忙的时候照样把别的服务挤卡;想"限死"一个跑批作业,却怎么也限不住;也不清楚 CPU 争抢与 I/O 争抢,究竟该用哪一套设置。
这类困惑的根子是:CPU 限制其实是两族参数——一族管"相对公平"(权重),一族管"绝对封顶"(配额)。用错族就等于没设:权重只在存在争抢时按比例分配,对"独占型满载"的作业没有任何作用,因为没人跟它抢,它自然能用满。
这篇先让你回答一个问题(是"不想抢别人"还是"不许用太多"),再讲两族参数的行为差异、I/O 是另一套、落点与"哪一层真正生效",最后是虚拟化边界。权重与配额的数值、核数一律用占位符。
先回答一个问题:防抢,还是封顶
| 你的目标 | 用哪族参数 | 行为 | 不适合的情况 |
|---|---|---|---|
| 防抢(不想抢别人,求相对公平) | 权重类 | 只在存在争抢时按比例分配 | 独占型满载场景:没人跟它抢,它就照用满 |
| 封顶(不许用太多,要绝对上限) | 配额类(周期 + 配额) | 每个周期内最多用多少,表现为周期性节流 | 你期待的是"平滑变慢"——配额不是这个形态 |
| 两者都要 | 两族可以一起用 | 先按权重相对分配,再受配额封顶 | 得同时理解两族的行为差异 |
| 判据只有一句话:先说出你要的效果是"相对公平"还是"绝对封顶",再选参数族。 |
权重不是百分比,而且只统计"活跃的兄弟"
权重的语义有一个容易忽略的地方:它是个相对值(有取值区间与默认值),而实际分到的份额是——
自己的权重 ÷ 当时活跃兄弟的权重之和。
关键在于"活跃"两个字:没有在跑作业的那一支,会被排除在计算之外。所以当某一支停下来之后,其余几支会按它们的权重之和重新分配。
后果很直观:看着"几支设了同样的权重",但谁在跑会改变份额。这也解释了那类困惑——“我明明设了权重,怎么看着没用”。
还有一个前提必须记住:官方明确说过,权重只在"可用 CPU 资源被耗尽"时才起作用。空闲时你看不到任何差别,因为那时候没有争抢——这不是没生效,是没有可分配的东西可分。
配额:每周期最多用多少,表现是周期性节流
配额类参数有周期与额度两个字段,默认值是不限。配额按相对单核的百分比理解——大于 100% 表示可以用到多个核;周期长度也可以单独调整(有平台允许的范围,以现读为准)。
顺带把单位说清:100% 是"相对单核"的基准,不是建议你设的值——具体额度按你的负载定(这里不给数值)。
它的行为要把预期先讲清:配额限制的是长期平均,所以"一个周期内先跑满、然后被节流"是正常表现——访客侧的体感是周期性卡顿,而不是平滑地变慢。
这一点必须提前说,否则你会以为"配额没生效"。判据:要"永远不许超过某个用量",配额是对的;要"别把别人挤卡",配额可能不是你要的形态。
CPU 侧优先级对 I/O 完全无感
争的是 CPU 还是 I/O,决定用哪一套参数。 I/O 有自己的一套优先级族(从"让路"到"优先"有若干档位),而 CPU 侧的设置对 I/O 争抢没有任何作用。
这里有个风险:选最"让路"的那一档,有被饿死的风险——长期分不到 I/O,表面看起来像"服务卡死"。具体档位名称以现读为准,别照抄别人的写法。
判据:先确认瓶颈在 CPU 还是在 I/O,再决定用哪一套。
落点:写进服务单元,并确认"哪一层真正生效"
- 优先写进服务单元:随服务生命周期生效、可审计、可回滚;比临时命令可靠。
- 多层可能同时存在:同一个服务可能同时被单元、容器、会话多层限制。所以必须现读生效值来判断"谁说了算",否则就会出现"改了没效果"。
特别是容器内运行时:单元级与容器、运行时层的限制会同时存在,必须按"哪一层真正生效"去核对。 - 顺序:能写单元就不写临时命令;多层存在时先确认哪一层生效再动手;一次只改一项并记录原值,便于回滚。
顺手提醒一句:权重项在早期版本里叫另一个名字,并且已经被替换。所以照抄老教程,很可能写出一份根本不被解析的配置——这也是"改了没反应"的一个来源。
虚拟化边界:权重效果会被宿主调度稀释
在客户机里,这些限制是完整可用的;但权重类参数的相对效果会被宿主侧的调度削弱——因为真实 CPU 时间最终是由宿主分配的。
判据(按顺序):
- 权重类改了没效果时,先看是不是"根本没有争抢";
- 再看是不是**“宿主侧稀释”**;
- 两者都不是,再考虑改机型、迁移——而不是继续往上加限制。
# 只读查看(权重/配额的数值与核数一律用占位符)
# 服务的限制生效值:按属性名查询该服务的当前生效设置
# 该服务所属的层级:确认它在哪一层被限制(单元 / 容器 / 会话)
# CPU 侧与 I/O 侧的当前设置:两侧分别现读,别只读一侧
判据:“生效值"回答"谁说了算”;“所属层级"回答"还有没有别的层在管它”;“两侧设置"回答"我改的是不是对的那一侧”。
现象到动作的判定表
| 现象 | 结论 | 动作 |
|---|---|---|
| 设了优先级仍把别人挤卡 | 可能是"根本没争抢",也可能用错了族 | 先确认是否真在争抢;要封顶就换配额类 |
| 想封顶却封不住 | 用错族(只用了权重) | 换成配额类(周期 + 配额) |
| 设了之后出现周期性卡顿 | 配额在起作用:一个周期内先跑满、再被节流 | 这是配额的正常形态;不够就调整周期或额度 |
| 改了没效果 | 多层限制里,不是你改的那一层说了算 | 现读生效值,先确认哪一层生效 |
| I/O 在抢,却在调 CPU 参数 | 侧别搞错了 | 改用 I/O 那一套优先级族 |
改完怎么验证:每项一条判据
- 现读生效值 = 期望值:不看配置文件,看现读。
- 在争抢窗口内观察到相对份额变化:空闲时测不出差别,这是最容易验证错的一点。
- 没有出现意料之外的周期性节流:出现了,说明配额给小了。
- 重启后仍生效:说明落点是服务单元,而不是临时命令。
- 回滚动作可用:原值记过、步骤清楚。
- 验证方式固定成一句话:在争抢窗口内现读生效值并观察到预期行为,且重启后仍成立,才算通过。
这篇不解决什么
- 不做"能开多少"的限额:讲《文件描述符与 inotify 上限》的那篇讲的是上限类系统参数的另一族(每个进程或系统能开多少);本篇是 CPU 与 I/O 的分配与封顶。
- 不做内存侧读数:讲《内存口径打架时信谁》的那篇是内存侧的读数口径;本篇是 CPU 与 I/O 侧的限制选择。
- 不判断整机压力在哪:判断"压力在不在 CPU"属于负载判断那条线;本篇决定"用哪个参数族设限"——两者互相指路。
- 不处理内存维度的限制与取证:cgroup 内存上限与 OOM 取证属于内存维度;本篇是 CPU 维度。
踩坑清单
- 拿权重当"封顶"用:没有争抢时它等于没设。
- 拿配额当"防抢"用:两者目标不同,配额的形态是周期节流。
- 以为权重是百分比:它是相对值,份额取决于当时活跃兄弟的权重之和。
- 忘了"不活跃的分支不参与计算":某支停了之后,份额会重新分配。
- 只看空闲时的表现就宣布生效:空闲时没有可分配的东西可分。
- 只改运行时、不落到服务单元:重启之后就回去了。
- 多层限制没确认哪层生效:改了没效果,还以为是参数写错。
- 用 CPU 参数去解 I/O 争抢:方向错了。
- 把最"让路"的 I/O 档位用在关键进程上:有被饿死的风险。
- 虚拟化下指望权重解决宿主侧争抢:真实时间在宿主手里。
- 照抄老教程里的旧权重项名:那一项已经被替换,写了也不生效。
- 改完不复核、也记不住原值:既不知道有没有生效,也回不去。
- 点赞
- 收藏
- 关注作者
评论(0)