cover

背景:某消费电子公司基于阿里云 ACK 容器化部署 Claw 服务,某日香港生产环境突发大规模异常,一次看似普通的排查,却揭开了一个隐藏已久的"定时炸弹"。


事故发现

运维同学发现香港生产环境告警不断,一查才发现情况远比想象中严重——namespace 下大量 Pod 无法启动,持续 CrashLoopBackOff:

# kubectl get pods -n xxxxclaw --field-selector=status.phase!=Running

NAME                                 READY   STATUS                  RESTARTS         AGE
xxxxclaw-13407xxx-74f8654bbc-qwdjm   0/1     Init:CrashLoopBackOff   41 (4m28s ago)   3h9m
xxxxclaw-13409xxx-f9bd56556-n26gh    0/1     Init:CrashLoopBackOff   41 (4m28s ago)   3h9m
xxxxclaw-18600xxx-55cd9fd77d-bz2xm   0/1     Init:CrashLoopBackOff   41 (3m52s ago)   3h9m
xxxxclaw-18601xxx-84767f5cc7-9l4j8   0/1     Init:CrashLoopBackOff   48 (3m53s ago)   3h45m
...(共计 100+ Pod 异常)

超过100个服务同时趴下,新建的 Pod 起不来,重启旧 Pod 也救不活。整个 xxxxclaw namespace 几乎全军覆没。


初步排查:定位到 Init Container

随机抓一个异常 Pod 详细看:

#  kubectl describe pod xxxxclaw-60000010-85dffdb5f6-ck2ts -n xxxxclaw
...
Status:       Pending
Init Containers:
  fix-permission:
    State:          Waiting
      Reason:       CrashLoopBackOff
    Restart Count:  18
    Last State:     Terminated
      Reason:       Error
      Exit Code:    255
      Started:      Fri, 05 Jun 2026 10:42:15 +0000
      Finished:     Fri, 05 Jun 2026 10:42:15 +0000
...

所有 Pod 都卡在同一个 Init Container fix-permission,退出码 255,重启了十几次甚至几十次。

这个 Init Container 的逻辑极其简单,就是修改一个挂载目录的权限:

mkdir -p /home/node/.openclaw && chown -R 1000:1000 /home/node/.openclaw

就这么一个命令,能有什么问题?


第一个怀疑:PVC 挂载异常?

Init Container 挂载了一块阿里云 ESSD 云盘:

volumes:
  - name: storage-xxxxclaw-60000xxx
    persistentVolumeClaim:
      claimName: pvc-xxxxclaw-60000xxx-essd

退出码 255,在容器场景下通常意味着容器运行时层面的错误,最常见的就是 Volume 挂载失败。

赶紧检查 PVC 状态:

# kubectl get pvc pvc-xxxxclaw-60000xxx-essd -n xxxxclaw

NAME                         STATUS   VOLUME                   CAPACITY   ACCESS MODES   STORAGECLASS
pvc-xxxxclaw-60000xxx-essd   Bound    d-j6c8lh82xanugohbmxxx   50Gi       RWO            xclaw-essd-1

PVC 状态正常,Bound。 云盘没问题,排除。

而且这么多 Pod 同时挂,不可能每个都是存储问题,一定有更深层的共同原因。


第二个怀疑:日志里藏着答案?

既然存储没问题,那就看日志:

# kubectl logs xxxxclaw-60000xxx-85dffdb5f6-ck2ts \
  -n xxxxclaw \
  -c fix-permission \
  --previous

unable to retrieve container logs for containerd://eb08ec548a9d07b11b72f8987ce51fdf8767053447c7d34746af8755cdf13b60

日志获取失败。

再回头看事件记录:

Started:      Fri, 05 Jun 2026 10:42:15 +0000
Finished:     Fri, 05 Jun 2026 10:42:15 +0000

容器同一秒启动、同一秒退出,几乎没有任何输出,日志自然丢失。这个容器根本来不及说任何话就死了。

事情开始变得诡异。


柳暗花明:真正的报错信息

再次尝试获取上一次容器的输出,这次有内容了:

# kubectl logs xxxxclaw-60000xxx-85dffdb5f6-ck2ts \
  -n xxxxclaw \
  -c fix-permission \
  --previous

