编程 Kubernetes v1.36 Haru 深度解析:从"灵活框架"到"企业级平台"的安全与AI双重跨越

2026-05-17 00:47:08 +0800 CST views 3

Kubernetes v1.36 Haru 深度解析:从"灵活框架"到"企业级平台"的安全与AI双重跨越

前言

2026年4月22日,Kubernetes v1.36 正式发布,代号称 Haru(日语"春"的音读)。这是 2026 年 Kubernetes 的首个重要版本——包含 70 项增强功能:18 项进入 Stable 阶段,25 项进入 Beta 阶段,25 项新的 Alpha 功能。

但这一次发布远不止版本号往前挪了 0.1 这么简单。

如果说过去几年的 Kubernetes 更新是在不断丰富功能图谱,那么 v1.36 的发布释放了一个明确的信号:Kubernetes 正在从一个"灵活的容器编排框架"转变为"拥有更标准化、更有强制性默认安全与资源规范的企業级平台"

这个转变体现在两个核心维度上:

  1. 安全默认配置的系统性加固:用户命名空间正式 GA、可变准入策略原生化、细粒度 Kubelet 授权……这些变化让"安全"不再是靠运维人员手动配置的软约束,而是内嵌到平台默认行为中的硬保障。
  2. AI/ML 工作负载的原生级支持:DRA 可分区设备、工作负载感知抢占、Gang 调度 API……Kubernetes 对 GPU 集群的调度能力从"能用"跃升到了"好用"。

106 家公司和 491 位个人贡献者参与了这次发布。这篇文章,我们从架构原理出发,逐层拆解 v1.36 的核心技术突破,并结合代码示例探讨实际落地方案。


一、背景:为什么 v1.36 值得关注

1.1 云原生基础设施的成熟拐点

Kubernetes 从 2015 年开源至今,已经走过了十一个年头。在这段时间里,它完成了从 Google 内部 Borg 系统到云原生操作系统的事实标准这一身份转变。但随着 Kubernetes 在企业生产环境中的深度渗透,两个核心矛盾日益突出:

矛盾一:灵活性的代价是安全碎片化
Kubernetes 的设计哲学强调"配置即代码"和"声明式 API",这给了运维团队极大的灵活性。但灵活性的另一面是:默认配置往往不够安全。以容器运行用户为例,Docker 默认以 root 身份在容器内运行进程,而 Kubernetes 默认 Pod 的 securityContext 也并未强制非 root 执行。在高安全要求场景下,安全配置完全依赖运维人员的手工加固——一旦有人配置疏漏,整个集群的安全性就会打折扣。

矛盾二:通用编排能力与 AI 工作负载特殊需求的错配
GPU 资源的调度在 Kubernetes 社区长期是一个痛点。传统的 GPU 分配模型是将整张显卡作为一个整数资源(如 nvidia.com/gpu: 1)直接分配给 Pod。但真实的 AI 训练场景要复杂得多:

  • 大型模型的分布式训练需要多卡协同,任何一张卡失败都意味着整个 job 需要重新调度
  • GPU 分区技术允许将一张物理卡划分成多个虚拟分区,供不同任务共享
  • 当某个 GPU 发生硬件故障时,需要快速将该卡上的任务迁移到其他节点

这些需求在传统整数 GPU 模型下几乎无法优雅地实现。Kubernetes 调度器在处理分布式训练任务时,会出现"七个进程都在运行,却因为一个进程无法调度而整体卡死"的问题。

v1.36 正是在这两个矛盾上给出了系统性答案。

1.2 v1.36 变更总览

在深入技术细节之前,我们先建立全局视图:

维度核心变化
安全User Namespaces GA、Mutating Admission Policies GA、细粒度 Kubelet 授权 GA
AI/MLWorkload-Aware Preemption Alpha、Gang 调度 API Beta、DRA 增强(可分区设备/可消耗容量/设备污点)
资源管理Pod 级资源原地垂直扩缩 Beta、Memory QoS Beta(cgroup v2)
API 扩展性分片列表与分片监听流 Alpha、ResizeDeferred 事件
稳定性提升SELinux 卷标签 GA、Volume Group Snapshots GA、DRA 管理员访问 GA
废弃移除gitRepo 卷插件移除、Kube-proxy IPVS 模式移除、FlexVolume 支持移除

二、安全加固:从"可选加固"到"默认安全"

2.1 用户命名空间(User Namespaces)GA——容器隔离的范式升级

这是 v1.36 最重磅的安全特性,也是 Kubernetes 历史上容器隔离能力的一次重大升级。

传统容器的 root 问题

在传统容器模型中,容器内的 root 用户(UID 0)实际上就是宿主机的 root 用户(UID 0)。虽然 namespace 隔离了进程视图,OverlayFS 等文件系统隔离了文件访问,但当容器以 root 运行时,一旦攻击者利用容器逃逸漏洞获得了主机 root 权限,后果将是灾难性的。

真实攻击场景:2020 年的 runc 符号链接攻击(CVE-2019-5736)允许容器内的恶意进程覆盖宿主机上的 runc 二进制文件。由于容器默认以 root 运行,攻击者可以在容器内直接覆写 /proc/self/exe,从而在宿主机上获得 root shell。

用户命名空间的工作原理

用户命名空间(User Namespaces)的核心思想是将容器内的 UID/GID 映射到宿主机上的非特权 UID/GID

具体来说,当 Pod 启用了用户命名空间后:

