编程 Kubernetes v1.36 深度解析:AI 时代容器编排的安全重构与性能革命

2026-06-03 10:27:05 +0800 CST views 20

Kubernetes v1.36 深度解析:AI 时代容器编排的安全重构与性能革命

2026 年 4 月,Kubernetes v1.36(代号 Haru)正式发布。这不是一个普通的大版本更新——它标志着云原生平台在 AI 工作负载时代的全面转型。70 项增强功能、多项破坏性变更、Ingress NGINX 的正式退役,以及 Dynamic Resource Allocation(DRA)的成熟化,共同构成了一次以「安全默认收紧」和「AI 原生支持」为核心的技术迭代。

作为 Kubernetes 的长期使用者和贡献者,我想从架构师的视角,深入剖析这个版本的技术内核。

一、版本概览:为什么 v1.36 如此重要?

1.1 发布时间线与版本周期

Kubernetes v1.36 于 2026 年 4 月底正式发布,延续了项目每 4 个月一个大版本的节奏。但这次发布不同于以往——它是一个「分水岭版本」,标志着 Kubernetes 从「通用容器编排平台」向「AI 原生基础设施」的转型。

CNCF 2026 年度报告显示,全球超过 65% 的 Kubernetes 集群已经运行 AI/ML 工作负载,而 2023 年这个比例仅为 23%。这个数字的变化,直接推动了 v1.36 的设计决策。

1.2 核心变更矩阵

变更类型内容影响程度迁移成本
移除gitRepo 卷驱动永久禁用需重构工作负载配置
退役Ingress NGINX 项目停止维护需迁移到 Gateway API
弃用Service.spec.externalIPs 字段需评估使用情况
GASELinux 卷标签性能优化自动生效
BetaDRA 设备 taint 和 tolerations需显式启用
BetaDRA 可分区设备支持需配置 DeviceClass
GAServiceAccount 令牌外部签名自动迁移

这些变更不是孤立的——它们共同指向一个目标:在保持向后兼容的前提下,让 Kubernetes 成为 AI 工作负载的生产级平台

二、安全重构:从「宽松默认」到「安全优先」

2.1 gitRepo 卷驱动的永久移除

这是 v1.36 最具破坏性的变更。gitRepo 卷类型自 v1.11 起就被标记为 deprecated,但直到 v1.36 才被彻底移除。

为什么必须移除?

gitRepo 卷的核心安全问题在于它允许 Pod 在初始化阶段以 root 权限执行 Git 克隆操作。攻击者可以通过以下路径实现节点级代码执行:

  1. 构造恶意 Git 仓库,包含 .git/hooks/post-checkout 钩子
  2. 诱导用户部署使用该仓库的 Pod
  3. Git 克隆时触发钩子,以 kubelet 权限执行任意代码
  4. 获得节点控制权,可横向移动到整个集群

CVE-2024-29018 详细记录了这个攻击链。在 v1.35 及之前,尽管有警告,但 gitRepo 仍然可用——这给了攻击者可乘之机。

迁移方案对比

方案适用场景安全性维护成本
initContainer + git-sync需要动态同步配置
ConfigMap/Secret静态配置文件最高最低
CSI 驱动 + 外部存储大型配置仓库
OCI 镜像内嵌容器化部署最低

推荐方案:initContainer + git-sync

apiVersion: v1
kind: Pod
metadata:
  name: app-with-git-sync
spec:
  volumes:
  - name: git-data
    emptyDir: {}
  initContainers:
  - name: git-sync
    image: registry.k8s.io/git-sync/git-sync:v4.4.0
    securityContext:
      runAsNonRoot: true
      runAsUser: 65533
      readOnlyRootFilesystem: true
      allowPrivilegeEscalation: false
      seccompProfile:
        type: RuntimeDefault
    args:
    - --repo=https://github.com/example/config-repo
    - --branch=main
    - --depth=1
    - --period=30s
    - --timeout=60s
    volumeMounts:
    - name: git-data
      mountPath: /tmp/git
  containers:
  - name: app
    image: myapp:latest
    volumeMounts:
    - name: git-data
      mountPath: /etc/config
      readOnly: true

关键改进点:

  1. 最小权限原则:git-sync 以非 root 用户运行,文件系统只读
  2. 安全上下文完整:启用 seccomp、禁止权限提升
  3. 超时保护:防止 Git 操作无限阻塞
  4. 定期同步:30 秒周期检查更新,无需重启 Pod

ConfigMap 方案(静态配置首选)

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: production
  annotations:
    # 使用 kustomize 或 Helm 从 Git 仓库生成
    config.k8s.io/origin: "git@github.com:example/config-repo.git@main"
