编程 Kubernetes v1.36「Haru」深度解析:4年磨一剑,从容器安全到 AI 原生的全面跃迁

2026-07-26 11:16:19 +0800 CST views 5

Kubernetes v1.36「Haru」深度解析:4年磨一剑,从容器安全到 AI 原生的全面跃迁

2026年4月22日,Kubernetes 社区如约交付了 v1.36,代号 Haru——日语「春」之意。没有颠覆性的新范式,没有惊雷般的架构变革,却是一个把过去三四年埋下的种子一一催开的版本。71项增强,18项GA,26项Beta——关键词不是「扩张」,而是「收口」。这对运维团队来说,比新增一堆实验性特性更有价值:你可以少用几个第三方工具,少维护几套自研组件,平台本身正在替你兜底。

本文将从架构设计、生产实战、安全加固、AI/ML 基础设施四条主线,系统拆解这次更新的核心技术细节与落地价值。无论你是平台工程师、SRE、还是正在搭建 AI 训练/推理平台的开发者,都能找到可以直接上手的东西。


一、版本概览:为什么说 v1.36 值得认真对待

先看一组数字:

  • 71项增强
  • 18项毕业至 Stable(GA)
  • 26项进入 Beta
  • 25项新的 Alpha 功能

对比过去几个版本,v1.36 的 GA 数量尤其值得关注。过去三四年里处于 Alpha/Beta 阶段的老功能——Pod User Namespaces、Mutating Admission Policies、SELinux 卷标签优化——这次终于迎来了自己的「毕业季」。

对于在生产环境维护 Kubernetes 的团队,这意味着两件事:

  1. 运维工具链可以简化:过去需要借助第三方运行时(gVisor/Kata Containers)或自研 Webhook 才能实现的安全能力,现在 Kubernetes 原生就提供了稳定实现;
  2. 升级收益可以直接量化:不需要冒险跑实验性特性,在已有工作负载上开启这些 GA 功能,就能获得可感知的收益。

二、Pod User Namespaces:从 Alpha 到 GA,四年磨一剑

2.1 它解决了什么问题

这是 v1.36 最值得优先了解的特性,没有之一。

Pod User Namespaces 最早于 Kubernetes v1.25(2022年8月)进入 Alpha,经过四个版本的打磨,终于在 v1.36 毕业至 GA。

核心能力:每个 Pod 拥有独立的用户 ID 和组 ID 命名空间。容器内看起来是 root(UID 0)的进程,在宿主机层面只是一个无特权用户。

这意味着什么?传统容器模型中,容器内的 root 本质上就是宿主机的 root——或者更准确地说,是一个映射到宿主机特定 UID 范围的特权用户。一旦攻击者通过容器逃逸漏洞突破了隔离层,他立即获得了对宿主机上那个映射 UID 的完整控制权。历史上多个高危漏洞(如 container escape CVE)都建立在这个前提之上。

Pod User Namespaces 彻底改变了这个假设。启用后:

spec:
  hostUsers: false   # 启用 User Namespace
  containers:
    - name: app
      image: my-app

容器内的 root(UID 0)映射到宿主机上的一个非特权 UID(通常是 60000+),即使攻击者成功逃逸,他拿到的也只是那个无特权身份,对宿主机几乎没有任何破坏能力。

2.2 技术原理:从内核到运行时的协作链路

Pod User Namespaces 的实现依赖 Linux 内核的用户命名空间(user namespace)特性。这项内核能力从 2013 年的 Linux 3.8 起就已经存在,但 Kubernetes 真正将它带入容器编排的标准化语义,经历了漫长的社区协作。

技术链路

  1. 容器运行时层(containerd/cri-o):需要在创建容器时配置 LinuxUserNamespace 字段,将容器进程加入独立的 UID/GID 映射;
  2. kubelet 层:负责在 Pod Spec 中接收 hostUsers: false 配置,并将其转换为运行时参数;
  3. 内核层:内核负责实际隔离——容器内的 UID/GID 视图与宿主机视图完全解耦。

一个常见误解是:Pod User Namespaces 和 rootless container 是一回事。实际上 rootless container(如 rootless Docker)解决的是「非 root 用户运行 Docker daemon」的问题,而 Pod User Namespaces 解决的是「容器内进程的身份在宿主机层面如何映射」的问题。两者可以结合使用,但解决的是不同层面的安全问题。

