第 15 篇:容器化后端可观测性与排障证据链
0. 本篇定位
这是容器技术路线的第 15 篇。上一篇解决 Helm 与 Kustomize 如何把多环境差异治理成可审查的声明,本篇继续往生产现场推进:服务已经跑进 Kubernetes 之后,怎么知道它正在发生什么,怎么在故障时从现象追到原因,怎么把一次事故沉淀成下一次更快恢复的工程能力。
本文聚焦 logs、metrics、traces、events、Prometheus/Grafana、OpenTelemetry、requestId/traceId、Kubernetes Pod/Service/Ingress/Node 证据链、告警设计和故障复盘。目标不是列命令速查,而是建立后端工程师在容器化环境里的判断路径:先分清信号类型,再串联请求、实例、版本和平台事件,最后把修复写回代码、配置、模板或发布流程。
读完后你应该能回答三个问题:第一,一个接口 500 时,如何判断问题在网关、Service、Pod、应用、数据库还是 Node;第二,为什么只看日志、只看 CPU 或只看 trace 都不够;第三,如何让 requestId、traceId、版本号、Pod 名称和发布事件在一次复盘里对得上。
1. 问题背景
容器化后端最危险的幻觉,是“服务在 Pod 里 Running,就代表线上可用”。Kubernetes 的 Running 只说明容器进程处于运行状态,不说明业务接口可用,不说明 Service selector 正确,不说明 Ingress 路由命中,不说明依赖数据库健康,也不说明用户请求有足够证据可追踪。
生产事故里常见的问题往往不是“完全没有信息”,而是信息散落在不同系统里:网关日志里有 502,应用日志里没有 requestId,Prometheus 只看到 CPU 飙高,trace 采样没采到慢请求,Kubernetes events 已经过期,发布平台只知道刚发过一个版本。每个局部信息都是真的,但没有共同的时间线和关联字段,就很难形成结论。
后端工程师需要把排障从“找熟悉的人问一圈”升级成“按证据链收敛”。一次请求进入系统后,应该能沿着入口层、服务发现层、工作负载层、应用层、依赖层和节点层逐步缩小范围。每一步都要知道看什么证据、证据说明什么、证据不能说明什么,以及下一步应该去哪里验证。
这也是可观测性和监控的区别。监控偏向“有没有超过阈值”,可观测性偏向“系统为什么进入这个状态”。在容器化后端里,可观测性不是额外装一个 Dashboard,而是让日志、指标、链路、事件、版本和发布记录从设计阶段就能互相关联。
2. 核心概念
本篇先把几个容易混用的概念拆开。它们不是互相替代的工具,而是从不同角度描述同一个系统。
| 信号 | 主要回答 | 典型工具 | 不擅长回答 |
|---|---|---|---|
| logs | 某个时刻发生了什么细节 | stdout、Loki、ELK、Cloud Logging | 长周期趋势和容量判断 |
| metrics | 一段时间内状态如何变化 | Prometheus、Grafana、Alertmanager | 单次请求的完整上下文 |
| traces | 一次请求经过哪些服务和依赖 | OpenTelemetry、Jaeger、Tempo | 没被采样请求的全部事实 |
| events | Kubernetes 对象发生了什么变化 | kubectl describe、event exporter | 长期审计和业务语义 |
2.1 四类信号的职责边界
日志适合记录离散事件,例如一次异常、一次外部调用失败、一次配置加载结果。它的价值在于上下文足够具体:requestId、traceId、userId 或 orderId、接口名、状态码、异常类型、耗时、实例名、版本号。日志不适合承担全局趋势判断,因为高并发场景下日志量巨大,靠搜索日志推断“整体是否变慢”既慢又容易漏。
指标适合记录趋势,例如 QPS、错误率、P95/P99 延迟、CPU、内存、GC、连接池、队列长度、Pod 重启次数。Prometheus 的核心优势是按时间序列聚合和查询,Grafana 的核心优势是把这些时间序列组织成面向排障的视图。指标不适合还原单次请求细节,所以看到 http_requests_total{status="500"} 增长后,还需要日志和 trace 继续定位。
Trace 适合回答“一次请求慢在哪里”。它把网关、应用、数据库、缓存、消息队列、外部 HTTP 调用串成 span。OpenTelemetry 的价值在于统一埋点、传播上下文和导出协议,让不同语言、框架和采集后端能用同一套语义协作。Trace 也有边界:采样策略、异步任务、消息队列上下文传递和代理层 header 处理都会影响证据完整性。
Events 适合回答“平台对象发生了什么”。例如镜像拉取失败、调度失败、探针失败、Pod 被 OOMKilled、Node NotReady、Ingress 配置同步异常。它们通常不是业务日志,却是容器化排障的第一手平台证据。忽略 events,会让团队把调度、资源和探针问题误判成应用代码问题。
2.2 requestId、traceId 与版本上下文
requestId 和 traceId 都是关联证据的标识,但语义不同。requestId 通常表示一次入口请求,便于在网关、应用日志和业务错误里搜索;traceId 表示一次分布式追踪上下文,便于在多个 span 之间重建调用链。小系统里二者可以同源,大系统里应明确字段含义,避免一部分系统写 X-Request-Id,另一部分只认 W3C traceparent。
容器化环境里,关联字段必须包含运行上下文。日志至少要能看到 requestId、traceId、service、env、version、pod、namespace、route、status、duration_ms。指标至少要能按 service、namespace、route、status、method、version 聚合,但要克制高基数字段,不能把 userId、orderId 直接放进 Prometheus label。
版本上下文经常被低估。没有版本号,错误率上涨后无法判断是不是刚发布的版本引入;没有 Pod 名称,无法判断是不是单个实例异常;没有 namespace,无法判断是不是看错环境;没有发布时间点,复盘只能靠口头回忆。可观测性字段的目标不是“日志看起来更丰富”,而是让每条证据都能回到某个服务、某个版本、某个实例和某次变更。
2.3 可观测性平台的分工
Prometheus 负责抓取和存储指标,Alertmanager 负责告警路由、抑制和静默,Grafana 负责把指标、日志、trace 和事件组织成排障视图。它们解决的是“系统状态如何变化”和“异常是否需要人处理”。一个好的 Dashboard 不应该只是资源大盘,还应该围绕后端请求链路组织:入口流量、错误率、延迟分位数、Pod 健康、依赖耗时、发布事件。
OpenTelemetry 解决的是采集标准化。它包含 API、SDK、自动/手动埋点、Collector 和导出协议。后端服务可以用 SDK 生成 span、metric 和 log correlation,Collector 负责接收、处理、采样、脱敏和转发。这样应用不必直接绑定某一个后端存储,后续从 Jaeger 换到 Tempo 或云厂商 APM 时,应用侧改动会小很多。
Kubernetes 自身提供对象状态、events、探针、资源指标和控制器状态。它不是完整可观测性平台,但它提供了判断“是不是平台层问题”的关键证据。排障时如果只看 Grafana 而不看 kubectl describe pod,很容易漏掉 FailedScheduling、ImagePullBackOff、Unhealthy、Killing、OOMKilled 这些直接原因。
3. 运行机制
容器化后端的可观测性可以理解成一条证据生成链:请求进入入口层,经过 Service 路由到 Pod,应用处理请求并访问依赖,平台控制器持续维护期望状态,采集系统把不同层的证据送到日志、指标、trace 和事件后端。排障就是沿着这条链逆向验证。
3.1 从请求入口到 Pod 的证据流
一次 HTTP 请求通常先进入 CDN、负载均衡器或 Ingress Controller,再进入 Kubernetes Service,最后被转发到某个 Pod。入口层应该生成或透传 requestId/traceId,并把状态码、上游耗时、后端地址、路由规则写入访问日志。这里能回答:请求有没有到达集群,网关返回的是 4xx、5xx 还是超时,错误发生在转发前还是转发后。
Service 层要验证 selector 和 endpoints。用户看到 503 时,应用可能完全没收到请求,因为 Service 没有匹配到 ready Pod;用户看到间歇性 500 时,可能只有某几个 Pod 异常。此时证据链应该进入 kubectl get endpoints、kubectl get pod -o wide、readiness probe 状态和网关 upstream 日志,而不是马上重启应用。
Pod 层要看容器状态、重启次数、退出码、previous logs、探针 events、资源限制和所在 Node。比如 CrashLoopBackOff 不能只看当前日志,因为当前容器可能刚启动,真正的异常在上一次退出前;OOMKilled 也不能只看 Java 堆大小,还要看容器 memory limit、非堆内存、线程栈、直接内存和 Node 资源压力。
3.2 Prometheus/Grafana 的采集与展示
Prometheus 通过 scrape 拉取指标,典型后端会暴露 /metrics。Spring Boot 常见路径是 Actuator + Micrometer,指标包括 HTTP server request、JVM memory、GC、线程池、连接池、Tomcat/Netty、数据库客户端和自定义业务指标。Kubernetes 侧还可以采集 kube-state-metrics、cAdvisor、node-exporter、Ingress Controller metrics。
Grafana 面板要服务排障路径,而不是堆满曲线。一个后端服务的基础面板建议包括:请求速率、错误率、延迟分位数、当前版本分布、Pod ready 数、重启次数、CPU throttling、内存使用率、GC 暂停、数据库连接池、下游依赖耗时、最近发布事件。面板变量至少要支持 namespace、service、version、pod 和 route。
告警要从“资源超过阈值”转向“用户影响和恢复动作”。CPU 高不一定故障,错误率持续升高、P99 延迟超过 SLO、ready Pod 数低于安全副本、重启次数快速增加、Ingress 5xx 激增、数据库连接池耗尽,才更接近需要响应的症状。告警内容应带上服务、环境、版本、影响范围、关联面板、初步排查命令和静默策略。
3.3 OpenTelemetry 的上下文传播
OpenTelemetry 的关键不是“有一个 trace 页面”,而是上下文能跨边界传播。HTTP 调用要传递 traceparent,消息队列要把 trace context 放进消息 header,异步线程要延续当前 context,网关和服务网格要避免丢失或重写关键 header。否则 trace 会断成多段,看起来像多个独立请求。
自动埋点能快速覆盖框架、HTTP client、数据库 client 和消息中间件,但关键业务语义仍需要手动补充。例如订单创建可以给 span 增加低基数属性:业务场景、支付渠道、库存服务状态、下游错误类型。不要把高敏感或高基数字段塞进 span attribute,例如身份证、手机号、完整 SQL 参数或无限增长的订单号集合。
采样策略要和故障定位目标匹配。全量采样成本高,固定低采样率又可能漏掉关键错误。更实用的方式是:普通流量按比例采样,错误请求和慢请求提高采样,核心接口保留更高比例,压测和灰度期间临时提高采样。采样策略最好在 Collector 或网关层集中治理,而不是散落在每个服务代码里。
4. 最小可运行示例
最小示例的目标不是搭一个完整平台,而是让你看到四类信号如何互相补证。假设有一个 order-api,它暴露 /orders,调用库存服务和数据库,并部署在 Kubernetes 中。
4.1 服务需要暴露哪些观测面
应用至少要输出结构化日志、健康检查和指标端点。结构化日志用 JSON 更容易被采集系统解析,字段中包含 requestId、traceId、service、version、pod、route、status、duration_ms、error_type。健康检查分为 liveness 和 readiness,前者判断进程是否需要重启,后者判断实例是否可以接流量。指标端点暴露 HTTP、JVM、连接池和业务关键指标。
log: requestId: req-7f2a traceId: 4bf92f3577b34da6a3ce929d0e0e4736 service: order-api version: 2026.07.05-15 pod: order-api-6c8f5f7d9d-k2p9m route: POST /orders status: 500 duration_ms: 842 error_type: InventoryTimeout这个示例里,日志提供单次请求细节;指标会告诉你 POST /orders 的错误率和延迟是否整体升高;trace 会告诉你耗时卡在库存服务还是数据库;events 会告诉你 Pod 是否刚重启、探针是否失败、Node 是否有资源压力。四类信号组合起来,才有资格形成排障结论。
4.2 Kubernetes 中如何验证证据
当 /orders 出现 500,先不要只搜应用异常。入口层看 Ingress 访问日志,确认 requestId 是否存在、上游服务是谁、状态码来自网关还是后端。服务发现层看 Service 和 Endpoints,确认是否所有 ready Pod 都在 endpoints 里。工作负载层看 Pod 状态、重启次数、镜像版本和所在 Node。
常用证据顺序可以这样组织:
1. get ingress/service/endpoints:确认流量入口和后端实例集合2. get pod -o wide:确认实例分布、版本、Node3. describe pod:确认 events、探针、调度、资源和最近状态变化4. logs --previous:确认重启前最后异常5. Grafana:确认错误率、延迟、资源和依赖指标6. trace backend:确认单次请求耗时分布和下游错误这不是固定命令清单,而是证据分层。越靠前越判断“请求有没有到正确对象”,越靠后越判断“对象内部为什么失败”。如果顺序反过来,很容易在应用日志里找不到请求,然后误以为日志系统坏了,实际是 Service endpoints 为空。
4.3 从示例迁移到真实项目
真实项目要补齐三类上下文。第一类是发布上下文:commit、image digest、chart version、config version、发布人、发布时间、灰度批次。第二类是运行上下文:namespace、pod、node、container、resources、probe 状态。第三类是业务上下文:接口、租户或业务线、错误类型、依赖名称,但要避免敏感和高基数字段进入指标 label。
迁移时要优先打通链路,而不是先追求工具完整。第一阶段,让所有日志都有 requestId/traceId/version/pod;第二阶段,让 Prometheus 能按 service/version/route 聚合 RED 指标;第三阶段,让 OpenTelemetry 覆盖入口、下游 HTTP、数据库和消息;第四阶段,把 Kubernetes events、发布事件和告警纳入同一条复盘时间线。
如果团队一开始就追求“大而全平台”,容易出现 Dashboard 很漂亮但事故仍然靠群里问人的情况。更稳的路线是用一个核心服务打样,验证从告警到定位再到复盘的闭环,然后把字段规范、采集配置、面板模板和排障手册推广到其他服务。
5. 配置逐行拆解
可观测性配置的核心不是“字段越多越好”,而是字段能不能支持判断。下面按日志、指标/告警、trace/events 三类拆解。
5.1 日志字段如何设计
日志字段要服务搜索、聚合和复盘。timestamp 用统一时区和格式,避免跨系统对不上;level 控制告警噪声,业务失败不一定都该打 ERROR;service/env/version 定位服务和发布批次;pod/node 定位运行实例;requestId/traceId 串联入口和调用链;route/status/duration_ms 描述请求结果;error_type 比完整异常字符串更适合聚合。
日志也要明确不能放什么。不要打印明文 token、密码、身份证、手机号、银行卡、完整 Cookie;不要把大对象完整序列化到日志;不要在高频路径打印无限制 DEBUG;不要依赖本地文件日志作为唯一来源,因为容器重建后现场会丢。容器化场景优先输出到 stdout/stderr,再由节点代理采集。
后端异常日志要避免“只有堆栈,没有上下文”。一个 NullPointerException 如果没有 requestId、接口、版本和关键业务阶段,很难判断影响范围。相反,一个结构化错误即使没有完整堆栈,也能快速聚合出某个版本、某个接口、某类下游调用失败正在放大。
5.2 指标与告警如何设计
后端服务的基础指标可以按 RED 和资源两类组织。RED 是 Rate、Errors、Duration,回答请求量、错误率和延迟。资源指标回答 CPU、内存、GC、线程、连接池、队列和文件句柄。平台指标回答 Pod ready 数、重启次数、CPU throttling、OOM、Node 压力和 HPA 状态。
Prometheus label 要克制。适合放低基数字段:service、namespace、route 模板、method、status class、version。不要放 requestId、traceId、userId、orderId、完整 URL、异常 message。高基数 label 会导致时间序列爆炸,轻则查询变慢,重则 Prometheus 存储和内存压力失控。
告警规则建议先围绕 SLO 设计,再补资源类辅助告警。比如“5 分钟内核心接口 5xx 比例超过 2% 且请求量超过最低阈值”比“某个 Pod CPU 超过 80%”更接近用户影响。告警要设置 for 持续时间、分级、去重、抑制和静默,避免发布期间、压测期间或单 Pod 短暂抖动制造噪声。
5.3 Trace 与 Events 如何纳入同一时间线
Trace 的关键字段包括 traceId、spanId、parentSpanId、service.name、span.name、duration、status、attributes 和 events。Span 名称要稳定,例如 HTTP POST /orders,不要把具体订单号拼进 span name。下游依赖要记录依赖名称、协议、状态码和错误类型,这样才能判断慢在数据库、缓存、消息队列还是外部 HTTP。
Kubernetes events 要按对象和时间线保留。默认 events 保留时间有限,生产环境建议通过 event exporter 或平台日志系统归档。否则事故发生后几个小时再复盘,最关键的 Unhealthy、BackOff、FailedScheduling、Killing 可能已经看不到。
把 trace 和 events 放进同一时间线,可以解决很多争论。例如 10:03 发布新版本,10:04 readiness probe 开始失败,10:05 endpoints 减少,10:06 Ingress 5xx 上升,10:07 trace 显示库存服务调用超时。这样的时间线能说明故障扩散过程,而不是只得到“接口变慢了”的模糊结论。
6. 后端工程场景
不同阶段对可观测性的要求不同。本地可以简化采集,但不能简化字段语义;生产可以增加平台能力,但不能替代应用契约。
6.1 本地开发与 CI 场景
本地开发阶段要让开发者容易看到 requestId、traceId 和关键日志。可以用控制台 JSON 日志、简单的 Prometheus endpoint、OpenTelemetry console exporter 或本地 Jaeger/Tempo。重点是验证字段是否存在、异常是否带上下文、健康检查是否区分活着和可接流量。
CI 阶段适合做静态和轻量运行校验。比如检查日志配置是否包含 traceId/requestId pattern,检查 Actuator /health 和 /metrics 是否可访问,检查镜像标签和版本变量是否注入,检查 OpenTelemetry 依赖和环境变量是否符合模板。CI 不需要模拟完整生产事故,但要阻止明显不可观测的服务进入集群。
如果 CI 只能证明“测试通过”,却不能证明“发布后能排障”,后续成本会在事故中爆发。容器化后端的 Definition of Done 应该包含观测面:日志字段、基础指标、健康检查、trace 传播、告警面板和回滚证据,而不是只包含功能测试。
6.2 测试、预发与灰度场景
测试环境要验证异常路径。只测正常请求,会让可观测性字段看起来都存在,但真正故障时才发现异常日志没有 requestId、下游超时没有 span、探针失败没有对应事件归档。测试应该主动制造数据库慢、下游 500、消息堆积、Pod 重启、readiness 失败、Service selector 错误等场景。
预发环境要验证“接近生产的证据链”。它不一定承载真实流量,但应该使用接近生产的 Ingress、Service、Pod 模板、资源限制、采集配置和告警规则。否则预发通过只能说明应用在一个简化环境里能跑,不能说明生产排障路径成立。
灰度期间要加强版本维度。Grafana 面板和日志查询必须能按 version 拆分错误率、延迟和实例状态;trace 也要能看出新旧版本调用链差异。没有版本维度,灰度就会变成“把一部分用户暴露给未知风险”,而不是“用小流量验证新版本”。
6.3 生产事故与复盘场景
生产事故中最先做的是保护证据,而不是马上清空现场。记录告警触发时间、影响范围、当前版本、最近变更、关键指标截图或链接、典型 requestId/traceId、异常 Pod、events、临时操作和恢复时间。重启 Pod 可能恢复服务,但也可能丢失 previous logs 和现场状态,所以要先保留关键证据。
排障分工要围绕证据链,而不是围绕职能墙。一个人看入口和流量,一个人看应用日志和 trace,一个人看 Pod/Node/events,一个人看最近发布和配置差异。每个人输出事实和时间点,避免“我感觉是数据库”“应该是网络”这种无法验证的判断。
复盘的产物不能只是一段原因描述。至少要沉淀四类改进:字段补齐,例如新增 version 到日志和指标;规则补齐,例如错误率告警增加请求量门槛;模板补齐,例如所有服务默认注入 OpenTelemetry 环境变量;流程补齐,例如发布平台自动记录发布事件到监控系统。
7. 常见错误与排障
这一节按现象给出排障思路。重点不是背命令,而是识别证据先后顺序。
7.1 只有 500,没有上下文
如果只看到用户反馈 500,第一步找入口证据:请求时间、URL、requestId、网关状态码、上游耗时和后端服务名。没有 requestId 时,退而求其次用时间窗口、用户操作、路由和状态码缩小范围。不要一开始就全量搜 ERROR,因为高并发服务里 ERROR 可能很多,且不一定和用户请求相关。
第二步确认请求是否到达应用。看 Ingress upstream、Service endpoints、目标 Pod 访问日志。如果网关报 upstream reset 或 no healthy upstream,重点转向 readiness、Service selector、Pod 重启和网络策略。如果应用日志有同一个 requestId,再进入 trace 和应用异常。
第三步判断错误来自本服务还是依赖。Trace 显示本服务处理时间短、下游 HTTP 或数据库 span 变慢,说明可能是依赖或连接池问题;应用日志显示业务校验失败,说明可能是输入或代码路径问题;Kubernetes events 显示探针失败或 OOM,说明运行状态本身不稳定。
7.2 Pod 重启、OOM 与探针失败
Pod 重启要先看 restartCount、lastState、退出码和 logs --previous。退出码 137 常见于 OOMKilled,但仍要确认是容器 memory limit、Node 内存压力还是应用主动退出。Java 服务还要结合堆、非堆、direct memory、线程数、GC 和容器限制一起看,不能只调大 -Xmx。
探针失败要区分 liveness 和 readiness。readiness 失败会摘流,通常保护用户;liveness 失败会重启容器,配置过激会把短暂慢请求放大成重启风暴。后端服务的 readiness 应该检查关键依赖是否满足接流量条件,liveness 应该尽量只判断进程是否卡死,避免依赖短抖动导致容器被杀。
如果 Pod 间歇性异常,要看它所在 Node。kubectl get pod -o wide 可以发现异常是否集中在某个 Node;Node 侧指标和 events 可以暴露磁盘压力、网络异常、CPU steal、镜像拉取慢或 kubelet 问题。只看 Pod 自身,容易把节点问题误判为应用问题。
7.3 告警噪声、trace 断链与 events 丢失
告警噪声大的常见原因是阈值脱离用户影响、缺少持续时间、缺少请求量门槛、没有按服务等级分级、发布和压测期间没有静默策略。修复不是简单调高阈值,而是把告警分成症状告警和原因告警:错误率、延迟和可用副本属于症状,CPU、GC、连接池和重启属于辅助原因。
Trace 断链通常来自 header 未透传、异步上下文丢失、消息队列未注入上下文、SDK 版本不一致、采样过低或代理层重写。排查时找一个确定的 requestId,从入口网关开始确认是否生成 traceparent,再看每个服务日志是否记录同一 traceId,最后看 trace 后端是否收到完整 span。
Events 丢失会让复盘缺少平台事实。默认 Kubernetes events 不是长期审计系统,生产环境应把 events 导出到日志或事件平台,并在事故模板里要求记录关键对象 events。否则团队会在复盘时知道 Pod 曾经不健康,却不知道是探针失败、镜像拉取、调度失败还是资源驱逐。
8. 生产化边界
生产化可观测性要控制成本、隐私和职责边界。不是所有信息都要采集,也不是所有异常都要叫醒人。
8.1 字段标准、脱敏与成本控制
字段标准要统一到团队模板里,而不是每个服务自由发挥。最小字段集包括 service、env、version、namespace、pod、requestId、traceId、route、status、duration_ms、error_type。指标 label 和 trace attribute 也要有白名单,避免高基数和敏感字段进入存储系统。
脱敏是上线前要求,不是事故后补丁。日志和 trace 里不要出现密码、token、身份证、手机号、完整地址、银行卡、Cookie 和完整请求体。必要时在采集侧做二次脱敏,但不能把责任完全推给采集系统,因为敏感数据一旦写到 stdout,节点本地和中间链路可能已经留下副本。
成本控制要从采集量、保留周期、采样率和索引字段入手。DEBUG 日志短期开启要有过期时间;trace 采样要按接口重要性和错误状态动态调整;日志索引字段要少而关键;高成本 Dashboard 查询要优化。可观测性系统本身也需要监控,否则它会在故障高峰期先被打挂。
8.2 告警分级与值班动作
告警要能指导动作。P1 告警表示用户明显受影响,需要立即响应;P2 表示风险正在扩大,需要工作时间或值班关注;P3 表示容量、成本或趋势问题,可以进入待办。每条告警都应包含影响、可能原因、首个排查链接、回滚或降级建议,而不是只发一条“CPU High”。
告警还要有抑制关系。Node NotReady 触发后,同一 Node 上几十个 Pod 的探针失败不应该全部叫醒团队;发布期间新版本错误率升高,应关联发布事件并优先提示回滚;数据库不可用时,下游多个服务错误率上升应聚合到依赖故障,而不是让每个服务各自报警。
值班动作要被写成 runbook。runbook 不需要复杂,但要说明先看哪些面板、如何拿到 requestId/traceId、如何判断是否回滚、如何静默噪声告警、如何升级给平台或业务负责人。没有 runbook 的告警只是噪声入口,有 runbook 的告警才是恢复流程的起点。
8.3 复盘反哺模板和流程
复盘不是找人背锅,而是找证据链断点。每次事故都应该问:最早的异常信号是什么,哪个信号缺失导致定位变慢,哪个字段缺失导致无法关联,哪个告警太晚或太吵,哪个 runbook 不可执行,哪个模板应该补默认值。
反哺要进入工程资产。比如发现日志缺 version,就把版本注入写进基础镜像或部署模板;发现 trace 在消息队列断链,就把消息中间件 instrumentation 加进公共库;发现 events 过期,就接入 event exporter;发现告警缺发布上下文,就让发布平台写入 annotation 或监控事件。
判断复盘是否有效,可以看下一次同类故障是否更快定位、更少误报、更少人工猜测。如果每次复盘都只写“加强监控”“提高意识”,说明改进没有进入系统,下一次仍然会依赖个人经验。
9. 什么时候用,什么时候不用
可观测性也有取舍。过早追求复杂平台会拖慢团队,过晚补齐证据链会放大事故成本。
9.1 引入顺序建议
第一阶段先统一日志字段和 requestId/traceId 透传,因为它们最便宜、最直接。没有关联 ID,后续指标和 trace 都难以落到具体请求。第二阶段接入 Prometheus/Grafana,建立服务级 RED 面板和基础资源面板。第三阶段接入 OpenTelemetry,把核心链路、数据库、HTTP client 和消息中间件纳入 trace。
第四阶段治理告警,把资源阈值告警升级为面向 SLO 的症状告警,并补充 runbook。第五阶段归档 Kubernetes events 和发布事件,让复盘时间线完整。这个顺序能避免一开始就搭很重的平台,却连日志里的 requestId 都没有。
小团队可以先用托管平台或轻量组合,大团队则要尽早制定字段规范、采样策略、权限边界和成本预算。工具可以不同,但证据链的语义应该统一。
9.2 不适合过度建设的场景
不是每个内部小脚本都需要完整 trace,也不是每个低流量后台任务都需要复杂 Dashboard。如果服务没有用户实时请求、没有跨服务调用、失败可以简单重跑,那么结构化日志、基础指标和任务状态可能已经足够。过度埋点会增加维护成本,也会让团队忽略真正关键的服务。
短生命周期实验环境也不适合照搬生产级采集。实验环境重点是快速反馈和低成本,可以降低日志保留周期、关闭部分 trace、简化告警。但即使简化,也要保留基本字段规范,否则实验成功后迁移到生产会重新补债。
判断是否需要增强观测,可以看三个问题:故障影响是否高,调用链是否跨多个服务,定位是否经常超过可接受时间。只要其中两个成立,就应该补齐日志、指标、trace 和 events 的关联能力。
9.3 最终判断标准
一套可观测性体系是否合格,不看工具数量,而看事故中能不能回答问题。用户报错时,能不能找到请求;错误率上涨时,能不能按版本和实例拆分;Pod 重启时,能不能看到 previous logs 和 events;链路变慢时,能不能定位到下游依赖;回滚后,能不能证明指标恢复。
它还要能被团队长期维护。字段规范是否写进模板,Dashboard 是否有人负责,告警是否定期清理,采样策略是否可调整,runbook 是否跟真实流程一致,复盘改进是否进入代码和配置。否则可观测性会变成一次项目,而不是持续能力。
最终目标是让后端服务从“能运行”升级为“能解释”。能运行只说明当前没有明显失败;能解释意味着服务在异常时会留下足够证据,让团队用事实恢复、复盘和改进。
10. 下一篇衔接
本篇把容器化后端的观测和排障证据链讲清楚:日志记录细节,指标描述趋势,trace 串联请求,events 暴露平台状态,requestId/traceId/version/pod 把证据关联起来,Prometheus/Grafana/OpenTelemetry/Kubernetes 共同构成生产现场的观察面。
下一篇会进入 第 16 篇:容器化后端生产最佳实践,把镜像不可变、配置治理、资源容量、安全权限、发布回滚、观测排障和团队流程收束成生产治理闭环。到那里,观测不再只是排障工具,而会成为发布门禁、值班响应、复盘改进和平台标准化的一部分。
如果你已经能基于一条 requestId 串起网关、Service、Pod、日志、指标、trace、events 和发布记录,就可以继续进入下一篇;如果还只能在事故中随机翻日志,建议回到本篇的四类信号和证据链顺序再练一次。