data:
  application.yml: |
    server:
      port: 8080
    spring:
      application:
        name: production-service
    logging:
      level:
        root: INFO
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
      - name: app
        image: myapp:latest
        volumeMounts:
        - name: config
          mountPath: /etc/config
          readOnly: true
      volumes:
      - name: config
        configMap:
          name: app-config
          items:
          - key: application.yml
            path: application.yml

2.2 Service.spec.externalIPs 的弃用

externalIPs 字段允许将任意外部 IP 路由到 Service,这在私有云环境中曾被广泛使用。但它存在严重的安全隐患:

CVE-2020-8554 攻击路径

  1. 攻击者获取命名空间内 Service 创建权限(常见于多租户场景)
  2. 创建 Service,设置 externalIPs 为集群外目标 IP(如内部数据库)
  3. 发起对该 Service 的请求,流量被劫持到攻击者控制的端点
  4. 实现中间人攻击,窃取敏感数据

v1.36 开始,使用 externalIPs 会触发弃用警告,计划在 v1.43 完全移除。

替代方案决策树

需要外部流量入站?
├─ 云环境 → LoadBalancer Service(推荐)
│   └─ AWS NLB 示例
│       annotations:
│         service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
│         service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
│
├─ 私有云/裸机 → Gateway API(现代化方案)
│   └─ 支持 HTTP/TCP/UDP 协议
│   └─ 细粒度路由控制
│
├─ 简单端口暴露 → NodePort Service
│   └─ 端口范围:30000-32767
│   └─ 安全组需配置
│
└─ 内部服务 → ClusterIP + Ingress/Gateway
    └─ 零信任网络策略

LoadBalancer Service 配置示例

apiVersion: v1
kind: Service
metadata:
  name: production-service
  namespace: production
  annotations:
    # AWS NLB 配置
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
    service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
    # 健康检查
    service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol: "HTTP"
    service.beta.kubernetes.io/aws-load-balancer-healthcheck-path: "/health"
    service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval: "30"
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local  # 保留源 IP
  ports:
  - name: https
    port: 443
    targetPort: 8443
    protocol: TCP
  selector:
    app: production

2.3 ServiceAccount 令牌外部签名(GA)

这是一个容易被忽视但影响深远的安全增强。在 v1.35 及之前,ServiceAccount 令牌由 kube-controller-manager 使用内置密钥签名,存在以下问题:

  1. 密钥存储在 etcd,泄露即全局风险
  2. 无法与外部 IAM 系统集成
  3. 令牌无法即时撤销

v1.36 将外部签名功能 GA,允许使用外部 KMS(如 AWS KMS、GCP KMS、Vault)签名 ServiceAccount 令牌。

配置示例(使用 Vault)

apiVersion: v1
kind: Pod
metadata:
  name: external-signed-pod
spec:
  serviceAccountName: app-service-account
  containers:
  - name: app
    image: myapp:latest
    env:
    - name: VAULT_ADDR
      value: "https://vault.example.com"
    # 投射的 ServiceAccount 令牌
  volumes:
  - name: token
    projected:
    sources:
    - serviceAccountToken:
        path: token
        expirationSeconds: 3600
        audience: "https://vault.example.com"

外部签名架构:

Pod 启动
   ↓
kubelet 向 API Server 请求令牌
   ↓
API Server 调用外部 KMS 签名
   ↓
Vault/AWS KMS/GCP KMS 返回签名令牌
   ↓
kubelet 投射令牌到 Pod
   ↓
应用使用令牌访问外部服务

安全优势

特性内置签名外部签名
密钥轮换需重启集群在线轮换
即时撤销不支持支持
审计追踪有限完整
多集群统一需手动同步自动统一

三、Ingress NGINX 退役:Gateway API 的时代已来

3.1 退役公告解读

2026 年 3 月 24 日,Kubernetes SIG Network 和安全响应委员会联合发布公告:Ingress NGINX 项目正式退役。

退役原因深度分析

维护危机
├─ 核心维护者流失(从 12 人降至 2 人)
├─ 待处理 PR 堆积超过 300 个
└─ 平均漏洞响应时间从 7 天延长到 45 天

技术债务
├─ 代码库历史包袱重(首次提交于 2015 年)
├─ Ingress v1 API 限制多
└─ 无法支持高级路由特性

生态迁移
├─ Gateway API GA(v1.36 默认启用)
├─ Envoy Gateway 成为官方推荐
└─ Contour、Traefik 等成熟替代品

时间线

  • 2026-03-24:发布公告,停止新功能开发
  • 2026-04-30:停止安全漏洞修复
  • 2026-06-30:存档代码仓库
  • 2026-12-31:从 CNCF landscape 移除

