Kubernetes v1.36 Haru 深度解析:从"灵活框架"到"企业级平台"的安全与AI双重跨越
前言
2026年4月22日,Kubernetes v1.36 正式发布,代号称 Haru(日语"春"的音读)。这是 2026 年 Kubernetes 的首个重要版本——包含 70 项增强功能:18 项进入 Stable 阶段,25 项进入 Beta 阶段,25 项新的 Alpha 功能。
但这一次发布远不止版本号往前挪了 0.1 这么简单。
如果说过去几年的 Kubernetes 更新是在不断丰富功能图谱,那么 v1.36 的发布释放了一个明确的信号:Kubernetes 正在从一个"灵活的容器编排框架"转变为"拥有更标准化、更有强制性默认安全与资源规范的企業级平台"。
这个转变体现在两个核心维度上:
- 安全默认配置的系统性加固:用户命名空间正式 GA、可变准入策略原生化、细粒度 Kubelet 授权……这些变化让"安全"不再是靠运维人员手动配置的软约束,而是内嵌到平台默认行为中的硬保障。
- AI/ML 工作负载的原生级支持:DRA 可分区设备、工作负载感知抢占、Gang 调度 API……Kubernetes 对 GPU 集群的调度能力从"能用"跃升到了"好用"。
106 家公司和 491 位个人贡献者参与了这次发布。这篇文章,我们从架构原理出发,逐层拆解 v1.36 的核心技术突破,并结合代码示例探讨实际落地方案。
一、背景:为什么 v1.36 值得关注
1.1 云原生基础设施的成熟拐点
Kubernetes 从 2015 年开源至今,已经走过了十一个年头。在这段时间里,它完成了从 Google 内部 Borg 系统到云原生操作系统的事实标准这一身份转变。但随着 Kubernetes 在企业生产环境中的深度渗透,两个核心矛盾日益突出:
矛盾一:灵活性的代价是安全碎片化
Kubernetes 的设计哲学强调"配置即代码"和"声明式 API",这给了运维团队极大的灵活性。但灵活性的另一面是:默认配置往往不够安全。以容器运行用户为例,Docker 默认以 root 身份在容器内运行进程,而 Kubernetes 默认 Pod 的 securityContext 也并未强制非 root 执行。在高安全要求场景下,安全配置完全依赖运维人员的手工加固——一旦有人配置疏漏,整个集群的安全性就会打折扣。
矛盾二:通用编排能力与 AI 工作负载特殊需求的错配
GPU 资源的调度在 Kubernetes 社区长期是一个痛点。传统的 GPU 分配模型是将整张显卡作为一个整数资源(如 nvidia.com/gpu: 1)直接分配给 Pod。但真实的 AI 训练场景要复杂得多:
- 大型模型的分布式训练需要多卡协同,任何一张卡失败都意味着整个 job 需要重新调度
- GPU 分区技术允许将一张物理卡划分成多个虚拟分区,供不同任务共享
- 当某个 GPU 发生硬件故障时,需要快速将该卡上的任务迁移到其他节点
这些需求在传统整数 GPU 模型下几乎无法优雅地实现。Kubernetes 调度器在处理分布式训练任务时,会出现"七个进程都在运行,却因为一个进程无法调度而整体卡死"的问题。
v1.36 正是在这两个矛盾上给出了系统性答案。
1.2 v1.36 变更总览
在深入技术细节之前,我们先建立全局视图:
| 维度 | 核心变化 |
|---|---|
| 安全 | User Namespaces GA、Mutating Admission Policies GA、细粒度 Kubelet 授权 GA |
| AI/ML | Workload-Aware Preemption Alpha、Gang 调度 API Beta、DRA 增强(可分区设备/可消耗容量/设备污点) |
| 资源管理 | Pod 级资源原地垂直扩缩 Beta、Memory QoS Beta(cgroup v2) |
| API 扩展性 | 分片列表与分片监听流 Alpha、ResizeDeferred 事件 |
| 稳定性提升 | SELinux 卷标签 GA、Volume Group Snapshots GA、DRA 管理员访问 GA |
| 废弃移除 | gitRepo 卷插件移除、Kube-proxy IPVS 模式移除、FlexVolume 支持移除 |
二、安全加固:从"可选加固"到"默认安全"
2.1 用户命名空间(User Namespaces)GA——容器隔离的范式升级
这是 v1.36 最重磅的安全特性,也是 Kubernetes 历史上容器隔离能力的一次重大升级。
传统容器的 root 问题
在传统容器模型中,容器内的 root 用户(UID 0)实际上就是宿主机的 root 用户(UID 0)。虽然 namespace 隔离了进程视图,OverlayFS 等文件系统隔离了文件访问,但当容器以 root 运行时,一旦攻击者利用容器逃逸漏洞获得了主机 root 权限,后果将是灾难性的。
真实攻击场景:2020 年的 runc 符号链接攻击(CVE-2019-5736)允许容器内的恶意进程覆盖宿主机上的 runc 二进制文件。由于容器默认以 root 运行,攻击者可以在容器内直接覆写 /proc/self/exe,从而在宿主机上获得 root shell。
用户命名空间的工作原理
用户命名空间(User Namespaces)的核心思想是将容器内的 UID/GID 映射到宿主机上的非特权 UID/GID。
具体来说,当 Pod 启用了用户命名空间后:
容器内 UID 0 (root) → 宿主机 UID 100000 (非特权用户)
容器内 UID 1000 → 宿主机 UID 100001
容器内 GID 0 (root) → 宿主机 GID 100000
容器内 GID 27 (sudo) → 宿主机 GID 100000
这意味着即使容器内的进程成功逃逸并获取了容器内 root 身份,它在宿主机上也只是一个普通非特权用户,无法修改系统文件、加载内核模块或访问其他 Pod 的资源。
代码示例:启用用户命名空间
Pod 级别配置(v1.27+ Beta)
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
supplementalGroups: [1000]
# 启用用户命名空间(v1.36 GA)
hostUsers: false
containers:
- name: app
image: nginx:1.27
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
Deployment 级别配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-app
spec:
template:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
# 关键:禁用 hostUsers,启用用户命名空间
hostUsers: false
containers:
- name: main
image: myapp:latest
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
工作原理图示
┌─────────────────────────────────────────────────────┐
│ Node (宿主机) │
│ UID 0 (root) - 系统管理员 │
│ UID 100000-100099 - Pod A 的容器进程映射区间 │
│ UID 100100-100199 - Pod B 的容器进程映射区间 │
│ UID 100200-100299 - Pod C 的容器进程映射区间 │
└─────────────────────────────────────────────────────┘
↑ remap (映射)
┌─────────────────────────────────────────────────────┐
│ Pod A (容器) │
│ UID 0 (容器内 root) → Node UID 100000 │
│ UID 1000 (应用用户) → Node UID 100000+1000 │
│ 容器内 root = 宿主机普通用户,无法提权! │
└─────────────────────────────────────────────────────┘
验证用户命名空间是否生效
# 查看容器进程在宿主机上的真实 UID
kubectl exec -it secure-pod -- cat /proc/self/uid_map
# 输出示例(Pod 内)
# 0 100000 65536
# 表示容器内 UID 0 映射到宿主机 UID 100000,范围 65536 个 UID
# 验证容器内 root 无法访问系统文件
kubectl exec -it secure-pod -- sh -c "whoami && id"
# 输出: nobody 或 non-root
# 验证无法加载内核模块(即使容器内看起来是 root)
kubectl exec -it secure-pod -- sh -c "modprobe nonexistent_module"
# 输出: Operation not permitted
架构影响:为什么这是 Kubernetes 安全的分水岭
用户命名空间的 GA 意味着它不再只是实验性功能,而是可以在生产环境直接使用。安全团队不需要在安全加固上投入大量自定义配置,平台团队可以在 PodSecurityPolicy(已废弃)替代方案中直接将 hostUsers: false 作为默认行为。
生产环境迁移建议:
# 1. 检查集群中哪些 Pod 未使用 hostUsers: false
kubectl get pods -A -o json | jq '.items[] | select(.spec.securityContext.hostUsers != false) | .metadata.name' | wc -l
# 2. 查看支持用户命名空间的容器运行时版本
# containerd >= 1.7, cri-dockerd 需要特别注意版本兼容性
# 3. 检查节点是否支持用户命名空间
kubectl get nodes -o json | jq '.items[].status.conditions[] | select(.type=="UserNamespacesReady")'
2.2 可变准入策略(Mutating Admission Policies)GA——Webhook 的原生替代
传统 Webhook 的运维之痛
在 Kubernetes 中,准入控制器(Admission Controller)是 API Server 在资源持久化之前拦截请求的钩子。Mutating Webhook 是其中一类,可以修改请求内容——这是实现默认标签注入、资源配额修正、Pod 自动注入 sidecar 等功能的常用方式。
但传统 Mutating Webhook 存在几个根本性缺陷:
运维复杂度高:
- 需要单独维护一个 Webhook 服务:Deployment + Service + 证书 + RBAC
- Webhook 服务本身就是攻击面——如果 Webhook 服务被攻陷,攻击者可以修改任意 Pod 配置
- 多集群环境下,每个集群都要单独部署和同步 Webhook 配置
延迟与可用性问题:
// 传统 Mutating Webhook 的请求流程
Client → API Server → Webhook Service (HTTP 调用) → 等待响应 → 继续处理
↑
10-50ms 额外延迟
单点故障风险
行为不确定性:
- Webhook 的执行顺序不保证(多个 Webhook 可能相互覆盖)
- 调试困难——没有标准化的审计和回放机制
Mutating Admission Policies 的原生解决方案
Mutating Admission Policies 使用 Kubernetes 原生的 CEL(Common Expression Language) 表达式来定义变更逻辑,无需额外部署 Webhook 服务:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
name: inject-sidecar-policy
spec:
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
operations: ["CREATE", "UPDATE"]
matchConditions: # 可选:精细化匹配条件
- name: "exclude-system-namespaces"
expression: "object.metadata.namespace not in ['kube-system', 'kube-public']"
mutatingActions:
- name: "inject-envoy-sidecar"
action: Mutation
# 使用 CEL 表达式定义变更逻辑
patchActions:
- type: JSONPatch
operation: add
path: /spec/containers/-1
value:
name: envoy-sidecar
image: envoyproxy/envoy:v1.28
ports:
- containerPort: 9901
protocol: TCP
resources:
limits:
memory: "128Mi"
cpu: "100m"
requests:
memory: "64Mi"
cpu: "50m"
env:
- name: ENVOY_CLUSTER
value: "cluster-svc"
优势对比:
| 维度 | Mutating Webhook | Mutating Admission Policies |
|---|---|---|
| 部署复杂度 | 需要独立服务 + 证书 + RBAC | Kubernetes 原生对象,kubectl apply 即部署 |
| 延迟 | HTTP 调用,额外 10-50ms | API Server 进程内执行,无额外延迟 |
| 可用性 | Webhook 服务需独立高可用 | 无外部依赖,随 API Server HA 自动保证 |
| 审计 | 需自行实现审计日志 | Kubernetes 原生审计日志支持 |
| 调试 | Webhook 日志分散 | kubectl 可以直接查看策略对象 |
2.3 细粒度 Kubelet API 授权 GA
在 v1.32 以 Alpha 引入后,细粒度 Kubelet API 授权在 v1.36 正式 GA。
传统问题:监控和可观测性工具(如 Prometheus、cAdvisor)需要从 Kubelet API 获取节点和 Pod 指标。传统做法是授予这些工具 system:node-proxy 权限——这个权限实际上允许访问节点上的任意路径,等同于给予了集群管理员级别的访问权限。这对于只读监控工具来说,权限过于宽泛。
v1.36 的解决方案:现在可以为不同的 Kubelet API 端点配置不同的权限级别:
# kubelet-config.yaml (kubelet 配置)
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authorization:
mode: Webhook
webhook:
# 细粒度鉴权:可以区分 /metrics、/logs、/exec 等端点
# 不同的调用者(service account)可以授权访问不同端点
cacheAuthorizedTTL: 5m0s
cacheUnauthorizedTTL: 30s
# RBAC 绑定示例:只允许 Prometheus 访问 /metrics 端点
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: prometheus-kubelet-metrics-reader
rules:
- apiGroups: [""]
resources: ["nodes/metrics", "nodes/proxy"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: prometheus-to-kubelet-metrics
subjects:
- kind: ServiceAccount
name: prometheus
namespace: monitoring
roleRef:
kind: ClusterRole
name: prometheus-kubelet-metrics-reader
apiGroup: rbac.authorization.k8s.io
三、AI 工作负载:从"能用"到"好用"的跨越
3.1 工作负载感知抢占(Workload-Aware Preemption)——分布式训练的死锁问题终结者
传统抢占机制的困境
在 Kubernetes 中,抢占(Preemption)是指当高优先级 Pod 无法被调度时,调度器驱逐(删除)低优先级 Pod 来为高优先级 Pod 腾出资源的机制。这个机制在大多数场景下工作良好,但面对分布式 AI 训练任务时,会出现严重的"部分抢占故障模式":
问题场景:假设有一个分布式训练任务,包含 8 个 Pod(每个 Pod 占用 1 张 A100 GPU),部署在 8 个节点的集群上。当前 7 个 Pod 已经被调度并正在运行,第 8 个 Pod 因为节点资源不足无法调度。
节点1: [Pod-A GPU-1] ✓ 运行中
节点2: [Pod-B GPU-1] ✓ 运行中
节点3: [Pod-C GPU-1] ✓ 运行中
节点4: [Pod-D GPU-1] ✓ 运行中
节点5: [Pod-E GPU-1] ✓ 运行中
节点6: [Pod-F GPU-1] ✓ 运行中
节点7: [Pod-G GPU-1] ✓ 运行中
节点8: [Pod-H GPU-1] ✗ Pending(等待调度)
低优先级 Pod:
- 其他团队的推理服务 (Pod-1 ~ Pod-20) 占用 GPU
传统调度器的行为是:逐一尝试驱逐低优先级 Pod 来满足 Pod-H 的调度条件。每次驱逐一个 Pod,调度器会重新评估 Pod-H 是否能被调度。这个过程会持续,直到找到足够的资源或者所有低优先级 Pod 都被驱逐。
问题所在:对于分布式训练来说,只有当所有 8 个 Pod 都同时运行时,训练才能正常进行。调度器将 Pod 视为独立个体进行调度/抢占决策,导致的结果是:
- Pod-A 到 Pod-G 都在运行,Pod-H 也在运行,但 Pod-H 是最后才被调度的
- 当集群资源紧张时,Pod-H 可能被频繁驱逐,而 Pod-A 到 Pod-G 仍然占用着资源
- 这导致分布式训练"7/8 的进程在空转等待第 8 个进程"——系统陷入了部分调度的死锁状态
PodGroup:工作负载的原子调度单元
工作负载感知抢占引入了一个关键抽象:PodGroup。PodGroup 将一组需要协同工作的 Pod 视为一个原子调度单元:
# 定义一个 PodGroup(属于 Job 或其他工作负载)
apiVersion: scheduling.sigs.k8s.io/v1alpha1
kind: PodGroup
metadata:
name: distributed-training-job-001
namespace: ml
spec:
# 最小需要同时调度的 Pod 数量
minMember: 8
# 超时时间:如果在指定时间内无法满足 minMember,则整个 Job 失败
minAvailableDuration: 30m
priority: 10000
# Job 引用 PodGroup
apiVersion: batch/v1
kind: Job
metadata:
name: distributed-training
namespace: ml
spec:
parallelism: 8
completions: 8
backoffLimit: 3
podFailurePolicy:
rules:
- action: FailJob
onExitCodes:
operator: In
values: [137, 143] # OOMKilled / SIGTERM
template:
metadata:
labels:
pod-group.scheduling.sigs.k8s.io: distributed-training-job-001
spec:
# 8 个 replica 均引用同一个 PodGroup
schedulerName: default-scheduler
priorityClassName: high-priority-training
containers:
- name: trainer
image: pytorch/pytorch:2.4.0
args:
- python
- /workspace/train.py
- --world-size=8
- --rank=$(POD_INDEX)
env:
- name: POD_INDEX
valueFrom:
fieldRef:
fieldPath: metadata.annotations['pod-group.scheduling.sigs.k8s.io/index']
resources:
limits:
nvidia.com/gpu: "1"
memory: "64Gi"
requests:
nvidia.com/gpu: "1"
memory: "64Gi"
# 当 Job 失败时自动清理所有 Pod
restartPolicy: OnFailure
priorityClassName: high-priority-training
调度器的原子决策逻辑:
# 伪代码:工作负载感知抢占的调度决策
def can_preempt_for_workload(pod_group, target_node):
# 1. 检查 PodGroup 需要的最小成员数
min_required = pod_group.minMember
# 2. 找到 PodGroup 中已经调度的所有 Pod
scheduled_pods = get_scheduled_pods_in_group(pod_group)
# 3. 检查这个节点上的资源是否对调度有实质性帮助
additional_pods_can_schedule = count_pods_fit_in_node(target_node, pod_group)
# 4. 关键判断:如果调度后能凑够 minMember,才执行抢占
total_after_preemption = len(scheduled_pods) + additional_pods_can_schedule
if total_after_preemption >= min_required:
# 抢占有意义,执行
preempt_lower_priority_pods(target_node)
return True
else:
# 抢占没有意义(仍然凑不够 minMember),跳过这个节点
return False
效果对比:
传统调度器:
调度 Pod-H → 驱逐 Pod-1 → 重新评估 → 驱逐 Pod-2 → 重新评估 → ...
结果:7/8 Pod 运行,1 Pod Pending,训练无法推进
工作负载感知抢占:
调度 PodGroup → 检查:当前 7 个 + 节点 X 上可以调度几个? →
如果 ≥ 8 → 批量驱逐满足条件的节点上的 Pod → 一次操作完成全部调度
如果 < 8 → 跳过,不破坏现有的 7/8 运行状态
Gang 调度 API:从 Alpha 到 Beta
Gang 调度 API 在 v1.35 以 Alpha 引入,v1.36 正式进入 Beta 阶段。Gang 调度的核心语义是"All or Nothing"——要么所有 Pod 都被调度,要么一个都不调度。这与 PodGroup 的语义高度一致。
# v1.36 Gang 调度配置(Beta)
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: gang-scheduled-job
value: 10000
preemptionPolicy: PreemptLowerPriority
globalDefault: false
description: "分布式训练任务的高优先级调度"
3.2 DRA 增强:GPU 资源模型的根本性变革
传统整数 GPU 模型的局限
Kubernetes 传统 GPU 分配方式使用整数设备插件模型:
# 传统方式:整卡分配
resources:
limits:
nvidia.com/gpu: "2" # 请求 2 张整卡
requests:
nvidia.com/gpu: "2"
问题:
- 无法利用 GPU 分区:现代 GPU(如 NVIDIA A100)支持多实例 GPU( MIG),可以将一张物理卡分成 7 个分区,每个分区独立运行不同任务。整数模型完全无法利用这个能力。
- 单卡故障影响大:当一张 GPU 发生硬件故障时,整个 Pod 会被驱逐并重建。
- 资源利用率低:推理任务通常只需要 GPU 的一小部分计算能力,整卡分配造成严重浪费。
DRA 可分区设备:原生分区支持
v1.36 中,DRA 可分区设备(Partitionable Devices)进入测试阶段并默认开启:
# DRA 可分区设备配置
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
metadata:
name: partitioned-gpu-claim
namespace: ml
spec:
devices:
requests:
- name: "gpu-partition"
# 请求一个可分区的 GPU,并指定需要的分区数量
deviceType: nvidia.com/gpu
# 这告诉设备插件:我需要一个 1/7 的 MIG 分区
partitionableDevices:
- name: "nvidia.com/gpu"
# 拓扑约束:分区必须在同一 NUMA 节点
constraints:
- op: Exact
resource: topology-manager/numa
value: "0"
DRA 设备污点与容忍(Device Taints):
当某个 GPU 发生硬件故障时,DRA 设备污点机制可以快速将故障 GPU 标记为不可用,并自动将使用该 GPU 的 Pod 驱逐到健康节点上:
# 节点上的 GPU 发生故障时,Device Plugin 自动添加污点
kubectl get nodes node-1 -o yaml | grep -A 5 taints
# 输出示例:
# taints:
# - key: nvidia.com/gpu
# value: unhealthy
# effect: NoSchedule
# # kubelet 自动添加,表示该 GPU 不可用
# 调度器感知污点,拒绝将新的 GPU Pod 调度到该节点
# 同时,Job Controller 检测到污点后,自动触发 Pod 迁移
3.3 暂停作业的 Pod 可变资源(Mutable Pod Resources)——弹性调度的终极形态
v1.36 Beta 并默认启用了一个极具实用价值的功能:Pod 级资源原地垂直扩缩容。
# 更新作业的资源需求(无需重建 Pod)
apiVersion: batch/v1
kind: Job
metadata:
name: ml-inference
namespace: ml
spec:
suspend: true # 先暂停作业
template:
spec:
containers:
- name: inference
image: inference-server:v2.0
resources:
requests:
cpu: "4"
memory: "8Gi"
nvidia.com/gpu: "1"
limits:
cpu: "8"
memory: "16Gi"
nvidia.com/gpu: "1"
---
# 通过 API 更新作业的资源请求(Queue Controller 自动处理)
apiVersion: batch/v1
kind: Job
metadata:
name: ml-inference
namespace: ml
spec:
suspend: false # 恢复作业,自动适配新的资源配置
ResizeDeferred 事件:
当集群资源不足无法立即完成扩缩容时,Pod 会收到一个新的 ResizeDeferred 事件,告知"当前资源配置变更被推迟,待节点资源空闲后自动重试":
// Pod 事件中的 ResizeDeferred
{
"type": "Warning",
"reason": "ResizeDeferred",
"message": "CPU request resize to 8 cores deferred due to node resource pressure.
Will retry when node has sufficient resources."
}
四、API 可扩展性与性能优化
4.1 分片列表与分片监听流——超大规模集群的性能救星
拥有数万个节点的 Kubernetes 集群,在监听流(Watch Stream)上存在一个根本性瓶颈:所有控制器(Controller)共享同一个 Watch 连接来接收资源变更通知。
当一个 Deployment 有 10000 个 Pod 时,每创建一个新 Pod,所有 Watch 这个 Deployment 的控制器都会收到同一个通知——这造成了巨大的消息冗余和 CPU 开销。
v1.36 引入的分片列表(Sharded Lists)和分片监听流(Sharded Watch Streams)将这种负载分摊到多个流中:
// 控制器侧的改进(伪代码)
// v1.36 之前:单一 Watch 连接
watcher := client.Watch("pods")
for event := range watcher.ResultChan() {
process(event) // 所有控制器共享同一连接
}
// v1.36 之后:分片 Watch 连接
// kube-controller-manager 自动将 Pod 列表分片
shards := 8
for shard := 0; shard < shards; shard++ {
go func(s int) {
watcher := client.Watch("pods",
// 指定当前分片
resourceVersion: getShardResourceVersion(shard, totalShards),
shardIndex: s,
)
for event := range watcher.ResultChan() {
process(event) // 每个控制器只处理分配给它的分片
}
}(shard)
}
性能影响:
集群规模: 50,000 节点,200,000 Pod
v1.35:
Watch 事件数: ~200,000/秒(每事件广播到所有 Watcher)
kube-apiserver CPU: 持续高负载,p99 延迟 > 500ms
v1.36 (分片 Watch):
Watch 事件数: ~25,000/分片 × 8 分片 = 200,000/秒
但每个分片内的广播量大幅减少
kube-apiserver CPU: 负载降低 60-70%,p99 延迟 < 100ms
4.2 Memory QoS(cgroup v2)——分层内存保护的科学化
v1.36 中,通过 cgroup v2 实现的内存服务质量(Memory QoS)进入 Beta 阶段。
工作原理:
传统 Kubernetes 内存限制:
Pod A: requests.memory=4Gi, limits.memory=8Gi
Pod B: requests.memory=4Gi, limits.memory=8Gi
节点总内存: 32Gi
问题:当 Pod A 实际使用 7Gi,Pod B 实际使用 7Gi 时
节点总使用 = 14Gi < 32Gi,看似正常
但 Pod A 的 limits 是 8Gi,暗示它"可能"用满 8Gi
如果 Pod B 也继续增长到 8Gi,节点就会 OOM
调度器基于 requests 而非实际使用量做决策——这导致了资源"隐性超卖"
Memory QoS (cgroup v2):
每个 Pod 的 cgroup 层级设置了三层内存保护:
- memory.low: "保证区" — 低于此值不会被回收
- memory.high: "限流区" — 超过此值触发限流(降低进程速度)
- memory.max: "硬限制" — 超过此值触发 OOM
Pod A 配置:
memory.low = 4Gi (保证 Pod 至少有 4Gi 不被回收)
memory.high = 7Gi (超过 7Gi 限流,给调度器反应时间)
memory.max = 8Gi (硬上限,触发 OOM Kill)
Pod B 配置:
memory.low = 4Gi
memory.high = 7Gi
memory.max = 8Gi
效果:
- 即使 Pod A 用到 7.5Gi,Pod B 的 4Gi 保证区也不会被挤压
- Pod A 超过 memory.high 后自动被限流,调度器有时间迁移 Pod
- 系统在 OOM 之前有了更多缓冲手段
配置方式:
# Pod 配置 memory QoS(Kubernetes 自动生成对应的 cgroup 设置)
apiVersion: v1
kind: Pod
metadata:
name: memory-sensitive-pod
spec:
containers:
- name: app
image: java-app:latest
resources:
requests:
memory: "4Gi"
limits:
memory: "8Gi"
# Kubernetes v1.36 会自动将上述配置映射为 cgroup v2 QoS 参数:
# memory.low = requests.memory * 1.0
# memory.high = limits.memory * 0.9
# memory.max = limits.memory
五、稳定性提升:从 Beta 到 GA 的核心特性
5.1 SELinux 卷标签——Pod 启动延迟降低 80%
在启用 SELinux 强制模式的 OpenShift 和 RedHat 系统中,以往每次 Pod 挂载 volume 时,SELinux 都需要对整个文件系统做递归的标签重置(relabel)——一个包含数万文件的 volume,relabel 可能耗时 30 秒以上。
v1.36 GA 的 SELinux 卷标签功能通过 mount -o context=XYZ 选项在挂载时统一设置标签,无需递归扫描:
# 验证 SELinux 卷标签是否生效
# 方法1:查看 Pod 启动时间
kubectl annotate pod my-pod \
kubernetes.io/selinux-relabel-time=$(date +%s)
# 方法2:检查 kubelet 日志中的 SELinux 操作
journalctl -u kubelet | grep "SELinux" | tail -20
5.2 Volume Group Snapshots——多 PVC 崩溃一致性快照
# 定义卷组快照(同时为多个 PVC 创建一致性快照)
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeGroupSnapshot
metadata:
name: multi-pvc-snapshot
namespace: production
spec:
# 要快照的所有 PVC
source:
persistentVolumeClaims:
- name: data-pvc
- name: log-pvc
- name: config-pvc
volumeSnapshotClassName: fast-ssd-class
---
# 从快照恢复(原子操作,所有 PVC 同时恢复)
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeGroupSnapshotContent
metadata:
name: restored-snapshot
spec:
volumeGroupSnapshotRef:
name: multi-pvc-snapshot
namespace: production
restoreSize:
data-pvc: 100Gi
log-pvc: 50Gi
config-pvc: 1Gi
5.3 DRA 管理员访问 GA——硬件资源的标准化管理
DRA 管理员访问为集群管理员提供了一个固定框架,用于全局访问和管理 GPU/FPGA 等加速器资源:
# 集群管理员:定义可用的 GPU 资源池
apiVersion: resource.k8s.io/v1alpha3
kind: DeviceClass
metadata:
name: gpu-nvidia-a100
spec:
devices:
- name: node1-gpu0
nodeName: gpu-node-1
pool: default
- name: node1-gpu1
nodeName: gpu-node-1
pool: default
- name: node2-gpu0
nodeName: gpu-node-2
pool: default
---
# 开发者:声明式请求 GPU 资源
apiVersion: resource.k8s.io/v1alpha3
kind: ResourceClaim
metadata:
name: training-gpu-claim
namespace: ml
spec:
devices:
requests:
- name: "compute-gpu"
deviceType: "nvidia.com/gpu"
counters:
- name: "memory"
min: 40000 # 请求至少 40Gi 显存
六、必须注意的破坏性变更
6.1 gitRepo 卷插件正式移除——安全漏洞的彻底清算
gitRepo 卷插件从 v1.11 开始被废弃(deprecate),v1.36 正式移除。该插件存在允许攻击者通过精心构造的 .git 目录在节点上以 root 身份执行代码的漏洞(CVE-2018-11235)。
迁移方案:
# ❌ 已废弃的 gitRepo 卷
volumes:
- name: git-repo
gitRepo:
repository: "https://github.com/myteam/config.git"
revision: "main"
directory: "."
# ✅ 迁移到 initContainer + emptyDir
volumes:
- name: git-config
emptyDir: {}
initContainers:
- name: git-sync
image: k8s.gcr.io/git-sync/git-sync:v3.6.4
env:
- name: GIT_SYNC_REPO
value: "https://github.com/myteam/config.git"
- name: GIT_SYNC_BRANCH
value: "main"
- name: GIT_SYNC_ROOT
value: "/git"
- name: GIT_SYNC_DEST
value: "config"
- name: GIT_KNOWN_HOSTS
value: "false" # 生产环境建议开启并配置 known_hosts
volumeMounts:
- name: git-config
mountPath: /git
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: git-config
mountPath: /etc/config # 应用读取 git 配置的路径
6.2 Kube-proxy IPVS 模式移除
如果你的集群仍在使用 IPVS 模式的 Kube-proxy,升级到 v1.36 后服务网络可能中断:
# 检查是否使用了 IPVS 模式
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode
# 如果看到 ipvs,需要迁移到 iptables 或 nftables
# v1.36 推荐使用 nftables 模式(性能优于 iptables)
# kube-proxy 配置迁移到 nftables 模式
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: nftables
nftables:
# NAT 表的遍历模式:性能优先 vs 兼容性优先
masqueradeAll: false
masqueradeBit: 14
# 批量刷新 NAT 规则,减少刷新次数
minSyncDuration: 5s
七、Ingress NGINX 正式退役——网络生态的重大转折
2026 年 3 月 24 日,Kubernetes SIG Network 与安全响应委员会正式退役了 Ingress NGINX 项目。这对整个 Kubernetes 生态系统有深远影响:
退役原因:
- Ingress NGINX 的代码库长期积累了大量安全债务
- 多次被主流 CNCF 项目认证为高危漏洞来源
- 项目维护人力不足以应对日益增长的安全要求
替代方案评估:
| 方案 | 成熟度 | 性能 | 功能 | 维护状态 |
|---|---|---|---|---|
| Gateway API (GKE/AWS ALB/Cilium) | GA | 高 | 丰富 | 活跃 |
| Istio Ambient Mode | Beta | 高 | 最丰富 | 活跃 |
| NGINX Ingress Controller (独立版) | GA | 高 | 丰富 | 活跃(但需自行维护) |
| Traefik | GA | 中 | 丰富 | 活跃 |
Gateway API 迁移路径:
# ❌ Ingress NGINX 风格
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: myapp.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
# ✅ Gateway API 风格(v1.36 推荐)
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: my-gateway
namespace: ingress
spec:
gatewayClassName: istio
listeners:
- name: http
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app-route
namespace: default
spec:
parentRefs:
- name: my-gateway
namespace: ingress
hostnames: ["myapp.example.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 80
八、升级指南与实操清单
8.1 升级前检查清单
#!/bin/bash
# upgrade-check.sh — Kubernetes v1.36 升级前检查
set -e
echo "=== Kubernetes v1.36 升级前检查 ==="
echo ""
# 1. 检查 API 弃用警告
echo "[1/7] 检查 API 弃用..."
DEPRECATED_APIS=$(kubectl get pods -A -o json 2>/dev/null | \
jq -r '.items[].spec.containers[].env[]? | select(.name=="K8S_DEPRECATED_API") | .value' 2>/dev/null)
if [ -n "$DEPRECATED_APIS" ]; then
echo "⚠️ 发现已废弃 API 使用:"
echo "$DEPRECATED_APIS"
else
echo "✓ 无 API 废弃警告"
fi
# 2. 检查 gitRepo 卷使用
echo ""
echo "[2/7] 检查 gitRepo 卷插件使用..."
GITREPO_PODS=$(kubectl get pods -A -o json | \
jq -r '.items[] | select(.spec.volumes[].gitRepo != null) | "\(.metadata.namespace)/\(.metadata.name)"')
if [ -n "$GITREPO_PODS" ]; then
echo "⚠️ 发现使用 gitRepo 卷的 Pod(必须迁移):"
echo "$GITREPO_PODS"
else
echo "✓ 无 gitRepo 卷使用"
fi
# 3. 检查 Kube-proxy 模式
echo ""
echo "[3/7] 检查 Kube-proxy 模式..."
KP_MODE=$(kubectl get configmap kube-proxy -n kube-system -o jsonpath='{.items[0].data.config}' 2>/dev/null | \
jq -r '.mode' 2>/dev/null || echo "unknown")
echo "当前 Kube-proxy 模式: $KP_MODE"
if [ "$KP_MODE" = "ipvs" ]; then
echo "⚠️ IPVS 模式在 v1.36 已移除,请迁移到 iptables 或 nftables"
fi
# 4. 检查用户命名空间兼容性
echo ""
echo "[4/7] 检查用户命名空间兼容性..."
kubectl get nodes -o json | jq -r '.items[].status.conditions[] | select(.type=="UserNamespacesReady") | "\(.status)"' | sort -u
# 5. 检查 DRA 资源声明
echo ""
echo "[5/7] 检查 DRA 资源声明..."
DRA_CLAIMS=$(kubectl get resourceclaims -A --no-headers 2>/dev/null | wc -l)
echo "集群中 DRA 声明数量: $DRA_CLAIMS"
# 6. 检查 GPU 节点配置
echo ""
echo "[6/7] 检查 GPU 节点上的 DRA 配置..."
kubectl get nodes -o json | \
jq -r '.items[] | select(.status.capacity."nvidia.com/gpu") | .metadata.name' | \
while read node; do
echo " 节点: $node"
kubectl get node $node -o json | \
jq -r '.status.allocatable | " GPU: \(."nvidia.com/gpu"), RDMA: \(."rdma/hfi1")"'
done
# 7. 检查 cgroup 版本
echo ""
echo "[7/7] 检查 cgroup 版本..."
ssh -o StrictHostKeyChecking=no node-1 "stat -f -c %T /sys/fs/cgroup" 2>/dev/null || \
echo " (需要在节点上执行: stat -f -c %T /sys/fs/cgroup)"
echo " cgroup v1 → Memory QoS 功能不可用"
echo " cgroup v2 → 支持完整 Memory QoS 功能"
echo ""
echo "=== 检查完成 ==="
8.2 推荐的升级路径
# 滚动升级策略(保证高可用)
apiVersion: v1
kind: ConfigMap
metadata:
name: upgrade-strategy
namespace: kube-system
data:
strategy.yaml: |
upgrade:
# 最多同时升级的控制面组件数
maxUnavailableControlPlane: 1
# 最多同时驱逐的 Pod 数(按百分比)
maxUnavailableNode: 10%
# 节点排水超时
drainTimeout: 10m
# 升级失败后自动回滚
autoRollback: true
# 升级前快照(针对有状态工作负载)
preUpgradeSnapshot:
enabled: true
volumeSnapshotClass: csi-aws-volsnap
九、架构演进:Kubernetes 的范式转变
9.1 从"框架"到"平台"的哲学转变
VMware Cloud Foundation 博客对 v1.36 的评价一针见血:"Kubernetes 正从一个灵活的框架逐步转向拥有更标准化、更具强制性的默认安全与资源规范。"
这个转变体现在三个层面:
第一层:默认行为的硬化
过去 Kubernetes 的很多安全特性需要手动开启才能生效——PodSecurityPolicy(已废弃)、网络策略、PSP……v1.36 的趋势是让安全基线直接内置到默认行为中:hostUsers: false 作为默认配置、可变准入策略的原生化、细粒度授权……这些变化减少了运维人员的认知负担,同时降低了因配置疏漏引发的安全风险。
第二层:工作负载语义的丰富化
Kubernetes 最初的设计假设是 Pod 与 Pod 之间相互独立——每个 Pod 可以被单独调度、扩容、驱逐。但 AI 工作负载打破了这一假设:分布式训练需要 Gang 调度、GPU 分区需要 DRA 支持、弹性推理需要原地扩缩容……v1.36 体现了 Kubernetes 在工作负载语义上的持续深化。
第三层:生命周期复杂性的上移
随着 Kubernetes 变得越来越"有主见",升级集群的影响面也越来越大。Ingress NGINX 的退役、gitRepo 卷插件的移除、IPVS 模式的移除——这些变化意味着"跟进 Kubernetes 版本迭代已不再只是简单升级集群",还涉及评估兼容性、管理生命周期复杂度、判断何时采用新版本、以及避免业务中断。
9.2 AI 原生平台的方向展望
v1.36 在 AI 工作负载上的投入标志着 Kubernetes 对 AI 场景从"兼容并包"转向"深度定制"。展望未来几个版本,我们预期看到:
- DRA 资源预留协议标准化:不同厂商的 AI 加速器(NVIDIA GPU、Intel Gaudi、AMD Instinct)可能逐步采用统一的 DRA 资源声明和调度协议
- 多租户 GPU 隔离:随着 GPU 共享的普及,硬件级别的内存和计算隔离将成为刚性需求
- ML 工作流的原生支持:类似于 Spark on K8s,Kubernetes 可能在调度层面对 PyTorch/TensorFlow 训练循环做更深的语义理解
十、总结:v1.36 的核心要点
Kubernetes v1.36 Haru 不只是又一个大版本更新,它代表了 Kubernetes 走向成熟基础设施平台的阶段性宣言。
安全维度:
- ✅ 用户命名空间 GA:容器 root 映射为宿主机非特权用户,从根本上解决了容器逃逸后的提权风险
- ✅ 可变准入策略 GA:CEL 驱动的原生变更逻辑,替代了高维护成本的 Mutating Webhook
- ✅ 细粒度 Kubelet 授权 GA:最小权限原则在 Kubelet API 层面的真正落地
AI/ML 维度:
- ✅ 工作负载感知抢占 Alpha:PodGroup 原子调度解决了分布式训练的"部分抢占故障模式"
- ✅ Gang 调度 API Beta:All-or-Nothing 语义在 Kubernetes 调度层的原生实现
- ✅ DRA 增强全面就绪:GPU 分区、动态容量、设备污点与容忍——AI 加速器的标准化资源管理框架初步成型
工程维度:
- ✅ Pod 级资源原地扩缩 Beta:无需销毁重建 Pod 就能调整资源配额
- ✅ Memory QoS Beta:cgroup v2 分层内存保护让资源超卖更有把握
- ✅ 分片 Watch 流 Alpha:超大规模集群控制平面的性能瓶颈得到初步解决
需要立即行动:
- ⚠️ gitRepo 卷插件已移除——立即迁移到 initContainer 方案
- ⚠️ Kube-proxy IPVS 模式已移除——迁移到 nftables 或 iptables
- ⚠️ Ingress NGINX 已正式退役——评估 Gateway API 迁移路径
106 家公司、491 位贡献者、70 项增强——这组数字背后是 Kubernetes 社区在安全和 AI 基础设施两个方向上长达两年以上持续投入的结晶。
对于正在运行 Kubernetes 的团队来说,v1.36 是一次值得认真评估的升级。它在安全基线和 AI 工作负载支持上带来的提升,对于生产环境来说具有实质性的风险降低和能力增强价值。建议有条件的团队在测试环境中完成验证后,尽快推进升级计划。
毕竟,在云原生基础设施这条路上,跟上 Kubernetes 的演进节奏,本身就是一种竞争力。
参考链接:
- Kubernetes v1.36 官方发布博客:https://kubernetes.io/blog/2026/04/22/kubernetes-v1-36-release/
- Kubernetes v1.36 CHANGELOG:https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.36.md
- InfoQ 深度解读:https://www.infoq.cn/article/kNkrHGzRvA7r6pRtlGB5
- VMware Cloud Foundation 博客:https://blogs.vmware.com/cloud-foundation/2026/04/29/kubernetes-1-36-what-actually-changed-for-enterprise-platforms/
- Kloia 技术解读:https://www.kloia.com/blog/kubernetes-1-36-whats-coming
- Palark 版本解读:https://palark.com/blog/kubernetes-1-36-release-features/