Appearance
第 12 讲|观测和发布
系统运行后,日常运维最怕两类信息空白:出问题时不知道发生了什么、发布新版本时不知道影响了什么。前者对应可观测性(Observability)——通过指标、日志、链路追踪、事件让系统状态可见;后者对应发布管理(Release Management)——通过 CI/CD、镜像、灰度、回滚让变更可控。
可观测性和发布管理是现代运维的两大支柱。本讲依次讲清楚:可观察信号的分类、指标的设计、日志的内容要求、链路追踪的工作原理、完整发布链路、灰度发布与回滚策略。
一、可观察信号的四种类型
进程存在不等于服务正常——这是观测性的核心起点:

- 用户已经打不开页面,但进程显示
running - 接口 P95 延迟飙到 3 秒,但服务"健康"
- 数据库连接池快被打满,但 CPU 使用率正常
仅看"进程是否存活"远远不够,要从多个维度的信号判断系统真实状态。
四类核心信号
| 信号 | 记录什么 | 适合回答的问题 |
|---|---|---|
| Metrics(指标) | 随时间变化的数值 | "是不是变差了?""影响范围多大?" |
| Logs(日志) | 一条条事件记录 | "具体发生了什么错?" |
| Traces(链路追踪) | 一次请求的完整调用路径 | "慢在哪一段?" |
| Events(事件) | 系统状态变化 | "什么时候发生了什么操作?" |
四类信号配合使用
四类信号各有所长,实际排查问题时是配合着用的。一次"接口慢"故障的标准排查链路:
1. 指标:看到 P95 延迟从 200ms 抬升到 2s
↓ "什么时间开始变差?"
2. 日志:在异常时段过滤错误,看到大量数据库相关错误
↓ "具体什么错?"
3. Trace:打开一次慢请求的链路,看到时间主要消耗在订单服务到数据库这一段
↓ "慢在哪?"
4. 数据库慢日志:看到同一条 SQL 的 Query_time 持续很高,Rows_examined 几十万
↓ "根因是什么?"
5. 定位:全表扫描,缺索引 → 加索引解决每一步都靠对应类型的信号回答,任何一类信号缺失,排查链路就在那一步断掉。
二、指标(Metrics)
指标把系统状态量化成数值,适合回答"是不是变差""从什么时候开始""影响范围多大"这类问题。
服务层最重要的几类指标
业界总结为 RED 方法(Rate、Errors、Duration)和 USE 方法(Utilization、Saturation、Errors)。对应到具体指标:
| 类型 | 指标 | 关注什么 |
|---|---|---|
| Rate | 请求量(QPS、TPS) | 流量是否异常变化(被刷、被攻击、活动开始) |
| Errors | 错误率(5xx 比例、业务失败率) | 错误是否上升 |
| Duration | 延迟(P50 / P95 / P99) | 响应时间是否抬升 |
| Utilization | CPU、内存、磁盘使用率 | 资源是否接近上限 |
| Saturation | 连接池、线程池、队列深度 | 是否有排队、是否要扩容 |
必须看 P95/P99,不是平均值
延迟这类指标必须看分位数(P95/P99),不是平均值——这是观测性最容易踩的坑。

某接口 100 个请求的延迟:
99 个请求 80ms
1 个请求 3000ms
平均延迟:(99 × 80 + 3000) / 100 = 109ms ← 看起来很美好
P99:3000ms ← 真实情况平均值会被"长尾"拉平,慢请求的痛感完全被掩盖。每 100 个用户里有 1 个等 3 秒,体感非常差——但看平均值你完全感知不到。
分位数的含义:
- P50 — 50% 的请求快于此值(中位数)
- P95 — 95% 的请求快于此值
- P99 — 99% 的请求快于此值
- P99.9 — 99.9% 的请求快于此值(尾部尤其重要)
监控面板上至少要显示 P50、P95、P99——只看平均值的监控基本是无用的。
告警设计:盯用户感知,不只盯资源
告警要盯着"用户感知的痛苦",而不只是"机器忙不忙"。
错误的告警设计:
CPU > 80% → 告警
内存 > 90% → 告警这种告警的问题:
- CPU 偶尔飙升但错误率和延迟无变化 → 告警了但实际无影响
- CPU 不高但应用线程池打满 → 不告警但实际严重影响
正确的告警优先级:
| 优先级 | 告警内容 |
|---|---|
| P0(立即响应) | 业务接口 5xx 比例 > 1%、核心链路 P95 延迟翻倍 |
| P1(数小时内响应) | 资源接近上限(连接池 80%、磁盘 85%) |
| P2(下个工作日) | 单实例 CPU 长时间高、慢查询数量上升 |
| 监控不告警 | 单实例瞬时 CPU 飙高、网络突发 |
SRE 的黄金信号(Google SRE Book 提出):延迟、流量、错误、饱和度——这四类是首要告警目标。
Prometheus + Grafana:主流指标方案
云原生时代的标准组合:
- Prometheus — 时序数据库,定时从应用拉取指标
- Grafana — 可视化面板
- Alertmanager — 告警规则与通知
应用暴露 /metrics 端点,内容是文本格式:
http_requests_total{method="GET",status="200"} 12345
http_requests_total{method="GET",status="500"} 23
http_request_duration_seconds{method="GET",quantile="0.99"} 1.234Prometheus 定期拉取这个端点,写入时序数据库。Grafana 通过 PromQL 查询并展示:
promql
# 5xx 错误率
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))
# P99 延迟
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))三、日志(Logs)
日志记录的是具体发生过的事件——具体某次请求、某次错误、某次状态变化。

