找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

6117

积分

0

好友

781

主题
发表于 4 天前 | 查看: 8| 回复: 0

1. 背景与现象

周一上午一次常规发布,运维在 tenant-a 命名空间新建一批 Pod,结果 kubectl get pod 里全是 PendingContainerCreating ,十几分钟没动静。更诡异的是,别的命名空间一切正常,只有 tenant-a 出问题。describe 一个 Pod 看事件:

kubectl describe pod -n tenant-a api-gw-7d9f-abc | tail -20
# Events:
#   Type     Reason        Age   From               Message
#   Warning  FailedCreate  3m    replicaset-controller
#     Error creating: Internal error occurred: failed calling webhook
#     "inject.sidecar.io": Post "https://sidecar-injector.svc:8443/mutate":
#     net/http: request canceled while waiting for connection (Client.Timeout exceeded)

关键句: failed calling webhook "inject.sidecar.io" ,连接 sidecar-injector.svc:8443 超时。这是典型的准入 Webhook挡住了 Pod 创建:apiserver 在收下创建请求后,要先过一遍准入控制器,其中一个是调用外部 Webhook 服务做 Sidecar 注入,而那个服务现在连不上。

2. 排查过程

先确认是哪个 Webhook 配置在生效,以及它的失败策略是什么:

# 列出所有 MutatingWebhookConfiguration
kubectl get mutatingwebhookconfigurations
# sidecar-injector

# 看它的关键字段:failurePolicy、namespaceSelector、clientConfig、timeoutSeconds
kubectl get mutatingwebhookconfiguration sidecar-injector -o yaml
# webhooks:
# - name: inject.sidecar.io
#   failurePolicy: Fail          # 关键:webhook 出错就整体失败
#   timeoutSeconds: 10
#   namespaceSelector:
#     matchLabels: {inject: "enabled"}   # tenant-a 正好打了这个 label
#   clientConfig:
#     service:
#       namespace: sidecar-injector
#       name: sidecar-injector
#       path: /mutate
#       port: 8443

failurePolicy: Fail 是元凶:它规定只要 Webhook 调用失败(含超时、证书错、服务不存在),apiserver 就拒绝整个创建请求。而 tenant-a 的 namespace 打了 inject: enabled ,正好被 namespaceSelector 选中,所以只有这个命名空间中招。

然后确认 Webhook 后端服务到底怎么了:

# Webhook 后端 Pod 是否活着
kubectl get pod -n sidecar-injector
# NAME                     READY  STATUS   RESTARTS  AGE
# sidecar-injector-0       0/1    CrashLoopBackOff   14  22m

# 看它为什么挂
kubectl logs -n sidecar-injector sidecar-injector-0 --tail=30
# failed to load TLS cert: tls: failed to find certificate "sidecar-injector.svc"
# in ca cert pool

后端 Pod 因为证书加载失败一直在 CrashLoopBackOff。证书是几个月前用自有 CA 签的,过期了没续,服务起不来,apiserver 调它自然超时。

补充一条排查 apiserver 本身的命令,确认超时发生在准入阶段而非别处:

# 在控制面节点看 apiserver 日志(高版本用 kubectl 取不到,需登节点)
# grep -i "calling webhook" /var/log/pods/kube-system_kube-apiserver*/kube-apiserver/*.log | tail
# 或直接看 webhook 延迟指标
kubectl get --raw /metrics | \
  grep admission_webhook_admission_duration_seconds_count | grep inject.sidecar

3. 根因分析

K8s 的准入控制(Admission Control)发生在"请求通过认证和 RBAC 鉴权之后、对象真正落库之前"。它分两阶段:先跑 Mutating Webhook(可以改请求体,比如注入 Sidecar、改资源),再跑 Validating Webhook(只能放行或拒绝)。

Webhook 本质是个 HTTPS 回调:apiserver 把对象序列化成 AdmissionReview 发到 Webhook 服务的 /mutate ,等服务回包。这里有几个脆弱点:

  • failurePolicy 决定 Webhook 不可达时怎么办。 Fail 会直接拒绝请求(安全但容易"一刀切"误伤), Ignore 则跳过这个 Webhook 继续往下走(可用但可能放过本该拦截的变更)。
  • 默认 timeoutSeconds 是 10 秒,Webhook 服务慢或网络抖一下就超时。
  • 证书:Webhook 服务用 HTTPS,apiserver 要校验它的服务端证书,CA bundle 配在 clientConfig.caBundle 。证书过期或 CA 不对,TLS 握手直接失败。
  • 网络: namespaceSelectorobjectSelector 决定哪些对象走这个 Webhook;背后 Service 的 8443 端口若被 NetworkPolicy 或 iptables 挡住,同样超时。

