☰
MutatingAdmissionPolicy 实战指南:用声明式策略替代准入 Webhook
2026/10/15 3:52:59 网站建设 项目流程

MutatingAdmissionPolicy 实战指南:用声明式策略替代准入 Webhook

【免费下载链接】websiteKubernetes website and documentation repo:项目地址: https://gitcode.com/GitHub_Trending/webs/website

MutatingAdmissionPolicy 是 Kubernetes 提供的声明式变更准入特性:它让 API Server 在资源持久化之前按策略改写对象,自 1.36 版本起正式 GA(默认开启)。它解决的是"自动给资源打标签、注 sidecar、补默认值"这类需求——以往只能写一个独立 Webhook 服务实现的事情,现在用两份 YAML 就能完成,不需要证书、独立部署和额外端口。

一个 Webhook 团队的日常,先算笔账

假设平台组维护着一个 MutatingWebhook,用途很朴素:给新建的 Deployment 打上team标签和 owner 注解。围绕它必须做的事:

  • 签发并轮换 Webhook 的 client CA 证书;
  • 配置 admissionregistration 里的caBundle与 443 端口 TLS 要求;
  • 至少 2 副本防单点,配好健康检查;
  • 每次调整注入逻辑(比如换镜像 tag)都要走一次 CI/CD 发布。

如果策略只是"加标签"这一类字段级修改,这套成本明显偏高。MutatingAdmissionPolicy 的执行逻辑直接编译进 kube-apiserver,用 CEL(Common Expression Language)表达式描述改动,产物是一个 patch,全程不出集群边界。

请求是如何被策略改写并落地的

一次匹配的 API 请求,在准入链中的流转如下:

策略不是单对象,而是由三类资源拼成,职责分离:

对象职责关键点
MutatingAdmissionPolicy描述"匹配什么、改什么"(抽象逻辑)paramKind声明参数类型;matchConditions写 CEL 前置条件
参数资源(ConfigMap、CRD 等)承载具体取值通过 binding 的spec.paramRef关联,CEL 中以params访问
MutatingAdmissionPolicyBinding把策略接到集群并限定作用范围没有 binding,策略不生效;matchResources/namespaceSelector在这里收窄

CEL 表达式可访问的常用变量:object(本次请求对象)、oldObject(变更前对象,CREATE 时为 null)、request(请求属性)、namespaceObject(所属命名空间)、params(参数资源)。注意matchConditions中访问不到namespaceObject和variables,详见官方概念文档。

如何部署第一条 MutatingAdmissionPolicy

目标:默认命名空间新建的 Deployment 自动带上team标签和 owner 注解,无需参数,先看最小可用形态。

apiVersion: admissionregistration.k8s.io/v1 kind: MutatingAdmissionPolicy metadata: name: default-deploy-policy spec: matchConstraints: resourceRules: - apiGroups: ["apps"] apiVersions: ["v1"] operations: ["CREATE"] resources: ["deployments"] matchConditions: - name: owner-missing expression: "!has(object.metadata.labels['owner'])" mutations: - patchType: JSONPatch jsonPatch: expression: > [ JSONPatch{op: "add", path: "/metadata/labels/team", value: "platform"} ]

matchConditions保证"已有 owner 标签的请求不再处理",避免重复执行;mutations里的 CEL 表达式求值后产出一条 JSON Patch。

apiVersion: admissionregistration.k8s.io/v1 kind: MutatingAdmissionPolicyBinding metadata: name: default-deploy-policy-binding spec: policyName: default-deploy-policy matchResources: namespaceSelector: matchLabels: purpose: default-apps failurePolicy: Ignore

policyName必须与 Policy 的metadata.name一致,这是两者唯一的连接点;matchResources限定只作用于purpose: default-apps的命名空间。failurePolicy设在 binding 上,Ignore表示 CEL 求值失败时跳过改动并记日志。

验证:

kubectl apply -f deploy-policy.yaml -f deploy-binding.yaml kubectl get mutatingadmissionpolicies kubectl get mutatingadmissionpolicybindings kubectl get deploy my-deploy -o jsonpath='{.metadata.labels}' # 期望输出包含 "team":"platform"

常见坑:Policy 建了、Binding 忘了建,策略会静默不生效,kubectl get两个资源对照一下 name 是否一致。

用 ConfigMap 作为参数资源给策略供数

目标:owner 和环境值不应该硬编码在策略里,改为从 ConfigMap 读取,换值不重新发布策略。

apiVersion: v1 kind: ConfigMap metadata: name: deploy-meta namespace: default data: owner: "team.example.com/billing" environment: "prod"
apiVersion: admissionregistration.k8s.io/v1 kind: MutatingAdmissionPolicy metadata: name: deploy-owner-policy spec: paramKind: apiVersion: v1 kind: ConfigMap matchConstraints: resourceRules: - apiGroups: ["apps"] apiVersions: ["v1"] operations: ["CREATE"] resources: ["deployments"] mutations: - patchType: JSONPatch jsonPatch: expression: > [ JSONPatch{op: "add", path: "/metadata/annotations/" + jsonpatch.escapeKey("team.example.com/owner"), value: params.data.owner}, JSONPatch{op: "add", path: "/metadata/annotations/team.example.com/environment", value: params.data.environment} ] --- apiVersion: admissionregistration.k8s.io/v1 kind: MutatingAdmissionPolicyBinding metadata: name: deploy-owner-policy-binding spec: policyName: deploy-owner-policy paramRef: name: deploy-meta namespace: default matchResources: namespaceSelector: matchLabels: purpose: default-apps failurePolicy: Ignore

