Kubernetes 资源分配指南:如何科学设置 CPU 与 Memory 的 Request & Limit

前言

在 Kubernetes (K8s) 集群中,服务 YAML 文件里的 resources 配置块往往是被开发者误解最深的区域。面对 requestslimits,许多人的做法是“凭感觉填”或者“抄隔壁项目的配置”。

但实际上,这短短几行 YAML 决定了你的微服务在面对突发流量时是安然度过、被无情限流(Throttling),还是直接崩溃(OOMKilled);也决定了你的 K8s 集群是能够极致降本增效,还是沦为资源浪费的黑洞。

要彻底搞懂参数该怎么设,我们必须先认清 K8s 调度的底层逻辑。

在k8s集群中,我们编写服务yaml的时候有个关键的resources的参数需要填写,但是具体应该怎样设置呢,以下就是我在实际使用过程中的一点思考:

          resources:
            requests:  # 描述服务启动时所需的资源是多少
              memory: "1Gi"
              cpu: "50m"
            limits: # 描述服务最大能使用多少资源
              memory: "3584Mi"
              cpu: "2"

核心基石:K8s 的“瞎眼会计”与 Linux 的“硬核保安”

在决定给服务分配多少资源前,必须理解 K8s 调度和清理资源的两个核心机制:

调度机制:只看账本,不看现实的“瞎眼会计”

  • K8s 调度器(kube-scheduler)在分配 Pod 到具体 Node 节点时,唯一参考的指标是 requests(账面需求)
  • 它绝对不会去查看某台宿主机当前的物理真实 CPU 和内存利用率是 10% 还是 100%。只要 节点总容量 - 已调度 Pod 的 Requests 总和 > 新 Pod 的 Request,调度就会成功。
  • 结论: requests 决定了集群的装箱率(调度密度)。设置得越高,节点能调度的 Pod 越少,资源浪费越严重。

清理策略:可压缩与不可压缩资源的冰火两重天

当节点物理资源真正不够用时,Linux 内核与 K8s 节点代理(kubelet)会介入,对 CPU 和内存采取完全不同的镇压手段:

  • CPU(可压缩资源):集体变慢,绝不杀人。
    • 如果物理 CPU 跑到 100%,系统会根据各个 Pod 的 CPU Request 权重(底层转换为 cpu.shares)重新分配时间片。所有服务都会变慢(RT 飙升),但绝对不会有服务因为 CPU 不够被强杀。
  • Memory(不可压缩资源):无情击杀,精准或盲杀。
    • 精准击杀(OOMKilled): 某个 Pod 的实际内存达到了它自己的 Memory Limit,内核的 OOM Killer 会直接杀掉该容器并重启。
    • 节点驱逐/盲杀(Eviction/Node OOM): 如果大量 Pod 没达到自己的 Limit,但宿主机的物理内存总和被吃光了。Kubelet 会拉响警报,强制驱逐那些内存使用量超过自己 Request 的 Pod,以保护节点不死。

逐个击破:四大参数的设置法则

基于上述底层逻辑,我们来推演这四个参数到底该怎么填。

CPU Request:必须设置,且尽量贴近真实低谷

  • 是否应该设置? 必须设置。 这是 K8s 调度的依据,也是在宿主机 CPU 满载时,你的服务能分到多少最低算力保障的“股权证明”。
  • 设置多少? 建议通过 Prometheus 观察服务在日常平稳期的真实 CPU 消耗,设置一个偏低的值(例如 50m 或 100m)。这能极大释放账面调度空间。
  • 如果你在 Kubernetes 中完全不设置 CPU Request,K8s 会根据你是否设置了 CPU Limit 触发两种截然不同的底层机制。

但结论可以先放在前面:这是一种极其危险的做法,会导致你的服务在集群高负载时“离奇假死”。

我们来拆解这两种情况的底层逻辑:

情况一:既不设 CPU Request,也不设 CPU Limit(最惨的“底层平民”)

