调整分配给容器的 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 规约中的所需
requests
和limits
来请求扩缩。这通常使用针对 Pod 的resize
子资源的kubectl patch
、kubectl apply
或kubectl 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
。
注意
如果 Pod 的整体restartPolicy
是 Never
,则任何容器的 resizePolicy
对于所有资源都必须是 NotRequired
。你不能在此类 Pod 中配置需要重新启动的扩缩策略。示例场景
考虑一个容器,其 CPU 配置了 restartPolicy: NotRequired
,而内存配置了 restartPolicy: RestartContainer
。
- 如果只修改 CPU 资源,容器将就地扩缩。
- 如果只修改内存资源,容器将重新启动。
- 如果同时修改 CPU 和内存资源,容器将重新启动(由于内存策略)。
限制
对于 Kubernetes 1.33,就地扩缩 Pod 资源存在以下限制:
- 资源类型: 只有 CPU 和内存资源可以扩缩。
- 内存减少: 内存限制不能减少,除非内存的
resizePolicy
是RestartContainer
。内存请求通常可以减少。 - QoS 类别: Pod 的原始服务质量 (QoS) 类别(Guaranteed、Burstable 或 BestEffort)在创建时确定,并且不能通过扩缩来更改。扩缩后的资源值仍必须遵循原始 QoS 类别的规则:
- Guaranteed:扩缩后,CPU 和内存的请求值必须继续等于限制值。
- Burstable:CPU 和内存的请求值和限制值不能同时相等(因为这将将其更改为 Guaranteed 类别)。
- BestEffort:不能添加资源需求(
requests
或limits
)(因为这将将其更改为 Burstable 或 Guaranteed 类别)。
- 容器类型: 不可重新启动的Init 容器和临时容器不能扩缩。Sidecar 容器可以扩缩。
- 资源移除: 一旦设置了资源请求和限制,就不能完全移除它们;只能更改为不同的值。
- 操作系统: Windows Pod 不支持就地扩缩。
- 节点策略: 由静态 CPU 或内存管理器策略管理的 Pod 不能就地扩缩。
- 交换: 利用交换内存的 Pod 不能扩缩内存请求,除非内存的
resizePolicy
是RestartContainer
。
这些限制在未来的 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].resources
和 status.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
注意
--subresource resize
命令行参数需要 kubectl
客户端版本 v1.32.0 或更高。更旧的版本将报告 invalid subresource
错误。修补后再次检查 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 的resizePolicy
是NotRequired
。
示例 2:需要重新启动才能扩缩内存
现在,将同一个 Pod 的内存扩缩,将其增加到 300Mi
。由于内存的 resizePolicy
是 RestartContainer
,容器预计会重新启动。
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: PodResizePending
且reason: 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