2.3 实战:如何启用 User Namespaces

当前版本(v1.36)的 User Namespaces 有一些前置条件和注意事项:

前置条件

  • Linux 内核 ≥ 5.16(推荐 ≥ 6.0)
  • 容器运行时支持 User Namespaces(containerd ≥ 1.7,cri-o ≥ 1.27)
  • 节点上不能有 Pod 使用 HostNetwork 或 HostPID

启用步骤

# Pod 级别启用
apiVersion: v1
kind: Pod
metadata:
  name: secure-app
spec:
  hostUsers: false          # 启用 User Namespace
  securityContext:
    runAsUser: 0            # 容器内以 root 身份运行
    runAsGroup: 0
  containers:
    - name: app
      image: my-app:latest
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true

兼容性注意事项

⚠️ 多容器 Pod:同一个 Pod 内所有容器必须同时使用或不使用 User Namespace,不能混配;
⚠️ HostPath 和 HostPort:使用 User Namespace 时,某些 HostPath 挂载行为会发生变化;
⚠️ Volume 挂载:挂载的宿主机路径在容器内看到的权限可能与预期不同;
⚠️ 特权容器hostUsers: falseprivileged: true 互斥。

2.4 迁移路径与收益评估

对于已有集群,User Namespaces 的迁移成本是可控的——它只需要在 Pod Spec 中添加一行配置,不需要修改镜像或重建集群。

推荐迁移顺序

  1. 第一阶段:在测试环境对无状态应用启用,观察兼容性;
  2. 第二阶段:对有安全合规需求的工作负载(多租户环境、金融系统、政企平台)优先启用;
  3. 第三阶段:全面推广到所有工作负载。

收益量化

场景传统隔离+ User Namespaces
容器逃逸后宿主机权限完全控制无特权用户身份
多租户隔离强度Pod Security Policy命名空间级别 root 隔离
合规报告需要额外说明Kubernetes 原生保证

三、Mutating Admission Policies:告别 Webhook Server 的运维重负

3.1 痛点:Mutating Webhook 有多烦

写过生产级 Mutating Webhook 的工程师,对这套体系的「烦」应该深有体会:

运维负担清单

  • 维护一个独立的 HTTP Server(通常用 Go/Python 编写)
  • 配置 TLS 证书轮换(证书过期会导致所有 Pod 创建失败)
  • 处理 Webhook 的超时和失败策略(超时可能阻塞 API Server)
  • 管理 Webhook Server 的高可用(单点故障影响整个集群)
  • 持续监控 Webhook Server 的可用性和延迟

这一切,只是为了在 Pod 创建时自动注入几个标签,或者给容器设置默认的资源限制。

3.2 解决方案:原生 CEL 表达式

v1.36 将 MutatingAdmissionPolicy 推至 GA 并默认开启。变更逻辑可以直接用 Kubernetes 原生对象表达,不再需要外部 Webhook 服务:

# 使用 CEL 表达式定义 Mutating Policy
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicy
metadata:
  name: set-default-resources
spec:
  matchConstraints:
    resourceRules:
    - apiGroups: [""]
      apiVersions: ["v1"]
      resources: ["pods"]
      operations: ["CREATE"]
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    resources: ["pods"]
  mutation:
    patches:
    - patch: |
        [{"op":"add","path":"/spec/containers/0/resources",
          "value":{"limits":{"cpu":"100m","memory":"256Mi"},
                   "requests":{"cpu":"50m","memory":"128Mi"}}}]
      patchesJson6902: false

更优雅的方式是用 CEL 表达式替代 JSON Patch:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicy
metadata:
  name: inject-monitoring-label
spec:
  matchConstraints:
    resourceRules:
    - apiGroups: [""]
      apiVersions: ["v1"]
      resources: ["pods"]
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
  mutation:
    patches:
    - expression: |
        # 给所有 Pod 注入监控标签
        if object.metadata.labels["app.monitoring/enabled"] == null {
          object.metadata.labels["app.monitoring/enabled"] = "true"
        }
      patchesJson6902: false

3.3 性能对比:Webhook vs. 原生 Policy

