学完前面的章节,已经认识了很多对象:镜像、Pod、Service、配置、卷、探针和控制器。真正遇到故障时,难点往往不在于忘了一条命令,而在于不知道下一条证据该从哪里找。
这一章把读书笔记服务放进几个教学现场。下面的数据和状态是为推理练习设计的案例,不是某次真实生产事故的日志。你可以先独立作答,再阅读分析;如果在实验集群复现,只使用可丢弃的测试资源与测试数据。
开始之前先保留现场
用户说“网站打不开”,这句话还不够精确。先问清发生时间、具体路径、影响范围和最近变化。是所有用户都失败,还是只有图片上传?是连接失败,还是HTTP返回500?是持续失败,还是偶发?
然后保留能关联起来的信息:一次请求的时间与请求ID、实际访问的域名和路径、发布版本、Pod身份以及相关事件。先记录再修改,可以避免修复动作把原来的线索覆盖掉。
故障现场最危险的习惯,是看到一个熟悉的词就直接操作。遇到CrashLoopBackOff就删除Pod,看到超时就增加副本,看到权限错误就授予管理员权限,都可能暂时改变现象,却没有解释原因。
把每次排查写成三个短句更有效:现在知道什么;怀疑什么;下一项观察怎样区分这些假设。
现场一 Pod运行着 请求却到不了应用
已知:
- 两个notes-api Pod都显示Running,Ready为True
- 在Pod内部访问
127.0.0.1:8080/readyz成功 - Service名可以解析到ClusterIP
- Service配置为
port: 80、targetPort: 8080,通过Service的80端口访问失败 - Service的selector为
app: note-api,Pod标签为app: notes-api
先别归因于DNS
DNS已经返回地址,说明名称解析这一步至少在当前观察中成功。它没有证明Service有可用后端。
这里最有区分力的证据是后端列表。对由selector管理的普通Service,先检查EndpointSlice是否包含预期Pod及端口,然后回看selector是否选中了它们。案例中的 note-api 与 notes-api 相差一个字母,控制器不会猜测它们本来想表达同一件事。
合理的修复是纠正声明中的标签契约,再验证EndpointSlice出现预期后端,并重新发起真实业务请求。不要只看到配置提交成功就结束。
如果selector正确但仍失败,下一层再看targetPort、应用监听地址、网络策略和数据面。应用只监听容器内的127.0.0.1时,来自其他Pod的连接也可能进不来。排查顺序是为了缩小范围,不是规定所有故障都必须执行相同的二十条命令。
题目 1
为什么Ready为True与DNS解析成功,仍然不能证明Service可用?
提示
探针、名称解析与后端选择分别验证了什么?
答案
探针验证配置的健康条件;DNS验证名称到地址的解析;Service还需要选中正确后端并使用正确端口,实际流量也要经过可用网络路径。它们是不同层次的证据,不能相互替代。
现场二 新密码已经更新 应用仍然连不上数据库
已知:
- 数据库密码完成轮换
- 对应Secret已更新
- API从环境变量读取密码
- Deployment的Pod模板没有变化
- 运行中的Pod仍是轮换前创建的那一批
配置的来源与读取时刻都重要
Secret对象已经更新,只说明存储的配置变了。容器创建时注入的环境变量,不会因为API对象后来改变就自动改写到运行进程中。
这里应检查配置通过什么方式传入,以及程序什么时候读取。环境变量通常需要通过受控重建让新容器获得新值。文件投射则有传播延迟,应用也可能只在启动时读取;使用subPath挂载还有不同的更新限制。不能把“配置文件最终会更新”推成“应用立即切换”。
更稳妥的密码轮换应提前设计新旧凭据的过渡窗口:先让系统能够接受或使用新凭据,确认消费者完成切换,再按授权流程撤销旧凭据。具体顺序取决于数据库和凭据管理方式。直接使旧密码失效,再等待所有实例慢慢更新,容易造成不必要的中断。
修复后还需确认所有副本都已使用新配置,并观察旧连接退出和新连接建立情况。凭据值不应出现在日志、截图或排障记录里,只记录身份、版本或轮换状态。
题目 2
把Secret改成卷挂载,就一定能实现无感热更新吗?
提示
考虑API对象、节点文件、应用内存和已有连接四个位置。
答案
不能。普通投射卷更新有传播过程,subPath挂载不自动更新;程序还需要主动重读或重载配置,已有数据库连接也可能保留原来的认证状态。应明确每一步的更新机制和失败处理,而不是只改变挂载方式。
现场三 扩容发生了 延迟仍在上升
已知:
- API从两个副本扩到六个,全部Ready
- 各副本CPU消耗较低
- 每个实例的数据库连接池上限为二十
- 数据库可供这项服务使用的连接预算为八十
- 请求主要等待获取数据库连接,数据库查询也变慢
先算清总量
两个副本最多申请四十个连接,六个副本则可能申请一百二十个,超过给这项服务的八十个预算。还要考虑发布期间额外副本、后台任务和运维连接。
这并不证明连接数就是唯一根因,但它给出了一个可以验证的假设:扩容增加了数据库侧竞争。下一步应同时检查连接等待时间、活跃查询、慢查询、锁与数据库资源,判断是连接池配置失衡,还是查询本身让连接长期不能归还。
如果只是把连接池上限一律调大,可能让数据库处理更多并发、排队更长。可以先限制并发、改善慢查询或缩小连接池预算,再评估数据库容量。需要保护用户体验时,也可对昂贵操作限流或排队,但应明确反馈状态,避免无限等待。
HPA的动作没有错到“没有执行”;它确实增加了实例。问题在于新增实例不能解除当前的瓶颈。排障要区分控制动作是否落地,与这个动作是否能改善业务结果。
题目 3
如果需要为一次滚动发布预留一个额外API副本,六个常驻副本每个连接池上限设为十,是否就一定安全?
提示
七个实例乘以十只是预算的一部分。
答案
七个实例最多七十个连接,小于八十,但仍需考虑终止中的实例、worker、其他客户端、连接泄漏以及数据库能够高效处理的真实并发。连接预算满足是必要检查之一,并不等于查询负载一定安全。
现场四 回滚之后旧版本也出错了
已知:
- 新版API不再接受旧的
favorite请求字段,只接受shelf;数据库旧列也被移除 - 新镜像启动成功,健康接口返回200
- 旧前端请求开始失败
- 后端回滚到旧镜像,错误仍然持续
一次版本号变化包含多个状态
镜像回滚恢复的是代码与运行模板。数据库已经发生的变化仍然存在;浏览器中的旧前端也不会因为后端回滚就立即更新。
现在应列出受影响的兼容关系:旧前端调用什么接口,旧后端依赖什么字段,新后端接受哪些数据。基于这些证据选择向前兼容修复或经过评估的数据恢复。不要在没有备份与影响评估时继续反复改表,扩大恢复难度。
从预防角度看,可以先增加新结构,发布兼容两种形式的代码,完成回填并验证消费者迁移,最后清理旧结构。回退窗口结束之前保留必要兼容能力,也要在测试中覆盖新旧版本并存。
题目 4
为什么健康接口通过,没有发现这次发布问题?
提示
进程存活与“收藏”这条业务路径是否兼容,是两个判断。
答案
健康接口只验证了它被设计验证的条件,没有覆盖旧客户端与新数据库的组合。发布验收应加入代表性的业务请求及新旧兼容测试,并观察分版本错误率,而不能只看Pod数量与健康接口。
把排查过程变成团队可以复用的知识
四个现场看起来不同,但共同点是跨越了责任边界:网络对象到应用监听,配置对象到进程内存,副本数到数据库容量,代码版本到持久数据。
故障复盘也应该保留这些边界。记录现象、影响、最早证据、关键假设、验证动作和恢复结果。把“重启之后好了”改写为更完整的问题:重启改变了什么状态,它为什么能暂时改善,根因是否仍然存在。
改进项尽量对应具体机制。selector拼写错误可以增加配置验证和服务连通性测试;密码轮换中断可以补充消费端切换与回退流程;扩容压垮数据库可以统一连接预算并限制并发;回滚失效可以增加兼容迁移与恢复演练。
不必为了每次事故都新增一个平台。一个可靠的测试、一条清楚的告警、一个避免泄密的排障脚本,有时比再安装一套工具更有价值。
每个动作对应一个可验证假设,修复之后回到用户请求确认结果。
你应该能带走的判断能力
再遇到一个陌生的云原生组件,先看它管理哪种状态、观察什么信号、采取什么动作、失败后怎样恢复。沿着这几个问题,往往能把工具重新放回整个系统中。
本合集从一次部署走到这里,容器解决了交付与运行边界的一部分,Kubernetes协调工作负载,网络与存储连接请求和数据,交付与观测让变化可验证。最终负责业务正确性的,仍然是把这些机制组合起来的工程设计。
