Kubernetes v1.36 深度实战:User Namespaces 原生落地、Mutating Admission 干掉 Webhook、OCI 卷与 DRA 分片——当云原生终于补齐安全与 AI 基建两块短板
2026 年 4 月 22 日,Kubernetes 社区如约交付了 2026 年的首个正式版本 v1.36,代号取自日语「ハル(Haru)」——意为「春」。这一个版本没有惊雷般的架构革命,却把过去三四年埋下的种子一并催开:71 项增强、18 项毕业至 Stable。对在生产环境维护 K8s 的团队来说,这不是一个可以忽略的小版本,而是值得认真排期的那种发布。 本文从工程视角,把 v1.36 三条主线(安全、AI/ML 基础设施、平台化能力)上最值得落地的特性逐一拆开讲透,并给出可运行的 YAML、CEL 与 Go 实战代码。
一、背景:为什么 v1.36 值得你专门排期升级
先说一个反直觉的判断:Kubernetes 正在从"加新功能"转向"替你兜底"。
过去几个大版本,社区反复在 Alpha 阶段跋涉的老功能终于熬出头。对运维团队而言,这种"收口"型版本比一堆实验性特性更有价值——你可以少装几个第三方运行时、少维护几套自研准入组件、少写几段 init 容器拉模型的脚本。平台本身正在替你承担安全与异构算力的复杂度。
v1.36 的三个关键词:
- 安全默认配置强化——Pod User Namespaces 毕业 GA,容器里跑 root 不再等于宿主机 root;Kubelet API 细粒度授权 GA;外部 KMS/HSM 签发 ServiceAccount Token GA;非规范 IP/CIDR 校验收紧(直指 CVE-2021-29923 类攻击面)。
- AI/ML 工作负载基础设施成熟——OCI VolumeSource GA,把模型权重当卷挂载;DRA(Dynamic Resource Allocation)支持可分片设备与设备污点(Beta),一块 GPU 终于能被多个负载共享。
- 平台化能力内建——Mutating Admission Policies GA,变更逻辑用原生 K8s 对象表达,彻底告别维护 Webhook Server 的痛苦。
下面我们按"核心概念 → 架构分析 → 代码实战 → 性能优化"的顺序,把其中最硬核的几块拆开。
二、核心概念:Pod User Namespaces——原生容器逃逸防护
2.1 问题的本质:容器里的 root 到底是谁的 root
传统容器(不含 User Namespaces)与宿主机共享同一个用户命名空间。这意味着:容器里 UID 0 的进程,在宿主机内核眼里就是 UID 0。一旦攻击者通过内核漏洞(如 Dirty Pipe、Dirty COW 一脉的衍生漏洞)突破了容器边界,它就拿到了宿主机的 root 特权——可以挂载宿主文件系统、读取 /var/lib/kubelet、甚至篡改 kubelet 配置。
这个问题的正统解法过去只有两条路:
- gVisor / Kata Containers:用独立内核或微型 hypervisor 做强隔离,代价是性能损耗与运维复杂度陡增。
- 认栽跑非特权容器:把所有镜像改成非 root 运行(
USER 1000),但大量遗留镜像、调试工具、需要绑定 1024 以下端口的场景根本改不动。
User Namespaces 提供第三条路:在容器内部保留 root 的"体感",但在宿主机映射成一个无特权 UID。
2.2 Linux 内核的 User Namespaces 机制
User Namespaces 是 Linux 内核的命名空间之一。它的核心是 UID/GID 映射:进程在某个 user namespace 内拥有完整特权,但映射回父命名空间时只是一个普通用户。内核通过 /proc/<pid>/uid_map 描述这个映射关系:
0 100000 65536
# 格式:容器内起始UID 宿主机起始UID 映射数量
# 含义:容器内 UID 0~65535 映射到宿主机的 100000~165535
也就是说,容器里"看起来是 root(UID 0)"的进程,在宿主机只是 UID 100000 的一个凡人。它即便拿到宿主机视角,也没有任何特权。
Kubernetes 从 v1.25(2022 年 8 月)把 Pod User Namespaces 引入 Alpha,历经四年打磨 kubelet、运行时(containerd / CRI-O)、内核的协作链路,终于在 v1.36 毕业 GA 并默认启用。这四年,正是社区把"理论可行"做成"生产稳定"的过程。
2.3 启用方式与内核前提
启用只需一行字段:
apiVersion: v1
kind: Pod
metadata:
name: app-with-userns
spec:
# 关键:把 Pod 放进独立的 user namespace
hostUsers: false
containers:
- name: app
image: my-app:latest
# 即便镜像里是 USER 0,容器内仍"以为"自己是 root
command: ["sleep", "infinity"]
内核前提:节点内核需支持 user namespaces(主流发行版 2020 年后的内核均支持),且 /proc/sys/user/max_user_namespaces 大于 0。Kubernetes 通过 kubelet 的 UserNamespacesSupport 特性门控与节点可分配资源协同判断能否调度。
2.4 实战验证:亲眼看见隔离
部署后用 kubectl exec 观察映射:
# 容器内:看到自己"是" root
kubectl exec app-with-userns -- id
# uid=0(root) gid=0(root) groups=0(root)
# 容器内:看 uid_map
kubectl exec app-with-userns -- cat /proc/self/uid_map
# 0 100000 65536
# 在宿主机上,找到该容器的 init 进程,看它真实 UID
# 容器逃逸后即便执行 `kill -9 1` 也只影响自己命名空间内的进程
安全收益:官方文档明确指出,若干被评级为 HIGH / CRITICAL 的漏洞,在 User Namespaces 启用时无法被利用。因为它把"突破容器"后的有效特权清零了——攻击者连挂载宿主 / 的权限都没有。
2.5 注意事项(工程上必须知道)
- 不是所有工作负载都适合:需要访问宿主
/dev、hostPath挂载、或需要真实特权的 DaemonSet(如某些 CNI、存储插件)不能开hostUsers: false,否则会失败。 - 与
runAsNonRoot互补不互斥:User Namespaces 解决"容器逃逸后拿 root",runAsNonRoot解决"镜像本就不该以 root 跑"。两者叠加是纵深防御。 - 宿主 UID 范围冲突:多 Pod 共用
max_user_namespaces配额,极端高密度节点需评估上限。 - 只读根文件系统 + seccomp:User Namespaces 不替代这些基线防护,应组合使用。
三、核心概念:Mutating Admission Policies GA——和 Webhook Server 说再见
3.1 写过 Mutating Webhook 的人都懂的那种痛
给 Pod 注入几个 label、设置默认资源限制、统一加 securityContext,本是平台团队的日常。但过去要做到这一点,你得:
- 起一个 HTTPS Server,配置 TLS 证书(还要管证书轮换);
- 向 API Server 注册
MutatingWebhookConfiguration,配caBundle; - 祈祷 Webhook 永远在线且低延迟——因为 Webhook 是同步阻塞 API 请求链路的,Webhook 挂了,整个集群的创建请求都可能被卡住(除非你显式配
failurePolicy: Ignore,但那又意味着安全策略可能形同虚设); - 承受每次 API 请求一次网络往返的延迟。
为了注入几个 label 维护一整套服务,实在不划算。
3.2 从 Validating 到 Mutating:CEL 原生策略的统一
Kubernetes 在 v1.30 把 ValidatingAdmissionPolicy(基于 CEL 表达式的校验策略)推到 Beta,v1.36 则把 MutatingAdmissionPolicy 推至 GA 并默认开启。变更逻辑现在可以用原生 K8s 对象表达,不再需要任何外部 Webhook 服务,可以纳入 GitOps 管理。
架构差异:
| 维度 | Mutating Webhook | MutatingAdmissionPolicy |
|---|---|---|
| 执行位置 | 远程 HTTP Server(网络往返) | API Server 进程内(CEL 求值) |
| 失败模式 | 可能阻塞整个 API 链路 | 无外部依赖,无单点 |
| 运维对象 | Server + 证书 + Deployment | 纯 K8s 资源(YAML) |
| GitOps | 需同时管代码与服务 | 声明即策略,一个 apply 搞定 |
| 延迟 | 毫秒级网络 + 序列化 | 微秒级内存求值 |
3.3 CEL 速成:策略即表达式
CEL(Common Expression Language)是一种无状态、强类型、沙箱化的表达式语言。在策略里,你能访问:
object:被准入的资源(如 Pod)request:准入请求(userInfo、operation)namespaceObject/namespaceLabels:命名空间上下文variables:由validations预先计算的可复用值
3.4 实战一:给 Pod 自动注入安全基线(替代 Webhook 最常做的事)
场景:强制所有 Pod 默认加上 runAsNonRoot: true 与 allowPrivilegeEscalation: false,除非显式声明豁免。
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicy
metadata:
name: default-security-context
spec:
# 匹配 Pod 的 CREATE/UPDATE
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
# 用 CEL 声明"如何改"
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
# 注意:CEL 里用 ?: 处理字段可能为空的情况
expression: >
Object{
spec: Object.spec.withDefault(
Object.spec,
Object.spec.withField(
'securityContext',
Object.spec.securityContext.withDefault(
Object.spec.securityContext,
Object.spec.securityContext.withField('runAsNonRoot', true)
)
)
)
}
---
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicyBinding
metadata:
name: default-security-context-binding
spec:
policyName: default-security-context
# 绑定到所有命名空间(可加 namespaceSelector 缩小范围)
validationActions: [Warn, Audit] # 先 Warn/Audit 观察,再改 Deny/Apply
工程建议:先用
validationActions: [Warn, Audit]灰度观察一段时间(Audit 写入 API Server 日志,Warn 返回给客户端警告但不阻断),确认没有误伤业务 Pod 后,再切到Apply真正注入。
3.5 实战二:按命名空间自动打标签 + 注入 ResourceQuota 默认限制
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicy
metadata:
name: inject-team-label
spec:
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["pods"]
variables:
- name: team
expression: "namespaceLabels['team']" # 从命名空间标签取团队名
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
expression: >
Object{
metadata: Object.metadata.withField(
'labels',
Object.metadata.labels.withField('team', variables.team)
)
}
这几行 YAML 替代了过去几十行 Go + 几百行部署清单的 Webhook 服务。
3.6 性能视角:为什么它快
Webhook 模式下,每次 Pod 创建都要:API Server → 序列化 AdmissionReview → 网络 → 反序列化 → 业务代码 → 序列化响应 → 网络 → 反序列化。即便本地回环也有 syscall 与序列化开销,且 Webhook 成为关键路径上的外部依赖。
MutatingAdmissionPolicy 在 API Server 进程内直接用 CEL 解释器求值,没有网络往返、没有 TLS 握手、没有独立进程。在大规模集群(每秒上百个 Pod 创建)下,这对 API Server 的 P99 延迟是实打实的改善,也消除了"Webhook 抖动拖垮整个控制面"的隐患。
四、架构分析:OCI VolumeSource GA——让镜像仓库成为"存储"
4.1 AI/ML 场景的老问题
把模型权重(动辄几个 GB)、大型配置文件、离线二进制工具送进 Pod,过去只有几条路:
- 把主镜像撑大:模型打进业务镜像,镜像体积爆炸,更新模型要重新构建整个镜像,构建慢、分发慢;
- init 容器去拉:自己写拉取逻辑、管缓存、管失败重试,还要处理对象存储的鉴权;
- ConfigMap 硬塞:1 MB 上限(etcd 压力),根本装不下模型。
OCI VolumeSource 的思路很优雅:把任意 OCI artifact(不限于容器镜像,可以是模型、数据集、tar 包)当成卷来引用。Kubernetes 会像拉取容器镜像一样把它从镜像仓库拉下来、挂载进 Pod。打包、分发、缓存、版本管理——全部复用现有镜像仓库(如 Harbor、GHCR、ECR)基础设施,与业务镜像完全解耦。
4.2 它是怎么工作的
底层依赖 OCI Artifacts 规范(通过 oras 等工具推送任意内容到 OCI 仓库)与 kubelet 的镜像拉取能力。Pod 里声明一个 image 类型的卷即可:
apiVersion: v1
kind: Pod
metadata:
name: llm-inference
spec:
hostUsers: false # 配合 User Namespaces 一起用
containers:
- name: vllm
image: vllm/vllm-openai:latest
args: ["--model", "/models/Qwen3-32B", "--tensor-parallel-size", "2"]
volumeMounts:
- name: model-weights
mountPath: /models
readOnly: true
resources:
limits:
nvidia.com/gpu: 2
volumes:
- name: model-weights
image:
# 指向一个 OCI artifact,而非容器镜像
reference: registry.example.com/models/qwen3-32b:2026.07
# 可选:拉取策略、只读
pullPolicy: IfNotPresent
镜像仓库里这个 qwen3-32b:2026.07 是一个用 oras push 上传的模型目录:
# 把本地模型目录打包成 OCI artifact 推送到仓库(一次性)
oras push registry.example.com/models/qwen3-32b:2026.07 \
./Qwen3-32B/:application/vnd.qwen.model.v1.tar
工程价值:模型版本与代码版本解耦、仓库自动缓存加速、鉴权复用已有的 imagePullSecret、回滚只需换 tag。这是 AI 平台团队梦寐以求的"模型分发范式"。
五、代码实战:DRA 进阶——一块 GPU 服务多个负载
5.1 为什么传统 Device Plugin 不够用
传统 GPU 调度靠 Device Plugin 把 nvidia.com/gpu: 1 注册成整数扩展资源。它有两个硬伤:
- 只能整卡分配:半张卡的需求无法满足,独占造成严重利用率浪费;
- 无拓扑/无共享语义:无法表达"这张卡只给推理用、那张卡留给训练""这张卡已损坏请别调度"。
DRA(Dynamic Resource Allocation)用 ResourceClaim + 设备驱动 + 结构化参数 替代整数计数,让 Kubernetes 能描述"一块可切分的加速器"。v1.36 再推两项关键增强(均 Beta、部分默认开启):
- KEP-4815 可分片设备(Sliceable Devices):把单个硬件加速器切分成多个逻辑单元,多负载共享;
- KEP-5055 设备污点与容忍(Device Taints & Tolerations):把某些设备标记为不可用或仅允许特定负载,语义类似 Node Taints。
5.2 实战:声明一个可共享的 GPU 设备类
# 1) 定义 DeviceClass:带污点,仅容忍 "gpu-tier=shared" 的负载可用
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
name: shared-a100
spec:
# 由对应的 DRA 驱动(如 nvidia-dra-driver)实现分配逻辑
selectors:
- cel:
expression: device.driver == "nvidia.com/gpu"
# KEP-5055:给设备打污点
taints:
- key: gpu-tier
value: shared
effect: NoSchedule # 只有容忍该污点的 Pod 才能用
---
# 2) 提交 ResourceClaim:请求"可切分"的 0.5 卡算力
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
metadata:
name: half-a100
spec:
deviceClassName: shared-a100
# 部分 DRA 驱动支持 sliced 请求(取决于驱动实现 KEP-4815)
allocationMode: Immediate
---
# 3) Pod 引用该 Claim,并容忍污点
apiVersion: v1
kind: Pod
metadata:
name: inference-a
spec:
tolerations:
- key: gpu-tier
operator: Equal
value: shared
effect: NoSchedule
containers:
- name: serve
image: vllm/vllm-openai:latest
resources:
claims:
- name: gpu
resourceClaimName: half-a100
resourceClaims:
- name: gpu
resourceClaimName: half-a100
另一路推理负载 inference-b 可以引用同一个 DeviceClass、申请另一半切片,从而一块 A100 同时服务两个中等模型。对成本敏感的多租户推理平台,这是把 GPU 利用率从 30% 拉到 70%+ 的关键。
5.3 用 Go 读 Pod 级 DRA 指标(Kubelet PodResources API for DRA,GA)
v1.36 让 Kubelet 的 PodResources API 暴露 Pod 级别的 DRA 资源(精确到 GPU/加速器粒度)。运维可以写一个小工具做计费与故障定位:
// 基于 kubelet PodResources gRPC API 读取 DRA 分配(示意)
package main
import (
"context"
"log"
podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
conn, err := grpc.Dial("unix:///var/lib/kubelet/pod-resources/kubelet.sock",
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := podresourcesapi.NewPodResourcesListerClient(conn)
resp, err := client.List(context.Background(), &podresourcesapi.ListPodResourcesRequest{})
if err != nil {
log.Fatal(err)
}
for _, pod := range resp.GetPodResources() {
for _, c := range pod.GetContainers() {
for _, dev := range c.GetDevices() {
if len(dev.GetResourceClaims()) > 0 {
log.Printf("Pod=%s Container=%s DRA=%v\n",
pod.GetName(), c.GetName(), dev.GetResourceClaims())
}
}
}
}
}
这段代码跑在节点上,能精确告诉你每个 Pod 实际吃到了哪块 GPU 的哪个切片——这是做 GPU 计费与抢占式回收的数据基础。
六、性能优化与可观测性:那些"体感"提升
6.1 SELinux 递归重打标签加速(KEP-1710,GA)
从 v1.27(2023)就进 Beta,v1.36 终于稳定。过去挂载带 SELinux 标签的卷时,kubelet 要对卷内每个文件递归重打标签,大卷挂载慢得让人怀疑人生。稳定后,挂卷容器启动速度有可感知提升。对强制 SELinux 的金融/政企环境,这是升级的直接红利。
6.2 Pod 级原地伸缩(In-Place Pod Level Resize,Beta)
传统的 kubectl scale 改的是副本数;想改单个 Pod 的 CPU/内存,过去只能重建 Pod。v1.36 把Pod 级别(而非仅容器级别)的 CPU/内存在线伸缩推进到 Beta(面向 cgroup v2)。对 Guaranteed QoS 的 AI/ML、HPC 负载意义重大:训练任务中间发现内存不够,不必杀掉重来,直接在线改限额。
# cgroup v2 环境下,无需重建 Pod 即可调整资源
kubectl resize pod trainer --cpu=4 --memory=16Gi
6.3 混合版本代理(Mixed Version Proxy,Beta 默认开启)
升级大版本时,集群内会短暂并存多个版本的 kube-apiserver。新特性若引用了旧版本 API Server 不认识的资源,客户端会吃到莫名其妙的 404。UnknownVersionInteroperabilityProxy 让 API Server 把这类请求代理给正确的对等 API Server,避免升级窗口期的 404 风暴。开启方式:
kube-apiserver \
--feature-gates=UnknownVersionInteroperabilityProxy=true \
--peer-ca-file=/etc/kubernetes/pki/apiserver-ca.crt \
--proxy-client-cert-file=/etc/kubernetes/pki/proxy-client.crt \
--proxy-client-key-file=/etc/kubernetes/pki/proxy-client.key
6.4 非规范 IP/CIDR 校验收紧(KEP-4858)
010.000.001.005、::ffff:10.0.1.5 这类非规范 IP,以及 192.168.1.5/24(网络号写错)这类模糊 CIDR,核心对象不再接受。背后是 CVE-2021-29923 级别的解释歧义攻击面。升级前务必排查 YAML / Helm Chart / Operator 里的非规范表达,否则 apply 会被拒。
七、升级清单:破坏性变更务必清点
v1.36 有几处"升级前不处理就会炸"的改动:
| 变更 | 影响 | 应对 |
|---|---|---|
| gitRepo 卷插件移除(KEP-5040) | 容器内以 root 执行代码的严重隐患 | 迁移到 init 容器 + emptyDir + git clone,或 OCI Volume |
| kube-proxy IPVS 模式移除(v1.35 已弃用) | IPVS 相关配置失效 | 切到 iptables 或 nftables 模式 |
| Service.externalIPs 弃用警告(CVE-2020-8554) | 计划 v1.43 移除,中间人攻击隐患 | 评估并迁移,长期别用 |
| kubeadm 移除 flex-volumes 内置支持 | 老存储插件失效 | SIG Storage 早已建议迁移 CSI |
| Ingress-NGINX 退役(2026-03-24) | 不再发版/修漏洞 | 评估 Gateway API 作为替代 |
Ingress-NGINX 退役是独立于版本但必须单独提醒的大事件:SIG Network 与 Security Response Committee 已正式停止维护。已有部署仍能跑,但长期运维视角下,迁移到 Gateway API(或 Cilium Ingress / Envoy Gateway)应提上日程。
升级 checklist(实用主义版):
kubectl apply --dry-run=server全量预检,捕捉 IP/CIDR 与 API 弃用报错;- 全局搜索
gitRepo、ipvs、externalIPs,列出受影响对象; - 对 Mutating/Validating Webhook 做"可替代性审计"——能用 CEL Policy 替代的,先迁,降低升级期 Webhook 不兼容风险;
- 升级窗口开启 Mixed Version Proxy,避免 404;
- 升级后优先验证 User Namespaces(
hostUsers: false)在你的 CNI/CSI 下是否兼容。
八、总结与展望:云原生的"成年礼"
如果把 v1.34「Of Wind & Will」看作"意志"的推进、v1.35「Timbernetes(世界树)」看作"根系"的扩展,那么 v1.36「Haru」就是一次"新芽"的收束——不喧嚣,却足够扎实。
它把三条主线上多年的积累集中兑现:
- 安全:User Namespaces、Kubelet API 细粒度授权、外部 Token 签名、IP/CIDR 校验收紧;
- AI/ML 基础设施:OCI Volume、DRA 分片与设备污点、PodResources for DRA;
- 平台化能力:Mutating Admission Policies。
而对还在 Alpha 阶段"来年之春的种子",也值得提前关注:Workload-Aware Scheduling(面向分布式训练的感知调度)、HPA 外部指标获取失败回退(外部指标 API 抖动时合理降级,避免灾难性伸缩)、HPA 仅统计目标控制器管理的 Pod 指标、PVC 增加 UnusedSince 字段(存储生命周期治理)。
对生产环境维护 K8s 的团队,我的建议很明确:v1.36 不是一个可以忽略的小版本。它让你用更少的第三方组件、更低的运维负担,换来更扎实的安全底座与更成熟的 AI 算力调度。先在测试集群验证 User Namespaces 与 CEL 策略的兼容性,把 Webhook 逐个替换为原生策略,用 OCI Volume 重构模型分发链路——再把 DRA 分片用起来压低 GPU 成本。当这些"新芽"在你自己的生产集群里如期绽放,kubectl apply 的每一次提交,都会更平安、更踏实。
春山可望,稳中有为。愿你的集群,亦复如是。🌱
参考资料:Kubernetes 官方 v1.36 发布博客、v1.36 CHANGELOG、Kubernetes 官方文档(User Namespaces / MutatingAdmissionPolicy / OCI Volume / DRA)、KubeSphere v1.36 解读。