容器内 UID 0 (root)  →  宿主机 UID 100000 (非特权用户)
容器内 UID 1000      →  宿主机 UID 100001
容器内 GID 0 (root)  →  宿主机 GID 100000
容器内 GID 27 (sudo) →  宿主机 GID 100000

这意味着即使容器内的进程成功逃逸并获取了容器内 root 身份,它在宿主机上也只是一个普通非特权用户,无法修改系统文件、加载内核模块或访问其他 Pod 的资源。

代码示例:启用用户命名空间

Pod 级别配置(v1.27+ Beta)

apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 1000
    supplementalGroups: [1000]
  # 启用用户命名空间(v1.36 GA)
  hostUsers: false
  containers:
  - name: app
    image: nginx:1.27
    securityContext:
      runAsNonRoot: true
      allowPrivilegeEscalation: false
      capabilities:
        drop:
        - ALL

Deployment 级别配置

apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
spec:
  template:
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        runAsGroup: 1000
        # 关键:禁用 hostUsers,启用用户命名空间
        hostUsers: false
      containers:
      - name: main
        image: myapp:latest
        securityContext:
          runAsNonRoot: true
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop: [ALL]

工作原理图示

┌─────────────────────────────────────────────────────┐
│                   Node (宿主机)                      │
│  UID 0 (root) - 系统管理员                           │
│  UID 100000-100099 - Pod A 的容器进程映射区间        │
│  UID 100100-100199 - Pod B 的容器进程映射区间        │
│  UID 100200-100299 - Pod C 的容器进程映射区间        │
└─────────────────────────────────────────────────────┘
              ↑ remap (映射)
┌─────────────────────────────────────────────────────┐
│  Pod A (容器)                                       │
│  UID 0 (容器内 root)  →  Node UID 100000           │
│  UID 1000 (应用用户)  →  Node UID 100000+1000     │
│  容器内 root = 宿主机普通用户,无法提权!             │
└─────────────────────────────────────────────────────┘

验证用户命名空间是否生效

# 查看容器进程在宿主机上的真实 UID
kubectl exec -it secure-pod -- cat /proc/self/uid_map

# 输出示例(Pod 内)
#          0          100000       65536
# 表示容器内 UID 0 映射到宿主机 UID 100000,范围 65536 个 UID

# 验证容器内 root 无法访问系统文件
kubectl exec -it secure-pod -- sh -c "whoami && id"
# 输出: nobody 或 non-root

# 验证无法加载内核模块(即使容器内看起来是 root)
kubectl exec -it secure-pod -- sh -c "modprobe nonexistent_module"
# 输出: Operation not permitted

架构影响:为什么这是 Kubernetes 安全的分水岭

用户命名空间的 GA 意味着它不再只是实验性功能,而是可以在生产环境直接使用。安全团队不需要在安全加固上投入大量自定义配置,平台团队可以在 PodSecurityPolicy(已废弃)替代方案中直接将 hostUsers: false 作为默认行为。

生产环境迁移建议

# 1. 检查集群中哪些 Pod 未使用 hostUsers: false
kubectl get pods -A -o json | jq '.items[] | select(.spec.securityContext.hostUsers != false) | .metadata.name' | wc -l

# 2. 查看支持用户命名空间的容器运行时版本
# containerd >= 1.7, cri-dockerd 需要特别注意版本兼容性

# 3. 检查节点是否支持用户命名空间
kubectl get nodes -o json | jq '.items[].status.conditions[] | select(.type=="UserNamespacesReady")'

2.2 可变准入策略(Mutating Admission Policies)GA——Webhook 的原生替代

传统 Webhook 的运维之痛

在 Kubernetes 中,准入控制器(Admission Controller)是 API Server 在资源持久化之前拦截请求的钩子。Mutating Webhook 是其中一类,可以修改请求内容——这是实现默认标签注入、资源配额修正、Pod 自动注入 sidecar 等功能的常用方式。

但传统 Mutating Webhook 存在几个根本性缺陷:

运维复杂度高

  • 需要单独维护一个 Webhook 服务:Deployment + Service + 证书 + RBAC
  • Webhook 服务本身就是攻击面——如果 Webhook 服务被攻陷,攻击者可以修改任意 Pod 配置
  • 多集群环境下,每个集群都要单独部署和同步 Webhook 配置

延迟与可用性问题

// 传统 Mutating Webhook 的请求流程
Client → API Server → Webhook Service (HTTP 调用) → 等待响应 → 继续处理
                                      ↑
                                  10-50ms 额外延迟
                                  单点故障风险

行为不确定性

  • Webhook 的执行顺序不保证(多个 Webhook 可能相互覆盖)
  • 调试困难——没有标准化的审计和回放机制

Mutating Admission Policies 的原生解决方案

Mutating Admission Policies 使用 Kubernetes 原生的 CEL(Common Expression Language) 表达式来定义变更逻辑,无需额外部署 Webhook 服务:

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
  name: inject-sidecar-policy
spec:
  matchConstraints:
    resourceRules:
    - apiGroups: [""]
      apiVersions: ["v1"]
      resources: ["pods"]
      operations: ["CREATE", "UPDATE"]
  matchConditions:  # 可选:精细化匹配条件
    - name: "exclude-system-namespaces"
      expression: "object.metadata.namespace not in ['kube-system', 'kube-public']"
  mutatingActions:
    - name: "inject-envoy-sidecar"
      action: Mutation
      # 使用 CEL 表达式定义变更逻辑
      patchActions:
      - type: JSONPatch
        operation: add
        path: /spec/containers/-1
        value:
          name: envoy-sidecar
          image: envoyproxy/envoy:v1.28
          ports:
          - containerPort: 9901
            protocol: TCP
          resources:
            limits:
              memory: "128Mi"
              cpu: "100m"
            requests:
              memory: "64Mi"
              cpu: "50m"
          env:
          - name: ENVOY_CLUSTER
            value: "cluster-svc"