这次事故是"证书过期 → 后端起不来 → apiserver 调超时 → failurePolicy=Fail → 创建被拒"的完整链条。

补充一个排查视角: MutatingWebhookConfiguration 负责改对象, ValidatingWebhookConfiguration 只负责放行或拒绝,两者都会触发外部调用。如果 describe Pod 报的是 validating 而非 mutating,排查路径一样,只是看的另一份配置。另外 objectSelector (按对象的 label 选)和 namespaceSelector (按命名空间 label 选)是 Webhook 作用范围的开关,定位"为什么只有某批 Pod 受影响"时,这两个 selector 是第一个要看的字段。

4. 解决方案

先止血,保住发布。最快的办法是把 failurePolicy 临时改成 Ignore ,让创建请求绕过这个坏掉的 Webhook:

# 临时改成 Ignore 止血(紧急发布用,事后必须修)
kubectl patch mutatingwebhookconfiguration sidecar-injector \
  --type='json' \
  -p='[{"op":"replace","path":"/webhooks/0/failurePolicy","value":"Ignore"}]'

# 确认
kubectl get mutatingwebhookconfiguration sidecar-injector \
  -o jsonpath='{.webhooks[0].failurePolicy}'
# Ignore

改完立刻再建 Pod, tenant-a 的 Pod 正常起来了(只是这次没被注入 Sidecar,可接受,因为后端挂了本来也注入不了)。

然后根治。证书过期是根因,重新签一份有效证书并滚动后端:

# 用现有 CA 重新签发服务端证书(示例用 openssl)
openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout tls.key -out tls.crt \
  -days 365 -subj "/CN=sidecar-injector.sidecar-injector.svc"

# 更新 Secret,滚动重启 Webhook  Deployment
kubectl -n sidecar-injector create secret tls sidecar-injector-tls \
  --cert=tls.crt --key=tls.key --dry-run=client -o yaml | kubectl apply -f -
kubectl -n sidecar-injector rollout restart deployment sidecar-injector

# 后端 Ready 后,把 failurePolicy 改回 Fail(恢复安全策略)
kubectl patch mutatingwebhookconfiguration sidecar-injector \
  --type='json' \
  -p='[{"op":"replace","path":"/webhooks/0/failurePolicy","value":"Fail"}]'

几个加固点顺手做了:把 timeoutSeconds 从默认 10 调到合理值(如 3~5 秒,别拖太长),给 namespaceSelectorinject: enabled 的精确匹配避免误伤,并在 CA 证书上挂续期监控。

5. 复盘与预防

准入 Webhook 是个"单点",它挂了能直接卡住整个创建链路,所以稳定性要求比普通服务高。建议:

监控 Webhook 健康度,apiserver 自带指标:

- alert: AdmissionWebhookSlow
  expr: histogram_quantile(0.99,
        rate(apiserver_admission_webhook_admission_duration_seconds_bucket{name="inject.sidecar.io"}[5m]))
  > 3
  for: 10m
  labels: {severity: warning}
- alert: AdmissionWebhookError
  expr: sum(rate(apiserver_admission_webhook_request_total{name="inject.sidecar.io",error="true"}[5m]))
  > 0
  for: 5m
  labels: {severity: critical}

证书续期用 cert-manager 托管,别手写自签;并在证书剩余 30 天时告警,别等过期当天爆雷。

变更管控:Webhook 的 Deployment、证书、MutatingWebhookConfiguration 必须走灰度发布,先 namespaceSelector 缩小到测试命名空间验证,再放大。紧急止血的 failurePolicy: Ignore 改完记得回填 Fail —— 很多人止血后忘了恢复,埋下安全隐患。

6. 小结

准入 Webhook 超时导致 Pod 卡创建,排查就三板斧:describe Pod 看是不是 failed calling webhookkubectl get mutatingwebhookconfigurationfailurePolicy 和后端服务,再 kubectl logs 后端确认证书/存活。 failurePolicy: Fail 是双刃剑,止血先改 Ignore ,根治后回填 Fail

下篇换个角度聊 K8s Pod 一直 ContainerCreating 的另一类根因:Secret/ConfigMap 挂载失败与 volume 阻塞——什么时候是 Webhook 问题、什么时候是存储问题,一篇文章讲清楚。




上一篇:高频做市复盘:订单队列、库存与风控,我们究竟在优化什么?
下一篇:AI 编程工具不能一直待在终端里,PI-Desktop 桌面工作台解析
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-9-17 16:15 , Processed in 0.552204 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表