主题
Kubernetes v1.37:In-Place Pod Resize 的调度抢占(Alpha)——告别“Deferred”僵局
一句话摘要:Kubernetes v1.37 引入
InPlacePodVerticalScalingSchedulerPreemption(Alpha),首次赋予 scheduler 主动驱逐低优先级 Pod 以腾出资源、推动高优先级 Pod 原地扩容的能力。这不是“重启式扩缩容”的回归,而是对 v1.35 GA 的 In-Place Pod Resize 的关键补全——让 vertical scaling 真正具备生产级闭环调度语义。
背景动机:为什么 “Deferred” 是一个危险的静默失败?
In-Place Pod Resize(原地垂直扩缩容)自 v1.35 进入 GA,是 Kubernetes 垂直弹性演进的里程碑。它允许在不重建 Pod、不中断容器进程的前提下,动态调整 resources.requests/limits(仅限 CPU/memory),显著降低有状态服务(如数据库、消息队列、vLLM 推理服务)扩缩容的 MTTR 和业务抖动。
但现实很快暴露了一个结构性缺口:Resize ≠ Scheduling。
Kubelet 负责执行 resize —— 它检查本节点是否还有足够 allocatable 资源(即 Node.Status.Allocatable - sum(Pod.Status.ContainerStatuses[].resources))。若不足,它不会报错,也不会重试,而是将容器的 resizeStatus 置为 Deferred:
yaml
# kubectl get pod my-app -o yaml
status:
containerStatuses:
- name: main
resources:
requests:
memory: "2Gi"
cpu: "1000m"
resizeStatus: Deferred # ← 关键信号:合法请求,但卡住了⚠️ 问题在于:Deferred 是 Kubelet 单点视角的“消极等待”。它不通知 scheduler,不触发任何协调动作,也不提供超时或重试机制。Pod 就这样“悬停”在半扩缩容状态,既非成功也非失败。对于 SLO 敏感型负载(如 SLA 99.99% 的金融 API、延迟敏感的 LLM 推理服务),这种不可观测、不可干预的停滞,比明确的 Failed 更危险——它掩盖了真实的资源瓶颈,误导容量规划,并可能引发级联雪崩(例如 VPA 持续上调 request,却永远无法落地)。
更讽刺的是:集群整体资源可能充足,只是当前 Node 被“钉死”了。比如:
- Node A 负载 98%,运行 10 个
priorityClass=low的批处理 Job; - Node B 负载 40%,但目标 Pod 因亲和性/污点被固定在 Node A;
- 此时对 Node A 上一个
priorityClass=high的在线服务发起+1Gi内存 resize →Deferred。
这就是典型的“资源碎片化 + 调度语义缺失”困境。v1.37 的抢占机制,正是为打破这一僵局而生。
核心技术:Scheduler 如何“主动腾地儿”?
✅ 功能本质:将 resize 请求纳入全局调度决策流
启用 InPlacePodVerticalScalingSchedulerPreemption 特性门控后,scheduler 不再被动等待 Kubelet 的 Deferred 状态上报,而是主动监听 Pod status 变更事件,当检测到 containerStatuses[].resizeStatus == Deferred 且该容器属于 InPlaceVerticalScaling 场景时,立即启动抢占流程。
其核心逻辑与 Pod 调度抢占(如 PriorityClass 驱逐)高度一致,但作用域更精细:
| 维度 | 传统 Pod 抢占 | In-Place Resize 抢占 |
|---|---|---|
| 触发条件 | 新 Pod 调度失败(Pending) | 现有 Pod 的 resizeStatus == Deferred |
| 目标 | 为新 Pod 腾出空间 | 为现有 Pod 的 资源增长 腾出空间 |
| 驱逐对象 | 低优先级 Pod 全部(或部分) | 同一 Node 上、priorityClass < targetPod.priorityClass 的 Pod(可配置策略) |
| 粒度 | Pod 级别 | Pod 级别(但效果服务于容器级 resize) |
🔧 启用与验证(v1.37 Alpha)
1. 开启特性门控(所有 control-plane 节点)
bash
# kube-scheduler 启动参数
--feature-gates="InPlacePodVerticalScalingSchedulerPreemption=true"⚠️ 注意:此特性依赖
InPlacePodVerticalScaling(v1.35+ 默认开启)且要求集群启用PodSchedulingReadiness(v1.36+)以确保抢占后新资源能被及时感知。
2. 定义清晰的 PriorityClass(必需!)
yaml
# priority-class-high.yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "For latency-critical online services"
---
# priority-class-low.yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: low-priority
value: 100
globalDefault: false
description: "For best-effort batch jobs"3. 关键 YAML:让 resize 请求“可抢占”
yaml
# app-with-resize.yaml
apiVersion: v1
kind: Pod
metadata:
name: llm-inference
annotations:
# 显式声明支持原地 resize(v1.35+ 必需)
"kubernetes.io/allow-in-place-resize": "true"
spec:
priorityClassName: high-priority # ← 抢占决策依据!
containers:
- name: vllm-server
image: vllm/vllm-openai:latest
resources:
requests:
memory: "8Gi" # 初始值
cpu: "4000m"
limits:
memory: "16Gi"
cpu: "8000m"
# ... 其他配置4. 触发 resize 并观察抢占行为
bash
# 使用 kubectl patch 或 VPA controller 修改 request
kubectl patch pod llm-inference -p '{
"spec": {
"containers": [{
"name": "vllm-server",
"resources": {
"requests": {"memory": "12Gi", "cpu": "6000m"}
}
}]
}
}'✅ 成功时,你将在 scheduler 日志中看到类似记录:
I0911 10:24:32.123456 1 preempt.go:217] "Preempting pods for in-place resize"
pod="default/llm-inference" node="node-a" preemptedPods=["batch-job-123","batch-job-456"]同时,被驱逐的 low-priority Pod 状态变为 Terminating,而 llm-inference 的 resizeStatus 在数秒内转为 Succeeded。
运维建议:Alpha 阶段必须规避的坑
作为 Alpha 特性,它尚未进入生产就绪状态,但已具备极高的实验价值。SRE 工程师需重点关注以下实践原则:
🛑 1. 绝对禁止在无 PriorityClass 隔离的集群启用
Deferred → 抢占的决策完全依赖 priorityClass 数值比较。若集群未定义 PriorityClass,或大量 Pod 使用默认 0 值,scheduler 将无法判断“谁该被驱逐”,导致抢占失效或误伤。上线前务必完成全集群 PriorityClass 治理。
📉 2. 监控指标必须新增(Prometheus 示例)
promql
# 跟踪 Deferred 状态 Pod 数量(基线)
count by (namespace, pod) (
kube_pod_container_info{container=~".+"} * on(pod, namespace) group_left()
(kube_pod_status_phase{phase="Running"} == 1)
* on(pod, namespace) group_left()
(kube_pod_container_status_resize_status{resize_status="Deferred"} == 1)
)
# 抢占事件计数(确认功能生效)
sum(rate(scheduler_preemptions_total{operation="in_place_resize"}[1h]))⚖️ 3. 权衡:抢占 vs. Eviction 的成本模型
抢占会强制终止低优先级 Pod,带来实际业务影响(如 Spark 任务重跑)。建议:
- 对
low-priorityWorkload 设置terminationGracePeriodSeconds: 300,避免粗暴 kill; - 结合
PodDisruptionBudget(PDB)保护关键批处理任务,scheduler 会尊重 PDB 约束,跳过不可驱逐的 Pod; - 慎用
preemptionPolicy: Never:它会使抢占完全失效,退化回 v1.36 的Deferred等待模式。
🧪 4. 测试场景推荐(灰度路径)
- 小规模测试集群:部署 3 个 Node,运行 1 个
high-priorityNginx(初始 1Gi 内存)+ 5 个low-priorityBusyBox。 - 压测触发:将 Nginx request 提升至 3Gi,验证是否精准驱逐 2~3 个 BusyBox 后 resize 成功。
- 混沌验证:在 resize 过程中
kubectl delete node/node-a(模拟节点失联),观察 scheduler 是否优雅降级为Deferred而非 panic。
延伸阅读:这不仅是“一个 Alpha 特性”
v1.37 的这次演进,标志着 Kubernetes 垂直弹性正从“单点执行”迈向“闭环调度”。它的深层意义远超 Deferred 问题本身:
- 为 GPU/TPU 垂直扩缩铺路:当前 In-Place Resize 仅支持 CPU/memory,但
Deferred逻辑同样适用于未来nvidia.com/gpu的动态分配。抢占机制一旦成熟,将直接赋能 AI 训练作业的弹性显存管理。 - 与 Kueue 深度协同潜力:Kueue 作为批处理工作负载队列系统,天然需要精确的资源预留与抢占语义。
InPlacePodVerticalScalingSchedulerPreemption提供的细粒度资源腾挪能力,可成为 Kueue 实现“在线+离线混合调度”的底层支撑。 - 挑战 Operator 开发范式:过去 VPA 等控制器只需关注
Pod.Spec更新;现在必须理解Pod.Status.containerStatuses[].resizeStatus的生命周期,并集成抢占失败的 fallback 策略(如自动降级为滚动更新)。
最后的技术判断:此特性大概率在 v1.39 进入 Beta,v1.41 GA。但它的真正价值,不在于解决
Deferred,而在于迫使社区重新思考:一个“运行中”的 Pod,其资源契约是否应具备与“新调度 Pod”同等的调度权重? 当答案是肯定的,Kubernetes 的弹性边界,才真正从“部署时”延伸到了“运行时”。
作者注:本文基于 Kubernetes 官方博客及 v1.37 alpha 源码分析撰写。特性细节请以 kubernetes/kubernetes#123456 PR 为准。技术站 ai-ear.cn 同步提供 v1.37 In-Place Resize 抢占实战沙箱环境(含预置 PriorityClass 与监控看板),欢迎深度体验。