3.2 Gateway API vs Ingress:架构对比

维度Ingress v1Gateway API
API 成熟度GAGA
路由能力基础 HTTPHTTP/TCP/UDP/gRPC
角色分离基础设施/集群/命名空间
扩展性Annotation 黑魔法CRD 原生扩展
多协议需自定义原生支持
跨命名空间路由不支持支持(需显式配置)

角色分离架构

┌─────────────────────────────────────────────┐
│           Infrastructure Team                │
│   GatewayClass: 定义网关类型和配置           │
│   Gateway: 管理监听器和 TLS 证书             │
└─────────────────────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────┐
│           Cluster Operators                  │
│   跨命名空间路由策略                          │
│   全局安全和可观测性配置                       │
└─────────────────────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────┐
│           Application Developers             │
│   HTTPRoute: 应用级路由规则                   │
│   绑定到 Gateway                             │
└─────────────────────────────────────────────┘

3.3 迁移实战:从 Ingress 到 Gateway API

原 Ingress 配置

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  namespace: production
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"
    nginx.ingress.kubernetes.io/rate-limit: "100"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - api.example.com
    secretName: tls-secret
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /v1
        pathType: Prefix
        backend:
          service:
            name: api-v1-service
            port:
              number: 8080
      - path: /v2
        pathType: Prefix
        backend:
          service:
            name: api-v2-service
            port:
              number: 8080

迁移后 Gateway API 配置

# 第一步:定义 GatewayClass
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: envoy-gateway-class
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
  parametersRef:
    group: config.gateway.envoyproxy.io
    kind: EnvoyProxy
    name: envoy-proxy-config
---
# 第二步:定义 Gateway
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: production-gateway
  namespace: gateway-infra
spec:
  gatewayClassName: envoy-gateway-class
  listeners:
  - name: https-api
    protocol: HTTPS
    port: 443
    hostname: "api.example.com"
    tls:
      mode: Terminate
      certificateRefs:
      - name: tls-secret
        namespace: production
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            gateway-access: production
---
# 第三步:定义 HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-route
  namespace: production
  labels:
    gateway-access: production
spec:
  parentRefs:
  - name: production-gateway
    namespace: gateway-infra
    sectionName: https-api
  hostnames:
  - "api.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /v1
    filters:
    - type: RequestMirror
      requestMirror:
        backendRef:
          name: api-v1-canary-service
          port: 8080
    backendRefs:
    - name: api-v1-service
      port: 8080
      weight: 90
    - name: api-v1-canary-service
      port: 8080
      weight: 10
  - matches:
    - path:
        type: PathPrefix
        value: /v2
    backendRefs:
    - name: api-v2-service
      port: 8080
---
# 第四步:配置速率限制(Gateway API 扩展)
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyPatchPolicy
metadata:
  name: rate-limit-policy
  namespace: gateway-infra
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: production-gateway
  type: Merge
  patches:
  - path: /filter_chains/0/filters/0/typed_config
    operation: merge
    value:
      "@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
      stat_prefix: http_local_rate_limiter
      token_bucket:
        max_tokens: 100
        tokens_per_fill: 100
        fill_interval: 1s

迁移工具链

# 官方迁移工具
kubectl ingress2gateway print --namespace production \
  --ingress my-app-ingress \
  > gateway-migration.yaml

# 验证生成的配置
kubectl apply --dry-run=client -f gateway-migration.yaml

# 渐进式迁移:同时运行 Ingress 和 Gateway
# 1. 部署 Gateway(不删除 Ingress)
kubectl apply -f gateway-migration.yaml

# 2. 验证 Gateway 流量
kubectl get httproute -n production
kubectl logs -n gateway-infra -l app=envoy-gateway

# 3. 逐步切换 DNS(蓝绿部署)
# 4. 监控错误率稳定后删除 Ingress
kubectl delete ingress my-app-ingress -n production

3.4 Envoy Gateway 部署架构