优势对比

维度Mutating WebhookMutating Admission Policies
部署复杂度需要独立服务 + 证书 + RBACKubernetes 原生对象,kubectl apply 即部署
延迟HTTP 调用,额外 10-50msAPI Server 进程内执行,无额外延迟
可用性Webhook 服务需独立高可用无外部依赖,随 API Server HA 自动保证
审计需自行实现审计日志Kubernetes 原生审计日志支持
调试Webhook 日志分散kubectl 可以直接查看策略对象

2.3 细粒度 Kubelet API 授权 GA

在 v1.32 以 Alpha 引入后,细粒度 Kubelet API 授权在 v1.36 正式 GA。

传统问题:监控和可观测性工具(如 Prometheus、cAdvisor)需要从 Kubelet API 获取节点和 Pod 指标。传统做法是授予这些工具 system:node-proxy 权限——这个权限实际上允许访问节点上的任意路径,等同于给予了集群管理员级别的访问权限。这对于只读监控工具来说,权限过于宽泛。

v1.36 的解决方案:现在可以为不同的 Kubelet API 端点配置不同的权限级别:

# kubelet-config.yaml (kubelet 配置)
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authorization:
  mode: Webhook
  webhook:
    # 细粒度鉴权:可以区分 /metrics、/logs、/exec 等端点
    # 不同的调用者(service account)可以授权访问不同端点
    cacheAuthorizedTTL: 5m0s
    cacheUnauthorizedTTL: 30s
# RBAC 绑定示例:只允许 Prometheus 访问 /metrics 端点
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus-kubelet-metrics-reader
rules:
- apiGroups: [""]
  resources: ["nodes/metrics", "nodes/proxy"]
  verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: prometheus-to-kubelet-metrics
subjects:
- kind: ServiceAccount
  name: prometheus
  namespace: monitoring
roleRef:
  kind: ClusterRole
  name: prometheus-kubelet-metrics-reader
  apiGroup: rbac.authorization.k8s.io

三、AI 工作负载:从"能用"到"好用"的跨越

3.1 工作负载感知抢占(Workload-Aware Preemption)——分布式训练的死锁问题终结者

传统抢占机制的困境

在 Kubernetes 中,抢占(Preemption)是指当高优先级 Pod 无法被调度时,调度器驱逐(删除)低优先级 Pod 来为高优先级 Pod 腾出资源的机制。这个机制在大多数场景下工作良好,但面对分布式 AI 训练任务时,会出现严重的"部分抢占故障模式":

问题场景:假设有一个分布式训练任务,包含 8 个 Pod(每个 Pod 占用 1 张 A100 GPU),部署在 8 个节点的集群上。当前 7 个 Pod 已经被调度并正在运行,第 8 个 Pod 因为节点资源不足无法调度。

节点1: [Pod-A GPU-1]  ✓ 运行中
节点2: [Pod-B GPU-1]  ✓ 运行中
节点3: [Pod-C GPU-1]  ✓ 运行中
节点4: [Pod-D GPU-1]  ✓ 运行中
节点5: [Pod-E GPU-1]  ✓ 运行中
节点6: [Pod-F GPU-1]  ✓ 运行中
节点7: [Pod-G GPU-1]  ✓ 运行中
节点8: [Pod-H GPU-1]  ✗  Pending(等待调度)

低优先级 Pod:
  - 其他团队的推理服务 (Pod-1 ~ Pod-20) 占用 GPU

传统调度器的行为是:逐一尝试驱逐低优先级 Pod 来满足 Pod-H 的调度条件。每次驱逐一个 Pod,调度器会重新评估 Pod-H 是否能被调度。这个过程会持续,直到找到足够的资源或者所有低优先级 Pod 都被驱逐。

问题所在:对于分布式训练来说,只有当所有 8 个 Pod 都同时运行时,训练才能正常进行。调度器将 Pod 视为独立个体进行调度/抢占决策,导致的结果是:

  • Pod-A 到 Pod-G 都在运行,Pod-H 也在运行,但 Pod-H 是最后才被调度的
  • 当集群资源紧张时,Pod-H 可能被频繁驱逐,而 Pod-A 到 Pod-G 仍然占用着资源
  • 这导致分布式训练"7/8 的进程在空转等待第 8 个进程"——系统陷入了部分调度的死锁状态

PodGroup:工作负载的原子调度单元

工作负载感知抢占引入了一个关键抽象:PodGroup。PodGroup 将一组需要协同工作的 Pod 视为一个原子调度单元:

# 定义一个 PodGroup(属于 Job 或其他工作负载)
apiVersion: scheduling.sigs.k8s.io/v1alpha1
kind: PodGroup
metadata:
  name: distributed-training-job-001
  namespace: ml
spec:
  # 最小需要同时调度的 Pod 数量
  minMember: 8
  # 超时时间:如果在指定时间内无法满足 minMember,则整个 Job 失败
  minAvailableDuration: 30m
  priority: 10000
# Job 引用 PodGroup
apiVersion: batch/v1
kind: Job
metadata:
  name: distributed-training
  namespace: ml
