Loading... # Kubernetes CPU Requests and Limits 深度解析——第二章 # 一、概述 ## 1. 背景介绍 ### A. 系列文章定位 本文是 Kubernetes 资源管理深度解析系列文章的第二章,专注于 CPU 资源的 requests 和 limits 在 Linux 操作系统层面的实际工作机制。 ### B. 前置知识 第一章已介绍 Pod 如何基于资源请求大小和节点容量进行调度匹配,以及节点的"满载程度"完全基于 requests,与实际资源使用或 limits 无关。 ## 2. 核心问题 Kubernetes 中的 CPU requests 和 limits 如何转换为 Linux OS 层面的具体配置?这意味着什么?如何基于此预测、保证或排除 CPU 资源相关的行为? # 二、从抽象到具体的转换机制 ## 1. 资源抽象的实现层次 ### A. 架构概览 Kubernetes 不是操作系统,它只是编排器。实际强制执行资源设置的职责由 Linux 承担。但 Linux 不理解 requests 和 limits 的抽象概念,需要进行转换。 ```mermaid graph TD A[Pod Spec CPU Requests/Limits] --> B[Kubelet] B --> C[容器运行时] C --> D[Linux Cgroups] D --> E[进程实际执行] ```  ### B. 实现组件 大多数 Kubernetes 资源抽象由 kubelet 和容器运行时使用 Linux 控制组(cgroups)及其设置实现。 ## 2. Cgroup 版本说明 当前有两种 cgroup API 版本: - cgroup v1:旧版本 - cgroup v2:新版本(本文以此为默认) 两者在功能上等效,v2 使用更统一的设置名称。 # 三、CPU 资源的复杂性 ## 1. Linux CFS 调度器基础 ### A. 核心概念 控制、分配和保留进程 CPU 时间的底层内核设施并非像"请给 nginx 250 毫核,谢谢"那样简单。 相关控制全部关联到 Linux 的完全公平调度器(CFS)。 ### B. CFS 特性 CFS 是一个比例进程调度器。假设不干预,CFS 会给每个可运行进程相等量的 CPU 时间。 ## 2. Kubernetes 使用的 CPU Cgroup 控制 ### A. Requests —— cpu.weight 在比例天平上放一个"手指": - 如果一个可运行进程的权重是另一个的两倍 - CFS 会给双倍权重的进程分配两倍的 CPU 时间 ### B. Limits —— cpu.max 不会改变进程可运行时的比例优先级,但可能导致进程周期性进入"暂停"状态,期间完全不获得 CPU 时间。 # 四、CPU Requests——比例天平上的权重 ## 1. 转换机制 ### A. 目标 Kubernetes 的目标是让每个容器的进程优先级对应于用户在容器 CPU resource request 中指定的比例。 ### B. 简化示例 在单核节点(1000m 容量)上: - 200m request:优先级 1/5 的 CPU 周期 - 250m request:优先级 1/4 的 CPU 周期 - 500m request:优先级 1/2 的 CPU 周期 ### C. 容量比例权重转换 Kubernetes 将毫核值转换为容量比例权重值。 ```mermaid graph LR A[CPU Request 250m] --> B{节点容量} B -->|1000m 单核| C[权重 1/4] B -->|2000m 双核| D[权重 1/8] C --> E[cpu.weight 设置] D --> E ```  ## 2. 比例权重的计算原理 ### A. 基础概念 cpu.weight 值本质上是相互比例的,没有神奇值能保证静态量的 CPU 时间。任何单个进程的比例优先级取决于其相对于其他运行进程的权重。 ### B. 通用分母法 要获得整数权重,可以计算通用分母并用于获得比例整数 cpu.weight 值。 ### C. Kubernetes 的实现保证 由于 Pod 调度和节点"满载"的实现方式,Kubernetes 确保为容器计算的小数值永远不会加总超过 1,因此 cgroup 的 CPU 优先级永远不会低于其 request-to-capacity 比例。 # 五、Burstable Pods 的特殊行为 ## 1. 突发容量的来源 节点上经常存在瞬间空闲 CPU 容量,未被特定容器保证: - 节点尚未"满载",部分容量未保证给任何特定容器 - 容器请求了 CPU 资源但当前未使用 ## 2. QoS 类的 Cgroup 层次结构 ### A. 层级结构 ```mermaid graph TD A[根 Cgroup] --> B[Guaranteed QoS] A --> C[Burstable QoS - burstable.slice] A --> D[BestEffort QoS - besteffort.slice] C --> E[Pod 1] C --> F[Pod 2] D --> G[Pod 3] B --> H[Pod 4] ```  ### B. 关键原则 - Cgroups 配置在层次结构中,像洋葱一样有层级 - 每个层级根据 cpu.weight 在同级对等体之间按比例分配 CPU 时间 - 一个层级分配给 cgroup 的 CPU 时间可以在下一层级进一步细分 ## 3. 突发容量的分配特性 ### A. 分配优先级 在 Kubernetes 设置的组上下文中: - Guaranteed QoS pods 相互竞争,以及与 Burstable 超级父级和 BestEffort 微父级竞争 - 如果即使一个 Burstable QoS 容器想要 CPU 时间,它将比 Guaranteed 或 BestEffort QoS pods 获得多得多的可用"额外"周期 - 即使节点上每个 BestEffort 容器都想要 CPU 时间,它们与 Guaranteed 和 Burstable QoS 容器竞争的 cpu.weight 总和永远不会超过 BestEffort 微父级分配的权重 1 ### B. 行为影响 ```mermaid graph LR A[空闲 CPU 容量] --> B{分配竞争} B --> C[Burstable QoS - 高优先级] B --> D[Guaranteed QoS - 中优先级] B --> E[BestEffort QoS - 低优先级] C --> F[获得大部分突发容量] D --> G[获得少量突发容量] E --> H[几乎无突发容量] ```  ### C. 潜在风险 对于没有任何最低性能要求或敏感性的最佳工作负载,这种突发优先级行为可能是高度理想的。但是,当工作负载有任何最低要求时,这种不平等的 CPU 突发容量分配可能会出乎意料或产生问题。 ## 4. 避免 CPU 饥饿的策略 ### A. 正确设置 Requests 确保最低 CPU 分配的最可靠方法是让工作负载请求它。为每个需要它们的容器设置 CPU requests,并将其设置为正确值。 ### B. 使用 Limits(不推荐) 另一种可能更复杂的缓解策略是尝试使用 CPU limits 来压制最可能的坏角色或贪婪 CPU 消费者的 CPU 使用。 ### C. 行业共识 limits 方法起初可能很诱人,但行业共识已围绕通用工作负载模板完全不使用 CPU limits,而是依赖 request 方法。 # 六、CPU Limits——进程暂停机制 ## 1. CFS 带宽控制 ### A. 核心概念 为容器定义 Kubernetes CPU limits 时,容器运行时将其转换为容器 cgroup 上的 cpu.max 值,容器进程将受制于 CFS 的带宽控制机制。 ### B. 关键概念 - 带宽控制基于时间周期 - 如果分配了 CPU 配额,则在每个周期授予的运行时间(微秒)方面设置 - 一旦 CFS 计费系统确定进程已消耗周期的所有配额,进程就会受到限制 - 受到限制时,进程实际上被暂停 - 每个周期开始时,配额刷新,受限进程再次可运行 ## 2. cpu.max 配置 cpu.max 通过设置 MAX PERIOD 字符串配置,其中 MAX 是组在每个 PERIOD(微秒)中可以运行的微秒数。 ## 3. Limits 的性能影响 ### A. 场景示例 假设一个应用需要 120ms CPU 时间处理请求: - 配额周期:100ms(Kubernetes 默认) - CPU limit:400m 计算: - 400m = 4/10 = 2/5 = 0.4 的每个周期 - 或每个 100ms 周期中的 40ms ### B. 延迟影响 ```mermaid sequenceDiagram participant Req as 请求到达 participant CPU as CPU 执行 participant Quota as 配额检查 Req->>CPU: 开始处理 (需要 120ms) CPU->>Quota: 使用 40ms 配额 Quota->>CPU: 配额耗尽,暂停 Note over CPU: 暂停 60ms Quota->>CPU: 新周期,配额刷新 CPU->>Quota: 使用 40ms 配额 Quota->>CPU: 配额耗尽,暂停 Note over CPU: 暂停 60ms Quota->>CPU: 新周期,配额刷新 CPU->>Req: 完成 (总耗时 220ms) ```  虽然这可以说是 limit 旨在做的事情,但当单独查看应用时,存在潜在的错失机会。如果在此请求处理期间没有其他进程竞争 CPU 时间,那么限制引入了 115ms 的可避免延迟。在此期间,CPU 没有被用于任何其他事情。 ### C. 设计反思 限制总是给应用引入约束,可能影响延迟。限制是面向约束的,那么为什么通常要引入这种隔离约束? 答案:通常不会。 ## 4. Limits vs Requests 的根本区别 ### A. 常见误解 人们通常直觉地认为 limits 与公平性相关,确保每个工作负载获得其分配的时间。 ### B. 实际机制 如我们所学,这实际上不是真的: - Limits 本身不是运行时的保证者 - 运行时分配保证来自 CPU requests,而不是 limits - Limits 唯一做的是防止个别应用使用节点上额外的 CPU 时间(如果碰巧有可用的话) ```mermaid graph TD A[资源保证机制] --> B[CPU Requests] A --> C[CPU Limits] B --> D[设置 cpu.weight] B --> E[保证最低 CPU 时间] B --> F[比例分配] C --> G[设置 cpu.max] C --> H[限制最大 CPU 时间] C --> I[可能导致限流] D --> J[✅ 保证机制] E --> J F --> J G --> K[❌ 约束机制] H --> K I --> K ```  # 七、最佳实践建议 ## 1. 优先使用 Requests ### A. 原因 - Requests 提供真正的资源保证 - 基于比例分配,充分利用空闲容量 - 不引入额外的延迟 ### B. 设置原则 为每个需要保证的容器设置 CPU requests,并设置为正确值。 ## 2. 慎用 Limits ### A. 潜在问题 - 引入不必要的延迟 - 限制应用利用空闲资源的能力 - 在单节点场景下可能完全浪费 CPU ### B. 适用场景 仅在需要防止特定工作负载占用过多资源时谨慎使用。 ## 3. 监控与调优 ### A. 监控指标 - CPU 使用率 - 节点容量利用率 - 请求延迟(特别是 P99) ### B. 调优建议 - 基于实际使用量设置 requests - 定期评估和调整资源配置 - 考虑使用垂直 Pod 自动扩缩容 # 八、总结 ## 1. 核心要点 ### A. Requests 机制 - 通过 cpu.weight 实现比例分配 - 保证最低 CPU 时间 - 允许突发使用空闲容量 ### B. Limits 机制 - 通过 cpu.max 实现带宽控制 - 限制最大 CPU 使用 - 可能引入额外延迟 ### C. QoS 层次结构 - Burstable QoS 在突发容量分配中有优势 - BestEffort QoS 几乎无法获得突发容量 - 合理设置 requests 对避免 CPU 饥饿至关重要 ## 2. 实践指导 - 优先使用 CPU requests 保证资源 - 慎用 CPU limits,避免不必要的性能损失 - 理解 QoS 层级对突发容量的影响 - 基于实际需求合理设置资源配置 # 九、后续章节预览 下一章将深入探讨内存资源: - 内存 requests 和 limits 如何转换为 Linux 进程设置 - 比例建模是否同样适用于内存 - 节点上发生内存竞争时会发生什么 - Requests 和 limits 如何影响结果 *** ## 参考资料 1. [How K8s CPU Requests and Limits Actually Work — Chapter 2](https://telegra.ph/How-K8s-CPU-Requests-and-Limits-Actually-Work--Chapter-2-11-07-3) 2. [Kubernetes Overview, News and Trends | The New Stack](https://thenewstack.io/how-k8s-cpu-requests-and-limits-actually-work-chapter-2/) 最后修改:2026 年 01 月 18 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