一条有用的日志要回答几个问题
| 字段 | 作用 |
|---|---|
| 时间戳(精确到毫秒) | 多服务关联、按时间过滤 |
| 日志级别(INFO/WARN/ERROR) | 按重要性过滤 |
trace_id | 跨服务串联(关键!) |
service / instance | 知道是哪个服务、哪个实例 |
method / path | 定位是哪个接口 |
status | HTTP 状态码或业务状态 |
cost_ms | 请求耗时 |
user_id / tenant_id | 影响范围判断 |
error | 错误类型和详情 |
无用日志 vs 有用日志的对比
反面例子(无用日志):
2026-06-23 14:23:01 ERROR: error occurred这条日志几乎无法排查——不知道哪个接口、哪个用户、什么错、能不能复现。
正面例子(有用日志):
2026-06-23 14:23:01.234 ERROR service=order-api instance=order-7d5b8 trace_id=8f3a4e5b method=POST path=/api/orders user_id=12345 cost_ms=231 error="duplicate key value violates unique constraint orders_no_key" sql="INSERT INTO orders ..."看到这条日志:
- 请求已经进入后端(走到了 SQL 执行)
- 错误是数据库唯一键冲突
- 排查方向明确:订单号生成逻辑、重复提交、幂等键、约束设计
结构化日志:让日志可查询
结构化日志(Structured Logging) 是把日志写成 JSON 或 key=value 格式,而不是自由文本:
json
{
"ts": "2026-06-23T14:23:01.234Z",
"level": "ERROR",
"service": "order-api",
"trace_id": "8f3a4e5b",
"method": "POST",
"path": "/api/orders",
"user_id": 12345,
"cost_ms": 231,
"error": "duplicate key",
"error_detail": "..."
}结构化日志的价值:
- 可被日志系统(ELK、Loki)按字段索引和查询
- 可做聚合分析("按 user_id 统计错误次数")
- 可关联到 trace_id 拉出完整链路
应用层用日志库(zap、logrus、slog、structlog)直接输出 JSON,比正则解析非结构化日志高效得多。
集中日志:多服务的日志聚合
微服务架构下,日志分散在几十上百个 Pod 里,必须集中收集才能高效查询。
主流集中日志方案:
| 方案 | 组件 |
|---|---|
| ELK | Elasticsearch + Logstash/Fluentd + Kibana |
| EFK | 同上,Logstash 换成 Fluentd |
| Loki | Grafana 出品,基于 Prometheus 思路的日志系统 |
| 云厂商 | 阿里 SLS、AWS CloudWatch、腾讯 CLS |
采集 Agent 在每台节点跑(K8s 用 DaemonSet),把所有 Pod 的标准输出收到集中存储,运维通过 Kibana/Grafana 查询。
四、链路追踪(Traces)