当你两个都不写时,K8s 会认为这个 Pod 对 CPU 的需求是 0

  • 调度层面(瞎眼分配): 既然你需要 0 个 CPU,K8s 调度器(kube-scheduler)会认为随便哪个节点都能塞下你。它会把你当成没有任何计算需求的“空气”,闭着眼睛把你调度到可能 CPU 已经负载 100% 的节点上。
  • 运行层面(完全丧失竞争权): 当你的 Pod 运行在宿主机上时,Linux 内核会给它分配一个全场最低的 CPU 权重(在 cgroups 底层,cpu.shares 默认只有 2,而 1 个核心的权重是 1024)。
  • 致命后果 —— “饥饿假死”(Starvation):
    • 平时(节点空闲): 你的程序跑得飞快,因为没人跟你抢,你可以占用节点 100% 的 CPU。
    • 战时(节点满载): 当同节点的其他服务(那些设置了 Request 的“特权阶级”)也开始狂用 CPU 时,Linux 内核会严格按照权重分配时间片。你的服务因为权重是底层的底,几乎分不到任何 CPU 运算时间
    • 表现: 你的 Pod 依然显示 Running,没有报错,没有 OOM,甚至都没被重启。但是你的接口请求会全部卡住、疯狂 Timeout,整个服务处于一种“脑死亡”状态,直到宿主机的 CPU 空闲下来它才会突然“复活”。

情况二:没设 CPU Request,但设了 CPU Limit(触发 K8s 潜规则)

如果你只写了 Limit: 2,而把 Request 留空,K8s 为了防止逻辑冲突,会强制执行一条底层潜规则:自动将你的 CPU Request 填充为等于 CPU Limit 的值。

  • 后果: K8s 会默默帮你把配置改成 Request: 2, Limit: 2
  • 这有什么坏处? 我们前面讨论过,CPU Request 设得太高(比如 2核),会严重吃掉账面调度空间。你可能本来只是想要个上限,结果无意中把 Request 也拉满了,导致大量算力在调度账面上被白白锁定,集群资源利用率暴跌。

架构师总结:为什么“10m”的低保也比“不设”强?

绝对不能让 CPU Request 留空。

哪怕你只给它设置一个极小的值,比如 requests.cpu: 10m 或者 50m,在底层意义上也是天壤之别:

  • 不设置,权重是 2(随时被踩在脚底饿死)。
  • 设置 50m,权重就是 51。虽然不多,但相当于你在这个节点上拿到了合法的“股权”和“最低生活保障”

当宿主机 CPU 被打爆到 100% 时,这 50m 的 Request 能保证内核依然会硬性切出一小块时间片给你的服务。你的服务处理请求会变慢(比如从 50ms 变成 1s),但绝对不会假死停摆,依然能缓慢但坚定地响应请求。

CPU Limit:建议不设置(Unset)

  • 是否需要设置? 现代云原生架构强烈建议:不设置(删除该字段)。
  • 为什么? K8s 的 CPU Limit 依赖 Linux CFS Quota 机制。如果设置了 Limit(例如 2.0),即使宿主机当时有 10 个空闲的 CPU 核心,只要你的服务在 100 毫秒内用完了属于自己的配额,也会被内核强行暂停(Throttling)。这会导致极严重的 P99 延迟毛刺。
  • 结论: 拔掉 CPU 限速器,让应用在突发计算时瞬间借用节点空闲算力秒级处理完毕,是降低延迟的最佳实践。

Memory Request:必须设置,生死攸关

  • 是否应该设置? 必须设置。 K8s 调度时用来占坑,也是宿主机内存不足时,判定你是否属于“超用资源”并决定驱逐优先级的核心标准。
  • 不设置的话他也是会任意调度到只要有资源的节点,有可能调度上去服务启动不起来。

Memory Limit:必须设置,且策略决定命运

  • 是否应该设置? 必须设置。 内存不像 CPU,如果没有上限,一行有 Bug 的代码造成的内存泄漏,就能把整台宿主机拖死。
  • 是否要与 Request 相等? 这里分化出了云原生架构中的两大流派:

两大流派:追求极致稳定 vs 压榨集群资源

在实际生产中,没有绝对的对错,只有业务场景的取舍。请根据你的服务重要等级对号入座:

流派 A:核心保命流(Maximum Stability)

适用场景: 核心交易链路、网关、有状态服务(数据库、中间件)、不支持重试的复杂业务。

配置公式: Memory Request == Memory LimitCPU Limit 不设置

  • 原理解析: 当内存的 Request 等于 Limit 时,K8s 会赋予该 Pod 最高的服务质量等级(Guaranteed)。这相当于拿到了免死金牌。
  • 优势: 哪怕同节点的其他服务疯狂吃内存导致宿主机 OOM,K8s 也绝对不会杀你的服务。你的内存边界是极其清晰和确定的。
  • 代价: 会产生“稳定性溢价”。例如你设置了 2G,平时只用 1G,剩下的 1G 在 K8s 调度账面上被永远锁死,造成物理内存的“账面浪费”。

