Kubernetes v1.36 Haru 深度实战:从用户命名空间到 AI 原生编排——2026 云原生安全与性能的双重革命
前言:春归万物生
2026年4月22日,Kubernetes 正式发布了 v1.36,代号 Haru——日语中"春"的意思。在上一个代号 Timbernetes(世界树)为 2025 年画下句点之后,Haru 以一个新季节的姿态,开启了 2026 年的第一次更新。
这不是一次例行的版本迭代。v1.36 包含 70 项增强功能:18 项进入 Stable 阶段、25 项进入 Beta 阶段、25 项新的 Alpha 功能。重点聚焦三大方向——安全加固、人工智能和机器学习工作负载支持、以及大规模 API 的可扩展性。
对于在生产环境中运行 Kubernetes 的工程师而言,这次更新有几项"迟到多年的春天":**用户命名空间(User Namespaces)**历经四年打磨终于 GA,**可变准入策略(Mutating Admission Policies)**基于 CEL 彻底告别独立 Webhook 服务器,调度器和动态资源分配(DRA)子系统也迎来实质性的架构升级。
本文将从背景讲起,深入剖析这些关键新特性的设计原理与生产落地实践,提供完整的代码示例与升级避坑指南。
一、版本背景与发布时间线
1.1 Kubernetes 版本发布节奏
Kubernetes 项目保持着每四个月发布一个大版本的节奏。v1.36 的发布时间线如下:
| 里程碑 | 日期 |
|---|---|
| 发布周期开始 | 2026年1月12日 |
| 功能增强冻结(Feature Freeze) | 2026年2月11日 |
| 代码冻结(Code Freeze) | 2026年3月18日 |
| 文档冻结 | 2026年4月8日 |
| 正式发布 | 2026年4月22日 |
截至发布日,Kubernetes 的最新稳定版本为 v1.36.1(补丁版本)。整体依赖升级至 Go 1.25,解决了此前 Go 编译器中存在的安全漏洞。
1.2 v1.35 到 v1.36 的演进脉络
v1.35(代号 Timbernetes)是 2025 年底的收官之作,带来了调度器 Framework 的重构、PersistentVolume 生命周期增强等特性。v1.36 在此基础上:
- 延续了调度器插件化架构的深化,引入了 PreBind 插件并行执行机制
- 将 v1.35 中处于 Alpha 的多项 DRA(动态资源分配)特性推进至 Beta/GA
- 正式将 Workload 和 PodGroup API 引入调度器,为批调度场景提供了原生支持
- 在安全层面完成了用户命名空间的最后一公里
理解这条演进脉络,有助于我们把握 v1.36 的设计意图——它不是凭空出现的,而是 Kubernetes 社区长期规划的系统性兑现。
二、用户命名空间:从 Alpha 到 GA 的四年长征
2.1 为什么容器 root 是个问题
在传统容器模型中,容器内的 root 用户(UID 0)就是宿主机的 root 用户(或至少映射到宿主机的一个特权 UID)。这意味着:
# 在容器内执行 id
uid=0(root) gid=0(root) groups=0(root)
# 在宿主机上查看同一进程
uid=0(root) gid=0(root) groups=0(root)
即便容器使用了 securityContext 进行限制,一旦攻击者利用容器逃逸漏洞突破了隔离层,他所获得的 root 权限就等同于宿主机的 root 权限。这是容器安全的根本性隐患。
在多租户环境、共享集群或任何对安全有较高要求的场景下,这个问题的严重性尤为突出。
2.2 用户命名空间的设计原理
用户命名空间(User Namespaces)是 Linux 内核提供的一种隔离机制,允许在 namespace 内部重新映射 UID/GID。Kubernetes v1.36 的 GA 实现将这一内核能力引入容器编排层:
容器内 UID 0 (root)
↓
用户命名空间映射
↓
宿主机 UID 100000+ (非特权用户)
即便容器内的进程成功突破隔离,其在宿主机上的身份仍然是一个普通非特权用户,无法执行系统级操作。
2.3 生产环境配置示例
Pod 级别启用用户命名空间:
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
namespace: production
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginx:1.27
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
# 用户命名空间配置(v1.36 GA)
resources:
limits:
memory: "256Mi"
cpu: "500m"
更细粒度的 namespace 映射配置:
spec:
securityContext:
# 显式指定用户命名空间配置
namespaceOptions:
# 指定 pod 所属的 host namespace ID(可选)
hostUsers: false
# UID/GID 映射由 kubelet 自动生成
# 容器内 root (0) → 宿主机非特权 UID
containers:
- name: app
securityContext:
# 容器内使用非 root 用户运行
runAsUser: 1000
runAsGroup: 1000
集群级别的准入控制(使用 MutatingAdmissionPolicy):
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
name: enforce-userns-on-secure-namespace
spec:
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
applyTo:
- groups: [""]
resources: ["pods"]
mutation:
patchEvaluations:
- apiVersion: admissionregistration.k8s.io/v1alpha1
kind: PatchEvaluation
spec:
patch:
# 强制设置 runAsNonRoot
- op: add
path: /spec/securityContext/runAsNonRoot
value: true
# 使用 CEL 表达式定义条件
condition:
expression: "object.namespace == 'production' && !('runAsNonRoot' in object.spec.securityContext)"
expressionType: CEL
2.4 用户命名空间的升级影响
需要关注的变更点:
镜像兼容性:并非所有镜像都支持在用户命名空间下运行。如果镜像内部硬编码了特定 UID/GID 的文件权限,可能会遇到权限问题。建议在测试环境充分验证后再生产部署。
Volume 挂载:某些挂载类型(特别是
hostPath和部分 CSI 驱动)在用户命名空间下可能有不同的行为。kubectl exec / attach:用户命名空间不影响这些操作的功能,但 exec 进去的 shell 进程在宿主机上的身份也是非特权用户。
# 检查当前 kubelet 是否支持用户命名空间
# 在节点上执行:
cat /boot/config-$(uname -r) | grep CONFIG_USER_NS
# 应该输出:CONFIG_USER_NS=y
# 检查节点能力
kubectl get nodes -o jsonpath='{.items[*].status.conditions[?(@.type=="UserNamespacesSupport")]}'
生产升级建议:
# 1. 先在非生产节点验证
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
# 执行 kubeadm upgrade plan 检查 v1.36 可用性
# 升级控制面
kubeadm upgrade apply v1.36.1
# 升级节点
kubeadm upgrade node
# 解除封锁
kubectl uncordon <node>
# 2. 使用 namespace 隔离逐步迁移
# 对核心业务命名空间先开启用户命名空间
kubectl label namespace production security.kubernetes.io/user-namespace=enabled
# 3. 监控迁移后的 pod 事件
kubectl get events -n production --field-selector involvedObject.name=<pod-name>
三、可变准入策略(Mutating Admission Policies):CEL 原生替代 Webhook
3.1 为什么 Webhook 是运维之痛
在 Kubernetes 中,准入 Webhook(Admission Webhook)是实现自定义策略的主流方式——在资源持久化到 etcd 之前,拦截 API 请求并执行验证或修改逻辑。常见的应用场景包括:
- 自动注入 sidecar 容器(如 Istio 的 Envoy 代理)
- 强制资源限制(设置默认的 CPU/内存 limits)
- 敏感信息脱敏(不允许在 ConfigMap 中写入 secrets)
然而,Webhook 的运维成本相当高:
# 传统 MutatingWebhookConfiguration 示例
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: my-mutating-webhook
webhooks:
- name: mutate-pods.example.com
clientConfig:
service:
name: webhook-service
namespace: webhook-system
path: "/mutate-pods"
caBundle: <base64-encoded-ca>
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
sideEffects: None
admissionReviewVersions: ["v1", "v1beta1"]
timeoutSeconds: 10
运维之痛:
- 额外的服务:必须额外部署和维护一个 Webhook 服务器(Deployment + Service + HPA)
- 网络延迟:每次 API 请求都需要额外的 HTTP 调用,通常增加 5-30ms 的 P99 延迟
- 高可用:Webhook 服务本身需要高可用设计,防止单点故障
- 证书管理:需要维护 CA 证书轮换
- 调试困难:Webhook 逻辑出问题时的排查链路长
3.2 CEL 驱动的原生可变准入策略
v1.36 GA 的**可变准入策略(Mutating Admission Policies)**彻底解决了上述问题。它允许团队使用 CEL(Common Expression Language) 在 API Server 内部直接定义变更逻辑,无需额外部署服务:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
name: default-resources
spec:
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["pods"]
scope: "Namespaced"
applyTo:
- groups: [""]
resources: ["pods"]
mutation:
patchEvaluations:
- apiVersion: admissionregistration.k8s.io/v1alpha1
kind: PatchEvaluation
spec:
patch:
# 设置默认资源限制
- op: add
path: /spec/containers/0/resources/limits/cpu
value: "500m"
- op: add
path: /spec/containers/0/resources/limits/memory
value: "512Mi"
- op: add
path: /spec/containers/0/resources/requests/cpu
value: "100m"
- op: add
path: /spec/containers/0/resources/requests/memory
value: "128Mi"
condition:
# 仅在没有设置 limits 时注入
expression: >
!('resources' in self.path('/spec/containers/0')) ||
!('limits' in self.path('/spec/containers/0/resources'))
expressionType: CEL
3.3 生产级 CEL 表达式实战
场景一:强制安全上下文
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
name: enforce-security-context
spec:
matchConstraints:
resourceRules:
- apiGroups: ["apps", ""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments", "statefulsets", "daemonsets"]
scope: "Namespaced"
applyTo:
- groups: ["apps", ""]
resources: ["deployments", "statefulsets", "daemonsets"]
mutation:
patchEvaluations:
- apiVersion: admissionregistration.k8s.io/v1alpha1
kind: PatchEvaluation
spec:
patch:
- op: add
path: /spec/template/spec/securityContext/runAsNonRoot
value: true
- op: add
path: /spec/template/spec/securityContext/seccompProfile
value:
type: "RuntimeDefault"
condition:
expression: "object.spec.template.spec.securityContext.runAsNonRoot != true"
expressionType: CEL
场景二:自动注入 Istio sidecar(CEL 条件判断)
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
name: inject-istio-sidecar
spec:
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["pods"]
scope: "Namespaced"
applyTo:
- groups: [""]
resources: ["pods"]
mutation:
patchEvaluations:
- apiVersion: admissionregistration.k8s.io/v1alpha1
kind: PatchEvaluation
spec:
patch:
- op: add
path: /spec/initContainers
value:
- name: istio-init
image: "istio/proxyv2:1.24.0"
command: ["istio-iptables"]
args:
- "-p"
- "15001"
- "-z"
- "15006"
- "-u"
- "1337"
- "-m"
- "REDIRECT"
- "-i"
- "*"
- "-x"
- ""
securityContext:
capabilities:
drop:
- ALL
allowPrivilegeEscalation: false
- op: add
path: /spec/containers/-
value:
name: istio-proxy
image: "istio/proxyv2:1.24.0"
args:
- "proxy"
- "run"
env:
- name: "ISTIO_META_DNS_AUTO_ALLOCATE"
value: "true"
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "2000m"
memory: "1024Mi"
condition:
expression: >
!('istio-injection' in object.metadata.annotations) ||
object.metadata.annotations['istio-injection'] == 'enabled'
expressionType: CEL
场景三:基于命名空间的动态资源配置
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
name: namespace-based-resource-limits
spec:
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["pods"]
applyTo:
- groups: [""]
resources: ["pods"]
mutation:
patchEvaluations:
- apiVersion: admissionregistration.k8s.io/v1alpha1
kind: PatchEvaluation
spec:
patch:
- op: add
path: /spec/containers/0/resources/limits/cpu
value: "1000m"
- op: add
path: /spec/containers/0/resources/limits/memory
value: "1Gi"
condition:
expression: >
self.path('/metadata/namespace') in ['production', 'staging'] &&
!('resources' in self.path('/spec/containers/0'))
expressionType: CEL
3.4 性能对比
| 指标 | MutatingWebhook | MutatingAdmissionPolicy |
|---|---|---|
| 额外延迟 | 5-30ms P99 | ~0(内置执行) |
| 运维复杂度 | 高(独立服务) | 低(声明式配置) |
| 可用性依赖 | 外部服务可用性 | 仅 API Server |
| 证书管理 | 需要 | 不需要 |
| 调试难度 | 高(跨服务) | 低(Kubernetes 原生) |
四、调度器升级:PreBind 插件并行与 Workload/PodGroup API
4.1 PreBind 插件并行执行
在 v1.36 之前,调度器的 PreBind 插件是串行执行的。当一个 Pod 需要绑定到节点时,所有 PreBind 插件(如 VolumeBinding、portAllocation 等)必须一个接一个地完成:
PreBind Plugin 1 (VolumeBinding) → 完成
↓
PreBind Plugin 2 (portAllocation) → 完成
↓
PreBind Plugin 3 (another binding) → 完成
↓
Pod 绑定完成
在大型集群中,如果 VolumeBinding 需要等待云厂商的存储卷挂载(可能需要数秒),整个调度链路就会被阻塞,后续插件无法开始。
v1.36 引入了 PreBind 插件并行执行机制:
// 调度器配置示例(kube-scheduler 配置文件)
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- pluginConfig:
- name: VolumeBinding
args:
BindTimeoutSeconds: 600
Shape:
- Score: 1
Min: 0
Max: 100
# 启用异步绑定(Alpha → Beta 升级)
- name: PreBind
args:
# v1.36: 插件可并行执行的条件
enableParallelPreBind: true
并行执行模型:
┌─ PreBind Plugin 1 (VolumeBinding) ──────┐
│ │
├─ PreBind Plugin 2 (portAllocation) ─────┼─→ 并行执行
│ │
├─ PreBind Plugin 3 (nodeResources) ───────┤
│ │
└─ 等待所有插件完成 → Pod 绑定 ──────────┘
升级注意事项:如果你的调度器使用了自定义 PreBind 插件(通过 scheduler framework 开发),需要适配新的并行执行 API:
// 自定义 PreBind 插件适配(v1.36 之前 → v1.36)
//
// v1.36 之前:
type PreBindPlugin interface {
PreBind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status
}
// v1.36 并行化后需要实现:
type PreBindPlugin interface {
PreBind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status
// 新增:返回该插件是否依赖于其他插件的输出
// 如果你的插件不依赖其他 PreBind 插件的结果,可返回 nil
// 这样调度器可以将其与其他不依赖的插件并行执行
}
// 使用新的辅助方法判断依赖
type PreBindExtensions interface {
PreBindExtensions() []PreBindPlugin
}
4.2 Workload 和 PodGroup API:批调度的原生支持
在 AI/ML 和大数据场景中,gang scheduling(组调度)是核心需求:多个 Pod 必须同时调度成功,否则宁愿全部等待。例如训练一个分布式模型:
# 分布式训练任务 — 需要 4 个 Worker 同时存在才能开始
apiVersion: batch/v1alpha1 # v1.36 引入的新 API
kind: PodGroup
metadata:
name: ml-training-pg
namespace: ml-workloads
spec:
# 最少需要 4 个 Pod 才能开始执行
minMembers: 4
# 超时时间,超过则 PodGroup 标记为失败
scheduleTimeoutSeconds: 300
---
apiVersion: v1
kind: Pod
metadata:
name: ml-worker-1
labels:
pod-group.kubernetes.io/group-name: ml-training-pg
spec:
# Pod 现在可以声明它属于哪个 PodGroup
schedulingProfile:
groupName: ml-training-pg
调度器对 PodGroup 的处理逻辑:
// PodGroup 调度流程(伪代码)
func (sched *Scheduler) schedulePodGroup(pg *PodGroup) error {
pods := getPodsInGroup(pg.Name)
// 尝试找到一个满足所有 Pod 的节点组合
for _, node := range eligibleNodes {
if canAllocateGroup(node, pods) {
// 所有 Pod 都可以调度到该节点(或跨节点但同时满足条件)
for _, pod := range pods {
err := bindPod(pod, node)
if err != nil {
// 任何一个绑定失败,回滚整个组
rollbackGroupBindings(pods)
break
}
}
return nil
}
}
// 无法满足组调度条件,等待
pg.Status.Phase = "Pending"
return fmt.Errorf("insufficient resources for gang scheduling")
}
Minicluster 场景示例:
apiVersion: batch/v1alpha1
kind: PodGroup
metadata:
name: distributed-training-group
spec:
minMembers: 8 # 4 个 PS + 4 个 Worker
scheduleTimeoutSeconds: 600
---
# 参数服务器
apiVersion: v1
kind: Pod
metadata:
name: ps-0
labels:
role: parameter-server
pod-group.kubernetes.io/group-name: distributed-training-group
spec:
nodeSelector:
gpu: "true"
tolerations:
- key: "nvidia.com/gpu"
operator: "Exists"
effect: "NoSchedule"
containers:
- name: ps
image: tensorflow/tensorflow:2.16
command: ["python", "-c", "..."]
resources:
limits:
nvidia.com/gpu: 1
---
# Worker 节点(4个类似定义)
apiVersion: v1
kind: Pod
metadata:
name: worker-0
labels:
role: worker
pod-group.kubernetes.io/group-name: distributed-training-group
spec:
# Worker 的调度策略与 PodGroup 关联
# 确保所有 8 个 Pod 同时满足调度条件
五、动态资源分配(DRA):GPU/FPGA 调度的架构升级
5.1 DRA 的前世今生
Kubernetes 传统资源模型使用 limits.cpu、limits.memory 来描述资源需求。然而,对于新型加速器(GPU、FPGA、DPU、智能网卡),这种粗粒度模型不够用:
- GPU 调度需要追踪型号(V100/H100/A100)、显存大小、多卡拓扑(NVLink/NVSwitch)
- 多个容器可能共享同一块 GPU(通过 MIG 或时间片)
- 资源需要在 Pod 生命周期内动态调整
v1.36 中,DRA 的多项功能从 Alpha 推进至 Beta/GA:
| DRA 功能 | 之前状态 | v1.36 状态 |
|---|---|---|
| 基础 DRA API | Alpha | Beta |
| 资源消费者(Pod) | Alpha | Beta |
| 资源驱动程序(CRI) | Alpha | Beta |
| DRA 元数据读取助手 | Alpha | GA |
5.2 生产级 DRA 配置示例
节点配置:声明可分配的 GPU 资源
# Node 的 extended resources(由 DRA 驱动自动注册)
apiVersion: v1
kind: Node
metadata:
name: gpu-node-1
status:
allocatable:
# DRA 驱动的资源格式:resource.k8s.io/<class>/<name>
nvidia.com/gpu: "4"
# 也可以使用 DRA 自己的 API 注册更细粒度的资源
---
# Pod 使用 DRA 申请特定 GPU
apiVersion: v1
kind: Pod
metadata:
name: llm-inference
spec:
containers:
- name: inference
image: vllm/vllm-openai:latest
resources:
claims:
- name: gpu-claim
env:
- name: VLLM_GPU_MEMORY_UTILIZATION
value: "0.9"
command: ["python", "-m", "vllm.entrypoints.openai.api_server"]
---
# ResourceClaim(声明所需资源)
apiVersion: resource.k8s.io/v1alpha3
kind: ResourceClaim
metadata:
name: gpu-claim
spec:
resourceClassName: nvidia.com/gpu
requests:
- drone.k8s.io/count: "1"
drone.k8s.io/type: "H100"
drone.k8s.io/memory: "80Gi"
# 共享模式:允许其他 Pod 共享同一 GPU(MIG 场景)
sharing:
maxShareCount: 2
5.3 DRA 元数据读取助手(GA)
v1.36 的 DRA 元数据读取助手正式达到 GA,简化了 DRA 驱动的开发:
// DRA 驱动中的元数据读取(使用新的 GA API)
package main
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/dynamic-resource-claim/api"
)
// v1.36 GA: 更简洁的错误处理
func decodeClaimParameters(obj runtime.Object) (*MyParams, error) {
// 新 API:自动处理版本兼容,版本未知时跳过解析
decoder := api.NewClaimParameterDecoder(scheme)
params, err := decoder.DecodeParams(obj, "example.com/v1")
if err != nil {
// 错误信息更明确,便于驱动调试
return nil, fmt.Errorf("failed to decode claim parameters: %w", err)
}
return params, nil
}
六、AI/ML 工作负载:Kubernetes 1.36 的原生智能化
6.1 CNCF 的警告:仅靠 K8s 不足以保障 LLM 安全
CNCF 在 2026 年初的一篇博客中指出了一个关键问题:Kubernetes 擅长编排和隔离工作负载,但它本身无法理解或控制 AI 系统的行为。
LLM 工作负载面临的威胁模型与传统微服务完全不同:
传统微服务威胁:
容器逃逸 → 获得宿主机权限 → 横向移动
AI/LLM 威胁:
模型投毒 → 推理时产生错误输出 → 业务决策失误
Prompt 注入 → 数据泄露 → 合规违规
资源耗尽 → 模型服务不可用 → 业务中断
Kubernetes 1.36 在基础设施层面强化了对 AI 工作负载的支持:
- 增强的资源抽象:DRA + PodGroup 组合支持大规模 AI 训练集群的调度
- QoS 隔离:更精细的资源优先级确保推理服务不被训练任务抢占
- 安全加固:用户命名空间防止 GPU 资源被恶意容器劫持
6.2 KubeRay 与 Kubernetes 1.36 的结合
KubeRay 是 Kubernetes 原生的 Ray 分布式计算框架管理插件,广泛用于机器学习、强化学习和数据处理场景。v1.36 的调度器改进使 KubeRay 编排的训练任务更加稳定:
apiVersion: ray.io/v1
kind: RayCluster
metadata:
name: ray-training-cluster
namespace: ml-platform
spec:
rayVersion: "2.35.0"
headGroupSpec:
serviceType: ClusterIP
rayActorOptions:
defaults: "env://"
template:
spec:
containers:
- name: ray-head
image: rayproject/ray:2.35.0-gpu
resources:
requests:
cpu: "2"
memory: "8Gi"
nvidia.com/gpu: "1"
limits:
cpu: "4"
memory: "16Gi"
nvidia.com/gpu: "1"
env:
- name: RAY_dashboard_frontend_host
value: "0.0.0.0"
nodeSelector:
node-type: gpu-head
workerGroupSpecs:
- replicas: 4
minReplicas: 4
maxReplicas: 8
groupName: worker-group
template:
spec:
containers:
- name: ray-worker
image: rayproject/ray:2.35.0-gpu
resources:
requests:
cpu: "8"
memory: "32Gi"
nvidia.com/gpu: "4"
limits:
cpu: "16"
memory: "64Gi"
nvidia.com/gpu: "4"
nodeSelector:
node-type: gpu-worker
tolerations:
- key: "nvidia.com/gpu"
operator: "Exists"
effect: "NoSchedule"
6.3 Pod 资源原地调整(In-Place Pod Vertical Scaling)
v1.36 增强了 Pod 资源的原地调整能力——无需重启容器即可调整 CPU 和内存限制:
# 直接调整 Pod 资源限制(原地生效,无需重建容器)
kubectl patch pod inference-service \
--subresource=resizepod \
--type=merge \
-p '{
"spec": {
"containers": [{
"name": "inference",
"resources": {
"limits": {
"memory": "32Gi",
"cpu": "8"
},
"requests": {
"memory": "16Gi",
"cpu": "4"
}
}
}]
}
}'
# 查看调整状态
kubectl get pod inference-service -o jsonpath='{.status.resize}'
# 输出: InProgress / Infeasible / Adequate
这对 AI 推理服务尤为重要——可以在不中断服务的情况下,根据负载动态扩展资源。
七、kubeadm 升级与兼容性:这次必须注意的 Breaking Changes
7.1 FlexVolume 内置支持移除
影响:kubeadm 不再内置 FlexVolume 插件支持。如果你还在使用 FlexVolume(deprecated),需要迁移到 CSI。
# 检查集群中是否有 FlexVolume 使用
kubectl get pv -o json | jq '.items[].spec.flexVolume'
# 如果有输出,说明集群在使用 FlexVolume,需要迁移到 CSI
# CSI 迁移步骤:
# 1. 找到对应的 CSI 驱动(如 NFS → csi-nfs)
# 2. 安装 CSI 驱动
# 3. 迁移现有的 PV/PVC 对象
# 4. 更新 StorageClass
# 5. 移除 FlexVolume 配置
7.2 监控指标重命名
v1.36 对部分 kubelet 和 API Server 指标进行了重命名。如果你的监控告警规则依赖这些指标名,需要更新:
# 升级前:导出当前的告警规则
promtool check rules /etc/prometheus/rules/*.yml > /tmp/rules-backup.yml
# 升级后:检查指标变化
# 常见的指标重命名模式:
# apiserver_request_duration_seconds → apiserver_request_latency_seconds
# kubelet_runtime_operations_total → kubelet_runtime_exec_operations_total
# 使用 promtool 验证更新后的规则
promtool update rules /etc/prometheus/rules/new-rules.yml
7.3 完整的 kubeadm 升级命令
#!/bin/bash
# kubernetes-v1.36-upgrade.sh
set -euo pipefail
K8S_VERSION="1.36.1"
echo "=== 1. 升级控制面 ==="
# 查看升级计划
kubeadm upgrade plan v${K8S_VERSION}
# 升级控制面组件
sudo kubeadm upgrade apply v${K8S_VERSION} \
--etcd-upgrade=true \
--certificate-renewal=true
echo "=== 2. 升级所有节点 ==="
# 获取所有节点
NODES=$(kubectl get nodes -o jsonpath='{.items[*].metadata.name}')
for NODE in $NODES; do
echo "正在升级节点: $NODE"
# 如果是非控制面节点,需要先排空
if ! kubectl get node $NODE | grep -q "control-plane"; then
kubectl drain $NODE \
--ignore-daemonsets \
--delete-emptydir-data \
--force \
--skip-wait-for-delete-timeout=60
fi
# 在目标节点执行
ssh $NODE "sudo kubeadm upgrade node"
ssh $NODE "sudo apt-get update && sudo apt-get install -y kubelet=${K8S_VERSION}-* kubectl=${K8S_VERSION}-* kubeadm=${K8S_VERSION}-*"
ssh $NODE "sudo systemctl restart kubelet"
# 解除封锁
kubectl uncordon $NODE
done
echo "=== 3. 验证升级 ==="
kubectl version --short
kubectl get nodes -o wide
kubectl get componentstatuses # 检查 etcd、scheduler、controller-manager 状态
echo "=== 4. 升级检查 ==="
# 检查是否有 Pod 处于 Pending/Error 状态
kubectl get pods --all-namespaces | grep -E "Pending|Error|ContainerCreating"
echo "升级完成!当前版本:"
kubectl version -o yaml | grep serverVersion -A 3
八、生产升级策略与灰度发布
8.1 推荐的升级路径
v1.34.x → v1.35.x → v1.36.x
↓
每两个版本升级一次(安全窗口更大)
↓
或使用 KubeSphere/Rancher 等发行版管理
不建议跳过两个以上的版本。Kubernetes 的 minor 版本间存在 API 兼容承诺(n-2 兼容),但实际升级时还是逐个版本验证更稳妥。
8.2 灰度升级策略
# 使用 nodeSelector 实现灰度升级
# 先升级少量"影子节点",验证通过后再全量升级
apiVersion: v1
kind: Node
metadata:
name: upgrade-candidate-1
labels:
# 添加升级候选标签
k8s-upgrade: "candidate"
# 指定目标版本
k8s-version: "v1.36.1"
---
# 在测试节点上先执行升级
# kubectl drain upgrade-candidate-1 --ignore-daemonsets
# kubeadm upgrade node --to-version v1.36.1
# 重启 kubelet
# kubectl uncordon upgrade-candidate-1
8.3 回滚方案
# 如果升级后发现问题,kubeadm 可以回滚到上一个版本
# 但仅限于同一 minor 版本内的 patch 版本间回滚
# major/minor 版本不支持自动回滚
# 回滚 patch 版本(1.36.1 → 1.36.0)
sudo apt-get install -y \
kubelet=1.36.0-* \
kubectl=1.36.0-* \
kubeadm=1.36.0-*
sudo systemctl restart kubelet
# 跨版本回滚需要:
# 1. 从 etcd 备份恢复(如果有的话)
# 2. 手动重建 Pod(Deployment/ReplicaSet 会重新创建)
# 因此升级前的 etcd 备份至关重要
九、展望:Kubernetes 的下一个四季
Haru(春)之后,Kubernetes 的演进方向已经清晰:
AI 原生编排:从 PodGroup 到更完善的 gang scheduling,再到 GPU 共享语义的原生支持,Kubernetes 正在成为 AI 基础设施的标准层
安全默认化:User Namespaces 和 MutatingAdmissionPolicies 的 GA 标志着"Kubernetes 零信任"从理念走向落地
性能极限:调度器并行化、DRA 优化、API Server 扩展性增强,都是在为大集群(1000+ 节点)和大工作负载(10万+ Pod)做准备
开发者体验:Webhook 的 CEL 替代、原地资源调整,都在降低 Kubernetes 的运维门槛
总结
Kubernetes v1.36 Haru 是一次承前启后的版本更新:
| 维度 | 关键变化 | 对工程师的影响 |
|---|---|---|
| 安全 | User Namespaces GA | 容器逃逸后不再获得 root,生产多租户更安全 |
| 准入 | CEL MutatingAdmissionPolicies GA | 告别 Webhook 服务,运维复杂度大幅降低 |
| 调度 | PreBind 并行 + PodGroup API | AI 训练任务调度更高效,gang scheduling 原生化 |
| 资源 | DRA Beta/GA | GPU/FPGA 调度更精细,无需重启即可调资源 |
| 升级 | FlexVolume 移除 | 需要迁移到 CSI,监控指标需同步更新 |
行动建议:
- 测试环境立即升级到 v1.36.1,验证用户命名空间与现有镜像的兼容性
- 审计现有 MutatingWebhookConfiguration,逐步迁移到 CEL MutatingAdmissionPolicies
- 评估 PodGroup API,如果你在运行 AI 训练或批处理任务,这是必须关注的方向
- 检查 FlexVolume 使用情况,制定向 CSI 的迁移计划
参考来源:Kubernetes v1.36 CHANGELOG(GitHub kubernetes/kubernetes),Kubernetes v1.36 发布博客,CNCF 官方博客,KubeSphere 技术社区。