spec:
  parallelism: 8
  completions: 8
  backoffLimit: 3
  podFailurePolicy:
    rules:
    - action: FailJob
      onExitCodes:
        operator: In
        values: [137, 143]  # OOMKilled / SIGTERM
  template:
    metadata:
      labels:
        pod-group.scheduling.sigs.k8s.io: distributed-training-job-001
    spec:
      # 8 个 replica 均引用同一个 PodGroup
      schedulerName: default-scheduler
      priorityClassName: high-priority-training
      containers:
      - name: trainer
        image: pytorch/pytorch:2.4.0
        args:
        - python
        - /workspace/train.py
        - --world-size=8
        - --rank=$(POD_INDEX)
        env:
        - name: POD_INDEX
          valueFrom:
            fieldRef:
              fieldPath: metadata.annotations['pod-group.scheduling.sigs.k8s.io/index']
        resources:
          limits:
            nvidia.com/gpu: "1"
            memory: "64Gi"
          requests:
            nvidia.com/gpu: "1"
            memory: "64Gi"
      # 当 Job 失败时自动清理所有 Pod
      restartPolicy: OnFailure
      priorityClassName: high-priority-training

调度器的原子决策逻辑

# 伪代码:工作负载感知抢占的调度决策

def can_preempt_for_workload(pod_group, target_node):
    # 1. 检查 PodGroup 需要的最小成员数
    min_required = pod_group.minMember
    
    # 2. 找到 PodGroup 中已经调度的所有 Pod
    scheduled_pods = get_scheduled_pods_in_group(pod_group)
    
    # 3. 检查这个节点上的资源是否对调度有实质性帮助
    additional_pods_can_schedule = count_pods_fit_in_node(target_node, pod_group)
    
    # 4. 关键判断:如果调度后能凑够 minMember,才执行抢占
    total_after_preemption = len(scheduled_pods) + additional_pods_can_schedule
    
    if total_after_preemption >= min_required:
        # 抢占有意义,执行
        preempt_lower_priority_pods(target_node)
        return True
    else:
        # 抢占没有意义(仍然凑不够 minMember),跳过这个节点
        return False

效果对比

传统调度器:
  调度 Pod-H → 驱逐 Pod-1 → 重新评估 → 驱逐 Pod-2 → 重新评估 → ... 
  结果:7/8 Pod 运行,1 Pod Pending,训练无法推进
  
工作负载感知抢占:
  调度 PodGroup → 检查:当前 7 个 + 节点 X 上可以调度几个? → 
  如果 ≥ 8 → 批量驱逐满足条件的节点上的 Pod → 一次操作完成全部调度
  如果 < 8 → 跳过,不破坏现有的 7/8 运行状态

Gang 调度 API:从 Alpha 到 Beta

Gang 调度 API 在 v1.35 以 Alpha 引入,v1.36 正式进入 Beta 阶段。Gang 调度的核心语义是"All or Nothing"——要么所有 Pod 都被调度,要么一个都不调度。这与 PodGroup 的语义高度一致。

# v1.36 Gang 调度配置(Beta)
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: gang-scheduled-job
value: 10000
preemptionPolicy: PreemptLowerPriority
globalDefault: false
description: "分布式训练任务的高优先级调度"

3.2 DRA 增强:GPU 资源模型的根本性变革

传统整数 GPU 模型的局限

Kubernetes 传统 GPU 分配方式使用整数设备插件模型:

# 传统方式:整卡分配
resources:
  limits:
    nvidia.com/gpu: "2"  # 请求 2 张整卡
  requests:
    nvidia.com/gpu: "2"

问题:

  1. 无法利用 GPU 分区:现代 GPU(如 NVIDIA A100)支持多实例 GPU( MIG),可以将一张物理卡分成 7 个分区,每个分区独立运行不同任务。整数模型完全无法利用这个能力。
  2. 单卡故障影响大:当一张 GPU 发生硬件故障时,整个 Pod 会被驱逐并重建。
  3. 资源利用率低:推理任务通常只需要 GPU 的一小部分计算能力,整卡分配造成严重浪费。

DRA 可分区设备:原生分区支持

v1.36 中,DRA 可分区设备(Partitionable Devices)进入测试阶段并默认开启:

# DRA 可分区设备配置
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
metadata:
  name: partitioned-gpu-claim
  namespace: ml
spec:
  devices:
    requests:
    - name: "gpu-partition"
      # 请求一个可分区的 GPU,并指定需要的分区数量
      deviceType: nvidia.com/gpu
      # 这告诉设备插件:我需要一个 1/7 的 MIG 分区
      partitionableDevices:
      - name: "nvidia.com/gpu"
        # 拓扑约束:分区必须在同一 NUMA 节点
        constraints:
        - op: Exact
          resource: topology-manager/numa
          value: "0"

DRA 设备污点与容忍(Device Taints)

当某个 GPU 发生硬件故障时,DRA 设备污点机制可以快速将故障 GPU 标记为不可用,并自动将使用该 GPU 的 Pod 驱逐到健康节点上:

# 节点上的 GPU 发生故障时,Device Plugin 自动添加污点
kubectl get nodes node-1 -o yaml | grep -A 5 taints

# 输出示例:
# taints:
# - key: nvidia.com/gpu
#   value: unhealthy
#   effect: NoSchedule
#   # kubelet 自动添加,表示该 GPU 不可用

# 调度器感知污点,拒绝将新的 GPU Pod 调度到该节点
# 同时,Job Controller 检测到污点后,自动触发 Pod 迁移