维度Mutating WebhookMutatingAdmissionPolicy
部署复杂度需要独立服务 + TLS纯 K8s 资源
延迟开销5-20ms(HTTP + TLS)<1ms(API Server 内联)
可用性风险Webhook 崩溃可阻塞 API Server无外部依赖
GitOps 支持部分支持(Server 配置外置)完全声明式
维护成本高(证书、版本、监控)低(仅维护 YAML)

3.4 典型场景实战

场景一:强制资源限制

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicy
metadata:
  name: enforce-resource-limits
spec:
  matchConstraints:
    resourceRules:
    - apiGroups: [""]
      apiVersions: ["v1"]
      resources: ["pods"]
      operations: ["CREATE"]
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
  mutation:
    patches:
    - expression: |
        # 给没有设置 CPU limit 的容器设置默认值
        for c in object.spec.containers {
          if c.resources.limits["cpu"] == null {
            c.resources.limits["cpu"] = "200m"
          }
          if c.resources.limits["memory"] == null {
            c.resources.limits["memory"] = "256Mi"
          }
        }
      patchesJson6902: false

场景二:自动注入 Sidecar

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicy
metadata:
  name: inject-istio-sidecar
spec:
  matchConstraints:
    resourceRules:
    - apiGroups: [""]
      apiVersions: ["v1"]
      resources: ["pods"]
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
  mutation:
    patches:
    - expression: |
        # 为带有特定标签的命名空间自动注入 istio-proxy
        if object.metadata.namespace != null &&
           object.metadata.namespace.endsWith("-prod") {
          # 注入逻辑通过 JSON Patch 实现
        }
      patchesJson6902: false

四、OCI VolumeSource:AI/ML 场景的游戏规则改变者

4.1 老问题:模型权重怎么送进 Pod

在 AI/ML 场景中,如何把模型权重、配置文件、数据集等大文件高效地送进 Pod,一直是个工程难题。过去的解法各有缺陷:

方案缺点
打包进容器镜像主镜像体积膨胀,更新频率受限,仓库存储成本高
initContainer 拉取启动流程变复杂,错误处理困难
ConfigMap/Secret单个对象体积限制(ConfigMap ≤1MiB,Secret ≤1MiB)
PV/PVC需要额外的存储基础设施

4.2 新范式:把镜像仓库当存储用

v1.36 GA 的 OCI VolumeSource 把这个问题的解法彻底改变了——将任意 OCI 镜像当作卷来引用:

apiVersion: v1
kind: Pod
metadata:
  name: llm-inference
spec:
  containers:
    - name: inference
      image: my-inference-server:latest
      volumeMounts:
      - name: model-weights
        mountPath: /models
  volumes:
  - name: model-weights
    image:
      reference: my-registry.example.com/models/llama3-8b:v1
      # 拉取凭证(如需要)
      pullSecret: reg-secret

工作原理:Kubernetes 会像拉取容器镜像一样,把这个「模型镜像」从仓库拉下来,以只读卷的形式挂载到 Pod 中。打包、分发、缓存、版本管理——全部复用现有的镜像仓库基础设施。

4.3 深度解析:为什么这是 AI/ML 场景的里程碑

存储成本优化:模型权重通常在几 GB 到几十 GB 量级(Llama-3 70B 的量化版本约 40GB)。传统方案需要为每个 Pod 分配独立存储,而 OCI VolumeSource 受益于镜像层的共享机制——同一个仓库中的多个 Pod 可以共享镜像层的缓存,大幅降低存储和网络成本。

版本管理的简化:模型的版本管理天然就是「不可变」的——每个版本对应一个镜像 tag。回滚模型只需要修改 Pod Spec 中的镜像引用,一切符合 GitOps 原则。

多副本一致性:分布式推理场景中,所有 Pod 实例必须使用完全相同的模型权重。OCI VolumeSource 确保所有副本从同一个镜像拉取,天然保证一致性。

与现有工具链的整合

# Python 示例:使用 skopeo 推送模型到镜像仓库
import subprocess

def push_model_to_registry(model_path: str, registry: str, tag: str):
    """将模型文件打包为 OCI 镜像并推送"""
    # 1. 使用 umoci 或其他工具创建 OCI 镜像
    result = subprocess.run([
        "skopeo", "copy",
        "--multi-arch=all",
        f"oci:{model_path}",
        f"docker://{registry}/models/my-model:{tag}"
    ], capture_output=True)
    return result.returncode == 0