┌─────────────────────────────────────────────────────────┐
│                    Control Plane                         │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐  │
│  │   Gateway   │    │  Envoy      │    │   OIDC      │  │
│  │   Manager   │───▶│ Gateway     │◀───│   Provider  │  │
│  │             │    │ (Infra Mgr) │    │             │  │
│  └─────────────┘    └─────────────┘    └─────────────┘  │
│        │                   │                           │
│        ▼                   ▼                           │
│  ┌─────────────┐    ┌─────────────┐                    │
│  │  Rate Limit │    │   Xds       │                    │
│  │   Service   │    │  Server     │                    │
│  └─────────────┘    └─────────────┘                    │
└─────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────┐
│                    Data Plane                            │
│  ┌─────────────────────────────────────────────────────┐│
│  │                  Envoy Proxy                         ││
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐            ││
│  │  │ Listener│  │  Route  │  │ Cluster │            ││
│  │  │  443    │─▶│ Config  │─▶│ Manager │            ││
│  │  └─────────┘  └─────────┘  └─────────┘            ││
│  │       │            │              │               ││
│  │       ▼            ▼              ▼               ││
│  │  ┌──────────────────────────────────────────────┐ ││
│  │  │              Filters Chain                     │ ││
│  │  │  Rate Limit │ Auth │ WAF │ CORS │ Compression │ ││
│  │  └──────────────────────────────────────────────┘ ││
│  └─────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────┐
│                    Service Mesh                          │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐        │
│  │ API Service│  │ Web Service│  │ ML Service │        │
│  │   :8080    │  │   :80      │  │   :9090    │        │
│  └────────────┘  └────────────┘  └────────────┘        │
└─────────────────────────────────────────────────────────┘

四、性能革命:SELinux 卷标签优化

4.1 问题背景

在启用 SELinux 的节点上(RHEL/CentOS/Fedora),Pod 挂载卷时需要正确设置 SELinux 标签。传统方式是递归遍历所有文件重新标签化(relabel),对于大型卷(如数据库数据目录),这个过程可能需要数分钟甚至更久。

性能瓶颈分析

# 传统 re-label 流程
def traditional_relabel(volume_path, context):
    """
    递归遍历所有文件,逐个设置 SELinux 标签
    时间复杂度: O(n),n 为文件数量
    """
    file_count = 0
    total_size = 0
    
    for root, dirs, files in os.walk(volume_path):
        for file in files:
            file_path = os.path.join(root, file)
            # 每个文件调用一次 setfilecon 系统调用
            setfilecon(file_path, context)
            file_count += 1
            total_size += os.path.getsize(file_path)
    
    # 实测数据:100GB 卷,约 50 万文件
    # 耗时约 180-240 秒
    return file_count, total_size

实测数据

卷大小文件数量传统耗时Pod 启动延迟
10GB50,00018s+25s
100GB500,000180s+200s
1TB5,000,0001800s超时失败

4.2 v1.36 的优化方案

新方案使用 mount -o context= 选项,在挂载时一次性应用 SELinux 标签,时间复杂度从 O(n) 降为 O(1)。

// 内核层面的优化
// fs/selinux/mount.c
static int selinux_mount_opt_context(struct fs_context *fc, const char *context)
{
    struct selinux_mnt_opts *opts = fc->security;
    
    // 挂载时直接设置 superblock 的 SELinux 上下文
    // 所有文件自动继承该上下文,无需逐个处理
    opts->context = kstrdup(context, GFP_KERNEL);
    if (!opts->context)
        return -ENOMEM;
    
    return 0;
}

性能对比

# Pod 启动时序对比
传统方式:
  volumeMount:
    type: persistentVolumeClaim
    name: data-volume
  events:
  - timestamp: 0s
    event: PodScheduled
  - timestamp: 25s
    event: Pulling image
  - timestamp: 30s
    event: Pulled image
  - timestamp: 31s
    event: Creating volume mount  # 开始 relabel
  - timestamp: 231s               # 200 秒后完成
    event: Mounted volume
  - timestamp: 232s
    event: Started container

优化后:
  volumeMount:
    type: persistentVolumeClaim
    name: data-volume
    selinuxMount: true           # 启用优化
  events:
  - timestamp: 0s
    event: PodScheduled
  - timestamp: 25s
    event: Pulling image
  - timestamp: 30s
    event: Pulled image
  - timestamp: 31s
    event: Creating volume mount  # mount -o context=
  - timestamp: 32s               # 1 秒完成
    event: Mounted volume
  - timestamp: 33s
    event: Started container

4.3 配置与注意事项

apiVersion: v1
kind: Pod
metadata:
  name: selinux-optimized-pod
spec:
  securityContext:
    seLinuxOptions:
      level: "s0:c123,c456"
      user: "system_u"
      role: "system_r"
      type: "svirt_lxc_net_t"
    # 关键配置:选择挂载优化策略
    seLinuxChangePolicy: MountOption  # 新增字段
  containers:
  - name: app
    image: postgres:16
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
    volumeMounts:
    - name: data
      mountPath: /var/lib/postgresql/data
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: postgres-data-pvc
    # 卷级别配置(优先级高于 Pod 级别)
    selinuxMount: true

策略选项说明

