摘要
在 Kubernetes 集群中,资源管理是保障应用稳定性和集群效率的核心环节。requestslimits 的配置直接影响 Pod 的调度行为、资源争抢时的分配策略,甚至容器的生存状态。本文将深入探讨这些配置的底层原理,并通过实际场景分析,揭示 Kubernetes 如何通过 Linux cgroups 实现资源隔离与控制。

1. 为什么需要关注资源管理?

Kubernetes 调度器通过 requestslimits 实现以下目标:

  • 调度决策:确保节点有足够资源运行 Pod。
  • 资源隔离:防止单个容器耗尽节点资源,导致其他应用崩溃。
  • 优先级分配:在资源紧张时,按策略分配 CPU/内存。

忽视资源管理可能导致:

  • 调度失败:Pod 因节点资源不足无法启动。
  • 资源争抢:容器因内存溢出(OOM)被强制终止。
  • 性能波动:CPU 密集型应用拖垮整个节点。

2. 核心概念:Requests 与 Limits

定义

  • requests:容器启动所需的最小资源量,直接影响调度决策。
  • limits:容器允许使用的资源上限,硬性限制运行时资源。

配置规则

  • requestslimits:若 requests > limits,Kubernetes 直接拒绝创建 Pod。
  • 未设置 requests 时,默认等于 limits(仅当 limits 存在时生效)。

3. 配置场景与行为分析

场景 1:不设置任何 Requests/Limits

containers:
- name: nginx
  image: nginx
  # 无 resources 配置

行为分析

  • 调度:Pod 可能被分配到高负载节点。
  • 运行时
    • CPU:无限制,按 CFS 公平竞争。
    • 内存:无限制,可能触发 OOM Killer。
  • 风险:节点稳定性差,容器可能被随机终止。

场景 2:仅设置 Requests

resources:
  requests:
    cpu: "100m"
    memory: "256Mi"

行为分析

  • 调度:选择满足 requests 的节点。
  • 运行时
    • CPU:可超用,按 cpu.shares 比例分配。
    • 内存:可超用,但 OOM 风险高。
  • 用途:适合需弹性资源的批处理任务。

场景 3:仅设置 Limits

resources:
  limits:
    cpu: "200m"
    memory: "512Mi"

行为分析

  • 调度requests 默认等于 limits,可能因节点资源不足导致调度失败。
  • 运行时
    • CPU:严格限制为 limits
    • 内存:严格限制为 limits,超限触发 OOM。
  • 风险:配置不合理时,资源利用率低。

场景 4:同时设置 Requests 和 Limits

resources:
  requests:
    cpu: "100m"
    memory: "256Mi"
  limits:
    cpu: "200m"
    memory: "512Mi"

行为分析

  • 调度:按 requests 选择节点。
  • 运行时
    • CPU:弹性使用(requests ~ limits)。
    • 内存:严格限制为 limits
  • 最佳实践:平衡资源保障与利用率。

4. 底层机制:cgroups 如何实现资源限制?