为什么需要 Trace
微服务架构下,一次请求穿过多个服务。问题"订单接口慢"可能是:
- 订单服务自己慢
- 订单服务调用的用户服务慢
- 用户服务调用的数据库慢
仅看每个服务的指标和日志,拼不出完整链路——链路追踪就是为解决这个问题。
Trace 与 Span
| 概念 | 含义 |
|---|---|
| Trace | 一次完整请求的全链路 |
| Span | 链路中的一个时间段(如一次 HTTP 调用、一次 SQL 查询) |
| Trace ID | 整个 Trace 的唯一标识 |
| Span ID | 单个 Span 的唯一标识 |
| Parent Span ID | 当前 Span 的父级,用于构建调用树 |
一次完整请求的 Trace 形态:
通过 Trace 视图能直接看出:总耗时 3 秒,主要消耗在 INSERT order 的 2.8 秒上——直接定位到数据库这一段,去查慢日志、看索引。
Trace ID 必须在服务间透传
链路追踪能工作的核心机制是 Trace ID 在服务间透传:
入口 → 生成 Trace ID
↓ 把 Trace ID 放入 HTTP Header
中间服务 → 从 Header 读取 Trace ID
↓ 调用下游时,继续传递 Trace ID
下游服务 → 同样接收并传递任何一个中间服务没有正确透传 Trace ID,链路就在那里断成两截,后面的耗时归因完全失效。
主流的 Header 命名约定:
traceparent(W3C Trace Context 标准,推荐)X-Trace-IDX-B3-TraceId(Zipkin)Uber-Trace-Id(Jaeger)
主流链路追踪方案
| 方案 | 特点 |
|---|---|
| Jaeger | CNCF 项目,Go 编写,生态成熟 |
| SkyWalking | Apache 项目,中文社区活跃 |
| Zipkin | Twitter 开源,历史悠久 |
| OpenTelemetry | CNCF 标准,数据采集层,后端可对接 Jaeger/SkyWalking |
| 云厂商 | 阿里 ARMS、AWS X-Ray |
OpenTelemetry 正在成为业界标准——它不绑定后端,采集层与展示层解耦,逐步替代旧的 Jaeger SDK、Zipkin SDK 等。
Trace 的采样
每个请求都生成完整 Trace,数据量极大,存储成本高。生产环境通常做采样:
- 头部采样:入口决定是否记录,常用 1% 采样
- 尾部采样:先记录,根据请求结果(出错的、慢的)再决定是否保留
- 业务采样:特定接口、特定用户全采样
合理的采样策略:正常请求低采样率(1%),异常请求(错误、慢)全采样。
五、发布链路
发布远不止"把代码放上去"——它是一个多阶段、可追溯的流程。
完整发布链路
每个阶段的作用:
| 阶段 | 作用 |
|---|---|
| Git | 代码版本管理,每次变更有 commit 记录 |
| CI | 自动化构建、单元测试、集成测试、安全扫描 |
| 镜像/制品 | 不可变的发布制品 |
| 制品仓库 | 集中存储,带版本管理与签名 |
| 部署系统 | 把制品部署到目标环境,记录发布历史 |
| 预发/Staging | 类生产环境验证 |
| 生产 | 正式服务用户 |
| 观察 | 发布后监控关键指标 |
CI/CD 工具
| 工具 | 特点 |
|---|---|
| Jenkins | 老牌,插件丰富,灵活 |
| GitLab CI | 与 GitLab 集成,YAML 配置 |
| GitHub Actions | 与 GitHub 集成,简单易用 |
| Drone | 容器化,轻量 |
| Argo CD | K8s 原生的 GitOps 工具 |
| Tekton | K8s 原生 CI/CD 框架 |
发布记录:可追溯的关键
留下完整的发布记录,经常比代码仓库更快定位问题。
线上出问题时的标准排查动作之一:
监控:14:03 错误率开始上升
发布系统:14:01 order-api 从 v1.8.2 滚动更新到 v1.8.3
结论:大概率是这次发布引入,围绕本次变更排查如果没有发布记录,只能回 git log 翻 commit、问同事"今天谁发了什么"——效率低得多。
发布记录至少要包含
| 字段 | 内容 |
|---|---|
| 谁发的 | 发布人(账号、姓名) |
| 什么时间 | 开始时间、结束时间 |
| 发了什么 | 服务名、版本号、commit ID、镜像 tag |
| 发到哪里 | 环境、集群、命名空间 |
| 配置变化 | 是否有 ConfigMap/Secret 变更 |
| 数据库变更 | 是否有 schema 迁移 |
| 变更原因 | 关联的需求/工单 |
| 滚动策略 | 灰度比例、放量节奏 |
| 关键指标 | 发布期间的错误率、延迟变化 |
六、灰度发布
为什么要灰度
新版本直接全量上线,如果有问题,全部用户立刻受影响。灰度发布让新版本先接收一部分流量,问题影响范围可控:
- 出问题只影响少量用户
- 验证通过后再扩大范围
- 出现严重问题立刻回滚,损失可控
灰度方式
| 方式 | 实现 | 适用场景 |
|---|---|---|
| 按比例 | 5% → 20% → 50% → 100% 逐步放量 | 最常见,任何场景 |
| 按用户 | 内部账号、测试账号、特定客户先用 | 需要识别用户的场景 |
| 按地域 | 某个区域先切新版本 | 多机房架构 |
| 按设备/客户端 | 某版本 App、某型号设备先用 | 移动端发布 |
| 按实例 | 少量实例替换为新版本,观察 | K8s 滚动更新的天然机制 |
| 按时间 | 工作时间小流量、夜间扩大 | 配合业务低峰 |
灰度观察:不能只看"服务还活着"
灰度期间的观察,必须对比新旧版本的关键指标:
- 错误率(新版本 vs 旧版本)
- P95/P99 延迟
- 关键业务指标(下单成功率、支付成功率)
- 日志中的新增错误类型
- 数据库慢查询
- 告警变化
某些问题在小流量下根本看不出来,放量到 50% 才暴露——所以**每放一档,都要观察足够长时间(通常 10-30 分钟,关键业务更长)**再放下一档。
渐进式发布的几种工具
| 工具 | 特点 |
|---|---|
| Flagger | 基于 K8s + 服务网格的自动化金丝雀 |
| Argo Rollouts | K8s 原生的渐进式发布工具 |
| Istio / Linkerd | 服务网格自带流量切分 |
| Spinnaker | Netflix 开源的多云部署平台 |
这些工具支持自动观察指标、自动决策放量或回滚。
七、回滚
回滚不是万能药
代码可以快速回滚,但数据库结构和数据不一定能回滚。
典型场景:新版本给订单表加了 coupon_id 字段,业务代码写入了这个字段。回滚到旧版本后:
- 旧代码不认识
coupon_id字段,可能报错或丢弃 - 已经写入的数据怎么处理?
- 用户已经看到了新功能,回滚后这些数据消失?
数据库变更的兼容性原则
涉及数据库变更的发布必须分步进行,确保新旧版本能兼容:
阶段 1:发布 v1.8(兼容版本)
- 数据库新增 coupon_id 字段(允许 NULL)
- 代码可以读 coupon_id 但不写
- 旧版本 v1.7 也能正常工作
阶段 2:发布 v1.9(使用新字段)
- 代码开始写入 coupon_id
- 数据库结构不变(已经在 v1.8 准备好)
阶段 3:发布 v2.0(清理旧逻辑)
- 代码不再处理 coupon_id 缺失的情况
- 数据库可以设 NOT NULL这套"先兼容再使用"的发布模式被称为 Expand-Contract 模式或蓝绿数据库变更。
一份典型的回滚记录
14:01 order-api v1.8.3 开始灰度 10%
14:06 指标显示 5xx 比例从 0.2% 升到 6.8%
集中在 POST /api/orders,错误类型:duplicate key
14:08 决策:停止放量,流量切回 v1.8.2
14:09 灰度比例降为 0%,旧版本 v1.8.2 承载 100% 流量
14:12 错误率回落到 0.3%
14:15 确认恢复,启动复盘流程
原因:v1.8.3 引入的并发场景下订单号生成冲突
后续:开发修复后,15:30 v1.8.4 重新灰度这种记录能直接对应监控曲线、入口日志、应用日志——复盘时能精确还原每个时间点的决策。
发布管理看起来繁琐,但真出事时,有这套流程和记录的团队与没有的团队,处理速度能差一个数量级。
八、可观测性 + 发布:配合是关键
可观测性和发布管理是相互配合的:
| 配合点 | 价值 |
|---|---|
| 发布前定义观察指标 | 知道发布期间该看什么 |
| 发布期间监控自动比对 | 自动识别新旧版本的差异 |
| 自动化金丝雀 | 监控指标触发自动放量或回滚 |
| 发布记录关联监控曲线 | 故障复盘可精确还原 |
| 链路追踪定位新版本异常 | 跨服务问题快速定位到引入版本 |
完整的现代发布工作流是:
- CI 构建测试 → 通过
- 部署到 staging,自动化测试 → 通过
- 灰度 10%,观察 15 分钟,关键指标稳定 → 自动放量
- 灰度 50%,观察 15 分钟 → 自动放量
- 100%,观察 30 分钟 → 发布完成
- 任意阶段指标异常 → 自动回滚 + 告警
这一切的基础是完善的可观测性——没有指标监控、没有日志、没有 Trace,自动化决策无从谈起,只能靠人工观察+手工操作,效率低且容易错过问题。
可观测性和发布管理,共同构成"现代运维的双支柱"。