4.4 生产环境注意事项

⚠️ 拉取时间:首次拉取大型模型镜像可能需要较长时间(取决于仓库带宽),需要配合 Pre-pulling 策略或 initContainer 确保 Pod 启动时模型已就位;

⚠️ 仓库配额:模型镜像通常很大,需要确认镜像仓库的存储配额和拉取速率限制;

⚠️ 安全扫描:建议对模型镜像进行安全扫描,因为它们本质上也是容器镜像;

⚠️ 多架构支持:如果使用 GPU 推理,确保镜像支持对应架构(amd64/arm64),或为不同架构维护不同镜像。


五、DRA 增强:异构算力管理的完整闭环

5.1 DRA 可分片设备:从整机调度到精细分配

GPU 等硬件加速器价格昂贵,独占式分配常常造成严重的利用率浪费——一个 80GB 的 H100 显卡,跑一个只需要 8GB 显存的推理任务,剩余 72GB 只能空转。

v1.36 将 DRA 可分片设备(KEP-4815)推进到 Beta,允许将单个硬件加速器切分成多个逻辑单元:

# DeviceClass 定义(管理员配置)
apiVersion: resource.k8s.io/v1alpha3
kind: DeviceClass
metadata:
  name: partitionable-gpu
spec:
  selectors:
  - cel:
      expression: device.driver == "nvidia.com/gpu"
  config:
  - opaque:
      domain: nvidia.com
      parameters: |
        {
          "partition_type": "1g.5gb",
          "max_partitions": 7
        }

---
# 工作负载请求分片 GPU
apiVersion: v1
kind: Pod
metadata:
  name: gpu-inference-small
spec:
  containers:
  - name: inference
    image: tensorflow/tensorflow:latest
    resources:
      claims:
      - name: gpu-partition
        deviceClassName: partitionable-gpu
        deviceSelectors:
        - cel:
            expression: device.attributes["nvidia.com/gpu.memory"] >= 5368709120

5.2 DRA 设备污点与容忍:从「谁都能用」到「精准授权」

DRA 设备污点与容忍(KEP-5055)也在 v1.36 进入 Beta 并默认开启:

# 给专用 GPU 节点打上污点(管理员操作)
kubectl taint nodes gpu-node-1 nvidia.com/gpu=reserved:NoSchedule

# 工作负载显式声明容忍
apiVersion: v1
kind: Pod
metadata:
  name: ml-training-job
spec:
  containers:
  - name: trainer
    image: pytorch/pytorch:latest
    resources:
      claims:
      - name: gpu
        tolerations:
        - key: "nvidia.com/gpu"
          operator: "Exists"
          effect: "NoSchedule"

两者结合,Kubernetes 在异构算力管理上已经具备了完整语义:谁可以用(DeviceClass 选择器)、能分多少(可分片设备)、谁能访问(污点与容忍)——GPU 调度的三个核心问题,第一次被原生能力完整覆盖。

5.3 架构图:DRA 分片的工作流程

┌─────────────────────────────────────────────────────────┐
│                    Kubernetes Cluster                    │
│                                                         │
│  ┌──────────────┐    ┌──────────────┐                   │
│  │   Pod A      │    │   Pod B      │    ┌──────────┐  │
│  │  (1g.5gb)    │    │  (1g.5gb)    │    │  Pod C   │  │
│  │              │    │              │    │  (2g.10gb)│  │
│  └──────┬───────┘    └──────┬───────┘    └────┬─────┘  │
│         │                   │                  │        │
│         ▼                   ▼                  ▼        │
│  ┌─────────────────────────────────────────────┐       │
│  │           DRA Controller                     │       │
│  │  ┌────────────┐  ┌────────────┐             │       │
│  │  │  Partitioner │  │  Taint/Toler │           │       │
│  │  │  (KEP-4815)  │  │  (KEP-5055)  │           │       │
│  │  └────────────┘  └────────────┘             │       │
│  └──────────────────────┬──────────────────────┘       │
│                         │                               │
│                         ▼                               │
│              ┌──────────────────────┐                   │
│              │   NVIDIA GPU (H100)   │                   │
│              │   80GB total          │                   │
│              │  ┌───┐ ┌───┐ ┌───┐   │                   │
│              │  │5GB│ │5GB│ │10GB│   │                   │
│              │  └───┘ └───┘ └───┘   │                   │
│              │   PodA  PodB   PodC   │                   │
│              └──────────────────────┘                   │
└─────────────────────────────────────────────────────────┘

