为什么 GKE 升级会卡在 Admission Webhook 上
在日常维护 Google Kubernetes Engine (GKE) 集群时,升级版本是很正常的一部分。升级可以修补安全漏洞、保持控制平面健康,也能让你拿到新的 Kubernetes 功能。但在实际操作中,升级还是可能卡住,并在 Logs Explorer 里出现像 Internal error 或 DeployPatch failed 这样的信息1。
很多时候,真正挡住升级的并不是 GKE 本身,而是集群里安装的准入 Webhook。像 Gatekeeper 或 Kyverno 这类第三方或自定义 webhook,可能会无意间拦截 GKE 在控制平面升级期间需要做的系统层级资源变更。Webhook 原本不是要挡住 GKE,但一旦它把这些请求拦下来,升级就会失败。
本文可以看作《管理 Kubernetes Webhook 故障:从诊断到解决方案》的延伸案例,重点说明为什么 Webhook 配置不当会成为 GKE 升级的隐患,并按照对生产环境影响从低到高,整理三种可操作的修复方式。
为什么 Admission Webhook 会挡住 GKE 升级
在控制平面升级期间,GKE 会重建核心控制平面组件,并同步系统资源,例如 ClusterRoles 和 ClusterRoleBindings。如果你的 webhook 会匹配这些请求,而且策略又很严格,API Server 就必须先调用 webhook,才能继续完成更新。
升级时之所以特别容易出问题,是因为很多事情会同时变化:
- 控制平面正在重启或重新配置;
- 负责 webhook 的 Pod 可能重启,或短时间内不可用;
- 如果
konnectivity-agent的运行或调度受到影响,可能出现类似No agent available的错误; - 控制平面和 webhook 服务之间的网络路径可能被打断。
如果 webhook 的 failurePolicy 设成 Fail,任何调用失败都会被当成准入失败。API Server 会直接拒绝这次请求,升级就可能卡住。
GKE 的官方排障文档也明确提到这个模式,并建议不要让 webhook 拦截带有 system: 前缀的系统资源1。
修复方式:从低风险到高风险
1. 用 CEL matchConditions 跳过系统前缀请求
matchConditions 在 Kubernetes v1.30 进入稳定状态。MutatingWebhookConfiguration 和 ValidatingWebhookConfiguration 可以通过这个字段,在 webhook 被调用之前,先用 CEL 表达式过滤请求2。
对于可能匹配系统资源的 webhook,常见做法是把 GKE 管理的、名称以 system: 开头的资源排除掉:
webhooks:
- name: validate.your-webhook-name
matchConditions:
- name: "exclude-system-prefixes"
expression: "!request.name.startsWith('system:')"
这条规则会在 API Server 层直接过滤请求。只要资源名称以 system: 开头,API Server 就会跳过 webhook,避免 GKE 核心组件在升级期间被错误拦截。
这里的表达式要根据你自己的 webhook 作用范围来调整。核心思路很简单:对用户工作负载继续保持严格校验,但把 GKE 升级时必须经过的系统层级路径排除出去。
这是影响范围最小的做法,因为它还能保留其他资源上的 failurePolicy: Fail。如果你的 webhook 是通过 Helm、Argo CD 或其他 GitOps 流程部署的,要特别注意这条临时规则不要在升级过程中被覆盖。
2. 暂时把 failurePolicy 改成 Ignore
如果你暂时还来不及引入 matchConditions,可以先在升级窗口里把 webhook 改成 Ignore,作为过渡方案。
webhooks:
- name: validate.your-webhook-name
failurePolicy: Ignore
当 failurePolicy 设成 Ignore 时,如果 webhook 调用失败或连不上服务,API Server 会跳过这次 webhook 调用,让请求继续往下走3。
这个做法只能当临时桥梁。升级完成、集群恢复稳定后,一定要把策略改回 Fail,让 webhook 继续执行原本的安全控制。
3. 临时删除造成干扰的 Webhook 配置
如果升级已经很紧急,而你又没有时间安全地调整策略,最后的手段就是先删掉 webhook 配置,完成升级后再重新应用回去。
# 备份并删除 mutating webhook 配置
kubectl get MutatingWebhookConfiguration [NAME] -o yaml > mutating-webhook-config.yaml
kubectl delete MutatingWebhookConfiguration [NAME]
# 备份并删除 validating webhook 配置
kubectl get ValidatingWebhookConfiguration [NAME] -o yaml > validating-webhook-config.yaml
kubectl delete ValidatingWebhookConfiguration [NAME]
这通常可以立刻解除升级阻塞,但它也是破坏性最大的方案,因为在 webhook 被删除期间,集群会暂时失去这层保护。升级结束后,请尽快把配置恢复回来。
简单对比
| 方案 | 风险 | 适用场景 |
|---|---|---|
用 CEL matchConditions 排除 |
低 | 集群版本支持时的最佳默认方案 |
failurePolicy: Ignore |
中 | 升级窗口里的短期过渡方案 |
| 删除 webhook 配置 | 高 | 紧急情况下的最后手段 |
总结
Admission Webhook 是很重要的安全防线,但它也可能成为 GKE 升级卡住时最隐蔽的原因。最稳妥的做法,是把 webhook 的作用范围写清楚,并在策略不需要检查系统资源时,把这些资源排除在准入路径之外。
- 排除 Google 托管的命名空间,例如
kube-system和kube-node-lease,避免 webhook 干扰系统组件。 - 如果集群版本支持,优先使用 CEL
matchConditions。 - 排除 GKE 管理的系统资源,尤其是带
system:前缀的资源。 - 只有在临时恢复升级时,才考虑使用
failurePolicy: Ignore或直接删除配置。
只要把 webhook 的作用范围当成升级设计的一部分,就能在保持集群安全的同时,避免一次控制平面升级变成事故。
参考资料