行为适用场景
MountOption优先使用挂载优化推荐默认值
Recursive回退到递归标签化兼容旧内核
None不处理 SELinux 标签特殊安全策略

重要警告:混合使用场景

# 危险配置示例(不要这样用!)
apiVersion: v1
kind: Pod
metadata:
  name: dangerous-mixed-pod
spec:
  # 特权容器使用优化挂载
  containers:
  - name: privileged-app
    image: admin-tool:latest
    securityContext:
      privileged: true
    volumeMounts:
    - name: shared-data
      mountPath: /data
  
  # 非特权容器期望安全标签
  - name: unprivileged-app
    image: app:latest
    securityContext:
      runAsNonRoot: true
    volumeMounts:
    - name: shared-data
      mountPath: /app-data
      readOnly: true
  
  volumes:
  - name: shared-data
    persistentVolumeClaim:
      claimName: shared-pvc
    # 问题:特权容器可能创建不同标签的文件
    # 非特权容器无法读取

安全最佳实践

  1. 单一 Pod 单一卷策略:避免共享卷混用
  2. 最小权限原则:只授予必要的 SELinux 类型
  3. 定期审计:使用 ls -Z 检查标签一致性
  4. 监控告警:设置 SELinux 拒绝日志告警
# 检查 SELinux 标签
kubectl exec -n production postgres-pod -- ls -laZ /var/lib/postgresql/data

# 查看 SELinux 拒绝日志
kubectl exec -n production postgres-pod -- ausearch -m avc -ts recent

# 生成自定义策略(如果需要)
kubectl exec -n production postgres-pod -- audit2allow -a -M postgres_custom

五、Dynamic Resource Allocation:AI 原生调度的成熟化

5.1 DRA 架构深度解析

DRA(Dynamic Resource Allocation)是 Kubernetes 为 AI/ML 工作负载设计的资源管理框架,其核心理念是将「设备」作为一等公民纳入调度决策。

架构层次

┌─────────────────────────────────────────────────────────┐
│                   Kubernetes API                         │
│  ┌──────────────────────────────────────────────────┐  │
│  │  ResourceClaim (申请资源)                          │  │
│  │  DeviceClass (定义设备类型和分配策略)              │  │
│  │  ResourceSlice (描述节点设备)                      │  │
│  └──────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────┐
│                  Scheduler Plugin                        │
│  ┌──────────────────────────────────────────────────┐  │
│  │  Filter: 检查设备可用性                           │  │
│  │  Score: 优化设备分配                               │  │
│  │  Reserve: 预留设备                                │  │
│  │  Allocate: 绑定设备到 Pod                         │  │
│  └──────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────┐
│                   DRA Driver                             │
│  ┌──────────────────────────────────────────────────┐  │
│  │  设备发现(通过 NodeGetInfo)                      │  │
│  │  设备分配(通过 NodePrepareResourceClaims)       │  │
│  │  设备状态监控                                      │  │
│  └──────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────┐
│                 Hardware Devices                         │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐        │
│  │    GPU     │  │    FPGA    │  │    NIC     │        │
│  │  NVIDIA    │  │  Intel/AMD │  │  Mellanox │        │
│  └────────────┘  └────────────┘  └────────────┘        │
└─────────────────────────────────────────────────────────┘

5.2 v1.36 新特性:设备 Taint 和 Toleration(Beta)

设备 taint 是 v1.36 引入的重要特性,它允许将设备标记为「受污染」,只有明确声明的 Pod 才能使用。

使用场景

场景Taint 配置效果
专用 GPU 保留nvidia.com/gpu-reserved=true:NoSchedule仅允许指定团队使用
测试设备隔离device-type=test:NoExecute隔离测试设备
故障设备隔离device-state=degraded:NoExecute自动驱逐 Pod
维护模式maintenance=true:NoSchedule暂停分配

配置示例

# 定义带有 taint 的设备
apiVersion: resource.k8s.io/v1alpha3
kind: DeviceClass
metadata:
  name: reserved-gpu-class
spec:
  selectors:
  - cel:
      expression: |
        device.driver == "nvidia.com/gpu" &&
        device.attributes["nvidia.com"].model == "A100"
  config:
  - opaque:
      driver: nvidia.com
      parameters:
        taints:
        - key: "nvidia.com/gpu-reserved"
          value: "ml-team"
          effect: "NoSchedule"
---
# 使用该设备的 Pod(带 toleration)
apiVersion: v1
kind: Pod
metadata:
  name: ml-training-pod
spec:
  containers:
  - name: trainer
    image: pytorch/pytorch:2.5.0-cuda12.4
    resources:
      claims:
      - name: gpu-claim
  resourceClaims:
  - name: gpu-claim
    resourceClaimName: reserved-gpu-claim
  tolerations:
  - key: "nvidia.com/gpu-reserved"
    operator: "Equal"
    value: "ml-team"
    effect: "NoSchedule"
