综合练习
为 tracks API 设计可观测性清单。
综合练习
这一课你将能做到
- 从零到一走完判题系统可观测体系建设的完整路径:Actuator → Prometheus → Grafana → OTel → SLO → 告警 → 排障流程
- 在预发环境完成「指标采集 → 面板设计 → 告警配置 → 故障演练」的端到端验证,产出可交付的配置文件和文档
- 用一句话向非技术同学解释:为什么这套体系能让「半夜被叫醒次数减少 80%」
为什么要学
前 11 课把工具和方法拆开讲完了,但真实工程是「串起来用」:没有指标的告警是瞎叫,没有告警的面板是摆设,没有 SLO 的排障是乱试,没有复盘的故障会再犯。本课把判题系统作为完整案例,从基础设施到发布门禁走一遍,产出可以直接交付给团队的配置和文档。
面试里「你怎么落地一个系统的可观测性」「从零开始怎么规划」是系统设计题的高频变种,本课给完整路线图。
先建立图像
| 阶段 | 产出物 | 验收标准 |
|---|
| 1. 基础设施 | Actuator 配置、Prometheus scrape 配置、Grafana 连接 | /actuator/prometheus 有指标,Prometheus target UP |
| 2. 指标埋点 | 业务 Counter/Timer、自动插桩验证、标签白名单 | 判题提交接口有 RED 指标,基数可控 |
| 3. 日志规范 | JSON 日志、traceId 贯穿、慢 SQL 日志 | 日志可按 traceId 检索,跨服务连续 |
| 4. 链路追踪 | OTel agent、Collector、Jaeger 查询 | 浏览器到 worker 的 trace 完整,可下钻到慢 span |
| 5. 面板设计 | on-call 首页(RED/SLO/依赖/资源分层) | 值班 30 秒内确认用户是否受伤 |
| 6. 告警配置 | Symptom 告警、burn rate 多窗口、runbook | page 告警 ≤ 5 条,每条有 runbook 和止血开关 |
| 7. 故障演练 | 注入故障、走完整排障流程、写 postmortem | 止血 ≤ 3 分钟,定位 ≤ 10 分钟,复盘含行动项 |
判题系统可观测体系全景:
浏览器 ──trace──► Nginx ──trace──► Spring Boot ──trace──► Kafka ──trace──► worker
│ │
├─ Micrometer → /actuator/prometheus ├─ OTel agent
├─ JSON logs → filebeat → Loki ├─ JSON logs
└─ OTel agent → OTLP → Collector └─ 手动埋点
│
├─→ Prometheus(metrics)
├─→ Jaeger(traces)
└─→ Loki(logs)
Grafana 面板:RED + SLO + 依赖 + 资源
告警:Symptom(page)+ Cause(ticket)+ burn rate(快烧/慢烧)
跟着做
步骤 1:搭建基础设施(30 分钟)
# application.yml
management:
endpoints:
web:
exposure:
include: health,prometheus,metrics,info
endpoint:
health:
show-details: when_authorized
metrics:
tags:
application: coderoadmap-backend
# prometheus.yml
scrape_configs:
- job_name: 'coderoadmap-backend'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['backend:8080']
# 检查 Actuator 暴露
curl http://localhost:8080/actuator/prometheus | findstr http_server_requests
# 检查 Prometheus target
open http://localhost:9090/targets # 应为 UP
步骤 2:埋点与验证(45 分钟)
registry.counter("judge_submissions_total",
"language", req.getLanguage())
.increment();
Timer.builder("judge_submission_create")
.register(registry)
.record(() -> doSubmit(req));
# 提交一次判题,然后查询
curl -s http://localhost:8080/actuator/prometheus | findstr judge_
# 应看到:judge_submissions_total{language="java"} 1.0
# judge_submission_create_seconds_count 1.0
步骤 3:日志规范化(30 分钟)
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeMdcKeyName>traceId</includeMdcKeyName>
<includeMdcKeyName>spanId</includeMdcKeyName>
</encoder>
# 发一次请求,从 backend 日志复制 traceId,然后在 worker 日志查询
grep "traceId=xxx" backend.log
grep "traceId=xxx" worker.log
# 应能匹配到同一条 trace
步骤 4:链路追踪接入(45 分钟)
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.exporter.otlp.endpoint=http://collector:4317 \
-Dotel.service.name=coderoadmap-backend \
-jar backend.jar
发一次判题提交,打开 Jaeger UI,搜索 service=coderoadmap-backend,应看到完整 trace:
- HTTP span(POST /api/judge/submissions)
- JDBC span(insert into submissions)
- Kafka producer span(send to judge-topic)
步骤 5:设计 on-call 面板(60 分钟)
Dashboard: coderoadmap-judge-oncall
Row 1 RED
Panel 1: QPS(rate(http_server_requests_seconds_count[5m]))
Panel 2: 5xx 率(rate(status=~"5..")/rate(total))
Panel 3: P99(histogram_quantile(0.99, ...))
Row 2 SLO
Panel 4: 30 天可用率(当前值 + 趋势线)
Panel 5: 剩余错误预算
Panel 6: burn rate(1h 和 6h)
Row 3 依赖
Panel 7: MySQL 连接池(hikaricp_connections_active / max)
Panel 8: Kafka consumer lag
Row 4 资源
Panel 9: Pod CPU/内存
步骤 6:配置告警(45 分钟)
# alertmanager.yml 概念配置
route:
group_by: ['alertname', 'severity']
routes:
- match:
severity: page
receiver: oncall-phone
- match:
severity: ticket
receiver: ticket-system
# rules/judge-slo.yml
groups:
- name: judge-slo
rules:
- alert: JudgeSubmissionHighErrorRate
expr: |
sum(rate(http_server_requests_seconds_count{uri="/api/judge/submissions",status=~"5.."}[5m]))
/
sum(rate(http_server_requests_seconds_count{uri="/api/judge/submissions"}[5m]))
> 0.01
for: 5m
labels:
severity: page
annotations:
summary: "判题接口 5xx 率过高"
runbook: "https://wiki.internal/runbooks/judge-5xx"
步骤 7:故障演练(90 分钟)
临时把 MySQL max_connections 调到 20,模拟连接池满。
- 确认:Grafana RED 面板看到 5xx 率上升
- 止血:扩容连接池 + 开启降级开关
- 定位:Jaeger 找到慢 trace → Loki 找到 "Connection pool is full"
- 修复:恢复 MySQL 配置 + 调整 HikariCP 参数
- 复盘:写 postmortem(按 obs-incident 模板)
- 止血时间 ≤ 3 分钟
- 定位时间 ≤ 10 分钟
- postmortem 含行动项和 owner
步骤 8:产出交付物(30 分钟)
交付物清单:
1. 配置文件
- application.yml(Actuator)
- prometheus.yml(scrape)
- alert-rules.yml(告警规则)
- grafana-dashboard.json(on-call 面板)
2. 文档
- runbook-judge-5xx.md(止血步骤)
- postmortem-template.md(复盘模板)
- slo-definition.md(SLO 定义和错误预算)
3. 验证记录
- 故障演练时间线
- 各阶段耗时统计
- 改进点列表
讲清楚要点
- 从零开始按阶段推进,不要试图一天搞完——先基础设施,再指标,再日志,再链路,再面板,再告警,最后演练。每阶段可独立验收。
- 每阶段都有「最小可交付物」——Actuator 配置是一行 YAML,Prometheus 配置是一段 scrape,不要过度设计。
- 故障演练是最终验收——前面所有阶段的配置,都要在演练中证明「能 30 秒确认、3 分钟止血、10 分钟定位」。
- 文档和配置同样重要——runbook 和 postmortem 模板是「把经验沉淀为流程」的关键。
- 和团队协作,不要闭门造车——SLO 要和产品/运营对齐,告警阈值要和 on-call 团队对齐,runbook 要让新人能跟着做。
你可能会懵的地方
-
应该从哪一步开始?
从「用户最痛的地方」开始。如果判题接口经常挂,先配 RED 指标和 Symptom 告警;如果慢查询多,先配慢 SQL 日志和 trace。不要按「技术完整度」排序,按「用户痛度」排序。
-
演练是不是必须?
必须。没有演练的体系是「纸面体系」——真故障时发现面板没数据、告警没触发、runbook 是空的。演练频率:核心系统每季度一次,新系统上线后一个月内一次。
-
小团队没 SRE 怎么做?
简化流程:一个后端 + 一个 DBA 就能搞定。RED 面板用 Grafana 模板导入,告警用 Alertmanager 默认配置,postmortem 用 Google Docs 模板。核心是「流程跑通」,不是「工具完美」。
错法对照
| 错法 | 问题 | 正解 |
|---|
| 一天搞完所有阶段 | 每步都做不扎实 | 分阶段推进,每阶段可验收 |
| 只做工具不做流程 | 面板再好看,没人会查 | 告警 + runbook + 演练三件套 |
| 演练只读文档不操作 | 真故障时手忙脚乱 | 每季度实际操作降级、回滚、面板跳转 |
| SLO 拍脑袋定 | 和产品/业务脱节 | 和产品对齐「用户能接受多差」再定目标 |
| postmortem 追责到人 | 下次故障被隐瞒 | blameless,问系统为什么没防住 |
卡点急救
- 演练时发现 Grafana 面板没数据:检查 Prometheus target 是否 UP;检查 Micrometer 导出名是否加了
_total 或 _seconds 后缀;检查面板查询的 uri 标签是否匹配实际请求路径。
- 演练时发现告警没触发:检查 Alertmanager 路由配置;检查规则的
for 持续时间是否太长;手动触发一次告警(如 curl -X POST http://alertmanager:9093/api/v1/alerts)验证通知链路。
- 演练时止血时间超过 3 分钟:说明降级开关或回滚流程不熟练。改进:把开关做成配置中心的一键切换;把回滚命令写成脚本;把面板跳转链接放在告警注解里。
面试常问
-
你怎么从零落地一个系统的可观测性?
背这个版本:按阶段推进——基础设施(Actuator + Prometheus)、指标埋点(RED + 业务 Counter)、日志规范(JSON + traceId)、链路追踪(OTel + Jaeger)、面板设计(分层布局)、告警配置(Symptom 优先 + runbook)、故障演练(注入故障 + 走完整流程 + 写 postmortem)。每阶段有最小可交付物和验收标准,不追求完美,先跑通流程再迭代。关键原则:从用户最痛的地方开始,不要按技术完整度排序;演练是最终验收,没有演练的体系是纸面体系。
-
可观测性建设的投资回报怎么衡量?
三个指标:MTTR(平均恢复时间)——从故障发生到恢复的时间,目标从 30 分钟降到 10 分钟;MTTD(平均发现时间)——从故障发生到被发现的时间,目标从用户投诉降到告警触发(< 5 分钟);告警噪音率——page 告警中非真实故障的比例,目标 < 10%。长期指标:半夜被叫醒次数、on-call 疲劳度、复盘行动项完成率。
-
小团队没 SRE 怎么做可观测性?
简化流程:一个后端 + 一个 DBA 就能搞定。工具用开源栈(Prometheus + Grafana + Loki + Jaeger),面板用模板导入,告警用默认配置,postmortem 用 Google Docs 模板。核心是「流程跑通」——有 RED 面板、有 Symptom 告警、有 runbook、每季度演练一次。不要追求完美工具链,先让「故障时能 10 分钟定位」成为现实。
自测三问
- 从零落地可观测性的七个阶段是什么?每个阶段的最小可交付物是什么?
- 故障演练的验收标准是什么?为什么必须演练?
- 向非技术同学解释:为什么这套体系能让「半夜被叫醒次数减少 80%」?
小结一句话
从零到一不是技术堆砌,是流程跑通——基础设施、指标、日志、链路、面板、告警、演练,每阶段可验收,演练是最终答案。
延伸