Kubernetes 1.36 深度实战:从 DRA 可切分设备到 Agent Sandbox,云原生调度器如何重新定义 AI 时代的硬件分配边界
引言:当 Kubernetes 遇上 AI 工作负载
Kubernetes 在 2026 年走到了一个关键岔口。过去十年,它的核心调度模型几乎没变——CPU、内存、存储,三板斧打天下。但 AI 时代的硬件图谱完全不同了:GPU、FPGA、ASIC、NPU、RDMA 网卡……每一类设备都有自己的分配逻辑和共享语义,传统的 Device Plugin 机制已经力不从心。
2026 年 4 月底,Kubernetes v1.36(代号「晴」,取自葛饰北斋《富嶽三十六景》的日式意象)正式发布。这个版本的最大看点不是某个炫酷的新功能,而是一整套围绕「动态资源分配」的架构升级——DRA 从 Alpha 到 Stable 再到可切分、可污点、可共享容量,调度器从「按整数分配设备」进化到「按容量精细切片」。
与此同时,Agent Sandbox 的出现标志着 Kubernetes 开始正式面对 AI Agent 的运行时安全问题——这不再是「跑个容器」那么简单。
本文将从架构设计、核心原理、代码实战、性能优化四个维度,深度拆解 Kubernetes 1.36 的关键变更,帮你理解这些变化背后的技术逻辑,以及如何在实际生产中落地。
一、Kubernetes 1.36 变更全景:不只是版本号递增
1.1 版本发布与升级节奏
Kubernetes v1.36 于 2026 年 4 月底正式发布,遵循每 4 个月一个大版本的节奏。但这个版本的特殊性在于:它同时包含了多项 GA(正式可用)和 Beta(默认启用)特性,以及若干破坏性变更。
核心变更矩阵:
| 变更类型 | 内容 | 影响程度 | 状态 |
|---|---|---|---|
| 移除 | gitRepo 卷驱动 | 高 | 永久移除 |
| 弃用 | Service.spec.externalIPs | 中 | 计划 v1.43 移除 |
| 退役 | Ingress NGINX | 高 | 不再维护 |
| GA | SELinux 卷标签优化 | 中 | 默认启用 |
| GA | DRA 按优先级排序列表 | 低 | 默认启用 |
| GA | DRA 管理性质访问(Admin Access) | 低 | 默认启用 |
| Beta | DRA 设备污点与容忍度 | 中 | 默认启用 |
| Beta | DRA 可切分设备 | 中 | 默认启用 |
| Beta | DRA 可消耗容量 | 中 | 默认启用 |
| Beta | DRA 扩展资源分配 | 低 | 默认启用 |
| Beta | 设备健康监控 | 中 | 默认启用 |
这个矩阵告诉我们一个关键信息:Kubernetes 正在把 AI 硬件分配从「补丁式支持」推向「一等公民」。 DRA 的多项子特性在 v1.36 集体进入 Beta/GA,这不是巧合,而是社区对 AI 工作负载需求的系统性回应。
1.2 升级前必须做的五件事
在升级到 v1.36 之前,别急着 kubeadm upgrade。先跑一遍这个检查清单:
#!/bin/bash
# k8s-v136-precheck.sh — Kubernetes v1.36 升级前检查脚本
set -euo pipefail
echo "=== Kubernetes v1.36 升级前检查 ==="
echo ""
# [1/5] 检查 gitRepo 卷使用 — v1.36 永久移除
echo "[1/5] 检查 gitRepo 卷使用情况..."
GITREPO_COUNT=$(kubectl get pods --all-namespaces -o json | \
jq '[.items[] | select(.spec.volumes[]?.gitRepo != null)] | length')
if [ "$GITREPO_COUNT" -gt 0 ]; then
echo " ⚠️ 发现 $GITREPO_COUNT 个 Pod 使用 gitRepo 卷,必须先迁移!"
kubectl get pods --all-namespaces -o json | \
jq -r '.items[] | select(.spec.volumes[]?.gitRepo != null) |
"\(.metadata.namespace)/\(.metadata.name)"'
else
echo " ✅ 无 gitRepo 卷使用"
fi
echo ""
# [2/5] 检查 externalIPs 使用 — v1.36 弃用警告
echo "[2/5] 检查 externalIPs 使用情况..."
EXTIP_COUNT=$(kubectl get svc --all-namespaces -o json | \
jq '[.items[] | select(.spec.externalIPs != null and .spec.externalIPs != [])] | length')
if [ "$EXTIP_COUNT" -gt 0 ]; then
echo " ⚠️ 发现 $EXTIP_COUNT 个 Service 使用 externalIPs,v1.43 将移除"
kubectl get svc --all-namespaces -o json | \
jq -r '.items[] | select(.spec.externalIPs != null and .spec.externalIPs != []) |
"\(.metadata.namespace)/\(.metadata.name): \(.spec.externalIPs)"'
else
echo " ✅ 无 externalIPs 使用"
fi
echo ""
# [3/5] 检查 Ingress NGINX — 已退役
echo "[3/5] 检查 Ingress NGINX 部署..."
INGRESS_NS=$(kubectl get namespaces -o json | \
jq -r '.items[] | select(.metadata.name | test("ingress-nginx")) | .metadata.name')
if [ -n "$INGRESS_NS" ]; then
echo " ⚠️ 发现 Ingress NGINX 命名空间: $INGRESS_NS"
echo " ⚠️ Ingress NGINX 已于 2026-03-24 退役,建议迁移到 Gateway API"
kubectl get deployments -n "$INGRESS_NS" -o wide 2>/dev/null || true
else
echo " ✅ 未发现 Ingress NGINX 部署"
fi
echo ""
# [4/5] 检查 SELinux 状态
echo "[4/5] 检查节点 SELinux 状态..."
for node in $(kubectl get nodes -o name); do
SESTATUS=$(kubectl debug "$node" --image=alpine -- cat /etc/selinux/config 2>/dev/null | \
grep "^SELINUX=" | head -1 || echo "unknown")
echo " $node: $SESTATUS"
done
echo ""
# [5/5] 当前版本
echo "[5/5] 当前 Kubernetes 版本..."
kubectl version -o yaml 2>/dev/null | grep -E "gitVersion|minor|major" || \
kubectl version --short 2>/dev/null || true
echo ""
echo "=== 检查完成 ==="
升级顺序建议:
- 先在 staging 环境做完整验证
- 备份 etcd(
etcdctl snapshot save) - 升级控制平面(kube-apiserver → kube-controller-manager → kube-scheduler)
- 逐节点 drain + 升级 kubelet + uncordon
- 执行应用层迁移(Ingress 配置、gitRepo 替换等)
二、DRA 深度解析:从设备插件到动态资源分配的范式跃迁
2.1 为什么 Device Plugin 不够用了?
在 DRA 出现之前,Kubernetes 通过 Device Plugin 机制支持异构硬件。Device Plugin 的工作方式简单粗暴:向 kubelet 注册设备列表,调度器按整数数量分配。
这个模型有一个根本缺陷——它把所有设备都当成不可分割的原子单位。
实际场景中的问题:
# 问题 1: 一个 80GB 的 A100 GPU,我只想用 20GB 跑推理
# Device Plugin: 抱歉,要么用整张卡,要么别用
# 问题 2: 3 个低优先级任务共享一张 GPU,1 个高优先级任务来了
# Device Plugin: 抱歉,不支持抢占,请手动驱逐
# 问题 3: 某张 GPU 的显存出了 ECC 错误,我想标记它
# Device Plugin: 抱歉,没有污点机制,只能手动删除设备资源
DRA 的设计哲学完全不同。它引入了四个核心 API 对象:
- ResourceSlice:设备驱动发布的设备清单(类比 PV)
- DeviceClass:设备分类和选择策略(类比 StorageClass)
- ResourceClaim:对设备的分配请求(类比 PVC)
- ResourceClaimTemplate:为每个 Pod 自动生成 ResourceClaim 的模板
这个设计直接借鉴了存储子系统的成功经验——动态卷制备(Dynamic Provisioning)。
2.2 DRA 工作流全链路拆解
让我用一个完整的 GPU 分配流程来展示 DRA 的工作机制:
第一步:设备驱动注册设备(ResourceSlice)
# 由 NVIDIA DRA 驱动自动创建
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: nvidia-gpu-node-01
spec:
driver: gpu.nvidia.com
nodeName: worker-gpu-01 # 设备挂载在这个节点
pool:
name: nvidia-gpu-pool
generation: 42
resourceSliceCount: 1
devices:
- name: gpu-0
attributes:
nvidia.com/gpu:
string: "A100-SXM4-80GB"
nvidia.com/memory:
string: "80Gi"
nvidia.com/health:
string: "healthy"
nvidia.com/uuid:
string: "GPU-12345678-abcd"
capacity:
memory:
value: "80Gi"
- name: gpu-1
attributes:
nvidia.com/gpu:
string: "A100-SXM4-80GB"
nvidia.com/memory:
string: "80Gi"
nvidia.com/health:
string: "degraded" # 注意:这张卡有降级标记
nvidia.com/uuid:
string: "GPU-87654321-efgh"
capacity:
memory:
value: "80Gi"
第二步:集群管理员定义设备类别(DeviceClass)
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
name: nvidia-a100
spec:
# 使用 CEL 表达式精确筛选设备
selectors:
- cel:
expression: |-
device.driver == "gpu.nvidia.com" &&
device.attributes["gpu.nvidia.com/gpu"] == "A100-SXM4-80GB" &&
device.attributes["gpu.nvidia.com/health"] == "healthy"
# 可选:绑定扩展资源名,兼容旧的 resources.limits 语法
extendedResourceName: nvidia.com/gpu
这里有一个精妙的设计:CEL 表达式过滤。Device Plugin 时代你只能按数量申请(nvidia.com/gpu: 2),现在你可以按任意属性组合过滤——只要驱动程序把属性暴露到 ResourceSlice 里。
第三步:工作负载运维人员创建资源请求
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: gpu-claim-template
spec:
spec:
devices:
requests:
- name: gpu-request
exactly:
deviceClassName: nvidia-a100
count: 1
第四步:在 Pod 中引用
apiVersion: v1
kind: Pod
metadata:
name: llm-inference
spec:
containers:
- name: inference
image: my-llm-server:latest
resources:
claims:
- name: gpu-claim
request: gpu-request
resourceClaims:
- name: gpu-claim
resourceClaimTemplateName: gpu-claim-template
完整的调度流程:
1. Pod 创建 → API Server 接收
2. resourceclaim-controller 根据 ResourceClaimTemplate 生成 ResourceClaim
3. 调度器扫描 ResourceSlice,找到满足 DeviceClass CEL 表达式且未分配的设备
4. 调度器将设备分配信息写入 ResourceClaim.status
5. 调度器将 Pod 绑定到能访问该设备的节点
6. kubelet 通过 DRA 驱动的 gRPC 接口准备设备
7. Pod 启动,容器内可见设备
2.3 按优先级排序列表:让调度器做「备选方案」决策
v1.36 中 GA 的「按优先级排序列表」特性,解决了一个非常实际的问题:首选设备不可用时怎么办?
以前,如果你的 ResourceClaim 要求 A100 但集群里没有空闲的 A100,Pod 就只能 Pending。现在你可以给调度器一个备选方案列表:
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: flexible-gpu-claim
spec:
spec:
devices:
requests:
- name: gpu
firstAvailable: # 关键字段:按优先级排序
# 优先选择 A100 80GB
- name: a100-preferred
deviceClassName: nvidia-a100-80g
selectors:
- cel:
expression: |-
device.attributes["gpu.nvidia.com/memory"] == "80Gi"
# 其次选择 A100 40GB(需要两张补偿性能差距)
- name: a100-40g-fallback
deviceClassName: nvidia-a100-40g
count: 2
# 最后选择任何可用的 NVIDIA GPU
- name: any-nvidia-gpu
deviceClassName: nvidia-gpu-generic
调度器的决策逻辑:
- 依次尝试 firstAvailable 列表中的每个子请求
- 第一个能成功分配的子请求被选中
- 如果多个节点都能满足不同优先级的子请求,优先选择能满足更高优先级的节点
这就像买票时的「优先高铁,其次飞机,最后大巴」——调度器自动帮你做降级决策,而不是让你卡在 Pending 状态。
2.4 可切分设备(Partitionable Devices):GPU 切片的艺术
这是 v1.36 Beta 特性中最让我兴奋的一个。它解决了 AI 推理场景的核心痛点:GPU 利用率低。
在推理场景中,一个模型通常只需要 10-20GB 显存,而 A100 有 80GB。如果不切片,4 个推理服务需要 4 张 A100,但实际显存利用率只有 25%。
DRA 可切分设备的实现基于 CounterSet 机制:
# 第一部分:定义共享计数器(代表物理设备的资源总量)
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: gpu-0-counters
spec:
nodeName: worker-gpu-01
driver: gpu.nvidia.com
pool:
name: nvidia-gpu-pool
generation: 1
resourceSliceCount: 2
# 共享计数器:表示物理 GPU 的资源容量
sharedCounters:
- name: gpu-0-memory
counters:
memory:
value: "80Gi" # A100 80GB 总显存
---
# 第二部分:定义逻辑设备(从计数器中消耗资源)
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: gpu-0-partitions
spec:
nodeName: worker-gpu-01
driver: gpu.nvidia.com
pool:
name: nvidia-gpu-pool
generation: 1
resourceSliceCount: 2
devices:
# 逻辑设备 1:20GB 切片
- name: gpu-0-slice-20g-a
consumesCounters:
- counterSet: gpu-0-memory
counters:
memory:
value: "20Gi"
# 逻辑设备 2:20GB 切片
- name: gpu-0-slice-20g-b
consumesCounters:
- counterSet: gpu-0-memory
counters:
memory:
value: "20Gi"
# 逻辑设备 3:40GB 切片(给更大的模型用)
- name: gpu-0-slice-40g
consumesCounters:
- counterSet: gpu-0-memory
counters:
memory:
value: "40Gi"
关键约束:同一 CounterSet 中所有逻辑设备的消耗量之和不能超过总量。 调度器会自动保证这一点。在上面的例子中:
gpu-0-slice-20g-a+gpu-0-slice-20g-b+gpu-0-slice-40g= 80Gi ✅- 如果再加一个 20Gi 的切片就超过 80Gi ❌
调度器的互斥逻辑:
如果两个逻辑设备消耗的 CounterSet 有重叠,调度器保证同一时刻只有其中一部分能被分配,总量不超过物理容量。这意味着:
- slice-20g-a + slice-20g-b + slice-40g 可以同时分配(20+20+40=80)
- slice-20g-a + slice-40g 可以同时分配(20+40=60 < 80)
- 但不能同时分配所有切片如果总量超限
实际应用:推理服务的 GPU 共享
apiVersion: apps/v1
kind: Deployment
metadata:
name: llm-inference-pool
spec:
replicas: 3 # 3 个推理副本
template:
spec:
containers:
- name: inference
image: vllm/vllm-openai:latest
resources:
claims:
- name: gpu-slice
request: gpu-slice-req
resourceClaims:
- name: gpu-slice
resourceClaimTemplateName: gpu-20g-slice
---
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: gpu-20g-slice
spec:
spec:
devices:
requests:
- name: gpu-slice-req
exactly:
deviceClassName: nvidia-gpu-20g-slice
count: 1
3 个 Pod,每个需要 20GB,总共 60GB,可以在一张 A100 80GB 上运行(剩余 20GB 给系统开销),而不是占 3 张 GPU。
性能考量: 切片是在显存层面的隔离,不是计算层面的硬隔离。如果多个切片的计算负载同时打满,会争抢 CUDA Core。对于推理场景(计算间歇、IO 密集)这不是大问题;对于训练场景(持续满算力),建议整卡分配。
2.5 可消耗容量(Consumable Capacity):更灵活的共享模式
可切分设备是「预定义切片」,你需要提前在 ResourceSlice 里定义好逻辑设备的边界。可消耗容量则更灵活——它允许设备被动态地按需分配,调度器实时跟踪容量消耗。
典型场景:网络带宽分配
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: rdma-nic-worker-01
spec:
nodeName: worker-gpu-01
driver: rdma.example.com
pool:
name: rdma-pool
generation: 1
resourceSliceCount: 1
devices:
- name: rdma-nic-0
allowMultipleAllocations: true # 允许多个 ResourceClaim 同时使用
attributes:
type:
string: "ConnectX-7"
capacity:
bandwidth:
value: "400Gbps" # 总带宽
requestPolicy:
default: "10Gbps"
validRange:
min: "10Gbps"
step: "10Gbps" # 以 10Gbps 为单位分配
# 高优先级训练任务:申请 200Gbps
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: high-bandwidth-claim
spec:
spec:
devices:
requests:
- name: rdma-req
exactly:
deviceClassName: rdma-high-perf
capacity:
requests:
bandwidth: 200Gbps # 从 400Gbps 中消耗 200Gbps
调度器会确保所有 ResourceClaim 消耗的带宽总和不超过 400Gbps。这意味着:
- 训练任务申请 200Gbps → 剩余 200Gbps
- 推理任务申请 100Gbps → 剩余 100Gbps
- 数据同步申请 100Gbps → 剩余 0Gbps
- 再来一个申请 → Pending,直到有资源释放
对比可切分设备 vs 可消耗容量:
| 维度 | 可切分设备 | 可消耗容量 |
|---|---|---|
| 分配粒度 | 预定义的逻辑设备 | 按请求动态消耗 |
| 适合资源 | 显存、计算单元 | 带宽、IOPS 等连续量 |
| 配置方式 | ResourceSlice 预定义切片 | ResourceSlice 定义容量 + Claim 指定消耗量 |
| 灵活性 | 中(需提前规划切片边界) | 高(按需分配) |
| 驱动实现复杂度 | 低 | 中(需实现容量追踪) |
2.6 设备污点与容忍度(Device Taints & Tolerations)
这终于填补了 DRA 生态中最让人头疼的空白——设备故障处理。
在 v1.36 之前,如果一张 GPU 出了 ECC 错误,你只能:
- 手动删除 ResourceSlice 中的设备条目
- 手动驱逐使用该 GPU 的 Pod
- 祈祷没有新的 Pod 被调度到这张坏卡上
v1.36 的设备污点机制让这一切自动化了:
驱动自动设置污点:
# 当 GPU 出现 ECC 错误时,驱动自动更新 ResourceSlice
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: nvidia-gpu-node-01
spec:
devices:
- name: gpu-1
taints:
- key: gpu.nvidia.com/ecc-error
value: "uncorrectable"
effect: NoExecute # 驱逐已调度的 Pod + 阻止新调度
管理员手动设置污点:
apiVersion: resource.k8s.io/v1alpha3
kind: DeviceTaintRule
metadata:
name: maintenance-gpu-driver-upgrade
spec:
deviceSelector:
driver: gpu.nvidia.com # 影响该驱动管理的所有设备
pool: worker-gpu-01 # 限定在某个节点
taint:
key: maintenance.nvidia.com/driver-upgrade
value: "in-progress"
effect: NoSchedule # 阻止新调度,不驱逐已有 Pod
Pod 容忍坏 GPU(用于降级服务):
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: tolerant-gpu-claim
spec:
spec:
devices:
requests:
- name: gpu-request
exactly:
deviceClassName: nvidia-a100
tolerations:
# 容忍 ECC 错误的 GPU(降级运行)
- key: gpu.nvidia.com/ecc-error
operator: Exists
effect: NoExecute
tolerationSeconds: 3600 # 最多容忍 1 小时
# 容忍维护中的 GPU
- key: maintenance.nvidia.com/driver-upgrade
operator: Exists
effect: NoSchedule
污点效果对照表:
| 效果 | 阻止新调度 | 驱逐已有 Pod | 典型场景 |
|---|---|---|---|
| NoSchedule | ✅ | ❌ | 驱动升级、计划维护 |
| NoExecute | ✅ | ✅ | 硬件故障、不可恢复错误 |
| None | ❌ | ❌ | 信息性标记(如性能降级) |
驱逐流程详解:
当设备被标记 NoExecute 污点时:
- 设备污点驱逐控制器(运行在 kube-controller-manager 中)检测到新污点
- 找到所有引用该设备的 ResourceClaim
- 找到所有引用这些 ResourceClaim 的 Pod
- 检查 Pod 的容忍度——不容忍的 Pod 被加入驱逐队列
- 驱逐延迟 =
tolerationSeconds(如果设置了) - 删除 Pod(不是优雅终止,是直接删除,让上层控制器重建)
# 检查驱逐状态
kubectl describe devicetaintrules maintenance-gpu-driver-upgrade
# 等待驱逐完成
kubectl wait --for=condition=EvictionInProgress=false \
DeviceTaintRule/maintenance-gpu-driver-upgrade
2.7 设备健康监控:从被动发现到主动感知
v1.36 另一个 Beta 特性是设备健康监控。以前,GPU 坏了你只能通过应用日志或系统日志发现;现在,DRA 驱动可以通过 DRAResourceHealth gRPC 服务将设备健康状况直接报告给 kubelet,并暴露在 Pod 状态中。
# 查看容器的设备健康状态
kubectl get pod llm-inference -o jsonpath='{.status.containerStatuses[0].allocatedResourcesStatus}'
输出示例:
{
"devices": [
{
"name": "gpu.nvidia.com/gpu-0",
"health": "Healthy"
},
{
"name": "gpu.nvidia.com/gpu-1",
"health": "Unhealthy",
"message": "ECC uncorrectable error detected on HBM bank 3"
}
]
}
与健康监控联动的自动化运维:
# 使用 Kubernetes Event 触发自动运维
apiVersion: events.k8s.io/v1
kind: Event
# 当设备状态变为 Unhealthy 时,kubelet 会产生事件
# 可以通过 Event Router 转发到 Prometheus Alertmanager
# Prometheus 告警规则
groups:
- name: device-health
rules:
- alert: UnhealthyGPU
expr: |
kube_pod_container_allocated_resources_device_health{health="Unhealthy"} > 0
for: 5m
labels:
severity: critical
annotations:
summary: "Pod {{ $labels.pod }} 的 GPU 设备不健康"
description: "设备 {{ $labels.device }} 报告状态: {{ $labels.health }}"
2.8 管理性质访问(Admin Access):调试与故障排查的特权通道
有时候你需要直接访问正在使用的设备来排查问题——比如查看 GPU 的温度、功耗、运行时状态。v1.36 GA 的管理性质访问允许你创建一个特权 ResourceClaim,可以访问已被其他 Pod 使用的设备。
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
metadata:
name: gpu-debug-claim
namespace: admin-tools # 必须在标记了 admin-access 标签的命名空间
spec:
devices:
requests:
- name: debug-gpu
exactly:
deviceClassName: nvidia-a100
allocationMode: All
adminAccess: true # 特权模式:可以访问使用中的设备
安全约束:
- 命名空间必须标记
resource.kubernetes.io/admin-access: "true" - 只有有权限在该命名空间创建 ResourceClaim 的用户才能使用
- 多租户集群中不应向普通用户开放
# 标记命名空间
kubectl label namespace admin-tools resource.kubernetes.io/admin-access=true
三、Agent Sandbox:当 Kubernetes 开始认真对待 AI Agent
3.1 AI Agent 的运行时安全困境
AI Agent 和传统微服务有本质区别。传统服务的行为是确定性的——代码写好了,输入给定了,输出就是可预测的。但 AI Agent 的行为由 LLM 驱动,本质上是非确定性的。一个「帮用户读邮件」的 Agent 可能因为 Prompt 注入而变成「把邮件转发给攻击者」的 Agent。
在 Kubernetes 中,这个问题更加严峻:
- API 访问风险:Agent 需要 Kubernetes API 来管理工作负载,但过度授权可能让它修改集群配置
- 网络逃逸风险:Agent 需要访问外部 API,恶意 Agent 可能扫描内网
- 资源爆炸风险:Agent 可能动态创建子任务,不加限制会耗尽集群资源
- 环境依赖地狱:不同 Agent 需要不同的模型文件、Python 环境、系统库
3.2 Agent Sandbox 架构设计
Agent Sandbox 的核心思想是「最小权限 + 深度防御」:
┌─────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ ┌─────────────────────────────────────────┐ │
│ │ Agent Sandbox Controller │ │
│ │ (CRD + Operator + Webhook) │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ┌────────────┼────────────┐ │
│ ▼ ▼ ▼ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Sandbox A │ │ Sandbox B │ │ Sandbox C │ │
│ │ ┌───────┐ │ │ ┌───────┐ │ │ ┌───────┐ │ │
│ │ │Agent │ │ │ │Agent │ │ │ │Agent │ │ │
│ │ │Code │ │ │ │Code │ │ │ │Code │ │ │
│ │ └───────┘ │ │ └───────┘ │ │ └───────┘ │ │
│ │ ┌───────┐ │ │ ┌───────┐ │ │ ┌───────┐ │ │
│ │ │Security│ │ │ │Security│ │ │ │Security│ │ │
│ │ │Policy │ │ │ │Policy │ │ │ │Policy │ │ │
│ │ └───────┘ │ │ └───────┘ │ │ └───────┘ │ │
│ └───────────┘ └───────────┘ └───────────┘ │
│ │ │ │ │
│ └────────────┼────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ NetworkPolicy + PSP Layer │ │
│ │ - 出站白名单 │ │
│ │ - 禁止特权升级 │ │
│ │ - 资源配额限制 │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
3.3 Agent Sandbox 实战部署
启用 Feature Gate:
# kube-apiserver 配置
apiVersion: v1
kind: Pod
metadata:
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --feature-gates=AgentSandbox=true
# ... 其他参数
创建 Agent Sandbox 配置:
apiVersion: v1
kind: Pod
metadata:
name: email-agent-sandbox
labels:
app: email-agent
sandbox: enabled
spec:
# 安全上下文:最小权限
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: agent
image: my-email-agent:v1.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
requests:
memory: "256Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "1000m"
env:
- name: MODEL_NAME
valueFrom:
configMapKeyRef:
name: agent-config
key: model-name
- name: API_KEY
valueFrom:
secretKeyRef:
name: agent-secrets
key: api-key
volumeMounts:
- name: config
mountPath: /etc/agent/config
readOnly: true
- name: tmp
mountPath: /tmp
volumes:
- name: config
configMap:
name: agent-config
- name: tmp
emptyDir:
sizeLimit: "100Mi"
# 关键:只允许访问特定的 API 端点
automountServiceAccountToken: false
网络策略:限制出站访问
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: agent-sandbox-egress
namespace: default
spec:
podSelector:
matchLabels:
sandbox: enabled
policyTypes:
- Egress
egress:
# 允许 DNS 查询
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53
# 只允许访问邮件 API(白名单模式)
- to:
- ipBlock:
cidr: 203.0.113.0/24 # 邮件 API 的 IP 范围
ports:
- protocol: TCP
port: 443
# 允许访问 LLM API
- to:
- ipBlock:
cidr: 198.51.100.0/24 # LLM API 的 IP 范围
ports:
- protocol: TCP
port: 443
资源配额:防止单个 Agent 耗尽集群资源
apiVersion: v1
kind: ResourceQuota
metadata:
name: agent-sandbox-quota
namespace: agent-sandbox
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
pods: "10"
# 限制 ConfigMap 和 Secret 数量,防止资源泄露
configmaps: "20"
secrets: "20"
3.4 Agent Sandbox 与 DRA 联动:GPU 安全分配
AI Agent 如果需要 GPU 加速,可以将 Agent Sandbox 和 DRA 结合使用:
apiVersion: v1
kind: Pod
metadata:
name: gpu-agent-sandbox
labels:
sandbox: enabled
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: agent
image: my-gpu-agent:v1.0
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
resources:
claims:
- name: gpu-claim
request: gpu-slice
limits:
memory: "1Gi"
cpu: "500m"
volumeMounts:
- name: model-cache
mountPath: /models
readOnly: true
resourceClaims:
- name: gpu-claim
resourceClaimTemplateName: agent-gpu-slice
volumes:
- name: model-cache
persistentVolumeClaim:
claimName: model-pvc
readOnly: true
这样,Agent 既能安全地使用 GPU 切片,又受到 Agent Sandbox 的全面约束。
四、破坏性变更深度拆解
4.1 gitRepo 卷驱动永久移除
gitRepo 卷类型自 v1.11 起弃用,v1.36 终于彻底移除。原因很明确:安全漏洞。
gitRepo 允许 Pod 在初始化时自动克隆 Git 仓库,但它的实现方式是在节点上以 root 身份运行 git clone——这意味着恶意用户可以通过构造特殊的仓库 URL,在节点上执行任意代码。
迁移方案 1: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.0.0
args:
- --repo=https://github.com/example/config-repo
- --branch=main
- --depth=1
- --period=30s # 每 30 秒检查更新
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
volumeMounts:
- name: git-data
mountPath: /tmp/git
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: git-data
mountPath: /etc/config
readOnly: true
迁移方案 2:ConfigMap + CI/CD 自动同步
"""Git 仓库到 ConfigMap 的自动同步工具"""
import subprocess
import tempfile
from pathlib import Path
import yaml
import hashlib
class GitToConfigMapSync:
"""将 Git 仓库内容同步为 Kubernetes ConfigMap"""
def __init__(self, repo_url: str, branch: str = "main"):
self.repo_url = repo_url
self.branch = branch
def clone_repo(self, target_dir: str) -> None:
"""浅克隆 Git 仓库"""
subprocess.run(
["git", "clone", "--depth", "1",
"--branch", self.branch,
self.repo_url, target_dir],
check=True,
capture_output=True
)
def generate_configmap(
self, source_dir: str, name: str,
namespace: str = "default"
) -> dict:
"""从目录生成 ConfigMap 定义"""
configmap = {
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": {
"name": name,
"namespace": namespace,
},
"data": {}
}
for file_path in Path(source_dir).rglob("*"):
if file_path.is_file() and not file_path.name.startswith("."):
relative_path = str(file_path.relative_to(source_dir))
try:
content = file_path.read_text(encoding="utf-8")
configmap["data"][relative_path] = content
except UnicodeDecodeError:
# 二进制文件跳过
continue
return configmap
def compute_hash(self, configmap: dict) -> str:
"""计算 ConfigMap 内容哈希,用于检测变更"""
content = yaml.dump(
configmap["data"],
allow_unicode=True,
default_flow_style=False
)
return hashlib.sha256(content.encode()).hexdigest()[:12]
def sync(self, configmap_name: str, namespace: str = "default") -> dict:
"""执行完整同步流程"""
with tempfile.TemporaryDirectory() as tmpdir:
self.clone_repo(tmpdir)
configmap = self.generate_configmap(
tmpdir, configmap_name, namespace
)
# 添加哈希标注,方便追踪版本
content_hash = self.compute_hash(configmap)
configmap["metadata"]["annotations"] = {
"git-sync/hash": content_hash
}
return configmap
# 使用示例
if __name__ == "__main__":
syncer = GitToConfigMapSync(
repo_url="https://github.com/example/config-repo",
branch="main"
)
configmap = syncer.sync("app-config", "default")
print(yaml.dump(configmap, allow_unicode=True))
4.2 Ingress NGINX 退役:Gateway API 迁移实战
Ingress NGINX 的退役是 v1.36 最具破坏性的变更。2026 年 3 月 24 日,SIG Network 正式宣布停止维护——不再有 bug 修复、安全补丁和功能更新。
迁移路径:Ingress → Gateway API + Envoy Gateway
# 旧配置:Ingress NGINX
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
spec:
tls:
- hosts:
- app.example.com
secretName: tls-secret
rules:
- http:
paths:
- path: /api(/|$)(.*)
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
- path: /(.*)
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
# 新配置:Gateway API
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
namespace: infra
spec:
gatewayClassName: envoy-gateway-class
listeners:
- name: http
protocol: HTTP
port: 80
# HTTP → HTTPS 重定向
allowedRoutes:
namespaces:
from: All
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: tls-secret
namespace: infra
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-api-route
spec:
parentRefs:
- name: production-gateway
namespace: infra
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: api-service
port: 8080
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: web-service
port: 80
Gateway API 相比 Ingress 的优势:
- 角色分离:Gateway(集群管理员)→ Route(应用开发者),权限边界清晰
- 扩展性:不需要通过 annotation 黑魔法来配置高级功能
- 多实现:Envoy Gateway、Contour、Traefik 等多种实现可互换
- 功能丰富:流量分割、重试、超时、请求镜像等原生支持
4.3 Service.externalIPs 弃用
externalIPs 允许将任意 IP 地址路由到 Service,这可能导致中间人攻击(CVE-2020-8554)。v1.36 开始弃用警告,v1.43 完全移除。
替代方案选择矩阵:
| 场景 | 替代方案 | 配置示例 |
|---|---|---|
| 云环境入站流量 | LoadBalancer Service | type: LoadBalancer + 云厂商注解 |
| 金属环境入站流量 | MetalLB | 安装 MetalLB,配置 IPAddressPool |
| 内部服务暴露 | Gateway API | HTTPRoute + Gateway |
| 简单端口暴露 | NodePort | type: NodePort |
五、SELinux 卷标签 GA:Pod 启动加速的底层原理
5.1 问题根源
在启用了 SELinux 的系统上(RHEL/CentOS/Fedora 默认启用),Kubernetes 需要为每个挂载的卷设置正确的 SELinux 标签。在 v1.36 之前,这个过程是递归的——对卷中的每一个文件和目录调用 setfiles 命令。
对于一个包含 100 万个文件的数据卷,这可能需要数分钟。Pod 的启动时间因此被严重拖慢。
5.2 优化原理
v1.36 的 SELinux 卷标签优化(GA)使用 mount -o context=XYZ 选项,在挂载时一次性应用 SELinux 标签。这不需要遍历任何文件,时间复杂度从 O(n) 降为 O(1)。
旧方式(递归重标签):
mount /dev/sdb1 /data
setfiles -R /data ← 遍历所有文件,逐个设置标签
耗时:与文件数量成正比,大卷可能需要数分钟
新方式(挂载时标签):
mount -o context=system_u:object_r:container_file_t:s0 /dev/sdb1 /data
← 挂载时一次性设置,所有文件自动继承
耗时:毫秒级
5.3 配置与注意事项
apiVersion: v1
kind: Pod
metadata:
name: selinux-optimized-pod
spec:
securityContext:
seLinuxOptions:
level: "s0:c123,c456"
seLinuxChangePolicy: MountOption # 显式启用挂载时标签
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: data-pvc
selinuxMount: true # 在 Pod 级别启用 SELinux 挂载优化
⚠️ 关键注意:混合使用风险
如果同一个卷被特权 Pod(不用 SELinux)和非特权 Pod(需要 SELinux 标签)共享,使用 MountOption 可能导致冲突。在这种场景下,必须确保所有使用该卷的 Pod 都使用相同的 SELinux 标签策略,或者回退到递归重标签模式。
六、生产环境 DRA 落地最佳实践
6.1 驱动选择与安装
目前 DRA 生态仍在快速发展,以下是主要硬件厂商的 DRA 驱动支持情况:
| 厂商 | 驱动 | DRA 支持 | 可切分 | 设备污点 |
|---|---|---|---|---|
| NVIDIA | nvidia-k8s-dra-driver | ✅ | ✅ | ✅ |
| AMD | amd-gpu-dra-driver | ✅ | 🔄 开发中 | ❌ |
| Intel | intel-dra-driver | ✅ | ❌ | ❌ |
| RDMA | k8s-rdma-dra-driver | ✅ | ✅(带宽) | ❌ |
6.2 多租户 GPU 集群设计
# 租户 A:推理团队,需要低成本 GPU 切片
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
name: tenant-a-inference-gpu
spec:
selectors:
- cel:
expression: |-
device.driver == "gpu.nvidia.com" &&
device.attributes["gpu.nvidia.com/gpu"] == "A100-SXM4-80GB"
# 通过 ResourceQuota 限制每个租户的设备用量
---
# 租户 B:训练团队,需要整卡独占
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
name: tenant-b-training-gpu
spec:
selectors:
- cel:
expression: |-
device.driver == "gpu.nvidia.com" &&
device.attributes["gpu.nvidia.com/gpu"] == "H100-SXM5-80GB" &&
device.allowMultipleAllocations == false # 只要不可切片的整卡
6.3 监控与可观测性
# Prometheus ServiceMonitor 采集 DRA 指标
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: dra-device-metrics
spec:
selector:
matchLabels:
app: dra-device-exporter
endpoints:
- port: metrics
interval: 30s
path: /metrics
---
# DRA 设备利用率告警
groups:
- name: dra-gpu
rules:
- alert: GPUUtilizationLow
expr: |
dra_device_utilization{device_class="nvidia-a100"} < 0.3
for: 1h
labels:
severity: info
annotations:
summary: "GPU 利用率低于 30%"
description: >
节点 {{ $labels.node }} 上的 GPU {{ $labels.device }}
过去 1 小时利用率仅为 {{ $value | humanizePercentage }},
考虑使用可切分设备共享给其他工作负载。
七、总结与展望
Kubernetes 1.36 不是一个革命性的版本,但它是一个系统性的版本。它做的不是加一个新功能,而是把 AI 时代的硬件管理从一个接一个的补丁,变成了一套完整的架构。
关键收获:
DRA 是 Kubernetes 硬件管理的未来。如果你还在用 Device Plugin 管理 GPU,现在就该开始规划 DRA 迁移。v1.36 的 DRA 已经 Stable,多项子特性进入 Beta,生态驱动正在快速成熟。
可切分设备和可消耗容量是 GPU 利用率的杀手锏。推理场景用切片,网络场景用可消耗容量——两者结合可以让硬件利用率从 20-30% 提升到 70-80%。
设备污点填补了 DRA 最后一块运维短板。以前坏 GPU 只能手动处理,现在可以自动化驱逐和隔离。
Agent Sandbox 是 Kubernetes 对 AI Agent 安全问题的正式回应。虽然还在早期阶段,但它代表了正确的方向——非确定性的代码需要确定性的安全边界。
Ingress NGINX 退役是不可避免的阵痛。Gateway API 更现代、更灵活,但迁移需要时间和精力。建议现在就开始在非生产环境测试 Gateway API。
展望 v1.37+:
- DRA 抢占(Preemption):允许高优先级工作loads 驱逐低优先级工作loads 占用的设备
- Agent Sandbox GA:更完善的 CRD 和 Operator 支持
- 设备拓扑感知调度:NUMA + GPU 拓扑的联合优化
- DRA 驱动标准化:类似 CSI 的标准化测试套件
Kubernetes 正在从一个「容器编排器」进化为一个「AI 基础设施操作系统」。v1.36 是这个进化过程中的重要一步——它可能不让你立刻惊叹,但当你真正在集群上跑 AI 工作负载时,你会感受到这些变化的份量。
参考资料:
- Kubernetes v1.36 Release Notes: https://kubernetes.io/blog/2026/04/30/kubernetes-v1.36-release/
- DRA 官方文档: https://kubernetes.io/docs/concepts/scheduling-eviction/dynamic-resource-allocation/
- KEP-3063: Dynamic Resource Allocation: https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/3063-dynamic-resource-allocation
- KEP-4381: DRA Partitionable Devices: https://github.com/kubernetes/enhancements/issues/4381
- KEP-4815: DRA Device Taints: https://github.com/kubernetes/enhancements/issues/4815
- Gateway API 文档: https://gateway-api.sigs.k8s.io/