一次 kubectl apply 为什么会被 503 拒绝,但
etcd 里没写任何脏数据?因为 Admission 链在 AuthZ
之后、storage.Interface 持久化之前运行——任何
Admission 插件或 webhook 返回非 OK
响应,整条请求就在这一层失败,不进 etcd。这个设计使
Admission 成为集群策略的最后一道门,也使其成为慢创建与 503
的高频来源。
生产里常见误判:把 Webhook 的 503 归因到 etcd,或把 ResourceQuota 拦截当成 storage 写入失败。定位时应先确认失败发生在 Admission 轴,再区分内置插件还是 webhook。
本篇钉 v1.30.3 的 Admission 链位置、Mutating →
Validating
两阶段顺序、内置插件的注册入口(pkg/kubeapiserver/admission/)与典型示例、webhook
集成的边界与 timeout,以及 fail-open vs
fail-closed 争议。第 9 篇详写
MutatingAdmissionWebhook 与 ValidatingAdmissionWebhook 的
timeout/failurePolicy/reinvocation 细节。
本篇在系列中的位置
篇目 核心内容 第 7 篇 · Watch 路径(服务端) 长连接、410 Gone、timeout 第 8 篇 · Admission 链概览 Mutating/Validating 顺序、内置插件、webhook 边界 第 9 篇 · Mutating/Validating Webhook timeout、failurePolicy 深度 系列目录 五轴、阅读路径
版本锚定:Kubernetes v1.30.3(源码 tag
v1.30.3)。机制钉staging/src/k8s.io/apiserver/pkg/admission/、plugin/pkg/admission/、pkg/kubeapiserver/admission/@ kubernetes/kubernetes v1.30.3。内置插件的完整注册顺序以 K8s 官方文档(Admission Controllers Reference)和该 tag 源码为准;本篇只举具体示例,不尝试穷举。无真实集群则不粘贴伪造kubectl输出。
一、Admission 在请求路径中的位置
请求在 kube-apiserver 中经过:AuthN → AuthZ → Admission → storage.Interface(etcd)。
flowchart LR
authn["AuthN\n(JWT/OIDC/SA)"]
authz["AuthZ\n(RBAC/Webhook)"]
decode["对象解码 / 默认值填充"]
mutating["Mutating Admission\n(内置 + MutatingWebhook)"]
validate_obj["对象验证\n(schema / CEL)"]
validating["Validating Admission\n(内置 + ValidatingWebhook)"]
storage["storage.Interface\n(etcd3 / cacher)"]
authn --> authz
authz --> decode
decode --> mutating
mutating --> validate_obj
validate_obj --> validating
validating --> storage
几个关键点:
- 对象解码与默认值填充发生在 Admission 之前,Admission 插件看到的是已经填充 defaultValue 的对象。
- Mutating 先于 Validating:Mutating 插件(含 MutatingAdmissionWebhook)可以修改对象;Validating 插件(含 ValidatingAdmissionWebhook)只能通过/拒绝,不能修改。
- 对象 schema 验证(OpenAPI / CEL)紧随 Mutating 完成后执行,Validating webhook 看到的是 schema 合法的对象。
- Admission 失败后立即返回错误给客户端,etcd 完全不感知——这是 Admission 与 storage 失败最重要的分列特征。
二、两阶段顺序:Mutating 与 Validating
Admission 在 v1 API 中固定分为两个阶段,顺序不可重排:
Mutating 阶段:所有实现了
admission.MutationInterface
的插件按注册顺序调用,包括
MutatingAdmissionWebhook
插件(负责调用用户配置的
MutatingWebhookConfiguration)。每个 mutating
插件可修改对象。
Validating 阶段:所有实现了
admission.ValidationInterface
的插件按注册顺序调用,包括
ValidatingAdmissionWebhook 和
ValidatingAdmissionPolicy(CEL 内联策略,v1.30
GA)。Validating 插件不修改对象,只返回通过或错误。
部分内置插件同时实现两个接口(既 mutate 又
validate),这类插件在两个阶段都会被调用(例如
ServiceAccount 插件:mutating 阶段注入
serviceAccountToken 挂载,validating 阶段检查 ServiceAccount
是否存在)。
三、内置插件:注册路径与典型示例
内置 Admission 插件通过
pkg/kubeapiserver/admission/
下的注册入口集中装配(RegisterAllAdmissionPlugins),再由
kube-apiserver 的 --enable-admission-plugins /
--disable-admission-plugins flag
控制启用。插件的实际注册顺序由 K8s
源码决定,官方文档 Admission
Controllers Reference 给出 v1.30
中各插件的建议启用集合。
以下是三类典型内置插件及其关键行为(举例,非完整顺序):
NamespaceLifecycle(mutating + validating)
- 若目标 Namespace 处于
Terminating状态,拒绝在其中创建新对象(防止终止中的 Namespace 被写入)。 - Namespace 自身的 CREATE 与 DELETE 不受此插件拦截。
LimitRanger(mutating + validating)
- 对没有显式设置
resources.requests/limits的 Pod/Container,按LimitRange对象填充默认值(mutating)。 - 校验对象的资源请求/限制是否在
LimitRange规定范围内(validating)。 - 排障场景:Pod 被拒绝,错误信息含
LimitRange,通常是resources.requests.cpu超出 LimitRange 上限,不是 etcd 或 storage 问题。
ResourceQuota(validating)
- 检查 Namespace 的配额使用量是否会因本次操作超限;超限则拒绝请求。
- 配额计数以 API 对象为单位(Pod 数量、CPU
总和等),存储在 etcd 的
ResourceQuota对象中;检查时 apiserver 从缓存读取当前用量,并以乐观锁方式更新。 - 排障场景:Create Pod 返回
exceeded quota,但 etcd 写入没有发生——Admission 阶段就已失败。
这些插件展示了 Admission 链的典型模式:先修改(LimitRanger 填默认值),再校验(ResourceQuota 计数检查)。复杂对象(如 StatefulSet)在 Admission 阶段可能经历多个 mutating 插件的累积修改,最后才进入 ValidatingAdmissionWebhook 和 storage。
四、webhook 集成:MutatingAdmissionWebhook 与 ValidatingAdmissionWebhook
MutatingAdmissionWebhook 和
ValidatingAdmissionWebhook
本身是两个内置插件,分别在 Mutating 和 Validating
阶段被调用。它们读取集群中的
MutatingWebhookConfiguration /
ValidatingWebhookConfiguration 对象,按配置的
rules、namespaceSelector、objectSelector 过滤,向注册的
webhook endpoint(用户自己部署的 HTTP 服务)发送
AdmissionReview 请求。
sequenceDiagram
participant apiserver
participant webhookSvc as webhook service\n(用户部署)
apiserver->>webhookSvc: POST /validate\nAdmissionReview{request: ...}
alt 允许
webhookSvc-->>apiserver: AdmissionReview{response: {allowed: true}}
else 拒绝
webhookSvc-->>apiserver: AdmissionReview{response: {allowed: false, status: ...}}
else 超时/连接失败
webhookSvc-->>apiserver: (无响应)
apiserver->>apiserver: failurePolicy 决定
end
timeout:WebhookClientConfig.TimeoutSeconds(默认
10s,上限 30s)。超时后进入 failurePolicy
判断(见第 9 篇)。
失败在 storage 之前:无论 webhook 返回 allowed=false 还是超时被 failurePolicy=Fail 判为拒绝,请求都在此终止,etcd 不写入。
五、fail-open vs fail-closed:工程争议
Webhook 的 failurePolicy 控制当 webhook
服务本身不可达(网络故障、Pod 崩溃、超时)时的行为:
failurePolicy: Fail(fail-closed):webhook 故障 → 请求被拒绝。安全性高,但 webhook 服务成为集群可用性的一部分——webhook Pod 异常会导致创建请求失败,影响控制器协调循环。failurePolicy: Ignore(fail-open):webhook 故障 → 请求通过(当作未审计)。可用性高,但安全保障变弱——策略漏洞可能在 webhook 故障时短暂失效。
争议的实质:这是 availability vs safety 的经典 trade-off,在分布式系统中没有统一答案。
K8s 官方文档(Admission Webhooks · Availability)建议:
- 安全敏感的 webhook(如 OPA/Gatekeeper
的策略校验、image policy)应使用
Fail,并保证 webhook 部署的 HA(至少 2 副本、PodDisruptionBudget); - 可观测性辅助类 webhook(如 label 注入、sidecar 注入)可视具体策略选择,但需评估 Ignore 后的业务影响。
工程上,fail-closed + webhook HA 的组合优于 fail-open + 单副本 webhook——后者在 webhook 升级或节点驱逐时会形成静默策略窗口(silent policy gap)。这个权衡在 etcd/16 §5.1 开放问题列表中也有提及:apiserver 侧 webhook 可用性 SLO 目前没有与 etcd compaction SLO 联合建模的标准。
六、Admission 轴排障口令
| 症状 | 倾向 |
|---|---|
| Create/Update 返回 4xx,etcd 无写入 | Admission 轴(检查错误 message) |
错误含 admission webhook ... denied |
ValidatingAdmissionWebhook 拒绝 |
错误含 failed calling webhook +
503/504 |
Webhook endpoint 不可达;查 failurePolicy |
错误含 exceeded quota |
ResourceQuota 插件拒绝 |
错误含 LimitRange |
LimitRanger 插件拒绝 |
错误含
unable to create ... namespace ... terminating |
NamespaceLifecycle 插件拒绝 |
| Create 成功但对象多了注入字段 | MutatingAdmissionWebhook 修改了对象 |
第 9 篇深写 webhook 的 timeout/failurePolicy/reinvocation;第 15 篇给出五轴口令完整表。
参考资料
规范 / 源码(A)
- kubernetes/kubernetes
v1.30.3:staging/src/k8s.io/apiserver/pkg/admission/;plugin/pkg/admission/;pkg/kubeapiserver/admission/ - Kubernetes · Admission Controllers Reference (v1.30)
- Kubernetes · Dynamic Admission Control
- KEP-3488 CEL for Admission Control (ValidatingAdmissionPolicy GA v1.30)
论文 / 对照(B)
- Brewer, E.A., Towards robust distributed systems, PODC 2000 keynote(CAP 定理;fail-open vs fail-closed 背景)——不支撑 Kubernetes 具体数值,但用于理解 availability vs safety 的范式
站内对照
上一篇:Watch 路径(服务端):长连接、410 Gone 与 timeout
下一篇:Mutating / Validating Webhook
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【kube-apiserver】Mutating / Validating Webhook:timeout、failurePolicy 与可用性门
钉 K8s v1.30.3 的 MutatingWebhookConfiguration / ValidatingWebhookConfiguration v1:timeoutSeconds、failurePolicy、sideEffects、reinvocationPolicy 字段语义;webhook 慢如何表现为写路径延迟而非 etcd lag;生产可用性门选取;CEL ValidatingAdmissionPolicy 作为内置替代路径;排障证据包。
【kube-apiserver】控制面全景:缺口、五轴坐标系与 16 篇路线
相对 etcd/13、distributed/50、k8s-network 补齐 kube-apiserver 生产内核缺口;以 Storage/Watch/Admission/Auth/APF 五轴为坐标系定义 16 篇阅读路线;版本锚定 Kubernetes v1.30.3。
【kube-apiserver】CRD / aggregation / 扩展边界:API 扩展停损线
厘清 CRD v1 与 Aggregated API 两条扩展路径在 kube-apiserver 中的存储与请求分界:CRD 对象存 etcd、conversion webhook 失败如何体现在 Storage 轴,APIService 则把请求转发到外部 Extension Server 可引发 503。明确 scheduler/controller/kubelet 为扩展停损线之外的数据面与控制循环。
【kube-apiserver】排障五轴:Storage、Watch、Admission、Auth、APF
按 Storage/Watch/Admission/Auth/APF 五轴做症状否证;给出完整五轴命令表与症状→轴映射(504、410、401、403、webhook 超时、List 风暴、OOM);说明 apiserver_request_duration_seconds 等核心 metrics 语义;并提供决策树:何时穿透到 etcd/15,何时留在 apiserver 轴。