六、安全增强全景:多层次防御体系的完善

6.1 IP/CIDR 校验收紧(KEP-4858)

非规范 IP 格式(如 010.000.001.005)和模糊 CIDR 值(如在期望 192.168.1.0/24 的上下文中出现 192.168.1.5/24),将不再被核心 Kubernetes 对象接受。

这个变更背后是 CVE-2021-29923 级别的安全隐患——历史上这些非规范格式在不同实现之间存在解释歧义,曾被用作攻击面。

影响范围排查

#!/bin/bash
# 检查集群中可能存在的非规范 IP 表达
echo "=== 检查非规范 IP 表达 ==="

# 检查 Service externalIPs
echo "[1/3] 检查 Service externalIPs..."
kubectl get svc -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.externalIPs}{"\n"}{end}' | \
  grep -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'

# 检查 Pod IP 配置
echo "[2/3] 检查 Pod IP 配置..."
kubectl get pods -A -o yaml | grep -E '^\s+podIP:' | \
  grep -vE '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'

# 检查 NetworkPolicy
echo "[3/3] 检查 NetworkPolicy CIDR..."
kubectl get networkpolicies -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\n"}{end}'

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

6.2 ServiceAccount Token 外部签名(GA)

对 PCI-DSS、FedRAMP、SOC 2 等合规框架有需求的团队,v1.36 GA 的外部 Token 签名能力(KEP-740)是一条清晰路径——将 token 签发委托给外部 KMS 或 HSM,kube-apiserver 本身不存储私钥:

apiVersion: apiserver.config.k8s.io/v1
kind: AuthenticationConfiguration
spec:
  serviceAccountTokenGetter:
  - name: external-signer
    audiences:
    - https://my-cluster.example.com
    signer:
      kubernetes.io/kube-apiserver-client:
        # 外部签名配置(示例,具体字段取决于 KMS 插件)
        kms:
          name: my-kms-plugin
          provider: "gcpkms"

6.3 Fine-grained Kubelet API Authorization(GA)

节点级安全能力的另一项重要增强。在「最小权限」架构上向前迈了一大步——kubelet API 的访问控制粒度从「所有经过认证的请求」细化到了「特定操作对应特定权限」。

6.4 安全加固检查清单

#!/bin/bash
# v1.36 安全配置检查脚本
echo "=== Kubernetes v1.36 安全配置检查 ==="

# 1. 检查是否使用了 gitRepo 卷(已移除)
echo "[1/6] 检查 gitRepo 卷使用情况..."
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\n"}{range .spec.volumes[*]}{"  volume: "}{.name}{" gitRepo: "}{.gitRepo}{"\n"}{end}{end}' | \
  grep -A1 "gitRepo" | head -20

# 2. 检查 externalIPs 使用(v1.36 显示警告)
echo "[2/6] 检查 externalIPs 使用..."
kubectl get services --all-namespaces -o json | \
  jq '.items[] | select(.spec.externalIPs != null) | {namespace: .metadata.namespace, name: .metadata.name, externalIPs: .spec.externalIPs}'

# 3. 检查 Ingress NGINX 部署(已退役)
echo "[3/6] 检查 Ingress NGINX..."
kubectl get deployments --all-namespaces -l app.kubernetes.io/name=ingress-nginx 2>/dev/null || echo "  无 Ingress NGINX 部署"

# 4. 检查 ServiceAccount Token 签名配置
echo "[4/6] 检查 ServiceAccount 配置..."
kubectl get serviceaccounts --all-namespaces -o json | \
  jq '[.items[] | select(.metadata.annotations["kubernetes.io/service-account.name"] != null)] | length' 2>/dev/null || echo "  检查完成"

# 5. 节点 SELinux 状态
echo "[5/6] 检查节点操作系统..."
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.nodeInfo.osImage}{"\n"}{end}'

# 6. 当前版本
echo "[6/6] 当前集群版本..."
kubectl version --short 2>/dev/null || kubectl version

echo "=== 检查完成 ==="
echo "如有任何问题,请参考官方升级指南:"
echo "https://kubernetes.io/docs/tasks/administer-cluster/upgrade/"

