3. Mutating 与 Validating Webhook:两种 Webhook 的区别、调用顺序、典型应用场景

Kubernetes 准入控制(Admission Control)中的 Webhook 分为 MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook 两种类型。它们在 API 请求持久化之前介入,但职责、调用顺序和典型用途截然不同。理解两者的差异是编写可靠 Webhook 的基础,也是校准存储版本漂移的关键前提。

3.1 核心职责对比

对比维度 Mutating Webhook Validating Webhook
核心职责 修改(Mutate)传入的对象 校验(Validate)传入的对象
是否可修改对象 是,可增删改字段、注入 Sidecar、设置默认值 否,只能读取,不可修改
返回结果 返回修改后的对象 JSON Patch 返回允许(Allowed)或拒绝(Denied)
失败策略 Fail / Ignore Fail / Ignore
超时控制 30 秒(可配置) 30 秒(可配置)
幂等性要求 必须幂等,避免重复注入 通常无幂等要求
关键原则:Mutating Webhook 必须保证幂等性。如果同一个 Pod 创建请求被多次拦截,注入 Sidecar 的 JSON Patch 不能重复添加同一个容器或 Volume,否则会导致 API 请求失败。

3.2 调用顺序与执行流程

在 Kubernetes API Server 处理一个创建、更新或删除请求时,Webhook 的调用顺序是固定的:

  1. 认证与授权(非 Webhook 阶段)
  2. Mutating Webhooks(按配置顺序执行)
  3. 对象 Schema 校验(内置校验)
  4. Validating Webhooks(按配置顺序执行)
  5. 持久化到 etcd

这种顺序设计有明确的工程考量:先让 Mutating Webhook 修改对象(如注入 Sidecar、设置默认值),再让 Validating Webhook 对修改后的完整对象进行校验。如果顺序颠倒,Validating Webhook 校验的将是一个不完整的对象,导致误判。

存储版本漂移校准提示:当 Webhook 修改了对象字段(Mutating),但 etcd 中存储的版本与 Webhook 修改后的版本不一致时,Validating Webhook 可能基于旧版本规则进行校验,导致拒绝本应通过的请求。校准策略通常是在 Validating Webhook 中增加版本兼容性检查。
Mutating 与 Validating Webhook 调用顺序流程图 Mutating → Validating Webhook 调用顺序 API 请求到达 Mutating Webhooks(修改对象) Schema 校验 Validating Webhooks(校验对象) 顺序不可颠倒 先修改,后校验 Mutating 修改后的对象 才进入 Validating 校验

3.3 典型应用场景

3.3.1 Mutating Webhook:注入 Sidecar

最经典的 Mutating Webhook 场景是 Sidecar 注入,例如 Istio 的 sidecar-injector。当用户创建 Pod 时,Webhook 拦截请求,自动向 Pod 的 spec.containers 中添加一个 Envoy 代理容器,并挂载必要的 Volume。

// 注入 Sidecar 的 JSON Patch 示例
[
  {
    "op": "add",
    "path": "/spec/containers/-",
    "value": {
      "name": "istio-proxy",
      "image": "istio/proxyv2:1.18.0",
      "args": ["proxy", "sidecar"],
      "ports": [
        { "containerPort": 15090, "protocol": "TCP" }
      ]
    }
  },
  {
    "op": "add",
    "path": "/spec/volumes/-",
    "value": {
      "name": "istio-envoy",
      "emptyDir": {}
    }
  }
]
幂等性陷阱:如果 Webhook 不检查容器是否已存在,直接执行 add 操作,当请求被重试或 Webhook 被多次调用时,Pod 中会出现多个同名容器,导致 API Server 拒绝该请求。正确的做法是先检查 /spec/containers 中是否已有 name: istio-proxy,有则跳过。

3.3.2 Validating Webhook:校验规则

Validating Webhook 用于执行 不可修改的校验逻辑,例如:

  • 资源配额校验:检查 Namespace 中 Deployment 的总副本数是否超过限制。
  • 安全策略校验:禁止容器以 root 用户运行,或禁止使用 hostNetwork。
  • 存储版本兼容性校验:检查请求中的资源版本是否与集群中存储的版本兼容,防止存储版本漂移导致的数据不一致。
  • 命名规范校验:强制资源名称符合正则表达式规则。
// Validating Webhook 拒绝响应示例
{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
    "allowed": false,
    "status": {
      "code": 403,
      "message": "Pod 使用 hostNetwork 被禁止,请移除 spec.hostNetwork: true"
    }
  }
}

3.4 两种 Webhook 的协同工作模式

在实际生产环境中,Mutating 和 Validating Webhook 经常配合使用,形成完整的准入控制链:

Mutating + Validating Webhook 协同工作架构图 Mutating + Validating Webhook 协同工作架构 用户 / kubectl API Server Mutating Webhook 注入 Sidecar / 设置默认值 Validating Webhook 校验规则 / 版本兼容性检查 etcd 持久化 协同流程 1. Mutating 修改对象 2. 修改后的对象返回 API Server 3. Validating 校验修改后的对象 4. 通过后持久化到 etcd

3.5 存储版本漂移场景下的选择建议

校准策略:在存储版本漂移校准场景中,建议将版本兼容性检查放在 Validating Webhook 中,将字段自动修正(如填充默认值、转换旧版本字段)放在 Mutating Webhook 中。这样既保证了修改的灵活性,又确保了最终对象的合规性。
漂移类型 推荐 Webhook 类型 处理方式
字段缺失(旧版本无新字段) Mutating Webhook 自动添加默认值
字段值不合法(新版本约束更严) Validating Webhook 拒绝并提示升级
字段结构变更(如 v1 与 v2 字段映射) Mutating Webhook 执行字段转换 JSON Patch
版本兼容性校验 Validating Webhook 检查 apiVersion 是否在允许列表中