监控告诉你系统"坏了";可观测性让你理解系统"为什么坏了"。
现代分布式系统的复杂性,已经超出了传统"仪表盘 + 告警"能覆盖的范围。
可观测性(Observability)不是工具,而是一种能力——让你通过系统的外部输出(日志、指标、链路),理解其内部状态。
目录
- 1. 引言:可观测性 vs 监控
- 2. 三大支柱:Logs、Metrics、Traces 及其关联
- 3. 结构化日志
- 4. 日志采集栈
- 5. Prometheus 指标体系
- 6. Prometheus 配置示例
- 7. Grafana 仪表盘设计
- 8. OpenTelemetry 统一采集
- 9. SLO/SLI/SLA 定义
- 10. 告警策略
- 11. 可观测性成本优化
- 12. 总结与可观测性成熟度模型
- 13. 延伸阅读
1. 引言:可观测性 vs 监控
| 维度 | 监控(Monitoring) | 可观测性(Observability) |
|---|---|---|
| 目标 | 系统"是否正常" | 系统"为何如此" |
| 数据 | 预定义指标 | 高基数、可探索 |
| 工具 | 仪表盘 + 阈值告警 | Logs + Metrics + Traces 联动 |
| 适用 | 已知问题 | 未知问题、复杂故障 |
| 思维 | 被动 | 主动、探索性 |
监控是可观测性的子集。真正的可观测性,让你能回答"我从没见过这种问题"。
2. 三大支柱:Logs、Metrics、Traces 及其关联
┌──────────────┐
│ Traces │ 请求路径,跨服务因果关系
└──────┬───────┘
│ span_id / trace_id
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Logs │ │ Metrics │ │ Logs │
│ Service A│ │ Service A│ │ Service B│
└──────────┘ └──────────┘ └──────────┘
- Logs:离散事件,详细描述"发生了什么"
- Metrics:聚合数值,回答"整体情况如何"
- Traces:跨服务请求路径,定位"哪里出了问题"
三者通过 trace_id、span_id、service_name 关联,形成完整的可观测性网络。
3. 结构化日志
3.1 JSON 格式标准
非结构化日志(如 printf)已不适合分布式系统。所有日志必须结构化:
{
"timestamp": "2025-11-30T10:15:23.456Z",
"level": "error",
"service_name": "order-service",
"trace_id": "abc123def456",
"span_id": "span-789",
"message": "failed to create order",
"error": "database connection timeout",
"user_id": "user-42",
"duration_ms": 1200,
"host": "order-service-7b8d9-xk2p"
}
3.2 字段规范
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
timestamp | ISO 8601 | ✅ | 统一使用 UTC |
level | enum | ✅ | debug / info / warn / error |
message | string | ✅ | 简明描述 |
service_name | string | ✅ | 服务标识 |
trace_id | string | ✅ | 链路追踪 ID |
span_id | string | 推荐 | 当前 span ID |
error | string | 条件 | 错误详情 |
duration_ms | number | 推荐 | 操作耗时 |
禁止:日志中写入 PII(如密码、Token、完整邮箱)。
4. 日志采集栈
推荐栈:Loki + Promtail + Grafana(轻量,与 Prometheus 生态统一)。
Promtail 配置示例:
server:
http_listen_port: 9080
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: kubernetes-pods
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
target_label: app
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
- source_labels: [__meta_kubernetes_pod_name]
target_label: pod
对于大规模场景,可考虑 ELK(Elasticsearch + Logstash + Kibana)或 OpenSearch。
5. Prometheus 指标体系
Prometheus 定义四种核心指标类型:
| 类型 | 描述 | 典型场景 |
|---|---|---|
| Counter | 单调递增计数器 | 请求总数、错误总数 |
| Gauge | 可增可减的数值 | 当前连接数、队列深度 |
| Histogram | 观测值的分桶分布 | 请求延迟分布 |
| Summary | 客户端计算的分位数 | 延迟 P50/P95/P99 |
命名规范:
<namespace>_<subsystem>_<name>_<unit>
http_requests_total
http_request_duration_seconds
process_cpu_seconds_total
go_goroutines
6. Prometheus 配置示例
6.1 抓取配置
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
cluster: production
env: prod
scrape_configs:
- job_name: 'kubernetes-services'
kubernetes_sd_configs:
- role: service
relabel_configs:
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_port]
action: replace
target_label: __address__
regex: (.+)
replacement: $1
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
6.2 告警规则
# alerting_rules.yml
groups:
- name: service_alerts
rules:
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
/
sum(rate(http_requests_total[5m])) by (service) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "{{ $labels.service }} error rate > 5%"
description: "Current error rate: {{ $value | humanizePercentage }}"
- alert: HighLatencyP99
expr: |
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
) > 2.0
for: 10m
labels:
severity: warning
annotations:
summary: "{{ $labels.service }} P99 latency > 2s"
7. Grafana 仪表盘设计
推荐遵循 RED 和 USE 模型:
RED 模型(面向服务):
- Rate:每秒请求数
- Errors:每秒错误数
- Duration:请求延迟分布
USE 模型(面向基础设施):
- Utilization:资源使用率(CPU、内存、磁盘)
- Saturation:资源饱和度(队列、等待)
- Errors:错误事件数
Google 四大金指标(Golden Signals):
- Latency(区分成功/失败)
- Traffic(并发流量)
- Errors(错误率)
- Saturation(容量利用率)
仪表盘设计原则:先看 Golden Signals,再看细节;先看趋势,再看瞬时值。
8. OpenTelemetry 统一采集
OpenTelemetry(OTel)是 CNCF 项目,统一了日志、指标、链路追踪的采集标准。
8.1 SDK 集成与 Context Propagation
OTel 的核心价值:
- 统一 API:一套 SDK 采集三种信号
- Context Propagation:trace_id 在服务间自动传递(W3C Trace Context / B3)
- 厂商无关:Exporter 可随时切换(Jaeger、Tempo、Datadog…)
8.2 Exporter 配置(Go 示例)
package main
import (
"context"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
)
func initTracer(ctx context.Context) (*sdktrace.TracerProvider, error) {
exporter, err := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint("otel-collector:4317"),
otlptracegrpc.WithInsecure(),
)
if err != nil {
return nil, err
}
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName("order-service"),
semconv.ServiceVersion("1.2.0"),
semconv.DeploymentEnvironment("production"),
),
)
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(res),
sdktrace.WithSampler(sdktrace.ParentBased(
sdktrace.TraceIDRatioBased(0.1), // 10% 采样
)),
)
otel.SetTracerProvider(tp)
return tp, nil
}
最佳实践:生产环境用 OTel Collector 作为中继,统一采集、处理、分发。
9. SLO/SLI/SLA 定义
| 概念 | 全称 | 含义 |
|---|---|---|
| SLI | Service Level Indicator | 可量化的服务指标(如可用性 99.9%) |
| SLO | Service Level Objective | 内部目标(如"30 天内 SLI ≥ 99.9%") |
| SLA | Service Level Agreement | 对外承诺(违约有惩罚) |
Error Budget 计算:
Error Budget = 1 - SLO
SLO = 99.9%
→ 30 天允许的不可用时间 = 30 × 24 × 60 × (1 - 0.999) = 43.2 分钟
典型 SLI 定义:
- 可用性:
成功的请求数 / 总请求数 - 延迟:
P99 请求延迟 < 500ms 的比例 - 吞吐量:
每秒处理的请求数 > 阈值
Error Budget 的意义:不是追求零故障,而是在可靠性与发布速度之间取得平衡。
10. 告警策略
告警疲劳是可观测性的最大敌人。
告警分类:
| 类型 | 描述 | 示例 |
|---|---|---|
| 基于症状(推荐) | 用户/业务受影响 | “错误率 > 5%” |
| 基于原因(少用) | 底层资源异常 | “CPU > 90%” |
| 信息性 | 无需立即处理 | “新版本部署完成” |
减少告警疲劳的原则:
- 只告警需要人介入的事——否则就是噪音
- 基于 SLO 告警,而不是基于瞬时阈值
- 告警必须附带 Runbook 链接
- 定期清理无效告警,每季度 Review 一次
- 分级告警:Critical(立即)/ Warning(工作时间)/ Info(仅记录)
11. 可观测性成本优化
可观测性数据的成本往往被低估。三大成本优化杠杆:
采样策略:
- 链路追踪:生产环境 1%–10% 采样
- 错误请求:100% 保留(Tail-based Sampling)
- 健康检查:0% 采样
日志级别管理:
- 生产环境默认 INFO 级别
- DEBUG 仅在问题排查时动态开启(通过
/debug端点或配置中心) - 使用采样日志减少高频事件输出
指标基数控制:
- 避免高基数标签(如
user_id、request_id) - 指标基数 > 10,000 时,必须评审
- 使用 Recording Rules 预聚合
成本公式:
可观测性成本 ≈ 数据量 × 保留天数 × 单位存储成本
保留策略:Metrics 15 个月、Logs 30 天(合规需求除外)、Traces 7 天。
12. 总结与可观测性成熟度模型
| 阶段 | 特征 | 能力 |
|---|---|---|
| L1 · 基础监控 | CPU/内存告警、简单日志 | 知道"机器活着" |
| L2 · 结构化可观测 | JSON 日志、Prometheus 指标、基础 Trace | 知道"服务是否正常" |
| L3 · 关联性分析 | trace_id 关联 Logs/Metrics/Traces | 能快速定位故障 |
| L4 · SLO 驱动 | 定义 SLI/SLO,Error Budget 告警 | 用业务视角衡量可靠性 |
| L5 · 自动化响应 | 基于可观测数据自动扩缩容/回滚 | 系统自我修复 |
可观测性 Checklist:
- 所有日志结构化(JSON),包含
trace_id - 所有服务暴露 Prometheus 指标(
/metrics) - 链路追踪覆盖核心路径,采样率合理
- Grafana 仪表盘遵循 RED / USE / Golden Signals
- SLO 已定义,Error Budget 告警已配置
- 告警分级,附带 Runbook
- 日志脱敏,PII 不入日志
- 数据保留策略明确,成本可控
一句话总结:可观测性的价值不在于"看仪表盘",而在于让你能理解系统的行为、预测系统的风险、定位系统的问题。
13. 延伸阅读
- Google SRE Book - Monitoring Distributed Systems
- OpenTelemetry Official Documentation
- Prometheus Querying Basics
- Grafana Loki Documentation
- The Three Pillars of Observability
- SLO Workbook - Google Cloud
- USE Method - Brendan Gregg
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。