故障现象
近期处理了一起颇为棘手的线上服务故障。现象是部分用户反馈服务异常,而另一部分用户访问完全正常。
初步检查服务链路中的所有组件,包括应用状态、基础监控指标,均未发现任何告警或异常。这“部分用户异常”的现象,立刻将排查方向指向了负载均衡或服务实例差异。
排查步骤
面对这种“一半海水,一半火焰”的情况,首先需要确认流量是否被正确路由。仔细检查了Nginx的路由配置、服务的Network Policy以及Kubernetes中Service的Endpoints,均未发现配置问题。
接着深入查看具体的Pod实例:
- 逐一检查了各个容器的运行日志,输出正常,并无报错信息。
- 确认所有Pod使用的镜像名称和Tag完全一致。
- 检查Pod历史,近期没有发生重启。
- 核查Deployment中配置的存活和就绪探针,设置合理。
至此,常规的排查路径似乎都走不通了。
发现问题
在常规手段失效后,转而从具体问题切入。从开发同学处获取到具体的故障API接口,然后对承载该服务的每一个Pod实例发起curl请求进行测试。
测试结果揭示了关键差异:其中一个Pod返回的响应数据结构与其他Pod不同。这直接表明,尽管镜像Tag相同,但容器内运行的代码版本确实不一致。
问题指向了镜像本身。立即登录运行异常Pod的Node节点,使用docker inspect命令检查该镜像的详细信息。
docker inspect imagexxx
命令输出显示,该镜像的Image SHA256校验和与其他Node上同Tag的镜像不一致。这坐实了我们的猜测:开发在构建新版本镜像时,使用了与旧版本相同的Tag。
还有一个辅助验证的技巧,就是查看镜像仓库(如Harbor)的操作日志。

在日志中,可以清晰地看到对于同一个镜像Tag,存在多次push或create操作记录,这是镜像Tag被覆盖的强有力证据。
总结与后续改进
这次故障的完整链条非常清晰:
- 源头:开发构建并推送了新版本镜像,但错误地使用了旧的、已存在的Tag。
- 策略放大:对应Deployment的镜像拉取策略(
imagePullPolicy)配置为IfNotPresent(优先使用本地镜像)。
- 问题爆发:当Pod被调度到存有旧镜像的Node上时,就会拉取本地旧的镜像启动,导致服务版本不一致,引发故障。
这次事件暴露了我们CI/CD流程中的一个重要漏洞:虽然在规范中要求开发人员必须更新镜像Tag,但流程上缺乏有效的技术拦截手段,导致相同Tag的镜像依然能够顺利完成构建并推送到仓库。
对于运维与DevOps实践而言,这是一个经典的案例。它提醒我们,除了制定规范,更需要在云原生技术链的关键环节(如镜像构建和推送阶段)设置强制性的卡点,例如通过脚本自动生成基于版本号或提交哈希的唯一Tag,从根本上杜绝此类人为疏忽。
分享此次排查经历,希望能为各位开发者提个醒。如果你在微服务部署和镜像管理方面有更多心得或疑问,欢迎到云栈社区的运维板块交流讨论。
|