params.data.owner即绑定 ConfigMap 的data.owner字段;jsonpatch.escapeKey()负责转义路径键中的/(转成~1),含斜杠的注解 key 必须走它。参数资源还可以是自定义 CRD,改镜像版本、换 owner 只改数据,不动策略本身。

JSON Patch 条件分支与 ApplyConfiguration 何时选用

JSON Patch 支持完整的 RFC 6902 操作符(add、remove、replace、move、copy、test),test可以构造"先查后改"的分支:

[ JSONPatch{op: "test", path: "/spec/template/spec/containers/0/image", value: "app/web:debug"}, JSONPatch{op: "replace", path: "/spec/template/spec/containers/0/image", value: "app/web:stable-2026.10"} ]

前一条test校验镜像是否为 debug 版,不满足时整组 patch 执行失败,由failurePolicy决定后续行为。

ApplyConfiguration 模式则是用 CEL 直接构造对象结构,按 Server-Side Apply 的合并规则改写:

Object{ metadata: Object.metadata{ annotations: Object.metadata.annotations{ "policy.k8s.local/applied-by": "deploy-owner-policy" } } }

它只覆盖写出的字段,其余内容保持不变;限制是不能直接修改原子字段(整个 map 或 array 会被视为整体)。

两种 patch 形态的取舍:

维度JSONPatchApplyConfiguration
典型场景点状增删改、条件分支、数组插入结构化字段声明式覆盖
路径写法JSON Pointer 字符串,需 escapeKey字段路径按Object.xxx写法
数组处理add到path/-追加受限,避免直接改原子 array
条件能力可用test操作符表达式本身可用完整 CEL 判断

两者都能做条件判断,选择看改动形态而非性能。仓库里可直接参考 applyconfiguration-example.yaml 和 json-patch-example.yaml 两个示例。

策略没生效时按这 5 步排查

👇 排障时按表从上往下走,前 4 项覆盖了绝大多数"看起来没工作"的情况:

症状常见原因处理
改动完全没发生只有 Policy 没有 Binding;或policyName与metadata.name对不上kubectl get mutatingadmissionpolicybindings -o yaml核对
改动完全没发生matchResources/namespaceSelector没覆盖目标命名空间确认目标 ns 带上了被选择器命中的标签
想按命名空间标签过滤,条件不生效matchConditions中namespaceObject恒为 null把命名空间条件挪到 binding 的namespaceSelector
请求被拒绝且日志有 CEL 报错CEL 求值失败且failurePolicy: Fail(默认)非关键改动改为Ignore,并用 CEL Playground 离线验证表达式
想给 Policy/Binding 自身打标签,不生效准入配置类资源豁免于策略(防自举死锁)用kubectl label手工维护,或走 manifest-based admission control

补充两条硬约束:TokenReview、SubjectAccessReview 等鉴权类虚拟资源对所有策略豁免;kubectl describe mutatingadmissionpolicybinding <name>可以看到 binding 的 status 信息,配合 apiserver 日志(--v=4以上可见 CEL 求值细节)定位表达式错误。

MutatingAdmissionPolicy 与 MutatingWebhook 怎么选

维度MutatingAdmissionPolicyMutatingAdmissionWebhook
运行位置API Server 进程内独立服务 + 网络调用
证书 / 端口 / 副本不需要全部需要维护
表达能力CEL 可表达的逻辑(含条件、参数)任意代码,可查外部 API、数据库
性能开销进程内求值每请求一次 HTTPS 往返
变更发布改 YAML 即生效发版 + 重启
故障形态按failurePolicy拒绝或跳过Webhook 不可达时同样按 failurePolicy,另叠加服务运维风险

适用边界:字段默认值、标签/注解注入、简单条件改写、按参数供数 → Policy;需要跨对象查询、调外部系统、复杂状态机 → Webhook。两者可以并存,同一请求会串行经过所有命中的变更插件。

选型速查表与版本注意

问题结论
我的集群版本够吗1.30–1.33 alpha、1.34–1.35 beta,1.36 起 GA 且默认开启,详见 Feature Gate 页面
API group 用哪个GA 后为admissionregistration.k8s.io/v1,旧教程里的v1alpha1需核对版本
策略需要参数吗不需要则省略spec.paramKind,binding 里也不写paramRef
想改镜像版本、owner 这类常变值放参数资源(ConfigMap/CRD),别写死在表达式里
CEL 表达式哪里试用 CEL Playground 离线验证,再kubectl apply
上线节奏开发集群先启用 →matchResources限定最小范围观察 → 再逐步扩大

决策原则一句话:能写成 CEL 表达式的字段级改动,优先 Policy;必须出集群查数据,才保留 Webhook。落地前以官方文档中你所用版本的字段说明为准。

【免费下载链接】websiteKubernetes website and documentation repo:项目地址: https://gitcode.com/GitHub_Trending/webs/website

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询