调整分配给容器的 CPU 和内存资源

特性状态: Kubernetes v1.33 [beta] (enabled by default: true)

本页面说明了如何在不重新创建 Pod 的情况下,更改分配给容器的 CPU 和内存资源请求与限制。

传统上,更改 Pod 的资源需求需要删除现有 Pod 并创建一个替代 Pod, 这通常由工作负载控制器管理。 而就地 Pod 调整功能允许在运行中的 Pod 内变更容器的 CPU 和内存分配,从而可能避免干扰应用。

关键概念:

  • 期望资源(Desired Resources):容器的 spec.containers[*].resources 字段表示容器的期望资源,对于 CPU 和内存是可变的。
  • 实际资源(Actual Resources)status.containerStatuses[*].resources 字段反映当前运行容器实际配置的资源。 对于尚未启动或重新启动的容器,该字段表示其下次启动时分配的资源。
  • 触发调整(Triggering a Resize):你可以通过更新 Pod 规约中的 requestslimits 来请求调整。 这通常通过 kubectlkubectl applykubectl edit 操作 Pod 的 resize 子资源来完成。 当期望资源与已分配资源不一致时,Kubelet 会尝试调整容器资源。
  • 已分配资源(Allocated Resources,进阶)status.containerStatuses[*].allocatedResources 字段用于记录由 Kubelet 确认的资源值,主要用于内部调度逻辑。 在大多数监控和验证场景中,建议关注 status.containerStatuses[*].resources 字段。

如果某个节点上存在处于挂起或未完成调整状态的 Pod(见下文 Pod 调整状态), 调度器会在进行调度决策时, 使用容器的期望请求、已分配请求和实际请求三者中的最大值

准备开始

你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 1.33.

要获知版本信息,请输入 kubectl version.

你需要在控制平面和集群中的所有节点上启用 InPlacePodVerticalScaling 特性门控

要使用 --subresource=resize 参数,kubectl 客户端版本需至少为 v1.32。

Pod 大小调整状态

Kubelet 会通过更新 Pod 的状态状况来反映调整请求的当前状态:

  • type: PodResizePending:Kubelet 当前无法立即执行该请求。message 字段会说明原因:
    • reason: Infeasible:请求的资源在当前节点上不可行(例如请求超出节点总资源)。
    • reason: Deferred:请求的资源当前无法满足,但未来可能满足(例如其他 Pod 被移除后), Kubelet 会重试调整。
  • type: PodResizeInProgress:Kubelet 已接受调整并分配了资源,但调整仍在进行中。
    这一状态通常很短暂,但也可能因资源类型或运行时行为而延长。 执行过程中的任何错误都会在 message 字段中报告,同时带有 reason: Error

容器调整策略

容器可以在资源需求中指定可选的 resizePolicy 数组。
该数组中的每一项定义了某种资源在就地调整期间应如何处理。

你可以通过在容器规约中设置 resizePolicy,控制在调整资源时容器是否需要重启。 这样可以针对不同资源类型(CPU 或内存)进行精细化控制。

    resizePolicy:
    - resourceName: cpu
      restartPolicy: NotRequired
    - resourceName: memory
      restartPolicy: RestartContainer
  • NotRequired:(默认)在不重启容器的情况下应用资源变更。
  • RestartContainer:重启容器以应用新的资源值。 对于内存变更,许多应用和运行时无法动态调整内存分配,因此通常需要重启。

如果未为某个资源指定 resizePolicy[*].restartPolicy,则默认为 NotRequired

示例场景:

考虑一个容器,其 CPU 的 restartPolicyNotRequired,内存的 restartPolicyRestartContainer

  • 如果仅更改 CPU 资源,容器将原地调整大小。
  • 如果仅更改内存资源,容器将重启。
  • 如果同时更改 CPU 和内存资源,容器将重启(由于内存策略)。

限制

对于 Kubernetes v1.33,原地调整 Pod 资源大小存在以下限制:

  • 资源类型:只能调整 CPU 和内存资源。
  • 内存减少:除非内存的 resizePolicyRestartContainer,否则内存限制不能减少。 内存请求通常可以减少。
  • QoS 类:Pod 的原始服务质量(QoS)类 (Guaranteed、Burstable 或 BestEffort)在创建时确定,不能通过调整大小来更改。 调整后的资源值仍必须遵守原始 QoS 类的规则:
    • Guaranteed:调整后,CPU 和内存的请求必须继续等于限制。
    • Burstable:CPU 和内存的请求和限制不能同时变为相等 (因为这会将其更改为 Guaranteed)。
    • BestEffort:不能添加资源要求(requestslimits) (因为这会将其更改为 Burstable 或 Guaranteed)。
  • 资源移除:一旦设置了资源请求和限制,就不能完全移除; 只能更改为不同的值。
  • 操作系统:Windows Pod 不支持原地调整大小。
  • 交换内存:使用交换内存的 Pod 不能调整内存请求, 除非内存的 resizePolicyRestartContainer