七、移除与弃用:升级前必须处理的事项

7.1 gitRepo 卷插件——永久移除

根因:安全漏洞——容器内以 root 身份执行代码的严重安全隐患。

迁移方案一:initContainer + git-sync

apiVersion: v1
kind: Pod
metadata:
  name: app-with-git-config
spec:
  volumes:
  - name: git-config
    emptyDir: {}
  initContainers:
  - name: git-sync
    image: registry.k8s.io/git-sync/git-sync:v4.2.0
    args:
    - --repo=https://github.com/example/config-repo
    - --branch=main
    - --depth=1
    - --period=30s
    - --dest=.
    volumeMounts:
    - name: git-config
      mountPath: /git
  containers:
  - name: app
    image: my-app:latest
    volumeMounts:
    - name: git-config
      mountPath: /etc/config
      readOnly: true

迁移方案二:ConfigMap/Secret(适合小文件)

# Python 工具:将 Git 仓库转换为 ConfigMap
import yaml
import subprocess
from pathlib import Path

class GitToConfigMap:
    def __init__(self, repo_url: str, branch: str = "main"):
        self.repo_url = repo_url
        self.branch = branch

    def sync(self, output_dir: str = "/tmp/config-repo"):
        """克隆仓库并生成为 ConfigMap YAML"""
        subprocess.run([
            "git", "clone", "--depth", "1", "--branch",
            self.branch, self.repo_url, output_dir
        ], check=True, capture_output=True)

        configmap = {
            "apiVersion": "v1",
            "kind": "ConfigMap",
            "metadata": {"name": "git-config"},
            "data": {}
        }

        for path in Path(output_dir).rglob("*"):
            if path.is_file() and not path.name.startswith('.'):
                # 跳过二进制文件和大型文件
                if path.stat().st_size > 1024 * 1024:  # >1MB
                    continue
                rel = path.relative_to(output_dir)
                configmap["data"][str(rel)] = path.read_text(encoding="utf-8", errors="ignore")

        return yaml.dump(configmap, allow_unicode=True)

# 使用示例
if __name__ == "__main__":
    tool = GitToConfigMap("https://github.com/example/config-repo")
    print(tool.sync())

7.2 kube-proxy IPVS 模式——已移除

v1.35 弃用的 IPVS 模式在 v1.36 中被移除。继续使用的场景需要切换到 iptablesnftables 模式:

# 检查当前模式
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode

# 切换到 iptables 模式
kubectl edit configmap kube-proxy -n kube-system
# 修改 mode 为 "iptables"

7.3 Ingress NGINX——正式退役

重大事件:2026年3月24日,Kubernetes SIG Network 和安全响应委员会正式宣布退役 Ingress NGINX 项目。

退役原因

  • 维护团队资源不足
  • 安全漏洞响应时间过长
  • 社区转向更现代的 Gateway API

替代方案对比

方案成熟度迁移成本推荐场景
Gateway API + Contour生产环境首选
Gateway API + Envoy Gateway新项目推荐
Traefik小规模集群
HAProxy Ingress简单 HTTP/HTTPS 路由

迁移到 Gateway API 完整示例

# Step 1: 安装 Gateway API CRD 和 Contour
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: contour
spec:
  controllerName: projectcontour.io/gateway-controller

---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: production-gateway
  namespace: projectcontour
spec:
  gatewayClassName: contour
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
      namespaces:
        from: All
  - name: https
    protocol: HTTPS
    port: 443
    allowedRoutes:
      namespaces:
        from: All
    tls:
      mode: Terminate
      certificateRefs:
      - name: wildcard-cert
        kind: Secret

---
# Step 2: 定义路由(替代原有的 Ingress 规则)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: webapp-route
  namespace: default
spec:
  parentRefs:
  - name: production-gateway
    namespace: projectcontour
    sectionName: https
  hostnames:
  - "app.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api
    - path:
        type: PathPrefix
        value: /v1
    backendRefs:
    - name: api-service
      port: 8080
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: frontend-service
      port: 80
  - headers:
    matches:
    - type: RegularExpression
      name: X-Canary
      value: "true"
    backendRefs:
    - name: api-service-canary
      port: 8080
      weight: 100

八、性能优化:被忽视但影响深远的改进