流派 B:极限压榨流(Extreme Cost Squeezing)

适用场景: 无状态 Web 服务、异步计算 Worker、离线跑批任务、可随时安全重启的边缘服务。

配置公式: Memory Request < Memory Limit(例如 Req=500M, Lim=2G),CPU Request 极低CPU Limit 不设置

  • 原理解析: 这属于 Burstable(突发性能)级别。账面上只占 500M 调度空间,让节点能塞进几倍数量的 Pod,极大提高装箱率。同时给予 2G 的 Limit,允许服务在突发流量时“借用”节点物理内存。
  • 优势: 极致的降本增效。用最少的机器跑最多的微服务。
  • 代价(风险): 节点级内存超卖。当多个突发流量重叠时,宿主机物理内存会被瞬间抽干。此时 K8s 会无情驱逐这些 Request < Limit 的 Pod。服务会被频繁杀死重启。

总结与架构师建议

最后,为读者提供一份简单粗暴的速查表:

| 资源维度 | 参数配置 | 核心作用与架构意义 |
| ———— | ————————- | ———————————————————— |
| CPU | requests = 偏低真实值 | 必须设置。决定集群调度密度,保障 CPU 满载时的最低算力权重。 |
| CPU | limits = 不设置 (Unset) | 建议不设。消除 CFS 限流导致的延迟长尾,利用空闲算力应对突发。 |
| Memory | requests = 高位真实值 | 必须设置。决定在宿主机内存不足时,被驱逐(Eviction)的风险概率。 |
| Memory | limits = 贴近最大峰值 | 必须设置。防止内存泄漏拖垮整个物理节点。 |
| 核心服务 | Mem Req == Mem Lim | 牺牲一定的装箱率(账面资源),换取绝对不被系统无辜连累猎杀的稳定性。 |
| 边缘服务 | Mem Req < Mem Lim | 承担可能被节点驱逐的风险,换取集群机器成本的大幅度降低(超卖)。 |

最佳实践补丁: 如果你选择了“核心保命流”(Req==Lim)又心疼浪费的资源,请不要通过降低 Request 来制造差值,而是应该引入 HPA(水平自动扩缩容)。将单体内存压低,遇到流量洪峰时通过增加 Pod 副本数来抗压,这才是云原生架构“横向扩展”的终极奥义。

突破静态配置的死局:动态扩缩容(HPA & VPA)的底层逻辑

单靠静态设定 RequestsLimits,永远无法完美契合业务流量的波峰波谷。云原生真正的威力在于“按需变形”。

HPA (Horizontal Pod Autoscaler) – 横向分流,对抗高并发的终极武器

千万不要试图用单实例的 CPU 或内存极限去硬扛大促峰值,这既危险又昂贵。

  • 它与 Resources 的强绑定关系: HPA 默认是基于资源的利用率触发的(比如 CPU 达到 80%)。注意这里的坑:K8s 计算利用率的公式是 当前真实使用量 / Pod 的 CPU Request
    • 如果你不设置 CPU Request,基于 CPU 的 HPA 将彻底失效,因为分母为 0(无法计算百分比)。
    • 如果你把 CPU Request 设得过低(比如 10m),只要流量稍微波动,利用率瞬间飙升到 500%,HPA 会被频繁触发,导致集群产生严重的“扩容抖动”。
  • 架构师建议: 将单 Pod 的 Memory 限制死(Req == Lim),剥夺 CPU Limit,然后将 HPA 的阈值设为 CPU Request 的 70% 或基于并发请求数(如 Knative RPS)进行秒级横向扩容。

VPA (Vertical Pod Autoscaler) – 垂直修正,治疗“拍脑袋配置”的良药

很多时候,开发人员根本不知道自己的 Java 或 Go 程序到底需要多少内存。

  • 它的核心价值: VPA 组件会持续读取历史监控数据,自动帮你计算出最科学的 RequestsLimits
  • 工作模式与痛点:
    • Recommend 模式(推荐使用): 只提供修改建议,不主动干预。你可以结合 CI/CD 流水线,在下次发布时自动应用这些建议。
    • Auto 模式(谨慎使用): 自动修改配置。但在 K8s 1.27 版本之前,修改资源的代价是必须杀掉 Pod 重启。如果核心服务在高峰期被 VPA 重启,那就是人为制造的灾难。(注:K8s 1.27+ 引入了原地扩缩容 In-place Resize,未来这一痛点将被消除)。
  • 冲突警告: 绝对不要在同一个指标(比如 CPU)上同时开启 HPA 和 VPA,它们会互相打架,导致 Pod 数量和单体配额同时疯狂震荡。