这些限制可能会在未来的 Kubernetes 版本中放宽。

示例 1:调整 CPU 而不重启

首先,创建一个设计用于原地 CPU 调整和需要重启的内存调整的 Pod。

apiVersion: v1
kind: Pod
metadata:
  name: resize-demo
spec:
  containers:
  - name: pause
    image: registry.k8s.io/pause:3.8
    resizePolicy:
    - resourceName: cpu
      restartPolicy: NotRequired # Default, but explicit here
    - resourceName: memory
      restartPolicy: RestartContainer
    resources:
      limits:
        memory: "200Mi"
        cpu: "700m"
      requests:
        memory: "200Mi"
        cpu: "700m"

创建 Pod:

kubectl create -f pod-resize.yaml

这个 Pod 以 Guaranteed QoS 类启动。验证其初始状态:

# 等待 Pod 运行
kubectl get pod resize-demo --output=yaml

观察 spec.containers[0].resourcesstatus.containerStatuses[0].resources。 它们应该与清单文件匹配(700m CPU,200Mi 内存)。注意 status.containerStatuses[0].restartCount(应该为 0)。

现在,将 CPU 请求和限制增加到 800m。使用带有 --subresource resize 命令行参数的 kubectl

kubectl  pod resize-demo --subresource resize -- \
  '{"spec":{"containers":[{"name":"pause", "resources":{"requests":{"cpu":"800m"}, "limits":{"cpu":"800m"}}}]}}'

# 替代方法:
# kubectl -n qos-example edit pod resize-demo --subresource resize
# kubectl -n qos-example apply -f <updated-manifest> --subresource resize

在应用补丁后再次检查 Pod 状态:

kubectl get pod resize-demo --output=yaml --namespace=qos-example

你应该看到:

  • spec.containers[0].resources 现在显示 cpu: 800m
  • status.containerStatuses[0].resources 也显示 cpu: 800m,表明节点上的调整已成功。
  • status.containerStatuses[0].restartCount 保持为 0,因为 CPU 的 resizePolicyNotRequired

示例 2:调整内存并重启

现在,将同一个 Pod 的内存增加到 300Mi。 由于内存的 resizePolicyRestartContainer,容器将会重启。

kubectl  pod resize-demo --subresource resize -- \
  '{"spec":{"containers":[{"name":"pause", "resources":{"requests":{"memory":"300Mi"}, "limits":{"memory":"300Mi"}}}]}}'

在应用补丁后立即检查 Pod 状态:

kubectl get pod resize-demo --output=yaml

你现在应该观察到:

  • spec.containers[0].resources 显示 memory: 300Mi
  • status.containerStatuses[0].resources 也显示 memory: 300Mi
  • status.containerStatuses[0].restartCount 增加到 1(如果之前发生过重启,可能会更多), 表明容器已重启以应用内存变更。

故障排查:不可行的调整请求

接下来,尝试请求不合理的 CPU 数量,例如 1000 个完整核心(写作 "1000" 而不是 "1000m" 毫核),这很可能超出节点容量。

# 尝试使用过大的 CPU 请求进行补丁
kubectl  pod resize-demo --subresource resize -- \
  '{"spec":{"containers":[{"name":"pause", "resources":{"requests":{"cpu":"1000"}, "limits":{"cpu":"1000"}}}]}}'

查询 Pod 的详细信息:

kubectl get pod resize-demo --output=yaml

你会看到表明问题的变更:

  • spec.containers[0].resources 反映了期望状态(cpu: "1000")。
  • Pod 添加了一个 type: PodResizePendingreason: Infeasible 的条件。
  • 状况的 message 会解释原因(Node didn't have enough capacity: cpu, requested: 800000, capacity: ...
  • 重要的是,status.containerStatuses[0].resources 仍然显示之前的值cpu: 800mmemory: 300Mi), 因为不可行的调整未被 Kubelet 应用。
  • 由于这次失败的尝试,restartCount 不会发生变化。

要修复这个问题,你需要使用可行的资源值再次对 Pod 进行补丁。

清理

删除 Pod:

kubectl delete pod resize-demo

接下来

对于应用开发人员

对于集群管理员