In-Place Pod Resize:v1.33 默认开启后,如何不重建 Pod 调整 CPU/内存
In-Place Pod Resize(KEP-1287,In-Place Update of Pod Resources,也叫 In-Place Pod Vertical Scaling / InPlacePodVerticalScaling)的目标很直接:不重建 Pod,直接改 Pod/容器 的 CPU 与内存 requests/limits,尽量避免重启容器带来的中断。
- KEP-1287:
- 容器级 resize 文档:
- Pod 级 resize 文档:
演进阶段
- v1.27:alpha,需要显式打开
InPlacePodVerticalScalingfeature gate。 - v1.33:进入 Beta 并默认启用。
- KEP-1287 计划在 Kubernetes v1.35 GA。
触发方式:/resize 子资源
resize 只能通过新的 /resize 子资源触发。/resize 接受 Update 与 Patch 动词,请求和响应都是整个 Pod 对象,但只允许修改特定字段(容器 resources 的 cpu/memory 请求与限制)。
kubectl 需要 v1.32+ 才支持 --subresource resize,旧版本会报 invalid subresource。
kubectl patch pod resize-demo -n qos-example --subresource resize --patch \
'{"spec":{"containers":[{"name":"pause", "resources":{"requests":{"cpu":"800m"}, "limits":{"cpu":"800m"}}}]}}'
字段语义:期望值 vs 实际值
spec.containers[*].resources 现在代表期望值,且对 cpu/memory 可变,不能再当作容器实际生效资源的依据。
实际生效值要看 status.containerStatuses[*].resources(kubelet 通过 CRI 查询 runtime 得到)。新增的 status.containerStatuses[*].allocatedResources 表示调度给该 Pod 的节点资源。status.containerStatuses[].restartCount 可用来确认是否真的发生了重启。
resizePolicy
spec.containers[*].resizePolicy 是 []v1.ContainerResizePolicy,按资源名(cpu/memory)声明是否允许不重启:
NotRequired(默认;v1.33 起语义细化,KEP 中改名为PreferNoRestart):尽量不重启、就地应用。RestartContainer:应用新资源值需要重启容器(比如 Java 进程要改-Xmx)。
如果同时改 CPU 和 memory,而 memory 的策略是 RestartContainer,容器就会重启。
状态与条件
v1.27 曾有 status.resize 字段,取值 Proposed / InProgress / Deferred / Infeasible。v1.33 起改用两个 Pod condition 跟踪:
PodResizePending:带 reason/message。Infeasible表示节点永远无法满足;Deferred表示暂时放不下,会重试。PodResizeInProgress:kubelet 已接受并分配资源,正在应用。
Kubelet 处理逻辑与 CRI 语义
kubelet 先检查期望资源能否放进节点可分配资源:计算节点上除被 resize Pod 外所有 Pod 已分配资源之和,再加上被 resize Pod 的新期望 requests。
- 放得下:接受请求,更新
allocatedResources,加上PodResizeInProgress,调用 CRIUpdateContainerResources更新容器限制;全部成功后把 status 的Resources更新为新值并移除该 condition。 - 放不下:标记
Deferred(持续重试)或Infeasible(永远无法满足)。
CRI 自 v1.20 起就提供 UpdateContainerResources,containerd 与 CRI-O 均已实现,且该调用必须幂等。v1.33 起 CRI 合约更新:runtime 不应为了调整资源而故意重启容器;若必须重启才能 resize,runtime 应返回错误(best-effort,无强制约束)。
v1.33 的变化
- 用 Pod condition 替换
status.resize。 - 禁止内存 limit 下调(memory limit downsizing 在后台被禁止)。
ResizeRestartPolicy的NotRequired改名PreferNoRestart,并更新 CRIUpdateContainerResources合约。- 新增 Pod 级
AllocatedResources。 - resize 执行改为边缘触发(edge-triggered)。
ResizePolicy变为不可变(immutable)。
Pod 级资源 resize
Pod 的 spec.resources 定义该 Pod 全部容器的聚合资源上界,可以直接修改运行中 Pod 的这个聚合并就地生效。它不需要 Pod 级重启策略:Pod 级资源只是 Pod cgroup 的总约束,不直接管理容器内运行时。是否重启单个容器,仍由容器级 resizePolicy 决定。
校验规则:
- 只有 Pod 级 requests >= 所有容器对应 requests 之和时,才允许 resize。
- 容器 limit <= Pod 级 limit 才允许。
- 容器 limit 之和可以超过 Pod 级 limit(允许 Pod 内容器共享资源)。
apiVersion: v1
kind: Pod
metadata:
name: pod-level-resize-demo
spec:
containers:
- name: pause
image: registry.k8s.io/pause:3.9
resizePolicy:
- resourceName: cpu
restartPolicy: NotRequired
- resourceName: memory
restartPolicy: RestartContainer
resources:
requests:
cpu: 100m
memory: 100Mi
- name: nginx-server
image: registry.k8s.io/nginx:latest
resizePolicy:
- resourceName: cpu
restartPolicy: RestartContainer
- resourceName: memory
restartPolicy: RestartContainer
resources:
requests:
cpu: 200m
memory: 200Mi
limits:
cpu: 200m
memory: 200Mi
把 Pod 级 CPU 调大到 300m:
kubectl patch pod pod-level-resize-demo --subresource resize --patch \
'{"spec":{"resources":{"requests":{"cpu":"300m"}, "limits":{"cpu":"300m"}}}}'
memory-backed emptyDir 的 sizeLimit
打开 InPlacePodVerticalScalingMemoryBackedVolumes feature gate 后,可以通过 Pod 的 /resize 子资源更新 medium: Memory 的 emptyDir 卷 sizeLimit,不需要重启容器或重建 Pod。前提是节点运行 cgroup v2;cgroup v1 节点会被判为 infeasible 并拒绝。磁盘型 emptyDir 与持久卷不能通过该子资源调整。
已知限制
- 只有 CPU 和内存可以 resize。
- Static CPU management policy 与本特性不兼容。
- containerd 低于 v1.6.9 缺少完整 CRI 支持,resize 会卡在
InProgress且 status 不更新。 - Windows Pod 不支持就地 resize。
- resize 与其它 Pod 更新可能竞态,导致延迟。
- status 反映新资源可能有延迟。
VPA 集成
resize 失败的多种 failure mode 会冒泡给 VPA(如 InPlaceOrRestart 等);VPA 对无法就地完成的 resize 会按策略重建 Pod。
排障对照
| 现象 | 含义 | 处理方向 |
|---|---|---|
PodResizePending + Deferred | 节点当前可分配资源放不下新期望值 | 等待重试,或腾出节点资源 |
PodResizePending + Infeasible | 节点永远无法满足(如 cgroup v1 上调整 memory-backed emptyDir sizeLimit) | 换节点/换特性路径,别继续等 |
PodResizeInProgress 长期不退 | kubelet 已接受,但 container runtime 未完成 | 检查 containerd 版本(< v1.6.9 会卡住) |
| status 资源未更新 | status 更新有延迟,或 resize 未真正落地 | 对比 status.containerStatuses[*].resources 与 allocatedResources |
restartCount 增加 | 该容器按 resizePolicy 走了 RestartContainer | 属预期行为,非故障 |
取舍上:CPU 通常能用 NotRequired/PreferNoRestart 平滑调整;内存若受运行时参数约束(如 JVM 堆),只能接受 RestartContainer;内存 limit 下调在 v1.33 起被禁止,需要降内存上限时只能走重建路径。