云原生可观测性实战:构建 Metrics、Logs 和 Traces 的统一视图
云原生可观测性实战:构建 Metrics、Logs 和 Traces 的统一视图
摘要:在微服务和分布式系统日益复杂的今天,可观测性(Observability)已从“锦上添花”变为“生存刚需”。本文将带你从零开始,理解可观测性的三大支柱(Metrics、Logs、Traces),并基于 OpenTelemetry、Prometheus、Grafana 和 Jaeger 等开源工具,构建一套生产级的统一可观测性平台。全文包含大量实战代码和配置,助你快速落地。
1. 为什么可观测性如此重要?
传统的监控(Monitoring)只能回答“系统是否挂了”,而可观测性(Observability)能回答“系统为什么挂了”以及“接下来会发生什么”。在 Kubernetes 和微服务环境下,一个请求可能流经数十个服务,任何一环的延迟或错误都可能导致用户体验下降。可观测性通过三大信号——指标(Metrics)、日志(Logs) 和 链路追踪(Traces)——让我们能够深入系统的内部状态,实现故障快速定位、性能瓶颈分析和容量规划。
2. 可观测性三大支柱概览
| 信号 | 作用 | 典型工具 |
|---|---|---|
| Metrics | 聚合的数值数据,用于告警和趋势 | Prometheus, Grafana |
| Logs | 离散的事件记录,用于调试和审计 | Elasticsearch, Loki, Fluentd |
| Traces | 请求在分布式系统中的完整路径 | Jaeger, Tempo, Zipkin |
三者相辅相成:Metrics 告诉你“出问题了”,Logs 提供“现场细节”,Traces 帮你“还原路径”。现代可观测性实践倾向于使用 OpenTelemetry 作为统一的数据采集和标准化层,从而让这三者能够关联查询。
3. 实战环境准备
我们将基于 Docker Compose 启动一套完整的可观测性栈,包括:
- Prometheus(时序数据库 + 指标采集)
- Grafana(可视化面板)
- Loki(日志聚合,与 Grafana 深度集成)
- Tempo(分布式追踪后端,兼容 Jaeger 协议)
- OpenTelemetry Collector(接收、处理并导出遥测数据)
同时,我们会编写一个简单的微服务示例(Go + Gin),演示如何埋点并发送数据。
3.1 Docker Compose 文件(docker-compose.yml)
version: '3'
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
tempo:
image: grafana/tempo:latest
ports:
- "3200:3200"
- "4318:4318" # OTLP HTTP receiver
command: -config.file=/etc/tempo.yaml
volumes:
- ./tempo.yaml:/etc/tempo.yaml
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
command: --config /etc/otel-collector-config.yaml3.2 配置文件要点
1. Prometheus 配置(prometheus.yml)
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'otel-collector'
static_configs:
- targets: ['otel-collector:8889']
- job_name: 'user-service'
static_configs:
- targets: ['user-service:8080']2. Tempo 配置(tempo.yaml)
server:
http_listen_port: 3200
distributor:
receivers:
otlp:
protocols:
grpc:
http:
ingester:
trace_idle_period: 10s
max_block_bytes: 1_000_000
max_block_duration: 5m
storage:
trace:
backend: local
local:
path: /var/tempo/traces3. OpenTelemetry 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: 1s
send_batch_size: 1024
memory_limiter:
check_interval: 1s
limit_percentage: 80
exporters:
prometheus:
endpoint: "0.0.0.0:8889"
namespace: "otel"
loki:
endpoint: "http://loki:3100/loki/api/v1/push"
tls:
insecure: true
tempo:
endpoint: "tempo:4317"
tls:
insecure: true
service:
pipelines:
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus]
logs:
receivers: [otlp]
processors: [batch]
exporters: [loki]
traces:
receivers: [otlp]
processors: [batch]
exporters: [tempo]4. 编写可观测性微服务(Go 示例)
我们的服务是一个简单的 HTTP API,模拟用户注册流程,包含数据库查询和外部调用。我们将使用 OpenTelemetry Go SDK 自动注入 instrumentation。
4.1 初始化 Tracer 和 Meter
package main
import (
"context"
"log"
"net/http"
"time"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"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.17.0"
)
func initTracer() func() {
ctx := context.Background()
exporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithInsecure(), otlptracegrpc.WithEndpoint("otel-collector:4317"))
if err != nil {
log.Fatal(err)
}
res, _ := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceNameKey.String("user-service"),
semconv.ServiceVersionKey.String("1.0.0"),
),
)
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(res),
)
otel.SetTracerProvider(tp)
return func() { _ = tp.Shutdown(ctx) }
}4.2 业务处理函数(带埋点)
func registerHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
tracer := otel.Tracer("user-service")
ctx, span := tracer.Start(ctx, "registerUser")
defer span.End()
step1(ctx, "validate input")
step2(ctx, "query database")
step3(ctx, "send welcome email")
w.WriteHeader(http.StatusOK)
w.Write([]byte("registered"))
}
func step1(ctx context.Context, name string) {
_, span := otel.Tracer("user-service").Start(ctx, name)
defer span.End()
time.Sleep(10 * time.Millisecond)
span.SetAttributes(attribute.String("step", name))
}4.3 启动 HTTP 服务并添加 Metrics
func main() {
cleanup := initTracer()
defer cleanup()
handler := otelhttp.NewHandler(http.HandlerFunc(registerHandler), "register")
http.Handle("/register", handler)
http.Handle("/metrics", otelhttp.NewHandler(http.DefaultServeMux, "metrics"))
log.Println("Server starting on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}5. 三支柱数据关联查询实战
5.1 场景:排查“用户注册超时”
- 从 Metrics 发现异常:Grafana 面板显示
/register接口的 P99 延迟突然从 200ms 上升到 2s,触发告警。 - 跳转到 Logs:在 Grafana Explore 中,根据时间范围和
service_name="user-service"过滤 Loki 日志,发现大量"database query timeout"错误。 - 关联到 Traces:在日志中提取
trace_id,点击链接跳转到 Tempo,查看该 trace 的完整瀑布图,发现瓶颈在step2的数据库查询。
5.2 实现日志与 Trace 关联
在 Go 中,我们可以在日志中加入 trace_id 和 span_id:
import "go.opentelemetry.io/otel/trace"
spanCtx := trace.SpanContextFromContext(ctx)
if spanCtx.IsValid() {
log.Printf("trace_id=%s span_id=%s msg=something", spanCtx.TraceID(), spanCtx.SpanID())
}6. 统一仪表盘与告警配置
在 Grafana 中创建:
- 服务概览面板:显示各服务的请求量、错误率、延迟(RED 方法)。
- 依赖关系图:基于 Trace 数据生成服务拓扑。
- 日志流面板:实时展示 Loki 中的日志,支持关键词搜索。
- 告警规则:如“错误率 > 5% 持续 1 分钟”触发 AlertManager。
7. 生产环境最佳实践
| 实践项 | 说明 |
|---|---|
| 采样策略 | 使用概率采样(如 10%)或基于错误/慢请求的尾部采样,控制 trace 数据量 |
| Metrics 高基数问题 | 避免使用无限维度(如 user_id),合理聚合,使用 recording rules |
| 日志结构化 | 统一 JSON 格式,包含 service, version, trace_id 等字段 |
| 存储容量规划 | Prometheus 使用 TSDB 压缩,Loki 使用块存储,Tempo 可配置 S3 廉价存储 |
| 安全与认证 | 启用 OAuth2 或 API Token,加密传输(TLS) |
| 持续优化 | 定期 review 仪表盘,裁剪无用指标,优化采集频率 |
8. 总结与展望
通过本文的实战,你已经掌握了如何基于开源生态构建一套完整的可观测性体系。从三大支柱的采集、存储到可视化关联,再到实际排障场景,这套方案足以支撑中小规模的生产环境。
随着 eBPF 和 OpenTelemetry 的快速发展,未来的可观测性将更加“零侵入”和“智能化”。但无论技术如何演进,理解数据背后的业务逻辑和系统架构,始终是我们构建可靠系统的基础。
希望这篇博文能为你落地可观测性提供一份清晰的路线图。如果你有任何问题或实践经验,欢迎在评论区交流讨论!
参考资源: