编程 Kubernetes v1.36 Haru 深度实战:从用户命名空间GA到AI原生编排——70项增强全景解析与生产升级指南

2026-05-22 14:49:17 +0800 CST views 8

Kubernetes v1.36 Haru 深度实战:从用户命名空间到 AI 原生编排——2026 云原生安全与性能的双重革命

前言:春归万物生

2026年4月22日,Kubernetes 正式发布了 v1.36,代号 Haru——日语中"春"的意思。在上一个代号 Timbernetes(世界树)为 2025 年画下句点之后,Haru 以一个新季节的姿态,开启了 2026 年的第一次更新。

这不是一次例行的版本迭代。v1.36 包含 70 项增强功能:18 项进入 Stable 阶段、25 项进入 Beta 阶段、25 项新的 Alpha 功能。重点聚焦三大方向——安全加固人工智能和机器学习工作负载支持、以及大规模 API 的可扩展性

对于在生产环境中运行 Kubernetes 的工程师而言,这次更新有几项"迟到多年的春天":**用户命名空间(User Namespaces)**历经四年打磨终于 GA,**可变准入策略(Mutating Admission Policies)**基于 CEL 彻底告别独立 Webhook 服务器,调度器和动态资源分配(DRA)子系统也迎来实质性的架构升级。

本文将从背景讲起,深入剖析这些关键新特性的设计原理与生产落地实践,提供完整的代码示例与升级避坑指南。


一、版本背景与发布时间线

1.1 Kubernetes 版本发布节奏

Kubernetes 项目保持着每四个月发布一个大版本的节奏。v1.36 的发布时间线如下:

里程碑日期
发布周期开始2026年1月12日
功能增强冻结(Feature Freeze)2026年2月11日
代码冻结(Code Freeze)2026年3月18日
文档冻结2026年4月8日
正式发布2026年4月22日

截至发布日,Kubernetes 的最新稳定版本为 v1.36.1(补丁版本)。整体依赖升级至 Go 1.25,解决了此前 Go 编译器中存在的安全漏洞。

1.2 v1.35 到 v1.36 的演进脉络

v1.35(代号 Timbernetes)是 2025 年底的收官之作,带来了调度器 Framework 的重构、PersistentVolume 生命周期增强等特性。v1.36 在此基础上:

  • 延续了调度器插件化架构的深化,引入了 PreBind 插件并行执行机制
  • 将 v1.35 中处于 Alpha 的多项 DRA(动态资源分配)特性推进至 Beta/GA
  • 正式将 Workload 和 PodGroup API 引入调度器,为批调度场景提供了原生支持
  • 在安全层面完成了用户命名空间的最后一公里

理解这条演进脉络,有助于我们把握 v1.36 的设计意图——它不是凭空出现的,而是 Kubernetes 社区长期规划的系统性兑现。


二、用户命名空间:从 Alpha 到 GA 的四年长征

2.1 为什么容器 root 是个问题

在传统容器模型中,容器内的 root 用户(UID 0)就是宿主机的 root 用户(或至少映射到宿主机的一个特权 UID)。这意味着:

# 在容器内执行 id
uid=0(root) gid=0(root) groups=0(root)

# 在宿主机上查看同一进程
uid=0(root) gid=0(root) groups=0(root)

即便容器使用了 securityContext 进行限制,一旦攻击者利用容器逃逸漏洞突破了隔离层,他所获得的 root 权限就等同于宿主机的 root 权限。这是容器安全的根本性隐患。

在多租户环境、共享集群或任何对安全有较高要求的场景下,这个问题的严重性尤为突出。

2.2 用户命名空间的设计原理

用户命名空间(User Namespaces)是 Linux 内核提供的一种隔离机制,允许在 namespace 内部重新映射 UID/GID。Kubernetes v1.36 的 GA 实现将这一内核能力引入容器编排层:

容器内 UID 0 (root)
        ↓
  用户命名空间映射
        ↓
宿主机 UID 100000+ (非特权用户)

即便容器内的进程成功突破隔离,其在宿主机上的身份仍然是一个普通非特权用户,无法执行系统级操作。

2.3 生产环境配置示例

Pod 级别启用用户命名空间:

apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
  namespace: production
spec:
  securityContext:
    runAsNonRoot: true
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: nginx:1.27
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop:
        - ALL
    # 用户命名空间配置(v1.36 GA)
    resources:
      limits:
        memory: "256Mi"
        cpu: "500m"

更细粒度的 namespace 映射配置:

spec:
  securityContext:
    # 显式指定用户命名空间配置
    namespaceOptions:
      # 指定 pod 所属的 host namespace ID(可选)
      hostUsers: false
    # UID/GID 映射由 kubelet 自动生成
    # 容器内 root (0) → 宿主机非特权 UID
  containers:
  - name: app
    securityContext:
      # 容器内使用非 root 用户运行
      runAsUser: 1000
      runAsGroup: 1000

集群级别的准入控制(使用 MutatingAdmissionPolicy):

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
  name: enforce-userns-on-secure-namespace
spec:
  matchConstraints:
    resourceRules:
    - apiGroups: [""]
      apiVersions: ["v1"]
      operations: ["CREATE", "UPDATE"]
      resources: ["pods"]
  applyTo:
  - groups: [""]
    resources: ["pods"]
  mutation:
    patchEvaluations:
    - apiVersion: admissionregistration.k8s.io/v1alpha1
      kind: PatchEvaluation
      spec:
        patch:
          # 强制设置 runAsNonRoot
          - op: add
            path: /spec/securityContext/runAsNonRoot
            value: true
        # 使用 CEL 表达式定义条件
        condition:
          expression: "object.namespace == 'production' && !('runAsNonRoot' in object.spec.securityContext)"
          expressionType: CEL

2.4 用户命名空间的升级影响

需要关注的变更点:

  1. 镜像兼容性:并非所有镜像都支持在用户命名空间下运行。如果镜像内部硬编码了特定 UID/GID 的文件权限,可能会遇到权限问题。建议在测试环境充分验证后再生产部署。

  2. Volume 挂载:某些挂载类型(特别是 hostPath 和部分 CSI 驱动)在用户命名空间下可能有不同的行为。

  3. kubectl exec / attach:用户命名空间不影响这些操作的功能,但 exec 进去的 shell 进程在宿主机上的身份也是非特权用户。

# 检查当前 kubelet 是否支持用户命名空间
# 在节点上执行:
cat /boot/config-$(uname -r) | grep CONFIG_USER_NS

# 应该输出:CONFIG_USER_NS=y

# 检查节点能力
kubectl get nodes -o jsonpath='{.items[*].status.conditions[?(@.type=="UserNamespacesSupport")]}'

生产升级建议

# 1. 先在非生产节点验证
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
# 执行 kubeadm upgrade plan 检查 v1.36 可用性
# 升级控制面
kubeadm upgrade apply v1.36.1
# 升级节点
kubeadm upgrade node
# 解除封锁
kubectl uncordon <node>

# 2. 使用 namespace 隔离逐步迁移
# 对核心业务命名空间先开启用户命名空间
kubectl label namespace production security.kubernetes.io/user-namespace=enabled

# 3. 监控迁移后的 pod 事件
kubectl get events -n production --field-selector involvedObject.name=<pod-name>

三、可变准入策略(Mutating Admission Policies):CEL 原生替代 Webhook

3.1 为什么 Webhook 是运维之痛

在 Kubernetes 中,准入 Webhook(Admission Webhook)是实现自定义策略的主流方式——在资源持久化到 etcd 之前,拦截 API 请求并执行验证或修改逻辑。常见的应用场景包括:

  • 自动注入 sidecar 容器(如 Istio 的 Envoy 代理)
  • 强制资源限制(设置默认的 CPU/内存 limits)
  • 敏感信息脱敏(不允许在 ConfigMap 中写入 secrets)

然而,Webhook 的运维成本相当高:

# 传统 MutatingWebhookConfiguration 示例
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: my-mutating-webhook
webhooks:
- name: mutate-pods.example.com
  clientConfig:
    service:
      name: webhook-service
      namespace: webhook-system
      path: "/mutate-pods"
    caBundle: <base64-encoded-ca>
  rules:
  - operations: ["CREATE", "UPDATE"]
    apiGroups: [""]
    apiVersions: ["v1"]
    resources: ["pods"]
  sideEffects: None
  admissionReviewVersions: ["v1", "v1beta1"]
  timeoutSeconds: 10

运维之痛

  1. 额外的服务:必须额外部署和维护一个 Webhook 服务器(Deployment + Service + HPA)
  2. 网络延迟:每次 API 请求都需要额外的 HTTP 调用,通常增加 5-30ms 的 P99 延迟
  3. 高可用:Webhook 服务本身需要高可用设计,防止单点故障
  4. 证书管理:需要维护 CA 证书轮换
  5. 调试困难:Webhook 逻辑出问题时的排查链路长

3.2 CEL 驱动的原生可变准入策略

v1.36 GA 的**可变准入策略(Mutating Admission Policies)**彻底解决了上述问题。它允许团队使用 CEL(Common Expression Language) 在 API Server 内部直接定义变更逻辑,无需额外部署服务:

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
  name: default-resources
spec:
  matchConstraints:
    resourceRules:
    - apiGroups: [""]
      apiVersions: ["v1"]
      operations: ["CREATE"]
      resources: ["pods"]
      scope: "Namespaced"
  applyTo:
  - groups: [""]
    resources: ["pods"]
  mutation:
    patchEvaluations:
    - apiVersion: admissionregistration.k8s.io/v1alpha1
      kind: PatchEvaluation
      spec:
        patch:
          # 设置默认资源限制
          - op: add
            path: /spec/containers/0/resources/limits/cpu
            value: "500m"
          - op: add
            path: /spec/containers/0/resources/limits/memory
            value: "512Mi"
          - op: add
            path: /spec/containers/0/resources/requests/cpu
            value: "100m"
          - op: add
            path: /spec/containers/0/resources/requests/memory
            value: "128Mi"
        condition:
          # 仅在没有设置 limits 时注入
          expression: >
            !('resources' in self.path('/spec/containers/0')) ||
            !('limits' in self.path('/spec/containers/0/resources'))
          expressionType: CEL

3.3 生产级 CEL 表达式实战

场景一:强制安全上下文

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
  name: enforce-security-context
spec:
  matchConstraints:
    resourceRules:
    - apiGroups: ["apps", ""]
      apiVersions: ["v1"]
      operations: ["CREATE", "UPDATE"]
      resources: ["deployments", "statefulsets", "daemonsets"]
      scope: "Namespaced"
  applyTo:
  - groups: ["apps", ""]
    resources: ["deployments", "statefulsets", "daemonsets"]
  mutation:
    patchEvaluations:
    - apiVersion: admissionregistration.k8s.io/v1alpha1
      kind: PatchEvaluation
      spec:
        patch:
          - op: add
            path: /spec/template/spec/securityContext/runAsNonRoot
            value: true
          - op: add
            path: /spec/template/spec/securityContext/seccompProfile
            value:
              type: "RuntimeDefault"
        condition:
          expression: "object.spec.template.spec.securityContext.runAsNonRoot != true"
          expressionType: CEL

场景二:自动注入 Istio sidecar(CEL 条件判断)

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
  name: inject-istio-sidecar
spec:
  matchConstraints:
    resourceRules:
    - apiGroups: [""]
      apiVersions: ["v1"]
      operations: ["CREATE"]
      resources: ["pods"]
      scope: "Namespaced"
  applyTo:
  - groups: [""]
    resources: ["pods"]
  mutation:
    patchEvaluations:
    - apiVersion: admissionregistration.k8s.io/v1alpha1
      kind: PatchEvaluation
      spec:
        patch:
          - op: add
            path: /spec/initContainers
            value:
              - name: istio-init
                image: "istio/proxyv2:1.24.0"
                command: ["istio-iptables"]
                args:
                  - "-p"
                  - "15001"
                  - "-z"
                  - "15006"
                  - "-u"
                  - "1337"
                  - "-m"
                  - "REDIRECT"
                  - "-i"
                  - "*"
                  - "-x"
                  - ""
                securityContext:
                  capabilities:
                    drop:
                    - ALL
                  allowPrivilegeEscalation: false
          - op: add
            path: /spec/containers/-
            value:
              name: istio-proxy
              image: "istio/proxyv2:1.24.0"
              args:
                - "proxy"
                - "run"
              env:
                - name: "ISTIO_META_DNS_AUTO_ALLOCATE"
                  value: "true"
              resources:
                requests:
                  cpu: "100m"
                  memory: "128Mi"
                limits:
                  cpu: "2000m"
                  memory: "1024Mi"
        condition:
          expression: >
            !('istio-injection' in object.metadata.annotations) ||
            object.metadata.annotations['istio-injection'] == 'enabled'
          expressionType: CEL

场景三:基于命名空间的动态资源配置

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
  name: namespace-based-resource-limits
spec:
  matchConstraints:
    resourceRules:
    - apiGroups: [""]
      apiVersions: ["v1"]
      operations: ["CREATE"]
      resources: ["pods"]
  applyTo:
  - groups: [""]
    resources: ["pods"]
  mutation:
    patchEvaluations:
    - apiVersion: admissionregistration.k8s.io/v1alpha1
      kind: PatchEvaluation
      spec:
        patch:
          - op: add
            path: /spec/containers/0/resources/limits/cpu
            value: "1000m"
          - op: add
            path: /spec/containers/0/resources/limits/memory
            value: "1Gi"
        condition:
          expression: >
            self.path('/metadata/namespace') in ['production', 'staging'] &&
            !('resources' in self.path('/spec/containers/0'))
          expressionType: CEL

3.4 性能对比

指标MutatingWebhookMutatingAdmissionPolicy
额外延迟5-30ms P99~0(内置执行)
运维复杂度高(独立服务)低(声明式配置)
可用性依赖外部服务可用性仅 API Server
证书管理需要不需要
调试难度高(跨服务)低(Kubernetes 原生)

四、调度器升级:PreBind 插件并行与 Workload/PodGroup API

4.1 PreBind 插件并行执行

在 v1.36 之前,调度器的 PreBind 插件是串行执行的。当一个 Pod 需要绑定到节点时,所有 PreBind 插件(如 VolumeBinding、portAllocation 等)必须一个接一个地完成:

PreBind Plugin 1 (VolumeBinding) → 完成
    ↓
PreBind Plugin 2 (portAllocation) → 完成
    ↓
PreBind Plugin 3 (another binding) → 完成
    ↓
Pod 绑定完成

在大型集群中,如果 VolumeBinding 需要等待云厂商的存储卷挂载(可能需要数秒),整个调度链路就会被阻塞,后续插件无法开始。

v1.36 引入了 PreBind 插件并行执行机制:

// 调度器配置示例(kube-scheduler 配置文件)
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- pluginConfig:
  - name: VolumeBinding
    args:
      BindTimeoutSeconds: 600
      Shape:
      - Score: 1
        Min: 0
        Max: 100
        # 启用异步绑定(Alpha → Beta 升级)
  - name: PreBind
    args:
      # v1.36: 插件可并行执行的条件
      enableParallelPreBind: true

并行执行模型:

┌─ PreBind Plugin 1 (VolumeBinding) ──────┐
│                                         │
├─ PreBind Plugin 2 (portAllocation) ─────┼─→ 并行执行
│                                         │
├─ PreBind Plugin 3 (nodeResources) ───────┤
│                                         │
└─ 等待所有插件完成 → Pod 绑定 ──────────┘

升级注意事项:如果你的调度器使用了自定义 PreBind 插件(通过 scheduler framework 开发),需要适配新的并行执行 API:

// 自定义 PreBind 插件适配(v1.36 之前 → v1.36)
// 
// v1.36 之前:
type PreBindPlugin interface {
    PreBind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status
}

// v1.36 并行化后需要实现:
type PreBindPlugin interface {
    PreBind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status
    // 新增:返回该插件是否依赖于其他插件的输出
    // 如果你的插件不依赖其他 PreBind 插件的结果,可返回 nil
    // 这样调度器可以将其与其他不依赖的插件并行执行
}

// 使用新的辅助方法判断依赖
type PreBindExtensions interface {
    PreBindExtensions() []PreBindPlugin
}

4.2 Workload 和 PodGroup API:批调度的原生支持

在 AI/ML 和大数据场景中,gang scheduling(组调度)是核心需求:多个 Pod 必须同时调度成功,否则宁愿全部等待。例如训练一个分布式模型:

# 分布式训练任务 — 需要 4 个 Worker 同时存在才能开始
apiVersion: batch/v1alpha1  # v1.36 引入的新 API
kind: PodGroup
metadata:
  name: ml-training-pg
  namespace: ml-workloads
spec:
  # 最少需要 4 个 Pod 才能开始执行
  minMembers: 4
  # 超时时间,超过则 PodGroup 标记为失败
  scheduleTimeoutSeconds: 300
---
apiVersion: v1
kind: Pod
metadata:
  name: ml-worker-1
  labels:
    pod-group.kubernetes.io/group-name: ml-training-pg
spec:
  # Pod 现在可以声明它属于哪个 PodGroup
  schedulingProfile:
    groupName: ml-training-pg

调度器对 PodGroup 的处理逻辑

// PodGroup 调度流程(伪代码)
func (sched *Scheduler) schedulePodGroup(pg *PodGroup) error {
    pods := getPodsInGroup(pg.Name)
    
    // 尝试找到一个满足所有 Pod 的节点组合
    for _, node := range eligibleNodes {
        if canAllocateGroup(node, pods) {
            // 所有 Pod 都可以调度到该节点(或跨节点但同时满足条件)
            for _, pod := range pods {
                err := bindPod(pod, node)
                if err != nil {
                    // 任何一个绑定失败,回滚整个组
                    rollbackGroupBindings(pods)
                    break
                }
            }
            return nil
        }
    }
    
    // 无法满足组调度条件,等待
    pg.Status.Phase = "Pending"
    return fmt.Errorf("insufficient resources for gang scheduling")
}

Minicluster 场景示例

apiVersion: batch/v1alpha1
kind: PodGroup
metadata:
  name: distributed-training-group
spec:
  minMembers: 8  # 4 个 PS + 4 个 Worker
  scheduleTimeoutSeconds: 600
---
# 参数服务器
apiVersion: v1
kind: Pod
metadata:
  name: ps-0
  labels:
    role: parameter-server
    pod-group.kubernetes.io/group-name: distributed-training-group
spec:
  nodeSelector:
    gpu: "true"
  tolerations:
  - key: "nvidia.com/gpu"
    operator: "Exists"
    effect: "NoSchedule"
  containers:
  - name: ps
    image: tensorflow/tensorflow:2.16
    command: ["python", "-c", "..."]
    resources:
      limits:
        nvidia.com/gpu: 1
---
# Worker 节点(4个类似定义)
apiVersion: v1
kind: Pod
metadata:
  name: worker-0
  labels:
    role: worker
    pod-group.kubernetes.io/group-name: distributed-training-group
spec:
  # Worker 的调度策略与 PodGroup 关联
  # 确保所有 8 个 Pod 同时满足调度条件

五、动态资源分配(DRA):GPU/FPGA 调度的架构升级

5.1 DRA 的前世今生

Kubernetes 传统资源模型使用 limits.cpulimits.memory 来描述资源需求。然而,对于新型加速器(GPU、FPGA、DPU、智能网卡),这种粗粒度模型不够用:

  • GPU 调度需要追踪型号(V100/H100/A100)、显存大小、多卡拓扑(NVLink/NVSwitch)
  • 多个容器可能共享同一块 GPU(通过 MIG 或时间片)
  • 资源需要在 Pod 生命周期内动态调整

v1.36 中,DRA 的多项功能从 Alpha 推进至 Beta/GA

DRA 功能之前状态v1.36 状态
基础 DRA APIAlphaBeta
资源消费者(Pod)AlphaBeta
资源驱动程序(CRI)AlphaBeta
DRA 元数据读取助手AlphaGA

5.2 生产级 DRA 配置示例

节点配置:声明可分配的 GPU 资源

# Node 的 extended resources(由 DRA 驱动自动注册)
apiVersion: v1
kind: Node
metadata:
  name: gpu-node-1
status:
  allocatable:
    # DRA 驱动的资源格式:resource.k8s.io/<class>/<name>
    nvidia.com/gpu: "4"
    # 也可以使用 DRA 自己的 API 注册更细粒度的资源
---
# Pod 使用 DRA 申请特定 GPU
apiVersion: v1
kind: Pod
metadata:
  name: llm-inference
spec:
  containers:
  - name: inference
    image: vllm/vllm-openai:latest
    resources:
      claims:
      - name: gpu-claim
    env:
    - name: VLLM_GPU_MEMORY_UTILIZATION
      value: "0.9"
    command: ["python", "-m", "vllm.entrypoints.openai.api_server"]
---
# ResourceClaim(声明所需资源)
apiVersion: resource.k8s.io/v1alpha3
kind: ResourceClaim
metadata:
  name: gpu-claim
spec:
  resourceClassName: nvidia.com/gpu
  requests:
  - drone.k8s.io/count: "1"
    drone.k8s.io/type: "H100"
    drone.k8s.io/memory: "80Gi"
  # 共享模式:允许其他 Pod 共享同一 GPU(MIG 场景)
  sharing:
    maxShareCount: 2

5.3 DRA 元数据读取助手(GA)

v1.36 的 DRA 元数据读取助手正式达到 GA,简化了 DRA 驱动的开发:

// DRA 驱动中的元数据读取(使用新的 GA API)
package main

import (
    "k8s.io/apimachinery/pkg/runtime"
    "k8s.io/dynamic-resource-claim/api"
)

// v1.36 GA: 更简洁的错误处理
func decodeClaimParameters(obj runtime.Object) (*MyParams, error) {
    // 新 API:自动处理版本兼容,版本未知时跳过解析
    decoder := api.NewClaimParameterDecoder(scheme)
    params, err := decoder.DecodeParams(obj, "example.com/v1")
    if err != nil {
        // 错误信息更明确,便于驱动调试
        return nil, fmt.Errorf("failed to decode claim parameters: %w", err)
    }
    return params, nil
}

六、AI/ML 工作负载:Kubernetes 1.36 的原生智能化

6.1 CNCF 的警告:仅靠 K8s 不足以保障 LLM 安全

CNCF 在 2026 年初的一篇博客中指出了一个关键问题:Kubernetes 擅长编排和隔离工作负载,但它本身无法理解或控制 AI 系统的行为

LLM 工作负载面临的威胁模型与传统微服务完全不同:

传统微服务威胁:
容器逃逸 → 获得宿主机权限 → 横向移动

AI/LLM 威胁:
模型投毒 → 推理时产生错误输出 → 业务决策失误
Prompt 注入 → 数据泄露 → 合规违规
资源耗尽 → 模型服务不可用 → 业务中断

Kubernetes 1.36 在基础设施层面强化了对 AI 工作负载的支持:

  1. 增强的资源抽象:DRA + PodGroup 组合支持大规模 AI 训练集群的调度
  2. QoS 隔离:更精细的资源优先级确保推理服务不被训练任务抢占
  3. 安全加固:用户命名空间防止 GPU 资源被恶意容器劫持

6.2 KubeRay 与 Kubernetes 1.36 的结合

KubeRay 是 Kubernetes 原生的 Ray 分布式计算框架管理插件,广泛用于机器学习、强化学习和数据处理场景。v1.36 的调度器改进使 KubeRay 编排的训练任务更加稳定:

apiVersion: ray.io/v1
kind: RayCluster
metadata:
  name: ray-training-cluster
  namespace: ml-platform
spec:
  rayVersion: "2.35.0"
  headGroupSpec:
    serviceType: ClusterIP
    rayActorOptions:
      defaults: "env://"
    template:
      spec:
        containers:
        - name: ray-head
          image: rayproject/ray:2.35.0-gpu
          resources:
            requests:
              cpu: "2"
              memory: "8Gi"
              nvidia.com/gpu: "1"
            limits:
              cpu: "4"
              memory: "16Gi"
              nvidia.com/gpu: "1"
          env:
          - name: RAY_dashboard_frontend_host
            value: "0.0.0.0"
        nodeSelector:
          node-type: gpu-head
  workerGroupSpecs:
  - replicas: 4
    minReplicas: 4
    maxReplicas: 8
    groupName: worker-group
    template:
      spec:
        containers:
        - name: ray-worker
          image: rayproject/ray:2.35.0-gpu
          resources:
            requests:
              cpu: "8"
              memory: "32Gi"
              nvidia.com/gpu: "4"
            limits:
              cpu: "16"
              memory: "64Gi"
              nvidia.com/gpu: "4"
        nodeSelector:
          node-type: gpu-worker
        tolerations:
        - key: "nvidia.com/gpu"
          operator: "Exists"
          effect: "NoSchedule"

6.3 Pod 资源原地调整(In-Place Pod Vertical Scaling)

v1.36 增强了 Pod 资源的原地调整能力——无需重启容器即可调整 CPU 和内存限制:

# 直接调整 Pod 资源限制(原地生效,无需重建容器)
kubectl patch pod inference-service \
  --subresource=resizepod \
  --type=merge \
  -p '{
    "spec": {
      "containers": [{
        "name": "inference",
        "resources": {
          "limits": {
            "memory": "32Gi",
            "cpu": "8"
          },
          "requests": {
            "memory": "16Gi",
            "cpu": "4"
          }
        }
      }]
    }
  }'

# 查看调整状态
kubectl get pod inference-service -o jsonpath='{.status.resize}'
# 输出: InProgress / Infeasible / Adequate

这对 AI 推理服务尤为重要——可以在不中断服务的情况下,根据负载动态扩展资源。


七、kubeadm 升级与兼容性:这次必须注意的 Breaking Changes

7.1 FlexVolume 内置支持移除

影响:kubeadm 不再内置 FlexVolume 插件支持。如果你还在使用 FlexVolume(deprecated),需要迁移到 CSI。

# 检查集群中是否有 FlexVolume 使用
kubectl get pv -o json | jq '.items[].spec.flexVolume'

# 如果有输出,说明集群在使用 FlexVolume,需要迁移到 CSI
# CSI 迁移步骤:
# 1. 找到对应的 CSI 驱动(如 NFS → csi-nfs)
# 2. 安装 CSI 驱动
# 3. 迁移现有的 PV/PVC 对象
# 4. 更新 StorageClass
# 5. 移除 FlexVolume 配置

7.2 监控指标重命名

v1.36 对部分 kubelet 和 API Server 指标进行了重命名。如果你的监控告警规则依赖这些指标名,需要更新:

# 升级前:导出当前的告警规则
promtool check rules /etc/prometheus/rules/*.yml > /tmp/rules-backup.yml

# 升级后:检查指标变化
# 常见的指标重命名模式:
# apiserver_request_duration_seconds → apiserver_request_latency_seconds
# kubelet_runtime_operations_total → kubelet_runtime_exec_operations_total

# 使用 promtool 验证更新后的规则
promtool update rules /etc/prometheus/rules/new-rules.yml

7.3 完整的 kubeadm 升级命令

#!/bin/bash
# kubernetes-v1.36-upgrade.sh

set -euo pipefail

K8S_VERSION="1.36.1"

echo "=== 1. 升级控制面 ==="
# 查看升级计划
kubeadm upgrade plan v${K8S_VERSION}

# 升级控制面组件
sudo kubeadm upgrade apply v${K8S_VERSION} \
  --etcd-upgrade=true \
  --certificate-renewal=true

echo "=== 2. 升级所有节点 ==="
# 获取所有节点
NODES=$(kubectl get nodes -o jsonpath='{.items[*].metadata.name}')

for NODE in $NODES; do
  echo "正在升级节点: $NODE"
  
  # 如果是非控制面节点,需要先排空
  if ! kubectl get node $NODE | grep -q "control-plane"; then
    kubectl drain $NODE \
      --ignore-daemonsets \
      --delete-emptydir-data \
      --force \
      --skip-wait-for-delete-timeout=60
  fi
  
  # 在目标节点执行
  ssh $NODE "sudo kubeadm upgrade node"
  ssh $NODE "sudo apt-get update && sudo apt-get install -y kubelet=${K8S_VERSION}-* kubectl=${K8S_VERSION}-* kubeadm=${K8S_VERSION}-*"
  ssh $NODE "sudo systemctl restart kubelet"
  
  # 解除封锁
  kubectl uncordon $NODE
done

echo "=== 3. 验证升级 ==="
kubectl version --short
kubectl get nodes -o wide
kubectl get componentstatuses  # 检查 etcd、scheduler、controller-manager 状态

echo "=== 4. 升级检查 ==="
# 检查是否有 Pod 处于 Pending/Error 状态
kubectl get pods --all-namespaces | grep -E "Pending|Error|ContainerCreating"

echo "升级完成!当前版本:"
kubectl version -o yaml | grep serverVersion -A 3

八、生产升级策略与灰度发布

8.1 推荐的升级路径

v1.34.x → v1.35.x → v1.36.x
   ↓
每两个版本升级一次(安全窗口更大)
   ↓
或使用 KubeSphere/Rancher 等发行版管理

不建议跳过两个以上的版本。Kubernetes 的 minor 版本间存在 API 兼容承诺(n-2 兼容),但实际升级时还是逐个版本验证更稳妥。

8.2 灰度升级策略

# 使用 nodeSelector 实现灰度升级
# 先升级少量"影子节点",验证通过后再全量升级

apiVersion: v1
kind: Node
metadata:
  name: upgrade-candidate-1
  labels:
    # 添加升级候选标签
    k8s-upgrade: "candidate"
    # 指定目标版本
    k8s-version: "v1.36.1"
---
# 在测试节点上先执行升级
# kubectl drain upgrade-candidate-1 --ignore-daemonsets
# kubeadm upgrade node --to-version v1.36.1
# 重启 kubelet
# kubectl uncordon upgrade-candidate-1

8.3 回滚方案

# 如果升级后发现问题,kubeadm 可以回滚到上一个版本
# 但仅限于同一 minor 版本内的 patch 版本间回滚
# major/minor 版本不支持自动回滚

# 回滚 patch 版本(1.36.1 → 1.36.0)
sudo apt-get install -y \
  kubelet=1.36.0-* \
  kubectl=1.36.0-* \
  kubeadm=1.36.0-*
sudo systemctl restart kubelet

# 跨版本回滚需要:
# 1. 从 etcd 备份恢复(如果有的话)
# 2. 手动重建 Pod(Deployment/ReplicaSet 会重新创建)
# 因此升级前的 etcd 备份至关重要

九、展望:Kubernetes 的下一个四季

Haru(春)之后,Kubernetes 的演进方向已经清晰:

  1. AI 原生编排:从 PodGroup 到更完善的 gang scheduling,再到 GPU 共享语义的原生支持,Kubernetes 正在成为 AI 基础设施的标准层

  2. 安全默认化:User Namespaces 和 MutatingAdmissionPolicies 的 GA 标志着"Kubernetes 零信任"从理念走向落地

  3. 性能极限:调度器并行化、DRA 优化、API Server 扩展性增强,都是在为大集群(1000+ 节点)和大工作负载(10万+ Pod)做准备

  4. 开发者体验:Webhook 的 CEL 替代、原地资源调整,都在降低 Kubernetes 的运维门槛


总结

Kubernetes v1.36 Haru 是一次承前启后的版本更新:

维度关键变化对工程师的影响
安全User Namespaces GA容器逃逸后不再获得 root,生产多租户更安全
准入CEL MutatingAdmissionPolicies GA告别 Webhook 服务,运维复杂度大幅降低
调度PreBind 并行 + PodGroup APIAI 训练任务调度更高效,gang scheduling 原生化
资源DRA Beta/GAGPU/FPGA 调度更精细,无需重启即可调资源
升级FlexVolume 移除需要迁移到 CSI,监控指标需同步更新

行动建议

  • 测试环境立即升级到 v1.36.1,验证用户命名空间与现有镜像的兼容性
  • 审计现有 MutatingWebhookConfiguration,逐步迁移到 CEL MutatingAdmissionPolicies
  • 评估 PodGroup API,如果你在运行 AI 训练或批处理任务,这是必须关注的方向
  • 检查 FlexVolume 使用情况,制定向 CSI 的迁移计划

参考来源:Kubernetes v1.36 CHANGELOG(GitHub kubernetes/kubernetes),Kubernetes v1.36 发布博客,CNCF 官方博客,KubeSphere 技术社区。

复制全文 生成海报 Kubernetes K8s 云原生 容器 DevOps

推荐文章

Grid布局的简洁性和高效性
2024-11-18 03:48:02 +0800 CST
一键配置本地yum源
2024-11-18 14:45:15 +0800 CST
如何实现生产环境代码加密
2024-11-18 14:19:35 +0800 CST
程序员茄子在线接单