exec /bin/sh: argument list too long

`argument list too long`

瞬间明白了。这不是存储问题,不是权限问题,甚至不是业务问题,而是——环境变量太多了


真相大白:4178 个 Service

Kubernetes 有一个默认开启的特性:当 enableServiceLinks: true 时,会将同 namespace 下所有 Service 的连接信息以环境变量的形式注入到每个 Pod 中。

每个 Service 注入 7 条环境变量:

{SVC_NAME}_SERVICE_HOST
{SVC_NAME}_SERVICE_PORT
{SVC_NAME}_PORT
{SVC_NAME}_PORT_xxx_TCP
{SVC_NAME}_PORT_xxx_TCP_PROTO
{SVC_NAME}_PORT_xxx_TCP_PORT
{SVC_NAME}_PORT_xxx_TCP_ADDR

赶紧看看 namespace 下有多少 Service:

# kubectl get svc -n xxxxclaw | wc -l
4000+

4178 个 Service。

4178 × 7 = 29,246 条环境变量

Linux 系统对进程启动时参数 + 环境变量的总长度有上限限制(ARG_MAX,默认约 2MB),29,246 条环境变量早已将其击穿

/bin/sh 在启动的瞬间,因为无法接收如此庞大的环境变量列表,直接报错退出。Exit Code 255,连日志都来不及写。

这就是为什么:

  • 所有新建 Pod 都起不来 —— 每个 Pod 都被注入了同样数量的环境变量
  • 重启旧 Pod 也救不活 —— 重启就意味着重新注入,依然超限
  • Init Container 瞬间死亡 —— /bin/sh 还没执行任何命令就已经挂了

那个"简单的 chown 命令"从来没有机会运行过。


紧急修复

# 修复单个 Deployment
# kubectl patch deployment xxxxclaw-60000xxx -n xxxxclaw \
  --type='json' \
  -p='[{"op":"add","path":"/spec/template/spec/enableServiceLinks","value":false}]'
# 对 namespace 下所有 Deployment 全量修复
for deploy in $(kubectl get deployment -n xxxxclaw -o name); do
  kubectl patch $deploy -n xxxxclaw \
    --type='json' \
    -p='[{"op":"add","path":"/spec/template/spec/enableServiceLinks","value":false}]'
done
# 观察 Pod 恢复情况
kubectl get pods -n xxxxclaw -w

随着 Deployment 滚动更新,新 Pod 陆续启动,Init Container 顺利完成,服务逐步恢复正常。


故障时间线

时间事件
事发前namespace 下 Service 数量持续积累,悄然逼近临界点
故障触发Service 数量超过阈值,新建/重启 Pod 全面失败
Pod 创建,Init Container 开始运行
Init Container 因 argument list too long 瞬间崩溃,无日志输出
CrashLoopBackOff,重启18次,退避时间不断拉长
排查中PVC 正常、日志丢失,通过 --previous 终于拿到真实报错
修复后全量关闭 enableServiceLinks,Pod 陆续恢复

经验总结

问题教训
Exit Code 255 不一定是存储问题要结合日志综合判断,不要先入为主
日志获取失败时不要放弃容器瞬间退出时 --previous 有时仍能拿到内容
100+ Pod 同时异常,必有共同根因大规模故障要找共性,而非逐个排查
enableServiceLinks 默认为 true大规模集群必须显式关闭,这是一颗定时炸弹

`enableServiceLinks: false` 对业务几乎没有影响。 现代微服务通过 DNS 访问其他服务,而非依赖环境变量注入。这个 Kubernetes 默认开启的"贴心特性",在 namespace 下 Service 数量失控时,会悄无声息地演变成一场灾难。

更可怕的是,它不会报一个明显的错误,只会给你一个 Exit Code 255,让你在存储、权限、镜像之间反复怀疑,而真相藏在一条轻描淡写的 argument list too long 里。


整个排查过程没有修改一行业务代码,问题藏在 Kubernetes 最基础的默认行为里。有时候,压垮系统的不是复杂的 Bug,而是一个默默开启了很久、从未有人注意过的默认值。

Kubernetes 生产事故排查:4000+个Service把整个集群打趴
转载前请阅读本站 版权协议,文章著作权归 饼铛 所有,转载请注明出处。
评论

目录