8.1 SELinux 卷标签优化——从分钟级到秒级

这个从 v1.27 就进入 Beta 的特性终于在 v1.36 稳定。原理是使用 mount -o context=XYZ 选项替代递归文件重标签:

apiVersion: v1
kind: Pod
metadata:
  name: selinux-optimized-app
spec:
  securityContext:
    seLinuxOptions:
      level: "s0:c123,c456"
    seLinuxChangePolicy: MountOption   # 关键配置
  containers:
  - name: app
    image: my-app:latest
    volumeMounts:
    - name: data
      mountPath: /data
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: data-pvc

性能对比

场景v1.35 之前v1.36
10GB PVC 首次挂载45-90秒(递归重标签)<2秒(挂载时应用标签)
Pod 启动时间(有大卷)数分钟秒级
节点重启后 Pod 调度严重延迟正常

⚠️ 注意事项:混合使用特权和非特权 Pod 共享同一卷时可能导致 SELinux 标签冲突。需要正确设置 seLinuxChangePolicy 字段。

8.2 Pod 级原地伸缩(Beta)

v1.36 将 Pod 级别的 CPU/内存在线伸缩推进到 Beta(面向 cgroup v2 环境):

apiVersion: v1
kind: Pod
metadata:
  name: scalable-service
spec:
  resizePolicy:
  - resourceName: cpu
    restartPolicy: NotRequired
  - resourceName: memory
    restartPolicy: NotRequired
  containers:
  - name: app
    image: my-app:latest
    resources:
      requests:
        cpu: "100m"
        memory: "128Mi"
      limits:
        cpu: "500m"
        memory: "512Mi"

在原地伸缩模式下,修改 Pod 的资源请求时不需要重启容器——这对有状态服务、数据库和高可用应用的运维体验是质的提升。


九、生产升级路径:从检查到执行

9.1 升级前检查流程

#!/bin/bash
# Kubernetes v1.36 升级前完整检查

set -e

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