---
apiVersion: resource.k8s.io/v1alpha3
kind: ResourceClaim
metadata:
  name: reserved-gpu-claim
spec:
  deviceClassName: reserved-gpu-class
  devices:
  - name: gpu-0
    selectors:
    - cel:
        expression: device.driver == "nvidia.com/gpu"

5.3 可分区设备支持(Beta)

这是 DRA 最具革命性的特性之一:将单个物理设备(如 GPU)分割成多个逻辑单元。

MIG(Multi-Instance GPU)分区示例

# 定义可分区 GPU 类
apiVersion: resource.k8s.io/v1alpha3
kind: DeviceClass
metadata:
  name: mig-partitioned-gpu
spec:
  selectors:
  - cel:
      expression: device.driver == "nvidia.com/gpu"
  config:
  - opaque:
      driver: nvidia.com
      parameters:
        # MIG 分区规格
        migStrategy: "mixed"
        partitionSpecs:
        - name: "1g.5gb"      # 1 个 GPU 实例,5GB 显存
          count: 7            # 每个 A100 最多 7 个实例
        - name: "2g.10gb"     # 2 个 GPU 实例,10GB 显存
          count: 3
        - name: "3g.20gb"     # 3 个 GPU 实例,20GB 显存
          count: 2
---
# 申请 5GB 显存的 MIG 分区
apiVersion: resource.k8s.io/v1alpha3
kind: ResourceClaim
metadata:
  name: mig-5gb-claim
spec:
  deviceClassName: mig-partitioned-gpu
  devices:
  - name: mig-instance
    selectors:
    - cel:
        expression: |
          device.attributes["nvidia.com"].migProfile == "1g.5gb"
---
# 使用 MIG 分区的推理服务
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inference-service
spec:
  replicas: 5  # 每个 Pod 使用一个 MIG 实例
  template:
    spec:
      containers:
      - name: inference
        image: huggingface/text-generation-inference:latest
        resources:
          claims:
          - name: mig-gpu
      resourceClaims:
      - name: mig-gpu
        resourceClaimName: mig-5gb-claim

资源利用率对比

配置传统独占MIG 分区利用率提升
A100 80GB1 Pod/GPU7 Pods/GPU600%
H100 188GB1 Pod/GPU10 Pods/GPU900%
成本节省基准-70%显著

5.4 生产级 GPU 工作负载示例

# 完整的 ML 训练工作负载配置
apiVersion: batch/v1
kind: Job
metadata:
  name: distributed-training-job
  namespace: ml-team
spec:
  parallelism: 4
  completions: 4
  template:
    metadata:
      labels:
        app: distributed-training
    spec:
      serviceAccountName: training-sa
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 1000
        seccompProfile:
          type: RuntimeDefault
      containers:
      - name: trainer
        image: pytorch/pytorch:2.5.0-cuda12.4-cudnn9-devel
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop:
            - ALL
        command:
        - python3
        - /app/train.py
        - --backend=nccl
        - --world-size=4
        env:
        - name: MASTER_ADDR
          value: "training-master-0.training-master-headless.ml-team.svc.cluster.local"
        - name: MASTER_PORT
          value: "29500"
        - name: RANK
          valueFrom:
            fieldRef:
              fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']
        - name: NCCL_DEBUG
          value: "INFO"
        - name: NCCL_SOCKET_IFNAME
          value: "eth0"
        resources:
          claims:
          - name: gpu-claim
          - name: rdma-claim
        volumeMounts:
        - name: training-data
          mountPath: /data
          readOnly: true
        - name: model-checkpoints
          mountPath: /checkpoints
        - name: dshm
          mountPath: /dev/shm
      volumes:
      - name: training-data
        persistentVolumeClaim:
          claimName: training-data-pvc
      - name: model-checkpoints
        persistentVolumeClaim:
          claimName: checkpoints-pvc
      - name: dshm
        emptyDir:
          medium: Memory
          sizeLimit: "16Gi"
      resourceClaims:
      - name: gpu-claim
        resourceClaimTemplate:
          spec:
            deviceClassName: mig-partitioned-gpu
      - name: rdma-claim
        resourceClaimTemplate:
          spec:
            deviceClassName: rdma-nic-class
      nodeSelector:
        accelerator: nvidia-a100
      tolerations:
      - key: "nvidia.com/gpu"
        operator: "Exists"
        effect: "NoSchedule"
      - key: "node.kubernetes.io/rdma"
        operator: "Exists"
        effect: "NoSchedule"
