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

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

此页面介绍了如何更改分配给容器的 CPU 和内存资源请求和限制,**无需重新创建 Pod**。

传统上,更改 Pod 的资源需求需要删除现有 Pod 并创建替代 Pod,这通常由工作负载控制器管理。就地 Pod 调整大小允许在运行中的 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 的状态条件以指示调整大小请求的状态

  • type: PodResizePending:Kubelet 无法立即批准请求。message 字段提供了解释。
    • reason: Infeasible:请求的调整大小在当前节点上不可能(例如,请求的资源超出节点拥有的资源)。
    • reason: Deferred:请求的调整大小目前不可能,但以后可能会变得可行(例如,如果其他 Pod 被移除)。Kubelet 将重试调整大小。
  • type: PodResizeInProgress:Kubelet 已接受调整大小并分配了资源,但更改仍在应用中。这通常很短暂,但根据资源类型和运行时行为可能需要更长时间。执行期间的任何错误都会在 message 字段中报告(以及 reason: Error)。

kubelet 如何重试延迟的调整大小

如果请求的调整大小被**延迟**,kubelet 将定期重新尝试调整大小,例如当另一个 Pod 被移除或缩容时。如果存在多个延迟的调整大小,它们将按照以下优先级重试

  • 具有更高优先级的 Pod(基于 PriorityClass)将首先重试其调整大小请求。
  • 如果两个 Pod 具有相同的优先级,则优先重试 Guaranteed Pod 的调整大小,然后再重试 Burstable Pod 的调整大小。
  • 如果其他条件都相同,则处于 Deferred 状态时间更长的 Pod 将被优先考虑。

较高优先级的调整大小被标记为待处理不会阻止剩余待处理的调整大小被尝试;即使较高优先级的调整大小再次被延迟,所有剩余的待处理调整大小仍将重试。

利用 observedGeneration 字段

特性状态: Kubernetes v1.34 [beta] (默认启用:true)
  • 顶级 status.observedGeneration 字段显示与 kubelet 已确认的最新 Pod 规范对应的 metadata.generation。你可以使用它来确定 kubelet 已处理的最新调整大小请求。
  • PodResizeInProgress 条件中,conditions[].observedGeneration 字段指示当前正在进行的调整大小启动时 PodSpec 的 metadata.generation
  • PodResizePending 条件中,conditions[].observedGeneration 字段指示待处理调整大小的分配上次尝试时 PodSpec 的 metadata.generation

容器调整大小策略

你可以通过在容器规范中设置 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.34,就地调整 Pod 资源大小有以下限制

  • 资源类型: 只能调整 CPU 和内存资源的大小。
  • 内存减少: 如果内存调整大小重启策略为 NotRequired(或未指定),kubelet 将尽力防止在减少内存限制时发生 oom-kill,但不提供任何保证。在减少容器内存限制之前,如果内存使用量超过请求的限制,则将跳过调整大小,状态将保持“进行中”。这被认为是尽力而为的,因为它仍然受到内存使用量在检查后立即飙升的竞态条件的影响。
  • QoS 等级: Pod 的原始服务质量 (QoS) 等级(Guaranteed、Burstable 或 BestEffort)在创建时确定,并且**不能**通过调整大小更改。调整大小后的资源值仍必须遵守原始 QoS 等级的规则
    • Guaranteed:调整大小后,CPU 和内存的请求必须继续等于限制。
    • Burstable:CPU 和内存的请求和限制不能同时相等(因为这会将其更改为 Guaranteed)。
    • BestEffort:不能添加资源需求(requestslimits)(因为这会将其更改为 Burstable 或 Guaranteed)。
  • 容器类型: 不可重启的初始化容器临时容器不能调整大小。Sidecar 容器可以调整大小。
  • 资源移除: 资源请求和限制一旦设置就不能完全移除;它们只能更改为不同的值。
  • 操作系统: Windows Pod 不支持就地调整大小。
  • 节点策略:静态 CPU 或内存管理器策略管理的 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 等级启动。验证其初始状态

# 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")。
  • 一个带有 type: PodResizePendingreason: Infeasible 的条件已添加到 Pod。
  • 条件的 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 年 7 月 29 日晚上 8:30 PST:ippr:文档化如何利用 observedGeneration 理解状态 (2612fd5723)