CPU 管理

  • cpu.shares:由 requests.cpu 计算,决定资源争抢时的分配比例。
    公式cpu.shares = requests.cpu * 1024
    (例如:requests.cpu=500mcpu.shares=512

  • cpu.cfs_quota_us:由 limits.cpu 计算,限制容器在周期(cpu.cfs_period_us,默认 100ms)内的最大 CPU 时间。
    公式quota = limits.cpu * period
    (例如:limits.cpu=1quota=100ms,即每秒最多使用 1 核)

内存管理

  • memory.limit_in_bytes:由 limits.memory 设置,容器内存使用量超过此值时触发 OOM Killer。

5. 常见问题解答

Q1:Requests 可以超过 Limits 吗?

不能。Kubernetes 会直接拒绝此类配置,因为逻辑矛盾(requests 表示最低需求,而 limits 是上限)。

Q2:为什么 CPU Limits 可能引发性能问题?

硬性限制(CFS 配额)可能导致 CPU 节流(Throttling),尤其是在突发负载场景下。建议对延迟敏感型应用谨慎设置 CPU Limits。

Q3:如何监控资源使用?

  • kubectl top pod:查看实时资源消耗。
  • Prometheus + Grafana:分析历史趋势。
  • 容器内工具:/sys/fs/cgroup/cpu//sys/fs/cgroup/memory/ 下的 cgroups 接口。

Q4:requests 能否超过 limits

答案:不能。
如果 requests 的值超过 limits,Kubernetes 会直接拒绝创建 Pod,并报错 Invalid value。这是因为:

  • 逻辑矛盾requests 表示容器需要的最小资源,而 limits 是资源使用的上限。若 requests > limits,调度器无法保证容器获得其声明的“最低需求”,导致资源分配矛盾。
  • 资源保障失效:节点可能分配了一个无法满足 requests 的 Pod,最终因资源不足导致容器崩溃。

示例错误配置:

containers:
- name: my-container
  image: nginx
  resources:
    requests:
      cpu: "200m"  # 0.2 核
      memory: "512Mi"
    limits:
      cpu: "100m"  # 0.1 核(小于 requests)
      memory: "256Mi" # 小于 requests

结果:Pod 创建失败,报错类似:
Error: Pod "invalid-pod" is invalid: spec.containers[0].resources.requests: Invalid value: "200m": must be less than or equal to cpu limit.

Q5:cpu.shares 是什么?与资源管理的关系

定义

cpu.shares 是 Linux cgroups 中用于控制 CPU 时间片分配比例 的参数。在 Kubernetes 中,它直接由容器的 requests.cpu 计算而来。

计算公式

  • cpu.shares = requests.cpu * 1024
    其中,1 核 CPU = 1024 shares。例如:
    • requests.cpu: 100m(即 0.1 核) → cpu.shares = 0.1 * 1024 = 102
    • requests.cpu: 500m(即 0.5 核) → cpu.shares = 512

作用

  • 资源竞争时的分配比例:当节点 CPU 资源紧张时,容器按 cpu.shares 的比例分配 CPU 时间片。
    • 例如:容器 A 的 cpu.shares=102,容器 B 的 cpu.shares=512,则它们的 CPU 时间分配比例为 1:5。
  • limits 的区别
    • limits 通过 cpu.cfs_quota_uscpu.cfs_period_us 硬性限制容器可用的 CPU 时间总量(即使节点有空闲 CPU,容器也无法突破 limits)。
    • cpu.shares 仅在资源竞争时生效,不限制容器在空闲时使用更多 CPU。

示例场景

假设两个容器运行在同一节点:

  • 容器 A:requests.cpu=100mcpu.shares=102
  • 容器 B:requests.cpu=500mcpu.shares=512

当 CPU 资源不足时:

  • 容器 A 获得 102/(102+512) ≈ 16.6% 的 CPU 时间。
  • 容器 B 获得 512/(102+512) ≈ 83.3% 的 CPU 时间。

6. 最佳实践

  1. 始终显式设置 Requests 和 Limits
    避免资源泄漏和调度不确定性。

  2. 内存 Limits 必须设置
    防止 OOM 导致容器随机终止。

  3. CPU Limits 谨慎使用
    若非必要,可仅设置 Requests 以允许弹性使用。

  4. 使用 Vertical Pod Autoscaler (VPA)
    自动调整 Requests/Limits 以适应应用负载。

  5. 测试与调优
    通过压力测试确定合理的资源值。


深入解析 cpu.cfs_quota_uscpu.cfs_period_us 的关系

在 Kubernetes 中,CPU 资源的硬性限制通过 Linux cgroups 的 cpu.cfs_quota_uscpu.cfs_period_us 实现。正确理解这两个参数的关系,是掌握 CPU 限制机制的关键。

1. 参数定义

  • cpu.cfs_period_us
    表示一个 CPU 资源分配的周期长度,单位为微秒(μs)。
    默认值100000μs(即 100 毫秒,或 0.1 秒)。

  • cpu.cfs_quota_us
    表示在一个周期内,容器允许使用的最大 CPU 时间,单位为微秒(μs)。
    计算公式

    quota = limits.cpu * period
    

2. 示例验证

假设配置 limits.cpu: 1(即 1 核),则:

  • period = 100,000μs(默认值,100ms)。
  • quota = 1(核) × 100,000μs = 100,000μs(即 100ms)。

此时,容器的 CPU 使用规则为:

  • 每个周期(100ms)内,最多使用 100ms 的 CPU 时间
  • 等效效果:容器可以持续占用 1 核的 CPU(100ms/100ms = 100%)。

3. 用户的疑问点

用户的问题本质是:
“如果 period 是 0.1 秒(100ms),而 quota 是 100ms,是否意味着每 0.1 秒最多使用 1 核?这与‘每秒最多使用 1 核’是否矛盾?”

答案:不矛盾,两者是等价的。

  • 单周期视角:每个 100ms 周期内,容器最多使用 100ms 的 CPU 时间(即 1 核的 100%)。
  • 宏观视角:每秒包含 10 个周期(1000ms / 100ms = 10),每个周期使用 100ms CPU 时间,总计 10 × 100ms = 1000ms(即 1 核/秒)。
  • 结论:无论是从单周期还是整秒的视角,容器都被允许完全占用 1 核的 CPU。

4. 其他场景验证

场景 1:限制为 0.5 核
  • 配置limits.cpu: 500m(即 0.5 核)。
  • 计算
    quota = 0.5 × 100,000μs = 50,000μs(即 50ms)。
  • 行为
    • 每个周期(100ms)内,容器最多使用 50ms 的 CPU 时间。
    • 等效效果:每秒使用 10 × 50ms = 500ms(即 0.5 核)。
场景 2:限制为 2 核
  • 配置limits.cpu: 2(即 2 核)。
  • 计算
    quota = 2 × 100,000μs = 200,000μs(即 200ms)。
  • 行为
    • 每个周期(100ms)内,容器最多使用 200ms 的 CPU 时间。
    • 注意:由于单个周期仅 100ms,容器需要多核支持(例如节点有 2 核),才能在每个周期内使用 200ms 的 CPU 时间(即 2 核并行)。

5. 公式的底层逻辑

  • 核心目标:通过 quota/period 的比值,限制容器使用 CPU 的比例。

    • 比值 = 1 → 容器可使用 1 核。
    • 比值 = 0.5 → 容器可使用 0.5 核。
    • 比值 = 2 → 容器需要至少 2 核的节点才能满足需求。
  • 单位一致性
    limits.cpu 是无单位的核数,period 是时间单位(μs),quota 是时间单位(μs)。
    公式 quota = limits.cpu × period 本质是通过时间比例实现核数限制。


6. 常见误区

误区 1:“quota 必须小于 period”
  • 纠正:若 quota > period,表示容器需要多核支持。例如:
    • quota=200,000μsperiod=100,000μs → 需要 2 核。
    • 若节点仅有 1 核,容器仍可运行,但会被限制为 1 核(实际效果与 quota=100,000μs 相同)。
误区 2:“limits.cpu=1 表示只能使用 1 核/秒”
  • 纠正limits.cpu=1 表示容器可以持续占用 1 核的 CPU,而非每秒累计使用 1 核。
    • 若进程需要 1 核的算力,它可以在每个周期内完全占用 CPU。

总结

  • 公式 quota = limits.cpu × period 是正确的,它通过时间比例实现核数限制。
  • limits.cpu=1 + 默认 period=100ms 的配置允许容器完全占用 1 核的 CPU。
  • 理解这一机制后,可以更精准地配置 CPU 限制,避免资源争抢或浪费。

结语

Kubernetes 资源管理看似简单,却直接影响集群的稳定性和效率。理解 requestslimits 与 cgroups 的协作机制,是构建高可用应用的关键一步。通过合理配置和持续优化,开发者可以在资源利用率与稳定性之间找到最佳平衡点。