3.3 暂停作业的 Pod 可变资源(Mutable Pod Resources)——弹性调度的终极形态

v1.36 Beta 并默认启用了一个极具实用价值的功能:Pod 级资源原地垂直扩缩容

# 更新作业的资源需求(无需重建 Pod)
apiVersion: batch/v1
kind: Job
metadata:
  name: ml-inference
  namespace: ml
spec:
  suspend: true  # 先暂停作业
  template:
    spec:
      containers:
      - name: inference
        image: inference-server:v2.0
        resources:
          requests:
            cpu: "4"
            memory: "8Gi"
            nvidia.com/gpu: "1"
          limits:
            cpu: "8"
            memory: "16Gi"
            nvidia.com/gpu: "1"
---
# 通过 API 更新作业的资源请求(Queue Controller 自动处理)
apiVersion: batch/v1
kind: Job
metadata:
  name: ml-inference
  namespace: ml
spec:
  suspend: false  # 恢复作业,自动适配新的资源配置

ResizeDeferred 事件

当集群资源不足无法立即完成扩缩容时,Pod 会收到一个新的 ResizeDeferred 事件,告知"当前资源配置变更被推迟,待节点资源空闲后自动重试":

// Pod 事件中的 ResizeDeferred
{
  "type": "Warning",
  "reason": "ResizeDeferred",
  "message": "CPU request resize to 8 cores deferred due to node resource pressure. 
              Will retry when node has sufficient resources."
}

四、API 可扩展性与性能优化

4.1 分片列表与分片监听流——超大规模集群的性能救星

拥有数万个节点的 Kubernetes 集群,在监听流(Watch Stream)上存在一个根本性瓶颈:所有控制器(Controller)共享同一个 Watch 连接来接收资源变更通知

当一个 Deployment 有 10000 个 Pod 时,每创建一个新 Pod,所有 Watch 这个 Deployment 的控制器都会收到同一个通知——这造成了巨大的消息冗余和 CPU 开销。

v1.36 引入的分片列表(Sharded Lists)和分片监听流(Sharded Watch Streams)将这种负载分摊到多个流中:

// 控制器侧的改进(伪代码)
// v1.36 之前:单一 Watch 连接
watcher := client.Watch("pods")
for event := range watcher.ResultChan() {
    process(event)  // 所有控制器共享同一连接
}

// v1.36 之后:分片 Watch 连接
// kube-controller-manager 自动将 Pod 列表分片
shards := 8
for shard := 0; shard < shards; shard++ {
    go func(s int) {
        watcher := client.Watch("pods", 
            // 指定当前分片
            resourceVersion: getShardResourceVersion(shard, totalShards),
            shardIndex: s,
        )
        for event := range watcher.ResultChan() {
            process(event)  // 每个控制器只处理分配给它的分片
        }
    }(shard)
}

性能影响

集群规模: 50,000 节点,200,000 Pod

v1.35:
  Watch 事件数: ~200,000/秒(每事件广播到所有 Watcher)
  kube-apiserver CPU: 持续高负载,p99 延迟 > 500ms

v1.36 (分片 Watch):
  Watch 事件数: ~25,000/分片 × 8 分片 = 200,000/秒
  但每个分片内的广播量大幅减少
  kube-apiserver CPU: 负载降低 60-70%,p99 延迟 < 100ms

4.2 Memory QoS(cgroup v2)——分层内存保护的科学化

v1.36 中,通过 cgroup v2 实现的内存服务质量(Memory QoS)进入 Beta 阶段。

工作原理

传统 Kubernetes 内存限制:
  Pod A: requests.memory=4Gi, limits.memory=8Gi
  Pod B: requests.memory=4Gi, limits.memory=8Gi
  节点总内存: 32Gi

  问题:当 Pod A 实际使用 7Gi,Pod B 实际使用 7Gi 时
        节点总使用 = 14Gi < 32Gi,看似正常
        但 Pod A 的 limits 是 8Gi,暗示它"可能"用满 8Gi
        如果 Pod B 也继续增长到 8Gi,节点就会 OOM
        调度器基于 requests 而非实际使用量做决策——这导致了资源"隐性超卖"

Memory QoS (cgroup v2):
  每个 Pod 的 cgroup 层级设置了三层内存保护:
  - memory.low:  "保证区" — 低于此值不会被回收
  - memory.high: "限流区" — 超过此值触发限流(降低进程速度)
  - memory.max:  "硬限制" — 超过此值触发 OOM
  
  Pod A 配置:
    memory.low = 4Gi    (保证 Pod 至少有 4Gi 不被回收)
    memory.high = 7Gi   (超过 7Gi 限流,给调度器反应时间)
    memory.max = 8Gi    (硬上限,触发 OOM Kill)

  Pod B 配置:
    memory.low = 4Gi
    memory.high = 7Gi
    memory.max = 8Gi

  效果:
    - 即使 Pod A 用到 7.5Gi,Pod B 的 4Gi 保证区也不会被挤压
    - Pod A 超过 memory.high 后自动被限流,调度器有时间迁移 Pod
    - 系统在 OOM 之前有了更多缓冲手段

配置方式

# Pod 配置 memory QoS(Kubernetes 自动生成对应的 cgroup 设置)
apiVersion: v1
kind: Pod
metadata:
  name: memory-sensitive-pod
spec:
  containers:
  - name: app
    image: java-app:latest
    resources:
      requests:
        memory: "4Gi"
      limits:
        memory: "8Gi"
    # Kubernetes v1.36 会自动将上述配置映射为 cgroup v2 QoS 参数:
    # memory.low = requests.memory * 1.0
    # memory.high = limits.memory * 0.9  
    # memory.max = limits.memory

