为什么 GKE 升级会卡在 Admission Webhook 上

为什么 GKE 升级会卡在 Admission Webhook 上

在日常维护 Google Kubernetes Engine (GKE) 集群时,升级版本是很正常的一部分。升级可以修补安全漏洞、保持控制平面健康,也能让你拿到新的 Kubernetes 功能。但在实际操作中,升级还是可能卡住,并在 Logs Explorer 里出现像 Internal errorDeployPatch failed 这样的信息1

很多时候,真正挡住升级的并不是 GKE 本身,而是集群里安装的准入 Webhook。像 Gatekeeper 或 Kyverno 这类第三方或自定义 webhook,可能会无意间拦截 GKE 在控制平面升级期间需要做的系统层级资源变更。Webhook 原本不是要挡住 GKE,但一旦它把这些请求拦下来,升级就会失败。

本文可以看作《管理 Kubernetes Webhook 故障:从诊断到解决方案》的延伸案例,重点说明为什么 Webhook 配置不当会成为 GKE 升级的隐患,并按照对生产环境影响从低到高,整理三种可操作的修复方式。

为什么 Admission Webhook 会挡住 GKE 升级

在控制平面升级期间,GKE 会重建核心控制平面组件,并同步系统资源,例如 ClusterRolesClusterRoleBindings。如果你的 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 进入稳定状态。MutatingWebhookConfigurationValidatingWebhookConfiguration 可以通过这个字段,在 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 的作用范围写清楚,并在策略不需要检查系统资源时,把这些资源排除在准入路径之外。

  1. 排除 Google 托管的命名空间,例如 kube-systemkube-node-lease,避免 webhook 干扰系统组件。
  2. 如果集群版本支持,优先使用 CEL matchConditions
  3. 排除 GKE 管理的系统资源,尤其是带 system: 前缀的资源。
  4. 只有在临时恢复升级时,才考虑使用 failurePolicy: Ignore 或直接删除配置。

只要把 webhook 的作用范围当成升级设计的一部分,就能在保持集群安全的同时,避免一次控制平面升级变成事故。

参考资料

Eason Cao
Eason Cao Eason is an engineer working at FANNG and living in Europe. He was accredited as AWS Professional Solution Architect, AWS Professional DevOps Engineer and CNCF Certified Kubernetes Administrator. He started his Kubernetes journey in 2017 and enjoys solving real-world business problems.
comments powered by Disqus