跳到主要内容

可观测性实践

可观测性三支柱

支柱特点回答的问题典型工具
Metrics聚合数值,低开销发生了什么?多严重?Prometheus、VictoriaMetrics
Logs非结构化/结构化文本具体发生了什么?ELK、Loki
Traces请求在服务间的完整路径为什么慢?调用了谁?Jaeger、Tempo、Zipkin

OpenTelemetry

OpenTelemetry(OTel)是 CNCF 统一的可观测性数据采集标准,覆盖 Traces、Metrics、Logs 三种信号。

架构

Collector 配置

otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318

processors:
batch:
timeout: 5s
send_batch_size: 512
# 尾部采样:只保留慢请求和错误请求的完整链路
tail_sampling:
policies:
- name: latency-policy
type: latency
latency: { threshold_ms: 500 }
- name: error-policy
type: status_code
status_code: { status_codes: [ERROR] }
- name: default-sample
type: probabilistic
probabilistic: { sampling_percentage: 10 }

exporters:
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
prometheus:
endpoint: 0.0.0.0:8889

service:
pipelines:
traces:
receivers: [otlp]
processors: [batch, tail_sampling]
exporters: [otlp/jaeger]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [prometheus]

应用集成示例(Go)

main.go
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

func initTracer() func() {
exporter, _ := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint("otel-collector:4317"),
otlptracegrpc.WithInsecure(),
)
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("my-service"),
)),
)
otel.SetTracerProvider(tp)
return func() { tp.Shutdown(ctx) }
}

分布式链路追踪

Trace 数据模型

Trace(一次完整请求)
├── Span A: API Gateway (200ms)
│ ├── Span B: User Service (80ms)
│ │ └── Span D: DB Query (30ms)
│ └── Span C: Order Service (100ms)
│ └── Span E: Redis Cache (5ms)

每个 Span 包含:

  • TraceID:全局唯一,贯穿整条链路
  • SpanID / ParentSpanID:标识父子关系
  • Operation Name:操作名称
  • Duration:耗时
  • Tags/Attributeshttp.method, http.status_code
  • Events/Logs:Span 内的事件记录

工具对比

JaegerTempoZipkin
存储ES/Cassandra/Kafka对象存储(S3)ES/MySQL
查询UI + APITraceQLUI + API
成本中等低(无索引)
适合通用场景大规模低成本轻量场景
Tempo 的优势

Grafana Tempo 只存储完整 Trace,不建索引,存储成本极低。通过 TraceID 精准查询,配合 Exemplar 从指标跳转到 Trace。

SLI / SLO / SLA

概念定义示例
SLI (指标)衡量服务质量的具体指标请求成功率、P99 延迟
SLO (目标)对 SLI 的目标值可用性 ≥ 99.9%、P99 ≤ 200ms
SLA (协议)与客户签订的服务承诺(含赔偿)可用性 99.9%,否则赔偿

常用 SLI

SLI 类型计算公式适用服务
可用性成功请求数 / 总请求数API 服务
延迟请求耗时 ≤ 阈值的比例API 服务
吞吐量每秒处理请求数(QPS)数据管道
错误率错误请求数 / 总请求数所有服务
新鲜度数据更新延迟 ≤ 阈值数据服务

错误预算

SLO = 99.9%(月度)
月度总时间 = 30 × 24 × 60 = 43,200 分钟
允许不可用时间(错误预算)= 43,200 × 0.1% = 43.2 分钟

已消耗错误预算 = 本月累计不可用分钟数
剩余错误预算 = 43.2 - 已消耗
错误预算耗尽怎么办

当错误预算即将耗尽时,团队应停止新功能发布,集中精力提升可靠性。这是 SRE 团队平衡「创新速度」与「系统稳定性」的核心机制。


常见面试问题

Q1: 可观测性和传统监控有什么区别?

答案

传统监控是「已知的已知」——预先定义好看什么指标、告警什么条件。可观测性是「已知的未知」和「未知的未知」——系统能让你在不预先预测问题的情况下,通过 Metrics + Logs + Traces 的关联分析来理解任何异常。

关键区别:

  • 监控回答「是否出问题了?」
  • 可观测性回答「出了什么问题?为什么?」

Q2: 如何设计合理的 SLO?

答案

  1. 基于用户体验:选择用户能感知的指标(可用性、延迟),而非内部指标(CPU)
  2. 分析历史数据:先观察 1-3 个月的实际表现,以此为基础设定
  3. 留有余量:SLO 略高于 SLA(如 SLA 99.9% → SLO 99.95%)
  4. 不追求 100%:100% SLO 意味着不能做任何变更
  5. 定期回顾:每季度评估 SLO 是否合理,必要时调整

Q3: 链路追踪在生产环境如何控制开销?

答案

  1. 采样策略
    • 头部采样:请求入口按比例采样(10%)
    • 尾部采样:先收集完整链路,只保留慢请求/错误请求
  2. 异步上报:SDK 异步批量发送 Span,不阻塞业务
  3. 选择性埋点:核心服务全量采样,边缘服务低采样率
  4. 存储优化:使用 Tempo(无索引)降低存储成本

相关链接