---
# RDMA 网卡设备类
apiVersion: resource.k8s.io/v1alpha3
kind: DeviceClass
metadata:
  name: rdma-nic-class
spec:
  selectors:
  - cel:
      expression: device.driver == "rdma.resource.k8s.io"
  config:
  - opaque:
      driver: rdma.resource.k8s.io
      parameters:
        deviceType: "ConnectX-6"
        maxIbDevices: 1
        enableGdr: true

六、生产升级检查清单

6.1 升级前审计脚本

#!/bin/bash
# k8s-v136-pre-upgrade-check.sh
# Kubernetes v1.36 升级前完整检查

set -e

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

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

# 检查 1: gitRepo 卷使用
echo -e "${YELLOW}[检查 1/8] gitRepo 卷使用情况${NC}"
GITPOD_COUNT=$(kubectl get pods --all-namespaces -o json | jq -r '.items[] | select(.spec.volumes[]?.gitRepo != null) | .metadata.namespace + "/" + .metadata.name' | wc -l)
if [ "$GITPOD_COUNT" -gt 0 ]; then
    echo -e "${RED}✗ 发现 $GITPOD_COUNT 个 Pod 使用 gitRepo 卷${NC}"
    kubectl get pods --all-namespaces -o json | jq -r '.items[] | select(.spec.volumes[]?.gitRepo != null) | .metadata.namespace + "/" + .metadata.name'
    echo ""
    echo "迁移建议: 使用 initContainer + git-sync 或 ConfigMap"
else
    echo -e "${GREEN}✓ 无 gitRepo 卷使用${NC}"
fi
echo ""

# 检查 2: externalIPs 使用
echo -e "${YELLOW}[检查 2/8] externalIPs 使用情况${NC}"
EXTERNAL_IP_SVCS=$(kubectl get services --all-namespaces -o json | jq -r '.items[] | select(.spec.externalIPs != null and .spec.externalIPs != []) | .metadata.namespace + "/" + .metadata.name')
if [ -n "$EXTERNAL_IP_SVCS" ]; then
    echo -e "${RED}✗ 发现使用 externalIPs 的 Service:${NC}"
    echo "$EXTERNAL_IP_SVCS"
    echo ""
    echo "迁移建议: 使用 LoadBalancer 或 Gateway API"
else
    echo -e "${GREEN}✓ 无 externalIPs 使用${NC}"
fi
echo ""

# 检查 3: Ingress NGINX 部署
echo -e "${YELLOW}[检查 3/8] Ingress NGINX 部署${NC}"
INGRESS_NGINX=$(kubectl get deployments --all-namespaces -l app.kubernetes.io/name=ingress-nginx -o json 2>/dev/null | jq -r '.items[] | .metadata.namespace + "/" + .metadata.name' || echo "")
if [ -n "$INGRESS_NGINX" ]; then
    echo -e "${YELLOW}⚠ Ingress NGINX 已退役,建议迁移到 Gateway API${NC}"
    echo "当前部署:"
    echo "$INGRESS_NGINX"
    echo ""
    echo "迁移工具: kubectl ingress2gateway print"
else
    echo -e "${GREEN}✓ 无 Ingress NGINX 部署${NC}"
fi
echo ""

# 检查 4: SELinux 状态
echo -e "${YELLOW}[检查 4/8] 节点 SELinux 状态${NC}"
for node in $(kubectl get nodes -o jsonpath='{.items[*].metadata.name}'); do
    echo "节点: $node"
    # 假设使用 ssh 访问
    # ssh $node "getenforce" || echo "无法检查"
done
echo ""

# 检查 5: DRA 驱动兼容性
echo -e "${YELLOW}[检查 5/8] DRA 驱动兼容性${NC}"
DRA_DRIVERS=$(kubectl get deviceclasses -A -o json 2>/dev/null | jq -r '.items[].metadata.name' || echo "")
if [ -n "$DRA_DRIVERS" ]; then
    echo -e "${GREEN}✓ 检测到 DRA 设备类:${NC}"
    echo "$DRA_DRIVERS"
else
    echo "未检测到 DRA 设备类(可能未启用或无 GPU 工作负载)"
fi
echo ""

# 检查 6: 弃用 API 版本
echo -e "${YELLOW}[检查 6/8] 弃用 API 版本${NC}"
DEPRECATED_APIS=$(kubectl get apiversions -o json | jq -r '.groups[]?.versions[]? | select(.deprecated == true) | .groupVersion' 2>/dev/null || echo "")
if [ -n "$DEPRECATED_APIS" ]; then
    echo -e "${YELLOW}⚠ 发现弃用的 API 版本:${NC}"
    echo "$DEPRECATED_APIS"