五、稳定性提升:从 Beta 到 GA 的核心特性

5.1 SELinux 卷标签——Pod 启动延迟降低 80%

在启用 SELinux 强制模式的 OpenShift 和 RedHat 系统中,以往每次 Pod 挂载 volume 时,SELinux 都需要对整个文件系统做递归的标签重置(relabel)——一个包含数万文件的 volume,relabel 可能耗时 30 秒以上。

v1.36 GA 的 SELinux 卷标签功能通过 mount -o context=XYZ 选项在挂载时统一设置标签,无需递归扫描:

# 验证 SELinux 卷标签是否生效
# 方法1:查看 Pod 启动时间
kubectl annotate pod my-pod \
  kubernetes.io/selinux-relabel-time=$(date +%s)

# 方法2:检查 kubelet 日志中的 SELinux 操作
journalctl -u kubelet | grep "SELinux" | tail -20

5.2 Volume Group Snapshots——多 PVC 崩溃一致性快照

# 定义卷组快照(同时为多个 PVC 创建一致性快照)
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeGroupSnapshot
metadata:
  name: multi-pvc-snapshot
  namespace: production
spec:
  # 要快照的所有 PVC
  source:
    persistentVolumeClaims:
    - name: data-pvc
    - name: log-pvc
    - name: config-pvc
  volumeSnapshotClassName: fast-ssd-class
---
# 从快照恢复(原子操作,所有 PVC 同时恢复)
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeGroupSnapshotContent
metadata:
  name: restored-snapshot
spec:
  volumeGroupSnapshotRef:
    name: multi-pvc-snapshot
    namespace: production
  restoreSize:
    data-pvc: 100Gi
    log-pvc: 50Gi
    config-pvc: 1Gi

5.3 DRA 管理员访问 GA——硬件资源的标准化管理

DRA 管理员访问为集群管理员提供了一个固定框架,用于全局访问和管理 GPU/FPGA 等加速器资源:

# 集群管理员:定义可用的 GPU 资源池
apiVersion: resource.k8s.io/v1alpha3
kind: DeviceClass
metadata:
  name: gpu-nvidia-a100
spec:
  devices:
  - name: node1-gpu0
    nodeName: gpu-node-1
    pool: default
  - name: node1-gpu1
    nodeName: gpu-node-1
    pool: default
  - name: node2-gpu0
    nodeName: gpu-node-2
    pool: default

---
# 开发者:声明式请求 GPU 资源
apiVersion: resource.k8s.io/v1alpha3
kind: ResourceClaim
metadata:
  name: training-gpu-claim
  namespace: ml
spec:
  devices:
    requests:
    - name: "compute-gpu"
      deviceType: "nvidia.com/gpu"
      counters:
      - name: "memory"
        min: 40000  # 请求至少 40Gi 显存

六、必须注意的破坏性变更

6.1 gitRepo 卷插件正式移除——安全漏洞的彻底清算

gitRepo 卷插件从 v1.11 开始被废弃(deprecate),v1.36 正式移除。该插件存在允许攻击者通过精心构造的 .git 目录在节点上以 root 身份执行代码的漏洞(CVE-2018-11235)。

迁移方案

# ❌ 已废弃的 gitRepo 卷
volumes:
- name: git-repo
  gitRepo:
    repository: "https://github.com/myteam/config.git"
    revision: "main"
    directory: "."

# ✅ 迁移到 initContainer + emptyDir
volumes:
- name: git-config
  emptyDir: {}

initContainers:
- name: git-sync
  image: k8s.gcr.io/git-sync/git-sync:v3.6.4
  env:
  - name: GIT_SYNC_REPO
    value: "https://github.com/myteam/config.git"
  - name: GIT_SYNC_BRANCH
    value: "main"
  - name: GIT_SYNC_ROOT
    value: "/git"
  - name: GIT_SYNC_DEST
    value: "config"
  - name: GIT_KNOWN_HOSTS
    value: "false"  # 生产环境建议开启并配置 known_hosts
  volumeMounts:
  - name: git-config
    mountPath: /git

containers:
- name: app
  image: myapp:latest
  volumeMounts:
  - name: git-config
    mountPath: /etc/config  # 应用读取 git 配置的路径

6.2 Kube-proxy IPVS 模式移除

如果你的集群仍在使用 IPVS 模式的 Kube-proxy,升级到 v1.36 后服务网络可能中断:

# 检查是否使用了 IPVS 模式
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode

# 如果看到 ipvs,需要迁移到 iptables 或 nftables
# v1.36 推荐使用 nftables 模式(性能优于 iptables)
# kube-proxy 配置迁移到 nftables 模式
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: nftables
nftables:
  # NAT 表的遍历模式:性能优先 vs 兼容性优先
  masqueradeAll: false
  masqueradeBit: 14
  # 批量刷新 NAT 规则,减少刷新次数
  minSyncDuration: 5s

七、Ingress NGINX 正式退役——网络生态的重大转折

2026 年 3 月 24 日,Kubernetes SIG Network 与安全响应委员会正式退役了 Ingress NGINX 项目。这对整个 Kubernetes 生态系统有深远影响:

退役原因

  • Ingress NGINX 的代码库长期积累了大量安全债务
  • 多次被主流 CNCF 项目认证为高危漏洞来源
  • 项目维护人力不足以应对日益增长的安全要求

替代方案评估

方案成熟度性能功能维护状态
Gateway API (GKE/AWS ALB/Cilium)GA丰富活跃
Istio Ambient ModeBeta最丰富活跃
NGINX Ingress Controller (独立版)GA丰富活跃(但需自行维护)
TraefikGA丰富活跃

Gateway API 迁移路径

# ❌ Ingress NGINX 风格
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 80

# ✅ Gateway API 风格(v1.36 推荐)
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: my-gateway
  namespace: ingress
spec:
  gatewayClassName: istio
  listeners:
  - name: http
    port: 80
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: Same

---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: my-app-route
  namespace: default
spec:
  parentRefs:
  - name: my-gateway
    namespace: ingress
  hostnames: ["myapp.example.com"]
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api
    backendRefs:
    - name: api-service
      port: 80

八、升级指南与实操清单

8.1 升级前检查清单

#!/bin/bash
# upgrade-check.sh — Kubernetes v1.36 升级前检查

set -e

echo "=== Kubernetes v1.36 升级前检查 ==="
echo ""

# 1. 检查 API 弃用警告
echo "[1/7] 检查 API 弃用..."
DEPRECATED_APIS=$(kubectl get pods -A -o json 2>/dev/null | \
  jq -r '.items[].spec.containers[].env[]? | select(.name=="K8S_DEPRECATED_API") | .value' 2>/dev/null)
if [ -n "$DEPRECATED_APIS" ]; then
  echo "⚠️  发现已废弃 API 使用:"
  echo "$DEPRECATED_APIS"
else
  echo "✓ 无 API 废弃警告"
fi

# 2. 检查 gitRepo 卷使用
echo ""
echo "[2/7] 检查 gitRepo 卷插件使用..."
GITREPO_PODS=$(kubectl get pods -A -o json | \
  jq -r '.items[] | select(.spec.volumes[].gitRepo != null) | "\(.metadata.namespace)/\(.metadata.name)"')
if [ -n "$GITREPO_PODS" ]; then
  echo "⚠️  发现使用 gitRepo 卷的 Pod(必须迁移):"
  echo "$GITREPO_PODS"
else
  echo "✓ 无 gitRepo 卷使用"
fi

# 3. 检查 Kube-proxy 模式
echo ""
echo "[3/7] 检查 Kube-proxy 模式..."
KP_MODE=$(kubectl get configmap kube-proxy -n kube-system -o jsonpath='{.items[0].data.config}' 2>/dev/null | \
  jq -r '.mode' 2>/dev/null || echo "unknown")
echo "当前 Kube-proxy 模式: $KP_MODE"
if [ "$KP_MODE" = "ipvs" ]; then
  echo "⚠️  IPVS 模式在 v1.36 已移除,请迁移到 iptables 或 nftables"
fi

# 4. 检查用户命名空间兼容性
echo ""
echo "[4/7] 检查用户命名空间兼容性..."
kubectl get nodes -o json | jq -r '.items[].status.conditions[] | select(.type=="UserNamespacesReady") | "\(.status)"' | sort -u

# 5. 检查 DRA 资源声明
echo ""
echo "[5/7] 检查 DRA 资源声明..."
DRA_CLAIMS=$(kubectl get resourceclaims -A --no-headers 2>/dev/null | wc -l)
echo "集群中 DRA 声明数量: $DRA_CLAIMS"

# 6. 检查 GPU 节点配置
echo ""
echo "[6/7] 检查 GPU 节点上的 DRA 配置..."
kubectl get nodes -o json | \
  jq -r '.items[] | select(.status.capacity."nvidia.com/gpu") | .metadata.name' | \
  while read node; do
    echo "  节点: $node"
    kubectl get node $node -o json | \
      jq -r '.status.allocatable | "    GPU: \(."nvidia.com/gpu"), RDMA: \(."rdma/hfi1")"'
  done

# 7. 检查 cgroup 版本
echo ""
echo "[7/7] 检查 cgroup 版本..."
ssh -o StrictHostKeyChecking=no node-1 "stat -f -c %T /sys/fs/cgroup" 2>/dev/null || \
  echo "  (需要在节点上执行: stat -f -c %T /sys/fs/cgroup)"
echo "  cgroup v1 → Memory QoS 功能不可用"
echo "  cgroup v2 → 支持完整 Memory QoS 功能"

echo ""
echo "=== 检查完成 ==="

8.2 推荐的升级路径

# 滚动升级策略(保证高可用)
apiVersion: v1
kind: ConfigMap
metadata:
  name: upgrade-strategy
  namespace: kube-system
data:
  strategy.yaml: |
    upgrade:
      # 最多同时升级的控制面组件数
      maxUnavailableControlPlane: 1
      # 最多同时驱逐的 Pod 数(按百分比)
      maxUnavailableNode: 10%
      # 节点排水超时
      drainTimeout: 10m
      # 升级失败后自动回滚
      autoRollback: true
      # 升级前快照(针对有状态工作负载)
      preUpgradeSnapshot:
        enabled: true
        volumeSnapshotClass: csi-aws-volsnap

九、架构演进:Kubernetes 的范式转变

9.1 从"框架"到"平台"的哲学转变

VMware Cloud Foundation 博客对 v1.36 的评价一针见血:"Kubernetes 正从一个灵活的框架逐步转向拥有更标准化、更具强制性的默认安全与资源规范。"

这个转变体现在三个层面:

