Kubernetes 的资源分配过去通常是在 Pod 初次调度和放置时做出的静态决定。核心 Pod 原地调整大小功能在 v1.35 正式可用后,应用开发者与集群运维者可以动态调整运行中容器的 CPU 和内存分配,不必造成破坏性重启或应用停机。
但原地调整引入了独特的资源调度缺口:运行中的 Pod 申请扩容,超出宿主节点可分配的剩余容量时,kubelet 只能将请求标为 Deferred。Pod 会无限期停留在这个状态,等待节点资源自然释放。
为填补这个缺口,Kubernetes v1.37 引入 Pod 原地调整大小的调度器抢占机制(Alpha),由特性门控 InPlacePodVerticalScalingSchedulerPreemption 控制。调度器可以抢占较低优先级的工作负载,主动在资源已用满的节点释放容量,使关键、高优先级应用等待中的原地调整能够完成。
调整被“推迟”的挑战
要理解为何需要抢占,先看 Kubernetes 如何处理运行中 Pod 的调整。当用户或控制器,例如 Vertical Pod Autoscaler,更新活动容器的资源请求时,kubelet 评估节点是否还有足够的可分配容量来满足增加量。
如果节点资源用满,无法满足新限制,kubelet 将容器的 resizeStatus(在 Pod 的 status.containerStatuses[] 中报告)设为 Deferred。这不同于 Infeasible:后者因超出物理机器边界、命名空间 LimitRange 或准入配额而立即被拒绝;Deferred 表示请求有效,只是暂时无法实施,正在等待节点容量。
抢占机制引入前,如果节点利用率很高,Pod 的原地扩容可能永久阻塞。即使内存数据库或实时 Web 服务等关键应用需要更多内存,来避免即将发生的内存不足(OOM)崩溃,只要节点没有空余容量,调整仍然会保持 Deferred。
这种情况下,管理员的选择有限:
- 手动驱逐节点上的低优先级 Pod,腾出容量。
- 等待集群自动扩缩容器创建更大节点,再重新调度 Pod。但这会造成明显中断,违背原地扩缩容“不重启”的核心价值。
- 依赖自定义自动扩缩容方案,例如能够触发动态节点调整的集群自动扩缩容器。
kube-scheduler 不知道运行中 Pod 存在推迟的调整,因此无法利用标准的优先级抢占,驱逐低优先级工作负载,为高优先级运行中 Pod 的资源增长腾出空间。
为什么重要
生产环境管理员希望最大化资源利用率和效率。常见策略是在尚未用满的节点上,以装箱方式填入低优先级工作负载,例如批处理、后台数据处理或尽力而为任务。
没有原地调整抢占时,这带来严重两难:低优先级任务占用剩余容量后,同节点上的高优先级应用为应对流量突增或内存尖峰而扩容,就会被阻塞在 Deferred。运维者只能在低利用率、保留空闲缓冲的集群,与关键任务需要时无法调整的风险之间选择。
有了原地调整的调度器抢占,就可以用低优先级任务填满集群闲置空间,而不必担心它们降低高优先级 Pod 的表现或阻止扩容。如果高优先级任务的原地调整超过节点可用容量,调度器会自动抢占低优先级 Pod,释放空间。
这兼顾集群高利用率、成本效率,以及关键服务的响应能力与可靠性。
架构机制:如何工作
原地调整抢占直接集成到核心调度周期中,动态、安全地协调资源。
调度器集中跟踪
kube-scheduler 监测集群中调整状态为 Deferred 的运行中 Pod。通常,填有 spec.nodeName 的 Pod 被视为已成功放置,跳过活动调度队列。启用此门控后,调度器拦截带有 Deferred 条件的 Pod,让它们继续参加活动调度评估,专门用于触发抢占。
调度器持续跟踪这些 Pod,直到 kubelet 成功完成调整。
仅限单节点的抢占边界
放置抢占会评估集群所有节点寻找最佳位置,而原地调整抢占严格局限于 Pod 当前所在节点。调度器找出同一宿主节点上符合条件的低优先级“受害者” Pod,并发起优雅驱逐,释放本地容量。
抢占严格限定在推迟调整的 Pod 所在节点。如果驱逐所有合格的低优先级任务后,节点仍无法容纳调整,请求仍保持 Deferred。
资源预留安全
为防止调度竞态与重复分配,调度器把调整申请的资源视为已占用。抢占生效后,kubelet 因而能实施调整。
职责分离与关键 Pod 准入
节点资源紧张时,kubelet 有一个称为关键 Pod 准入处理器的本地机制。初次准入时,如果关键系统 Pod 到达一个没有空余容量的节点,该机制能直接驱逐低优先级 Pod,确保关键工作负载获准进入。
新功能的重要架构收益,是严格分离 kubelet 与调度器职责。在此特性门控下,关键 Pod 准入处理器不会针对原地调整执行本地抢占检查或触发驱逐,而是推迟请求,把抢占决策完全委托给调度器。
这样,所有调整相关抢占逻辑由一个集中协调器管理,并遵循全局优先级、Pod 中断预算(PDB)和优雅终止策略。
处理竞争更新与竞态
如果活动抢占周期中,同节点另一个运行中 Pod 提交了更高优先级的调整请求,kubelet 会优先处理它。调度器观察这些更新;如果满足新状态需要更多容量,会动态触发新一轮抢占。
节点级抢占配置
管理员与集群自动扩缩容器等控制器,可以针对特定节点关闭原地调整抢占。使用 Node Spec 中新增的 spec.podPreemptionPolicy 字段配置:
apiVersion: v1
kind: Node
metadata:
name: batch-workload-node
spec:
podPreemptionPolicy:
disableResizePreemption:
- "cluster-autoscaler.kubernetes.io/disable-preemption"
- "operator.example.com/policy-override"
例如,控制器可能更愿意在可行时先缩小其他 Pod,或自行动态调整节点容量,把调度器抢占作为最后手段。
试一试
使用原地调整调度器抢占需要:
- 控制平面与所有工作节点均运行 Kubernetes v1.37 或更新版本。
- 在所有相关控制平面组件(
kube-apiserver、kube-scheduler)以及kubelet上启用InPlacePodVerticalScalingSchedulerPreemption。
小教程:观察调整抢占
可以在 CPU 剩余容量受限的单节点 kind 集群中测试。
1. 创建启用调整抢占的 kind 集群
创建 kind-config.yaml,启用该特性门控:
# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
InPlacePodVerticalScalingSchedulerPreemption: true
使用此配置创建集群,并通过 --image 确保 Kubernetes 版本为 v1.37 或更新:
kind create cluster --config kind-config.yaml --image kindest/node:v1.37.0
注意:节点镜像必须对应 v1.37 或更新版本,例如
kindest/node:v1.37.0。旧版 Kubernetes 不支持此特性门控。
集群就绪后,检查节点可分配 CPU 核数:
kubectl get nodes -o custom-columns=NAME:.metadata.name,ALLOCATABLE_CPU:.status.allocatable.cpu
原文的标准本地 kind 环境显示 8 个可分配 CPU 核:
NAME ALLOCATABLE_CPU
kind-control-plane 8
2. 创建 PriorityClass 并部署 Pod
创建两个 PriorityClass,部署请求 3 CPU 的低优先级 Pod 与请求 4 CPU 的高优先级 Pod。它们占用 8 个可用 CPU 核中的 7 个,留下 1 CPU 的可分配容量。
# preemption-demo.yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "High priority workload"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: low-priority
value: 1000
globalDefault: false
description: "Low priority workload"
---
apiVersion: v1
kind: Pod
metadata:
name: low-priority-pod
spec:
priorityClassName: low-priority
containers:
- name: worker
image: nginx
resources:
requests:
cpu: "3"
memory: "500Mi"
limits:
cpu: "3"
memory: "500Mi"
---
apiVersion: v1
kind: Pod
metadata:
name: high-priority-pod
spec:
priorityClassName: high-priority
containers:
- name: app
image: nginx
resources:
requests:
cpu: "4"
memory: "1Gi"
limits:
cpu: "4"
memory: "1Gi"
保存为 preemption-demo.yaml 并应用:
kubectl apply -f preemption-demo.yaml
等待两个 Pod 在节点上运行:
kubectl get pods
原文输出:
NAME READY STATUS RESTARTS AGE
high-priority-pod 1/1 Running 0 9s
low-priority-pod 1/1 Running 0 9s
3. 请求原地扩容
给高优先级 Pod 打补丁,把 CPU 请求从 4 增加到 6,即增加 2 CPU。节点仅剩 1 CPU,因此超过剩余可分配容量:
kubectl patch pod high-priority-pod --subresource resize --patch \
'{"spec":{"containers":[{"name":"app", "resources":{"requests":{"cpu":"6"}, "limits":{"cpu":"6"}}}]}}'
4. 检查低优先级 Pod 的抢占事件
启用该门控后,调度器拦截 high-priority-pod 的 Deferred 调整条件,并选中 low-priority-pod 抢占。
检查其事件,验证调度器主动执行抢占:
kubectl get events --field-selector involvedObject.name=low-priority-pod
在事件流,或 kubectl describe pod low-priority-pod 中,会看到调度器发出的 Preempted 事件:
LAST SEEN TYPE REASON OBJECT MESSAGE
5s Normal Preempted pod/low-priority-pod Preempted by pod 97dba925-6b5f-4e2f-99f9-d51c30016586 on node kind-control-plane
5s Normal Killing pod/low-priority-pod Stopping container worker
5. 跟踪高优先级 Pod 的调整事件生命周期
接着检查 high-priority-pod 事件历史,观察调整从推迟到成功完成的过程:
kubectl get events --field-selector involvedObject.name=high-priority-pod
kubelet 与调度器协调时,原文观察到以下事件序列:
LAST SEEN TYPE REASON OBJECT MESSAGE
33s Warning ResizeDeferred pod/high-priority-pod Pod resize OutOfcpu: {"containers":[{"name":"app","resources":{"limits":{"cpu":"6","memory":"1Gi"},"requests":{"cpu":"6","memory":"1Gi"}}}],"generation":2,"error":"Node didn't have enough resource: cpu, requested: 6000, used: 3950, capacity: 8000"}
32s Normal ResizeStarted pod/high-priority-pod Pod resize started: {"containers":[{"name":"app","resources":{"limits":{"cpu":"6","memory":"1Gi"},"requests":{"cpu":"6","memory":"1Gi"}}}],"generation":2}
32s Normal ResizeCompleted pod/high-priority-pod Pod resize completed: {"containers":[{"name":"app","resources":{"limits":{"cpu":"6","memory":"1Gi"},"requests":{"cpu":"6","memory":"1Gi"}}}],"generation":2}
ResizeDeferred:节点 CPU 剩余容量不足(OutOfcpu),kubelet 首先将请求标为推迟,事件级别为Warning。ResizeStarted:调度器抢占低优先级 Pod、容量释放后,kubelet 接受新分配并开始实施调整。ResizeCompleted:kubelet 通过容器运行时成功更新容器 cgroup 限制,无需重启 Pod。
最后确认容器获分配的 CPU 对应新请求。在 JSONPath 查询末尾添加 {"\n"},使终端输出包含尾部换行:
kubectl get pod high-priority-pod -o jsonpath='{.status.containerStatuses[0].allocatedResources.cpu}{"\n"}'
原文输出:
6
这表明原文示例中的原地调整成功完成。
参与社区
这项功能让资源调度向前迈出重要一步,将企业级密度控制与工作负载优先级管理带入动态资源扩缩容。作者邀请集群运维者、平台架构师和开发者在测试环境启用该门控并分享反馈。
可以通过 SIG Scheduling 或 SIG Node 社区渠道交流体验。
来源与许可
作者 Natasha Sarkar(Google);原文 Kubernetes v1.37: Scheduler Preemption for In-Place Pod Resize (Alpha),2026 年 9 月 10 日。本中文版本依据转载授权翻译,保留版本与实验条件。Kubernetes 文档许可与来源贡献记录见原站;未独立运行示例。











暂无评论内容