编程 Kubernetes 1.36 深度拆解:DRA 走向成熟、PodGroup 组调度与 30+ GA 特性的工程全貌

2026-07-17 11:47:34 +0800 CST views 10

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 了:

特性旧状态新状态意义
可消费容量管理BetaGA(默认启用)设备可以分区分配,多个 Pod 共享同一块物理 GPU
设备选择优先列表BetaGA管理员可以定义设备优先级策略
管理员访问权限BetaGA管理员可以强制管理设备分配
扩展资源类型AlphaBeta支持自定义资源类型,不局限于 GPU
设备绑定条件Beta(默认启用)Beta(强化)可以基于节点状态动态决定设备是否可用
设备污点与容忍AlphaBeta类似 K8s 原生污点机制,设备也可以设置污点
设备分区AlphaBetaGPU 时间片分区,多任务共享 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 或内存配额,你需要:

  1. 修改 Deployment 的 resources 配置
  2. K8s 触发滚动更新(RollingUpdate)
  3. 老 Pod 被 Terminating,新 Pod 被 Creating
  4. 服务中断(即使配置了探针,也会有连接抖动)

现在,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 年,为什么突然退役?核心原因:

  1. 架构局限性:基于传统 Nginx,无法充分利用现代云原生技术(Envoy、eBPF)
  2. 维护者枯竭:核心团队成员陆续离开,缺乏新鲜血液
  3. 替代方案成熟: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 时代的基础设施操作系统"。几个核心趋势:

  1. DRA 走向生产就绪:GPU/FPGA 编排终于有了标准方案,配合 PodGroup 组调度,AI 训练任务可以在 K8s 上原生运行

  2. 原地资源调整成为默认:打破了"改配置就要重启"的老大难问题,提升了生产服务的连续性

  3. 安全基线全面升级:UserNamespaces、MutatingAdmissionPolicy、ConstrainedImpersonation 扎堆 GA

  4. Ingress NGINX 退役:提醒我们,云原生领域没有永远的"标准答案",技术选型需要持续评估

  5. 存储和网络持续深化: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

推荐文章

使用Ollama部署本地大模型
2024-11-19 10:00:55 +0800 CST
SpaceX 600亿美元收购Cursor(中篇)
2026-06-22 03:30:23 +0800 CST
使用 Vue3 和 Axios 实现 CRUD 操作
2024-11-19 01:57:50 +0800 CST
thinkphp swoole websocket 结合的demo
2024-11-18 10:18:17 +0800 CST
Web 端 Office 文件预览工具库
2024-11-18 22:19:16 +0800 CST
内网穿透技术详解与工具对比
2025-04-01 22:12:02 +0800 CST
程序员茄子在线接单