# 检查 1: gitRepo 卷使用
echo "[1/7] 检查 gitRepo 卷..."
GITREPO_PODS=$(kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{" "}{.metadata.name}{"\n"}{range .spec.volumes[*]}{.gitRepo}{"\n"}{end}{end}' | grep -v "^$" | grep -v "^ " | grep -v "^$")
if [ -n "$GITREPO_PODS" ]; then
  echo "  ⚠️  发现使用 gitRepo 卷的 Pod,必须先迁移!"
  echo "$GITREPO_PODS"
else
  echo "  ✅ 无 gitRepo 卷使用"
fi

# 检查 2: externalIPs 使用
echo "[2/7] 检查 externalIPs..."
EXT_IPS=$(kubectl get services --all-namespaces -o json | \
  jq -r '.items[] | select(.spec.externalIPs != null and (.spec.externalIPs | length) > 0) | "\(.metadata.namespace)/\(.metadata.name)"')
if [ -n "$EXT_IPS" ]; then
  echo "  ⚠️  发现使用 externalIPs 的 Service(v1.36 显示警告):"
  echo "$EXT_IPS"
else
  echo "  ✅ 无 externalIPs 使用"
fi

# 检查 3: Ingress NGINX
echo "[3/7] 检查 Ingress NGINX..."
INGRESS=$(kubectl get deployments --all-namespaces \
  -l app.kubernetes.io/name=ingress-nginx \
  -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}')
if [ -n "$INGRESS" ]; then
  echo "  ⚠️  发现 Ingress NGINX 部署,已退役,建议迁移至 Gateway API:"
  echo "$INGRESS"
else
  echo "  ✅ 无 Ingress NGINX 部署"
fi

# 检查 4: kube-proxy 模式
echo "[4/7] 检查 kube-proxy 模式..."
MODE=$(kubectl get configmap kube-proxy -n kube-system -o jsonpath='{.data.config}' | \
  jq -r '.mode // "iptables"')
echo "  当前模式: $MODE"
if [ "$MODE" = "ipvs" ]; then
  echo "  ⚠️  IPVS 模式已在 v1.36 移除,必须切换到 iptables 或 nftables"
fi

# 检查 5: etcd 备份
echo "[5/7] 检查 etcd 备份状态..."
ETCD_PODS=$(kubectl get pods -n kube-system -l component=etcd -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')
echo "  etcd 节点: $ETCD_PODS"
echo "  ⚠️  请确认已执行: kubectl exec -n kube-system etcd-<node> -- etcdctl snapshot save /var/lib/etcd/snapshot.db"

# 检查 6: 当前版本
echo "[6/7] 当前集群版本..."
kubectl version --short 2>/dev/null || kubectl version

# 检查 7: 节点 OS 兼容性
echo "[7/7] 节点 OS 检查..."
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.nodeInfo.osImage}{"\t"}{.status.nodeInfo.kernelVersion}{"\n"}{end}'

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

9.2 推荐的升级步骤

标准升级路径(假设从 v1.34/v1.35 升级):

  1. 创建 etcd 快照(必须!)

    ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
      --cert=/etc/kubernetes/pki/etcd/server.crt \
      --key=/etc/kubernetes/pki/etcd/server.key \
      --cacert=/etc/kubernetes/pki/etcd/ca.crt \
      snapshot save /var/lib/etcd/snapshot-$(date +%Y%m%d).db
    
  2. 升级控制平面组件

    # 使用 kubeadm
    kubeadm upgrade plan v1.36.0
    kubeadm upgrade apply v1.36.0
    
  3. 升级 kubelet

    # 每台节点逐一执行
    kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
    apt-get install -y kubelet=1.36.0-*
    systemctl restart kubelet
    kubectl uncordon <node>
    
  4. 验证升级

    kubectl get nodes
    # 确认所有节点状态为 Ready,版本为 v1.36.0
    

十、总结与展望

Kubernetes v1.36「Haru」的核心价值,可以用三句话总结:

第一句:安全能力从「可选增强」变成「原生内置」。Pod User Namespaces、ServiceAccount 外部签名、IP/CIDR 校验收紧——过去需要借助第三方运行时或自研组件才能实现的安全能力,现在 Kubernetes 自身就能兜底。对多租户、金融、政企、合规场景,这是直接可用的基础设施升级。

第二句:AI/ML 基础设施正式进入 Kubernetes 的核心语义。OCI VolumeSource 解决了模型分发的老大难问题,DRA 可分片设备让 GPU 资源从「整机独享」走向「按需切分」,Gateway API 的流量治理能力支撑起金丝雀发布和 A/B 测试。当 KubeCon 的主题词从「Cloud Native」演变为「AI Native」,Kubernetes 正在用它最擅长的方式——标准化和平台化——为 AI 工作负载构建基础设施底座。

第三句:运维负担的结构性降低。Mutating Admission Policies 让你不再需要维护 Webhook Server 集群,SELinux 卷标签优化让大卷 Pod 的启动时间从分钟级降到秒级,Pod 级原地伸缩让你不需要为了调整资源限制而重启服务。这些改进单独看都不大,但累积起来,对日常运维体验是质的提升。

升级建议

  • 生产环境建议在官方发布后 2-4 周进行升级(有充分的生产案例验证后)
  • 先在测试环境验证所有工作负载兼容性
  • 优先处理:gitRepo 卷迁移、Ingress NGINX 到 Gateway API 的迁移路线图
  • 关注警告:externalIPs 弃用警告(计划在 v1.43 完全移除)

Kubernetes 正在从「容器编排平台」进化为「面向 AI 时代的企业级云原生操作系统」。v1.36 是这条进化路上的一个重要节点——它不追求惊雷般的变革,而是把过去数年积累的能力一一兑现。春山可望,稳中有为。


参考链接

推荐文章

git使用笔记
2024-11-18 18:17:44 +0800 CST
vue打包后如何进行调试错误
2024-11-17 18:20:37 +0800 CST
html一个全屏背景视频
2024-11-18 00:48:20 +0800 CST
MCP 协议升级测试[片段8]
2026-07-26 07:52:06 +0800 CST
批量导入scv数据库
2024-11-17 05:07:51 +0800 CST
在 Rust 生产项目中存储数据
2024-11-19 02:35:11 +0800 CST
mysql int bigint 自增索引范围
2024-11-18 07:29:12 +0800 CST
PostgreSQL日常运维命令总结分享
2024-11-18 06:58:22 +0800 CST
Vue3中的v-slot指令有什么改变?
2024-11-18 07:32:50 +0800 CST
程序员茄子在线接单