第一层:默认行为的硬化
过去 Kubernetes 的很多安全特性需要手动开启才能生效——PodSecurityPolicy(已废弃)、网络策略、PSP……v1.36 的趋势是让安全基线直接内置到默认行为中:hostUsers: false 作为默认配置、可变准入策略的原生化、细粒度授权……这些变化减少了运维人员的认知负担,同时降低了因配置疏漏引发的安全风险。

第二层:工作负载语义的丰富化
Kubernetes 最初的设计假设是 Pod 与 Pod 之间相互独立——每个 Pod 可以被单独调度、扩容、驱逐。但 AI 工作负载打破了这一假设:分布式训练需要 Gang 调度、GPU 分区需要 DRA 支持、弹性推理需要原地扩缩容……v1.36 体现了 Kubernetes 在工作负载语义上的持续深化。

第三层:生命周期复杂性的上移
随着 Kubernetes 变得越来越"有主见",升级集群的影响面也越来越大。Ingress NGINX 的退役、gitRepo 卷插件的移除、IPVS 模式的移除——这些变化意味着"跟进 Kubernetes 版本迭代已不再只是简单升级集群",还涉及评估兼容性、管理生命周期复杂度、判断何时采用新版本、以及避免业务中断。

9.2 AI 原生平台的方向展望

v1.36 在 AI 工作负载上的投入标志着 Kubernetes 对 AI 场景从"兼容并包"转向"深度定制"。展望未来几个版本,我们预期看到:

  • DRA 资源预留协议标准化:不同厂商的 AI 加速器(NVIDIA GPU、Intel Gaudi、AMD Instinct)可能逐步采用统一的 DRA 资源声明和调度协议
  • 多租户 GPU 隔离:随着 GPU 共享的普及,硬件级别的内存和计算隔离将成为刚性需求
  • ML 工作流的原生支持:类似于 Spark on K8s,Kubernetes 可能在调度层面对 PyTorch/TensorFlow 训练循环做更深的语义理解

十、总结:v1.36 的核心要点

Kubernetes v1.36 Haru 不只是又一个大版本更新,它代表了 Kubernetes 走向成熟基础设施平台的阶段性宣言。

安全维度

  • ✅ 用户命名空间 GA:容器 root 映射为宿主机非特权用户,从根本上解决了容器逃逸后的提权风险
  • ✅ 可变准入策略 GA:CEL 驱动的原生变更逻辑,替代了高维护成本的 Mutating Webhook
  • ✅ 细粒度 Kubelet 授权 GA:最小权限原则在 Kubelet API 层面的真正落地

AI/ML 维度

  • ✅ 工作负载感知抢占 Alpha:PodGroup 原子调度解决了分布式训练的"部分抢占故障模式"
  • ✅ Gang 调度 API Beta:All-or-Nothing 语义在 Kubernetes 调度层的原生实现
  • ✅ DRA 增强全面就绪:GPU 分区、动态容量、设备污点与容忍——AI 加速器的标准化资源管理框架初步成型

工程维度

  • ✅ Pod 级资源原地扩缩 Beta:无需销毁重建 Pod 就能调整资源配额
  • ✅ Memory QoS Beta:cgroup v2 分层内存保护让资源超卖更有把握
  • ✅ 分片 Watch 流 Alpha:超大规模集群控制平面的性能瓶颈得到初步解决

需要立即行动

  • ⚠️ gitRepo 卷插件已移除——立即迁移到 initContainer 方案
  • ⚠️ Kube-proxy IPVS 模式已移除——迁移到 nftables 或 iptables
  • ⚠️ Ingress NGINX 已正式退役——评估 Gateway API 迁移路径

106 家公司、491 位贡献者、70 项增强——这组数字背后是 Kubernetes 社区在安全和 AI 基础设施两个方向上长达两年以上持续投入的结晶。

对于正在运行 Kubernetes 的团队来说,v1.36 是一次值得认真评估的升级。它在安全基线和 AI 工作负载支持上带来的提升,对于生产环境来说具有实质性的风险降低和能力增强价值。建议有条件的团队在测试环境中完成验证后,尽快推进升级计划。

毕竟,在云原生基础设施这条路上,跟上 Kubernetes 的演进节奏,本身就是一种竞争力。


参考链接

  • Kubernetes v1.36 官方发布博客:https://kubernetes.io/blog/2026/04/22/kubernetes-v1-36-release/
  • Kubernetes v1.36 CHANGELOG:https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.36.md
  • InfoQ 深度解读:https://www.infoq.cn/article/kNkrHGzRvA7r6pRtlGB5
  • VMware Cloud Foundation 博客:https://blogs.vmware.com/cloud-foundation/2026/04/29/kubernetes-1-36-what-actually-changed-for-enterprise-platforms/
  • Kloia 技术解读:https://www.kloia.com/blog/kubernetes-1-36-whats-coming
  • Palark 版本解读:https://palark.com/blog/kubernetes-1-36-release-features/
复制全文 生成海报 Kubernetes k8s 云原生 容器 DRA AI训练 GPU调度

推荐文章

一些实用的前端开发工具网站
2024-11-18 14:30:55 +0800 CST
windon安装beego框架记录
2024-11-19 09:55:33 +0800 CST
如何使用go-redis库与Redis数据库
2024-11-17 04:52:02 +0800 CST
Python 获取网络时间和本地时间
2024-11-18 21:53:35 +0800 CST
总结出30个代码前端代码规范
2024-11-19 07:59:43 +0800 CST
Vue 3 中的 Watch 实现及最佳实践
2024-11-18 22:18:40 +0800 CST
程序员茄子在线接单