架构师的军火库:资源水位探测与超卖神器

明白了理论,落地时我们需要工具。针对你前面担忧的“为了稳定浪费内存(Req==Lim),为了省钱牺牲稳定(Req<Lim)”的死局,目前业界有以下几款顶级开源工具可以破局。

Robusta KRR (Kubernetes Resource Recommender) —— 监控数据的“提纯器”

这正是你之前导数据的工具。它不改变 K8s 的调度底层,而是作为你的“首席精算师”。

  • 核心打法: KRR 从 Prometheus 提取真实历史水位,帮你打破“凭感觉填 YAML”的困境。
  • 解决的问题:
    • 它建议你删掉 CPU Limit,直接根除了 CFS Throttling(限流卡顿)问题。
    • 它根据 P99 分位值建议你的 Memory RequestLimit,让你能够安心地使用 Req == Lim 的策略,把所谓的“浪费(稳定性溢价)”压到最低。
  • 定位: 静态配置优化工具。适合日常巡检、CI/CD 准入拦截。

Koordinator (阿里开源) —— 终极

如果你对那 0.5G 被账面锁死的内存耿耿于怀,Koordinator 是解决这个问题的标准答案。它是阿里双十一大规模混部(Colocation)技术的开源版。

  • 核心打法(QoS 重新定义): 它绕过了原生的 K8s 调度器,引入了更精细的优先级:
    • LS (Latency Sensitive – 在线服务): 比如你的微服务,要求绝对稳定,设置 Req == Lim == 2G
    • BE (Best Effort – 离线任务): 比如日志压缩、数据跑批。
  • 它是怎么压榨资源的? 你的微服务申请了 2G,但平时只用 1G。Koordinator 的底层组件(Koordlet)会实时计算出这 1G 的“真实物理空闲”,然后把这 1G 临时借给 BE 级别的离线任务用
  • 稳定性兜底(秒级驱逐): 当你的微服务突然来了流量,需要用到 1.8G 内存时。Koordlet 会在毫秒级反应过来,直接把占用内存的离线任务强杀掉,把物理内存还给你的微服务。
  • 定位: 节点级物理资源压榨器。让“账面资源满载率”和“物理真实利用率”同时达到 80%+,而核心业务毫发无损。

Crane (腾讯开源) —— 具有预知能力的智能调度

如果你的集群跑在腾讯云 TKE 上,或者业务有明显的潮汐特性(比如外卖系统中午流量大),Crane 非常合适。

  • 核心打法(时间序列预测): 基于历史监控,用算法预测未来 24 小时的流量走势。
  • 解决的问题: HPA 是滞后的(流量打进来了,CPU 飙高了才扩容,此时往往已经卡顿)。Crane 的 EHPA(Effective HPA)可以在高峰期到来前 10 分钟,提前帮你把 Pod 扩容好
  • 防干扰调度: Crane 会识别出哪些节点物理 CPU 真正到了高水位,从而拦截 K8s 瞎眼调度器的分配请求,防止某些节点被活活拖死。
  • 定位: 智能弹性与成本分析平台。

Goldilocks (Fairwinds) —— 轻量级 Baseline 工具

  • 核心打法: 利用 VPA 的 Recommend 模式,结合一个非常直观的 UI 面板,给你的每一个 Deployment 展示一套推荐的 Request/Limit 基线。
  • 定位: 如果觉得 KRR 的命令行不够直观,可以作为集群可视化的资源推荐补充工具。

终极总结:现代云原生微服务资源配置蓝图

作为集群的管理者,你可以按照以下路径构建你的资源防线:

  1. 第一层(配置基线): 借助 Robusta KRRGoldilocks,为所有服务刷上基于历史数据的 Baseline。核心服务锁死 Mem Req == Mem Lim,拔掉 CPU Limit
  2. 第二层(弹性抗压): 依托科学的 CPU Request 设定,全面铺开 HPA。用增加副本数来应对突发,而不是靠单 Pod 的内存硬扛。
  3. 第三层(极致压榨): 如果集群规模足够大,机器成本成为痛点,引入 Koordinator。将无状态后台任务与核心微服务混合部署,吃干榨净每一兆物理内存。
版权声明:除特殊说明,博客文章均为Mark原创,依据CC BY-SA 4.0许可证进行授权,转载请附上出处链接及本声明。VIP内容严禁转载! | 广告招租请留言
暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