Kubernetes 1.36 深度拆解:DRA 走向成熟、PodGroup 组调度与 30+ GA 特性的工程全貌
前言:为什么 Kubernetes 1.36 值得关注
2026 年,Kubernetes 已经走过了整整 10 年。从最初的容器编排实验,到如今云原生时代的基础设施操作系统,K8s 的每一次大版本发布都牵动着整个行业的技术神经。
Kubernetes 1.36 是 2026 年最重要的版本之一——它不是一个小修小补的过渡版本,而是包含了 DRA 动态资源分配走向生产就绪、PodGroup 组调度原生支持、Ingress NGINX 正式退役、30+ 特性进入 GA 等里程碑级别的变化。对于平台工程师、SRE 和云原生开发者来说,这是一次不得不认真对待的升级。
本文将从工程师视角,对每一个关键变化进行深入拆解,配完整 YAML 配置示例和生产避坑指南。
一、DRA(动态资源分配):GPU/FPGA 编排的工程革命
1.1 DRA 是什么?为什么它是 K8s 史上最重要的功能之一?
在 DRA 出现之前,Kubernetes 对 GPU、FPGA 等特殊硬件资源的调度完全是"事后补妆"的状态。调度器把 Pod 分配到某个节点之后,再由 kubelet 去检查设备是否真的可用——如果设备被占用或者驱动不匹配,Pod 就会启动失败,白白浪费调度时间。
DRA(Dynamic Resource Allocation)彻底改变了这个局面。它引入了一套完整的声明式设备管理 API,让设备(如 GPU)可以在 Pod 调度之前就被精确地预留和分配。
# ResourceClaim - 声明需要 2 块 NVIDIA A100 GPU
apiVersion: resource.k8s.io/v1alpha3
kind: ResourceClaim
metadata:
name: ml-training-gpus
spec:
resourceClassName: nvidia.com/gpu
selectors:
- matchExpressions:
- key: nvidia.com/gpu.product
operator: In
values:
- "A100-SXM4-80GB"
# 请求恰好 2 块 GPU
allocationMode: ExactCount
count: 2
---
# Pod 引用 ResourceClaim
apiVersion: v1
kind: Pod
metadata:
name: ml-training-pod
spec:
containers:
- name: trainer
image: pytorch/pytorch:2.4.0-cuda12.1-cudnn8-runtime
command: ["python", "train.py"]
resources:
limits:
nvidia.com/gpu: "2" # 旧式写法,依然兼容
resourceClaims:
- name: gpus
resourceClaimName: ml-training-gpus
1.2 Kubernetes 1.36 中 DRA 的重大增强
1.36 版本是 DRA 历史上功能最丰富的一个版本,多个关键特性终于 GA 或者 Beta 了:
| 特性 | 旧状态 | 新状态 | 意义 |
|---|---|---|---|
| 可消费容量管理 | Beta | GA(默认启用) | 设备可以分区分配,多个 Pod 共享同一块物理 GPU |
| 设备选择优先列表 | Beta | GA | 管理员可以定义设备优先级策略 |
| 管理员访问权限 | Beta | GA | 管理员可以强制管理设备分配 |
| 扩展资源类型 | Alpha | Beta | 支持自定义资源类型,不局限于 GPU |
| 设备绑定条件 | Beta(默认启用) | Beta(强化) | 可以基于节点状态动态决定设备是否可用 |
| 设备污点与容忍 | Alpha | Beta | 类似 K8s 原生污点机制,设备也可以设置污点 |
| 设备分区 | Alpha | Beta | GPU 时间片分区,多任务共享 GPU 显存 |
| 原生资源映射 | Alpha(新) | Alpha | 将 DRA 设备映射到原生 K8s CPU/内存资源 |
1.3 GPU 时间片分区:让 GPU 利用率从 30% 飙升到 90%
DRA 1.36 最激动人心的功能之一是设备分区(Device Partitioning)。在此之前,如果一块 80GB 的 A100 GPU 只跑了一个只需要 20GB 显存的小模型,剩下的 60GB 就白白浪费了。
有了设备分区,我们可以把一块物理 GPU "虚拟"成多个逻辑分区:
# 管理员定义分区策略
apiVersion: resource.k8s.io/v1alpha3
kind: ResourceClaimTemplate
metadata:
name: gpu-partition-template
spec:
spec:
resourceClassName: nvidia.com/gpu
allocationMode: PartitionCount
partitions:
- name: partition-0
count: 1
resources:
gpu-memory: "40Gi"
gpu-shares: 500 # 50% 计算资源
- name: partition-1
count: 1
resources:
gpu-memory: "40Gi"
gpu-shares: 500
---
# 用户请求分区 GPU
apiVersion: resource.k8s.io/v1alpha3
kind: ResourceClaim
metadata:
name: my-partitioned-gpu
spec:
resourceClassName: nvidia.com/gpu
from: gpu-partition-template
1.4 DRA 生产避坑指南
DRA 在 1.36 中的大量 GA 也带来了向后兼容性风险。如果你已经在生产环境使用 DRA,需要注意以下变化:
# ⚠️ RBAC 权限变更:1.36 要求更细粒度的 RBAC
# 旧版 RBAC(可能不再够用)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: dra-driver-role
rules:
# 1.36 新增:需要能更新 ResourceClaim status
- apiGroups: ["resource.k8s.io"]
resources: ["resourceclaims/status"]
verbs: ["update", "patch"]
# 1.36 新增:ResourceSlice 必须显式设置 ReconcilePoolWithName
- apiGroups: ["resource.k8s.io"]
resources: ["resourceslices"]
verbs: ["create", "update"]
二、调度器:从"调度单个 Pod"到"调度整个工作负载"
2.1 PodGroup API:组调度的原生支持
Kubernetes 1.36 引入了 scheduling.k8s.io/v1alpha2 API,其中最引人注目的是 PodGroup 资源。这解决了困扰 AI 训练和高性能计算场景多年的一个问题:如何保证一组 Pod 同时调度成功?
在没有 PodGroup 之前,AI 训练任务通常由框架自己处理调度(如 MPI 的 mpirun 或者 PyTorch 的 torchrun),但这种"框架层调度 + K8s 层调度"的双层调度带来了复杂性和脆弱性。
现在,K8s 原生支持组调度:
# 定义一个 PodGroup:3 个 Pod 必须同时调度成功或失败
apiVersion: scheduling.k8s.io/v1alpha2
kind: PodGroup
metadata:
name: distributed-training-group
namespace: ml-team
spec:
# 等待超时:如果 5 分钟内无法全部调度,则全部失败
waitingTimeout: 5m
# 成员 Pod 标签选择器
memberSelector:
matchLabels:
training-role: worker
# 最小成员数(允许部分成员延迟)
minMembers: 2
# 拓扑感知:优先将 Pod 调度到同一可用区
topologySpreadPolicy:
topologyKey: topology.kubernetes.io/zone
maxSkew: 1
---
# 所有标记了 training-role=worker 的 Pod 自动加入该组
apiVersion: v1
kind: Pod
metadata:
name: training-worker-1
labels:
training-role: worker
pod-group: distributed-training-group
spec:
schedulerName: default-scheduler
containers:
- name: worker
image: pytorch/pytorch:2.4.0
command: ["torchrun", "--nnodes=3", "train.py"]
---
apiVersion: v1
kind: Pod
metadata:
name: training-worker-2
labels:
training-role: worker
pod-group: distributed-training-group
spec:
schedulerName: default-scheduler
containers:
- name: worker
image: pytorch/pytorch:2.4.0
command: ["torchrun", "--nnodes=3", "train.py"]
2.2 工作负载感知抢占
以前,K8s 的抢占机制(Preemption)只作用于单个 Pod。如果一个高优先级 Pod 需要资源,调度器会杀掉一个低优先级 Pod,然后等待新 Pod 创建完成。这个过程对分布式训练来说是灾难性的——部分节点有 Pod,部分节点没有,训练无法进行。
1.36 的工作负载感知抢占解决了这个问题:当一个 PodGroup 需要抢占时,整个组一起抢占,要么全部成功,要么全部失败。
# PodGroup 的优先级通过 PriorityClass 表达
apiVersion: scheduling.k8s.io/v1alpha2
kind: PodGroup
metadata:
name: priority-training-group
spec:
priorityClassName: high-priority-training
memberSelector:
matchLabels:
app: distributed-ml
---
# 高优先级 PriorityClass
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority-training
value: 100000
preemptionPolicy: PreemptLowerPriority # 低于此优先级的 PodGroup 整体被驱逐
globalDefault: false
2.3 调度器插件接口变更:PreBind 并行化
1.36 对自定义调度器插件的接口做了重要变更。如果你在生产环境使用了自定义调度器插件,必须升级:
// 旧版 PreBind 插件接口(K8s < 1.36)
type PreBind plugin.PreBind
func (p *PreBind) PreBind(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) *framework.Status {
// 串行执行,所有插件依次运行
return nil
}
// 1.36 新版接口:支持并行 PreBind
type PreBindPreFlightResult struct {
// 关键字段:是否允许后续插件并行执行
AllowParallel bool
Result *framework.Status
}
func (p *PreBind) PreBindPreFlight(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) *PreBindPreFlightResult {
// 新接口:返回是否允许并行
return &PreBindPreFlightResult{
AllowParallel: true, // 设为 true 允许其他 PreBind 插件并行
Result: nil, // nil 表示通过检查
}
}
func (p *PreBind) PostBind(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) {
// 依然串行执行,用于清理和通知
}
三、原地 Pod 资源调整:从"重启才能改配置"到"热更新 CPU/内存"
3.1 InPlacePodLevelResourcesVerticalScaling:Beta → GA
这是 Kubernetes 历史上最受开发者欢迎的功能之一,终于在 1.36 进入 GA 且默认启用。
以往,如果你的生产服务需要增加 CPU 或内存配额,你需要:
- 修改 Deployment 的 resources 配置
- K8s 触发滚动更新(RollingUpdate)
- 老 Pod 被 Terminating,新 Pod 被 Creating
- 服务中断(即使配置了探针,也会有连接抖动)
现在,Pod 资源可以在原地调整,完全不触发重启:
# 生产实战:原地扩容 CPU(无需滚动更新)
kubectl patch pod my-app-pod-xyz \
--subresource='resize' \
--type='merge' \
-p '{"spec":{"containers":[{"name":"app","resources":{"requests":{"cpu":"4"},"limits":{"cpu":"6"}}}]}}'
# 查看调整状态
kubectl get pod my-app-pod-xyz -o jsonpath='{.spec.containers[0].resources}'
背后发生的事情是 kubelet 直接向容器运行时发送新的资源约束(通过 CRI),容器不需要重建,连接不会中断。
3.2 原地调整的边界条件
但原地调整不是万能的,以下情况仍会失败或触发滚动更新:
# ✅ 可以在原地调整
resources:
requests:
cpu: "2" → "4" # 从 2 核扩到 4 核
memory: "4Gi" → "8Gi" # 从 4GB 扩到 8GB
# ❌ 不可以在原地调整(会触发滚动更新)
resources:
# 缩小 CPU/Memory(容器可能已经用满了)
requests:
cpu: "4" → "1"
# 调整存储
resources:
ephemeral-storage: "10Gi" → "20Gi" # 不支持
# ⚠️ 以下情况会在准入阶段就拒绝(1.36 新行为)
# 调整后的资源请求超过节点可分配容量
# 1.36 之前:标记为 Infeasible,调度器继续
# 1.36 之后:准入阶段直接拒绝,Pod 不会进入调度队列
四、安全:GA 特性扎堆,K8s 安全基线再升级
4.1 UserNamespacesSupport:正式 GA
用户命名空间(User Namespaces)终于 GA 了!这意味着容器内的 UID/GID 可以与宿主机完全隔离——容器内的 root(UID 0)在宿主机上可能只是一个普通用户。
# Pod 启用用户命名空间
apiVersion: v1
kind: Pod
metadata:
name: secure-app
spec:
securityContext:
# 1.36 GA,默认无需设置此特性门即可使用
runAsUser: 100000
runAsGroup: 100000
supplementalGroups: [100000]
fsGroup: 100000
containers:
- name: app
image: nginx:1.27
securityContext:
# 容器内 root,宿主机上可能是 UID 100001
runAsNonRoot: false # 允许容器内以 root 运行(安全因为命名空间隔离)
4.2 MutatingAdmissionPolicy:GA,WebHook 之后的第二道防线
MutatingAdmissionPolicy 是比 MutatingWebhook 更轻量、更快速的准入策略机制,它直接集成在 API Server 进程中,不需要额外部署 WebHook 服务:
# 使用 MutatingAdmissionPolicy 自动注入 sidecar
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicy
metadata:
name: inject-otel-sidecar
spec:
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
operations: ["CREATE"]
# 变量匹配:只对带有特定标签的 Pod 注入
matchConditions:
- name: has-otel-injection-label
expression: "object.metadata.labels['otel-injection'] == 'enabled'"
mutatingOperations:
- CREATE
# 变更逻辑:使用 CEL 表达式
rewriteRules:
- patches:
- jsonPath:
path: "/spec/containers/-"
value: >
{
"name": "otel-collector",
"image": "otel/collector:0.96.0",
"env": [
{"name": "OTEL_EXPORTER_OTLP_ENDPOINT", "value": "otel-collector:4317"}
],
"resources": {"limits": {"cpu": "500m", "memory": "256Mi"}}
}
4.3 ConstrainedImpersonation:防止特权账户滥用
ConstrainedImpersonation(Beta,默认启用)限制了模拟(Impersonation)操作的范围——即使有人拿到了 cluster-admin 权限,他也无法模拟其他用户执行敏感操作:
# 管理员定义 impersonation 受限策略
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: constrained-impersonator
rules:
# 只能模拟与自己同部门的 ServiceAccount
- apiGroups: [""]
resources: ["pods"]
verbs: ["impersonate"]
subjects:
- kind: ServiceAccount
name: default
namespace: monitoring
五、网络:Ingress NGINX 退役与 IP 验证严格化
5.1 Ingress NGINX 正式退役:云原生入口网关的新时代
这是 2026 年最震撼的行业新闻之一:Ingress NGINX 在 2026 年 3 月 24 日正式宣布退役,不再有新发布、bug 修复或安全更新。
Ingress NGINX 统治 K8s 入口网关领域超过 8 年,为什么突然退役?核心原因:
- 架构局限性:基于传统 Nginx,无法充分利用现代云原生技术(Envoy、eBPF)
- 维护者枯竭:核心团队成员陆续离开,缺乏新鲜血液
- 替代方案成熟:Gateway API + Envoy 提供了更完整的解决方案
替代方案对比:
| 方案 | 适用场景 | 迁移难度 |
|---|---|---|
| Gateway API + Istio | 需要服务网格、可观测性 | 中等 |
| Gateway API + Cilium | 需要 eBPF 加速 | 中等 |
| Kong Ingress Controller | 需要 API 管理 | 较低 |
| AWS ALB Ingress | 纯 AWS 环境 | 低 |
5.2 StrictIPCIDRValidation:消灭 IP 配置的"幽灵错误"
1.36 默认启用了 StrictIPCIDRValidation,禁止了 IP 配置中常见的"幽灵错误":
# ✅ 正确的 CIDR 写法
spec:
ports:
- port: 80
clusterIP: 10.96.0.1 # 正确
---
# ❌ 1.36 会拒绝以下写法(之前只是警告)
spec:
clusterIP: 010.000.000.001 # 前导零:解释为 10.0.0.1,但禁止!
# 或者
clusterIPs:
- 192.168.001.001 # 前导零导致歧义
# ⚠️ 升级步骤:扫描所有 Service,检查 IP 格式
kubectl get svc -A -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.clusterIP}{"\n"}{end}' \
| awk '$2 ~ /^0/ {print "NEEDS_FIX: " $0}'
5.3 kube-proxy 性能优化:conntrack 清理不再卡顿
在大规模集群中,如果有大量短连接,kube-proxy 的 conntrack 表会快速膨胀。1.36 修复了 conntrack 清理逻辑在高并发场景下的性能问题:
# 验证 conntrack 表大小
kubectl logs -n kube-system -l k8s-app=kube-proxy --tail=100 | grep -i conntrack
# 性能基准对比(大规模集群 ~1000 个 Service)
# 1.35:conntrack 满表时,Service 更新延迟 P99 = 2.3s
# 1.36:conntrack 清理异步化后,P99 = 85ms
六、存储:VolumeAttributesClass 和镜像卷的 GA
6.1 VolumeAttributesClass:存储 QoS 终于标准化
在 1.36 之前,如果你想对 CSI 卷设置 IOPS 限制、吞吐量限制,每个存储插件都有自己的定制化方案。1.36 引入统一的 VolumeAttributesClass:
# 1.36 统一方案:VolumeAttributesClass
apiVersion: storage.k8s.io/v1
kind: VolumeAttributesClass
metadata:
name: ssd-high-iops
driver: ebs.csi.aws.com
parameters:
type: gp3
iops: "16000"
throughput: "1000"
encrypted: "true"
6.2 ImageVolume GA:直接把镜像当文件系统用
ImageVolume 特性在 1.36 正式 GA,允许将 OCI 镜像直接作为只读文件系统挂载到容器中,无需先拉取到节点:
# 将镜像作为只读文件系统挂载
apiVersion: v1
kind: Pod
metadata:
name: ml-inference
spec:
containers:
- name: model
image: ml-models:latest
volumeMounts:
- name: model-image
mountPath: /model
readOnly: true
command: ["python", "/model/inference_server.py"]
volumes:
- name: model-image
csi:
driver: image.csi.k8s.io
volumeAttributes:
image: ml-models:latest
readOnly: "true"
七、kubectl 工具链:开发者体验持续改善
7.1 kubectl wait 支持多条件等待
终于不用写两行了:
# 1.36:一次等待多个条件(全部满足才继续)
kubectl wait \
--for='jsonpath={.status.conditions[*].type}=Ready Initialized' \
--for='jsonpath={.status.phase}'=Running \
pod/my-app-pod
# 等待 Job 完成 或 超时
kubectl wait \
--for='jsonpath={.status.succeeded}'=1 \
--for='timeout=5m' \
job/batch-processing
7.2 kubectl describe 新增节点资源切片信息
# 1.36 新增:describe node 显示 ResourceSlices
kubectl describe node worker-node-1
# 输出新增以下内容:
# ResourceSlices (节点资源视图)
# Allocatable Resources:
# nvidia.com/gpu: 2 (1 allocated, 1 free)
八、升级指南:从 1.35 升级到 1.36 的完整清单
8.1 升级前必查清单
#!/bin/bash
# === 升级前检查脚本 ===
# 1. 检查自定义调度器插件(必须升级接口)
echo "检查调度器插件..."
kubectl get pods -n kube-system -l component=scheduler -o yaml \
| grep -i "scheduler.config.k8s.io/v1" || echo "使用默认调度器 ✓"
# 2. 扫描 Service 中的非法 IP 格式(前导零)
echo "检查 Service IP 格式..."
INVALID_IPS=$(kubectl get svc -A -o jsonpath='{range .items[*]}{\ .metadata.name}{"\t"}{\ .spec.clusterIP}{"\n"}{end}' \
| awk '$2 ~ /^0/ || $2 ~ /\..*\.0[0-9]/')
if [ -n "$INVALID_IPS" ]; then
echo "⚠️ 发现非法 IP 格式(1.36 将拒绝):"
echo "$INVALID_IPS"
else
echo "Service IP 格式检查 ✓"
fi
# 3. 检查 CSI 驱动版本是否兼容 DRA
echo "检查 CSI 驱动..."
kubectl get csidriver -A
# 4. 检查监控指标名称(需要更新 Prometheus 规则)
echo "检查 Prometheus 指标重命名..."
kubectl get prometheusrules -A -o yaml | grep -E \
"volume_operation_total_errors|etcd_bookmark_counts" \
&& echo "⚠️ 存在需要迁移的旧指标名" \
|| echo "指标检查 ✓"
8.2 kubeadm 升级命令
# 升级 etcd(必须先升级,数据目录格式有变化)
crictl pull registry.k8s.io/etcd:3.6.8
kubeadm upgrade plan # 确认所有组件版本
# 升级控制面(执行节点)
sudo kubeadm upgrade apply v1.36.0 \
--etcd-upgrade=true \
--certificate-renewal=true \
--patches=etcd-patch.yaml
# 升级工作节点
kubectl drain node worker-1 --ignore-daemonsets --delete-emptydir-data
ssh worker-1 "sudo kubeadm upgrade node"
kubectl uncordon node worker-1
九、总结与展望
Kubernetes 1.36 是一个标志性的版本,它标志着 K8s 正在从"通用的容器编排工具"进化为"面向 AI 时代的基础设施操作系统"。几个核心趋势:
DRA 走向生产就绪:GPU/FPGA 编排终于有了标准方案,配合 PodGroup 组调度,AI 训练任务可以在 K8s 上原生运行
原地资源调整成为默认:打破了"改配置就要重启"的老大难问题,提升了生产服务的连续性
安全基线全面升级:UserNamespaces、MutatingAdmissionPolicy、ConstrainedImpersonation 扎堆 GA
Ingress NGINX 退役:提醒我们,云原生领域没有永远的"标准答案",技术选型需要持续评估
存储和网络持续深化:VolumeAttributesClass 标准化、StrictIPCIDRValidation 消除隐患
对于正在管理 K8s 集群的团队,建议尽快在测试环境验证 1.36,特别是调度器插件和 DRA 的变化。这次升级的兼容性变更数量是近几个版本中最多的,生产环境建议至少等第一个 patch 版本(1.36.1)再升级。
Tags: Kubernetes|K8s|云原生|DRA|调度器|PodGroup|存储|安全|CSI|Prometheus
Keywords: Kubernetes 1.36|DRA|GPU调度|PodGroup|组调度|InPlacePodVerticalScaling|VolumeAttributesClass|UserNamespaces|MutatingAdmissionPolicy|Ingress NGINX|Kubectl