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

特性状态: Kubernetes v1.33 [beta] (默认启用: true)

本文解释了如何在不重新创建 Pod 的情况下修改分配给容器的 CPU 和内存资源请求和限制。

传统上,修改 Pod 的资源需求需要删除现有 Pod 并创建一个新的替代 Pod,这通常由工作负载控制器管理。就地 Pod 扩缩(In-place Pod Resize)允许在运行中的 Pod 内修改容器的 CPU/内存分配,同时可能避免应用程序中断。

核心概念

  • 所需资源: 容器的 spec.containers[*].resources 表示容器的所需资源,并且对于 CPU 和内存是可变的。
  • 实际资源: status.containerStatuses[*].resources 字段反映了运行中容器的当前配置资源。对于尚未启动或已重新启动的容器,它反映了其下次启动时分配的资源。
  • 触发扩缩: 你可以通过更新 Pod 规约中的所需 requestslimits 来请求扩缩。这通常使用针对 Pod 的 resize 子资源的 kubectl patchkubectl applykubectl edit 来完成。当所需资源与已分配资源不匹配时,Kubelet 将尝试扩缩容器。
  • 已分配资源(高级): status.containerStatuses[*].allocatedResources 字段跟踪由 Kubelet 确认的资源值,主要用于内部调度逻辑。对于大多数监控和验证目的,请关注 status.containerStatuses[*].resources

如果节点上有 Pod 正在等待或扩缩未完成(参阅下文的Pod 扩缩状态),调度器在做出调度决策时,会使用容器所需请求、已分配请求和实际状态请求中的最大值

准备工作

你需要拥有一个 Kubernetes 集群,并且必须配置 kubectl 命令行工具,以便与你的集群通信。建议你在至少有两个非控制平面主机的节点的集群上运行本教程。如果你还没有集群,可以使用 minikube 创建一个,或者使用以下 Kubernetes 演练场之一:

你的 Kubernetes 服务器版本必须是 1.33 或更高。

要检查版本,输入 kubectl version

你集群的控制平面和所有节点必须启用 InPlacePodVerticalScaling 特性门控

kubectl 客户端版本必须至少是 v1.32 才能使用 --subresource=resize 标志。

Pod 扩缩状态

Kubelet 更新 Pod 的状态条件(conditions)来指示扩缩请求的状态:

  • type: PodResizePending:Kubelet 无法立即满足请求。message 字段提供了原因说明。
    • reason: Infeasible:请求的扩缩在当前节点上不可能实现(例如,请求的资源量超过节点所拥有的资源)。
    • reason: Deferred:请求的扩缩当前不可能实现,但稍后可能变得可行(例如,如果另一个 Pod 被删除)。Kubelet 将重试扩缩。
  • type: PodResizeInProgress:Kubelet 已接受扩缩并分配了资源,但变更仍在应用中。这通常是短暂的,但也可能需要更长时间,具体取决于资源类型和运行时行为。应用过程中的任何错误都将报告在 message 字段中(以及 reason: Error)。

容器扩缩策略

你可以通过在容器规约中设置 resizePolicy 来控制容器在扩缩时是否需要重新启动。这允许基于资源类型(CPU 或内存)进行精细控制。

    resizePolicy:
    - resourceName: cpu
      restartPolicy: NotRequired
    - resourceName: memory
      restartPolicy: RestartContainer
  • NotRequired:(默认)将资源变更应用到运行中的容器,而无需重新启动它。
  • RestartContainer:重新启动容器以应用新的资源值。这对于内存变更通常是必需的,因为许多应用程序和运行时无法动态调整其内存分配。

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

示例场景

考虑一个容器,其 CPU 配置了 restartPolicy: NotRequired,而内存配置了 restartPolicy: RestartContainer

  • 如果只修改 CPU 资源,容器将就地扩缩。
  • 如果只修改内存资源,容器将重新启动。
  • 如果同时修改 CPU 和内存资源,容器将重新启动(由于内存策略)。

限制

对于 Kubernetes 1.33,就地扩缩 Pod 资源存在以下限制:

  • 资源类型: 只有 CPU 和内存资源可以扩缩。
  • 内存减少: 内存限制不能减少,除非内存的 resizePolicyRestartContainer。内存请求通常可以减少。
  • QoS 类别: Pod 的原始服务质量 (QoS) 类别(Guaranteed、Burstable 或 BestEffort)在创建时确定,并且不能通过扩缩来更改。扩缩后的资源值仍必须遵循原始 QoS 类别的规则:
    • Guaranteed:扩缩后,CPU 和内存的请求值必须继续等于限制值。
    • Burstable:CPU 和内存的请求值和限制值不能同时相等(因为这将将其更改为 Guaranteed 类别)。
    • BestEffort:不能添加资源需求(requestslimits)(因为这将将其更改为 Burstable 或 Guaranteed 类别)。
  • 容器类型: 不可重新启动的Init 容器临时容器不能扩缩。Sidecar 容器可以扩缩。
  • 资源移除: 一旦设置了资源请求和限制,就不能完全移除它们;只能更改为不同的值。
  • 操作系统: Windows Pod 不支持就地扩缩。
  • 节点策略:静态 CPU 或内存管理器策略管理的 Pod 不能就地扩缩。
  • 交换: 利用交换内存的 Pod 不能扩缩内存请求,除非内存的 resizePolicyRestartContainer

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

示例 1:无需重新启动即可扩缩 CPU

首先,创建一个 Pod,它被设计用于就地 CPU 扩缩和需要重新启动的内存扩缩。

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 类别。验证其初始状态:

# Wait a moment for the pod to be running
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 patch 命令。

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

# Alternative methods:
# 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 patch pod resize-demo --subresource resize --patch \
  '{"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"),这很可能超过节点容量。

# Attempt to patch with an excessively large CPU request
kubectl patch pod resize-demo --subresource resize --patch \
  '{"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: 800m, memory: 300Mi),因为 Kubelet 未应用不可行的扩缩。
  • 由于这次失败的尝试,restartCount 不会改变。

要解决此问题,你需要再次使用可行的资源值修补 Pod。

清理

删除 Pod:

kubectl delete pod resize-demo

下一步

针对应用开发者

针对集群管理员

最后修改于 2025 年 4 月 7 日太平洋标准时间 9:46:Update InPlacePodVerticalScaling docs for v1.33 beta (#50290) (c014f72fbb)