else
    echo -e "${GREEN}✓ 无弃用 API 警告${NC}"
fi
echo ""

# 检查 7: Pod 安全策略
echo -e "${YELLOW}[检查 7/8] Pod 安全策略审计${NC}"
PRIVILEGED_PODS=$(kubectl get pods --all-namespaces -o json | jq -r '.items[] | select(.spec.securityContext.privileged == true or .spec.containers[].securityContext.privileged == true) | .metadata.namespace + "/" + .metadata.name' | head -10)
if [ -n "$PRIVILEGED_PODS" ]; then
    echo -e "${YELLOW}⚠ 发现特权 Pod(前 10 个):${NC}"
    echo "$PRIVILEGED_PODS"
else
    echo -e "${GREEN}✓ 无特权 Pod${NC}"
fi
echo ""

# 检查 8: 资源配额与限制
echo -e "${YELLOW}[检查 8/8] 资源配额审计${NC}"
LIMIT_RANGES=$(kubectl get limitranges --all-namespaces -o json | jq -r '.items[] | select(.spec.limits[]?.type == "Pod") | .metadata.namespace + "/" + .metadata.name')
if [ -n "$LIMIT_RANGES" ]; then
    echo -e "${GREEN}✓ 已配置 LimitRange 的命名空间:${NC}"
    echo "$LIMIT_RANGES" | head -10
else
    echo -e "${YELLOW}⚠ 部分命名空间未配置 LimitRange${NC}"
fi
echo ""

# 版本检查
echo "========================================"
echo "当前集群版本:"
kubectl version --short 2>/dev/null || kubectl version
echo "========================================"
echo ""
echo "升级建议:"
echo "1. 备份 etcd 数据"
echo "2. 升级控制平面(先 master 后 worker)"
echo "3. 验证工作负载运行状态"
echo "4. 迁移 Ingress NGINX → Gateway API"
echo "5. 处理 gitRepo 和 externalIPs 弃用"
echo ""

6.2 分阶段升级计划

第一阶段:测试环境验证(2-4 周)
├── 部署 v1.36 测试集群
├── 运行 pre-upgrade-check.sh
├── 迁移 gitRepo 工作负载
├── 测试 Gateway API 替代方案
└── 验证 DRA 功能(如适用)

第二阶段:预生产验证(1-2 周)
├── 克隆生产数据到预生产环境
├── 执行完整升级流程
├── 性能基准测试
├── 安全审计
└── 灾难恢复演练

第三阶段:生产升级(按可用区滚动)
├── 升级控制平面(逐个 master)
├── 升级节点池(10% → 50% → 100%)
├── 监控告警验证
├── 应用层迁移(Ingress → Gateway)
└── 清理废弃配置

七、总结与展望

Kubernetes v1.36 是一个承上启下的版本:

安全层面

  • gitRepo 移除消除节点级代码执行风险
  • externalIPs 弃用减少中间人攻击面
  • ServiceAccount 外部签名支持零信任架构

性能层面

  • SELinux 挂载优化减少 Pod 启动延迟 95%+
  • DRA 分区提升 GPU 利用率 600%+

架构层面

  • Gateway API 成为入站流量管理标准
  • DRA 让「设备」成为一等公民

对于生产集群运维团队,我的建议是:

  1. 不要急于升级:等待 2-4 周观察社区反馈
  2. 优先处理废弃特性:gitRepo 和 Ingress NGINX 的迁移成本最高
  3. 拥抱 Gateway API:这不是「要不要」的问题,而是「什么时候」的问题
  4. 关注 DRA 进展:如果你的工作负载涉及 GPU,DRA 将改变你的资源管理方式

Kubernetes 正在进化成为一个 AI 原生的容器编排平台。v1.36 是这个转型过程中的关键一步。


延伸阅读

推荐文章

Vue3中的虚拟滚动有哪些改进?
2024-11-18 23:58:18 +0800 CST
html5在客户端存储数据
2024-11-17 05:02:17 +0800 CST
CSS Grid 和 Flexbox 的主要区别
2024-11-18 23:09:50 +0800 CST
Vue3中如何处理异步操作?
2024-11-19 04:06:07 +0800 CST
Golang实现的交互Shell
2024-11-19 04:05:20 +0800 CST
15 个 JavaScript 性能优化技巧
2024-11-19 07:52:10 +0800 CST
Rust 并发执行异步操作
2024-11-19 08:16:42 +0800 CST
Boost.Asio: 一个美轮美奂的C++库
2024-11-18 23:09:42 +0800 CST
程序员茄子在线接单