☰
从零构建可观测性体系:Prometheus+Grafana+Jaeger实战指南
2026/10/9 0:55:17 网站建设 项目流程

如果你在技术社区问“很懂表的人都会戴些什么表?”,得到的答案大概率不是劳力士、百达翡丽或欧米茄,而是一串你从未听过的缩写:Prometheus、Grafana、Jaeger、VictoriaMetrics……

没错,在程序员的世界里,“懂表”意味着精通监控指标表和链路追踪表,而不是手腕上的机械表。当系统半夜告警、线上流量暴跌、用户投诉页面白屏时,真正能救火的不是名贵腕表,而是你能否在30秒内从海量监控数据中定位到根因。

今天这篇文章,我们就来彻底拆解这个“技术人的腕表收藏夹”——现代可观测性技术栈。我不会只给你罗列一堆工具名词,而是要讲清楚:为什么监控体系是后端工程师的“第二双眼睛”;从零搭建一套生产可用的监控系统,核心要解决哪三个问题;以及,当你真正“懂表”之后,该如何像老手一样,优雅地利用这些数据驱动决策。

1. 这篇文章真正要解决的问题:从“救火队员”到“系统医生”的蜕变

很多开发团队对监控的认知还停留在“有总比没有强”的阶段:部署个Prometheus抓点基础指标,装个Grafana配几个看板,就觉得高枕无忧了。直到某天线上发生复杂故障,你会发现:

  • 问题一:告警轰炸,噪音淹没信号。CPU使用率、内存使用率、磁盘IO……几十个指标同时告警,你根本分不清谁是因、谁是果。
  • 问题二:数据孤岛,排查路径断裂。你知道应用响应慢了,但不知道是数据库慢、缓存慢、还是下游服务慢。指标、日志、链路追踪数据各自为政,你需要像侦探一样在三个系统间反复横跳。
  • 问题三:只有现象,没有根因。监控告诉你“接口错误率飙升”,但不会告诉你是因为最新一次发布引入了有问题的SQL查询,还是因为某个依赖的第三方服务挂了。

这篇文章要解决的,正是如何构建一个关联的、可行动的、以服务为中心的可观测性体系。让你不仅能“看到”系统异常,更能快速“理解”异常背后的故事,并“执行”有效的修复动作。这就像从只会看体温计的护士,升级为能通过CT、血液报告综合诊断的医生。

2. 基础概念:指标(Metrics)、日志(Logs)、追踪(Traces)——可观测性的三大支柱

在深入实践前,必须统一语言。可观测性(Observability)的核心是通过系统外部输出来推断其内部状态,它主要依赖三类数据:

数据维度是什么典型内容核心工具举例擅长回答的问题
指标 (Metrics)随时间变化的数值度量,通常是聚合后的。请求QPS、错误率、响应时间P99、CPU使用率。Prometheus, VictoriaMetrics“系统整体健康度如何?” “流量是否异常?”
日志 (Logs)离散的、带时间戳的文本记录,记录特定事件。ERROR [2023-10-27 14:30:01] UserController - Failed to query user id=123, SQLException: ...ELK Stack (Elasticsearch, Logstash, Kibana), Loki“在错误发生的时刻,系统执行了哪些操作?” “具体的错误堆栈是什么?”
追踪 (Traces)记录单个请求在分布式系统中流转的完整路径。一个用户登录请求,经过了网关 -> 用户服务 -> 数据库 -> 缓存 -> 积分服务。Jaeger, Zipkin, SkyWalking“这个慢请求到底时间耗在了哪个服务、哪个方法上?”

它们的关系是什么?想象一下侦探破案:

  • 指标告诉你“犯罪率在凌晨2点突然飙升”(宏观趋势)。
  • 日志提供了几个案发现场的详细笔录(具体事件)。
  • 追踪则画出了罪犯从入室到逃离的完整行动路线图(请求全链路)。

一个成熟的监控体系,必须让这三者能够相互关联。这是从“监控”走向“可观测性”的关键一步。

3. 环境准备:搭建你的本地可观测性沙箱

我们使用 Docker Compose 来快速搭建一个包含核心组件的实验环境。这避免了污染本地环境,也最贴近生产部署方式。

前置条件:

  • 操作系统:Linux, macOS 或 WSL2 (Windows)
  • 已安装 Docker 和 Docker Compose
  • 至少 4GB 可用内存

创建一个项目目录,并编写docker-compose.yml文件:

# docker-compose.yml version: '3.8' services: # 指标收集与存储:Prometheus prometheus: image: prom/prometheus:latest container_name: prometheus volumes: - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml - prometheus_data:/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.path=/prometheus' - '--web.console.libraries=/etc/prometheus/console_libraries' - '--web.console.templates=/etc/prometheus/consoles' - '--storage.tsdb.retention.time=200h' - '--web.enable-lifecycle' ports: - "9090:9090" networks: - observability-net restart: unless-stopped # 指标可视化:Grafana grafana: image: grafana/grafana:latest container_name: grafana volumes: - grafana_data:/var/lib/grafana - ./grafana/provisioning:/etc/grafana/provisioning environment: - GF_SECURITY_ADMIN_PASSWORD=admin123 - GF_INSTALL_PLUGINS=grafana-timestream-datasource ports: - "3000:3000" networks: - observability-net restart: unless-stopped depends_on: - prometheus # 链路追踪:Jaeger (all-in-one 模式,适合演示) jaeger: image: jaegertracing/all-in-one:latest container_name: jaeger ports: - "16686:16686" # Jaeger UI - "14268:14268" # 接收客户端上报的端口 - "14250:14250" # 接收 gRPC 格式的追踪数据 environment: - COLLECTOR_OTLP_ENABLED=true networks: - observability-net restart: unless-stopped # 日志聚合:Loki + Promtail (轻量级方案) loki: image: grafana/loki:latest container_name: loki ports: - "3100:3100" command: -config.file=/etc/loki/local-config.yaml networks: - observability-net restart: unless-stopped promtail: image: grafana/promtail:latest container_name: promtail volumes: - /var/log:/var/log:ro # 挂载宿主机日志目录,按需调整 - ./promtail/promtail-config.yaml:/etc/promtail/config.yaml command: -config.file=/etc/promtail/config.yaml networks: - observability-net restart: unless-stopped depends_on: - loki # 一个示例应用:用于生成指标、日志和追踪 demo-app: image: ghcr.io/open-telemetry/opentelemetry-demo/otel-demo-app:latest container_name: demo-app ports: - "8080:8080" environment: - OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4317 - OTEL_SERVICE_NAME=demo-app networks: - observability-net restart: unless-stopped networks: observability-net: driver: bridge volumes: prometheus_data: grafana_data:

接下来,创建 Prometheus 的配置文件:

# prometheus/prometheus.yml global: scrape_interval: 15s evaluation_interval: 15s scrape_configs: - job_name: 'prometheus' static_configs: - targets: ['localhost:9090'] - job_name: 'demo-app' static_configs: - targets: ['demo-app:8080'] # 抓取示例应用的指标 - job_name: 'node-exporter' static_configs: - targets: ['node-exporter:9100'] # 如果需要主机监控,可额外部署 node-exporter

创建 Promtail 的配置文件(用于收集日志并发送给 Loki):

# promtail/promtail-config.yaml server: http_listen_port: 9080 grpc_listen_port: 0 positions: filename: /tmp/positions.yaml clients: - url: http://loki:3100/loki/api/v1/push scrape_configs: - job_name: system static_configs: - targets: - localhost labels: job: varlogs __path__: /var/log/*.log # 收集宿主机系统日志,可根据需要调整路径

最后,启动整个栈:

# 在项目根目录执行 docker-compose up -d

启动后,你可以访问以下服务:

  • Grafana: http://localhost:3000 (用户名admin, 密码admin123)
  • Prometheus: http://localhost:9090
  • Jaeger UI: http://localhost:16686
  • Loki: http://localhost:3100 (API)

4. 核心流程拆解:如何让数据说话

环境跑起来了,但一堆数字和曲线本身没有意义。关键在于建立数据之间的关联,并设置有效的告警规则。流程可以拆解为四步:

第一步:指标埋点与暴露应用需要暴露指标。对于Spring Boot应用,最简单的方式是集成micrometer-registry-prometheus。

第二步:数据采集与存储Prometheus 会定期(scrape_interval)从应用暴露的 HTTP 端点(如/actuator/prometheus)拉取指标数据,并存入其内置的时序数据库。

第三步:可视化与探索Grafana 配置 Prometheus 作为数据源,通过编写查询语句(PromQL)将指标数据绘制成图表,并组装成业务/技术看板。

第四步:告警与联动在 Prometheus 或 Grafana 中定义告警规则(Alerting Rules),当指标达到阈值时,通过 Alertmanager 将告警信息路由到钉钉、企业微信、Slack 或 PagerDuty 等渠道。

其中最容易被忽视、也最关键的一环是:从告警到行动的闭环。一个高级的实践是,在告警信息中直接附上相关链路的 TraceID 或关键错误的 LogID,让接收者能一键跳转到 Jaeger 或 Loki 查看详情,极大缩短排查路径。

5. 完整示例:为Spring Boot应用注入可观测性

让我们从一个具体的微服务开始。假设我们有一个用户查询服务user-service。

5.1 添加依赖在pom.xml中添加以下依赖:

<!-- Spring Boot Actuator:提供健康检查、指标暴露端点 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- Micrometer Prometheus 注册表:将指标转换为Prometheus格式 --> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency> <!-- OpenTelemetry Starter:自动生成链路追踪 --> <dependency> <groupId>io.opentelemetry.instrumentation</groupId> <artifactId>opentelemetry-spring-boot-starter</artifactId> <version>2.5.0</version> <!-- 请使用最新稳定版本 --> </dependency>

5.2 配置应用属性在application.yml中配置:

# application.yml spring: application: name: user-service management: endpoints: web: exposure: include: health, info, prometheus # 暴露 prometheus 端点 metrics: tags: application: ${spring.application.name} # 为所有指标添加统一标签 tracing: sampling: probability: 1.0 # 采样率,生产环境可调低,如0.1 # OpenTelemetry 配置:将追踪数据发送到Jaeger otel: traces: exporter: otlp metrics: exporter: none # 指标我们还是用Prometheus,这里关掉OTLP的指标 logs: exporter: none service: name: ${spring.application.name} exporters: otlp: endpoint: http://localhost:14250 # Jaeger 的 OTLP gRPC 接收端点

5.3 添加自定义业务指标在业务代码中,我们可以使用 Micrometer 的MeterRegistry记录自定义指标,例如统计查询用户详情的次数和耗时。

// UserController.java import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/users") public class UserController { private final UserService userService; private final Counter userQueryCounter; private final Timer userQueryTimer; // 通过构造器注入 MeterRegistry public UserController(UserService userService, MeterRegistry registry) { this.userService = userService; // 定义一个计数器,标签为 method="getUserById" this.userQueryCounter = Counter.builder("user.query.requests") .tag("method", "getUserById") .description("The number of user query requests") .register(registry); // 定义一个计时器 this.userQueryTimer = Timer.builder("user.query.duration") .tag("method", "getUserById") .description("Time taken to handle user query") .register(registry); } @GetMapping("/{id}") public ResponseEntity<User> getUserById(@PathVariable Long id) { // 每次请求,计数器+1 userQueryCounter.increment(); // 使用计时器记录方法执行时间 return userQueryTimer.record(() -> { // 模拟业务逻辑 try { Thread.sleep(new Random().nextInt(100)); // 随机延迟模拟处理时间 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } User user = userService.findById(id); return ResponseEntity.ok(user); }); } }

5.4 配置日志关联(MDC)为了将链路追踪ID(TraceID)自动打入日志,方便在 Loki 中通过 TraceID 查询相关日志,我们需要配置日志模式。

# logback-spring.xml 或 application.yml 中的日志配置 logging: pattern: level: "%5p [${spring.application.name:},%X{trace_id:-},%X{span_id:-}]" # 这个模式会在日志中输出应用名、TraceID和SpanID。

启动应用后,访问http://localhost:8080/actuator/prometheus,你应该能看到类似下面的指标输出:

# HELP user_query_requests_total The number of user query requests # TYPE user_query_requests_total counter user_query_requests_total{application="user-service", method="getUserById",} 42.0 # HELP user_query_duration_seconds Time taken to handle user query # TYPE user_query_duration_seconds histogram user_query_duration_seconds_bucket{application="user-service", method="getUserById", le="0.005",} 10.0 user_query_duration_seconds_bucket{application="user-service", method="getUserById", le="0.01",} 35.0 ...

6. 运行结果与效果验证

6.1 验证指标采集

  1. 访问 Prometheus UI (http://localhost:9090)。
  2. 在 Graph 页面的查询框中输入user_query_requests_total并执行。
  3. 你应该能看到一条名为user_query_requests_total{application="user-service", ...}的指标曲线。访问几次你的应用接口,曲线会随之上升。

6.2 配置 Grafana 看板

  1. 登录 Grafana (http://localhost:3000)。
  2. 点击Configuration->Data Sources->Add data source,选择Prometheus。
  3. URL 填写http://prometheus:9090,点击Save & Test,应显示Data source is working。
  4. 点击Create->Dashboard->Add new panel。
  5. 在 Query 编辑框中,输入 PromQL 查询语句,例如:
    • 请求率:rate(user_query_requests_total[5m])
    • P95 响应时间:histogram_quantile(0.95, rate(user_query_duration_seconds_bucket[5m]))
  6. 配置图表标题、坐标轴等,点击Apply保存面板。

6.3 验证链路追踪

  1. 通过 Jaeger UI (http://localhost:16686) 查看追踪数据。
  2. 在 Service 下拉框中,你应该能看到user-service。
  3. 点击Find Traces,会列出最近的请求追踪。点击任意一条,可以看到该请求的完整调用链、各阶段的耗时以及携带的标签(如 HTTP 方法、路径、状态码)。

6.4 验证日志关联

  1. 在 Grafana 中再添加一个Loki数据源,URL 为http://loki:3100。
  2. 新建一个面板,数据源选择 Loki,查询语句可以输入{job="varlogs"} |= "trace_id"来查找包含 trace_id 的日志。
  3. 更强大的用法是,在 Jaeger 的 Trace 详情页,如果日志中正确打印了 TraceID,你可以直接点击一个按钮(如果配置了 Grafana-Loki 数据源链接)跳转到 Loki,并自动过滤出该 Trace 的所有相关日志。

7. 常见问题与排查思路

问题现象可能原因排查方式解决方案
Prometheus 抓取目标状态为DOWN网络不通、目标应用未启动、端点路径错误1. 在 Prometheus 容器内curl目标端点。
2. 检查 Prometheus 配置文件的targets地址和端口。
3. 检查目标应用是否暴露了/actuator/prometheus端点。
确保网络互通,修正配置,检查应用健康状态。
Grafana 中查询不到数据数据源配置错误、PromQL 写错、指标名称不对1. 在 Grafana 数据源配置页点击Save & Test。
2. 前往 Prometheus UI 的Graph页,用相同 PromQL 查询,验证是否有数据。
3. 在 Prometheus 的Status->Targets页查看抓取状态。
修正数据源 URL 或 PromQL,确保 Prometheus 正常抓取。
Jaeger 中看不到追踪数据应用未集成 SDK、采样率过低、导出地址错误1. 检查应用日志,看是否有 OpenTelemetry 相关的错误。
2. 确认otel.traces.sampler.probability配置大于0。
3. 确认otel.exporter.otlp.endpoint指向正确的 Jaeger 地址。
添加依赖,调整配置,检查网络连通性。
自定义指标在 Prometheus 中看不到指标名称不符合规范、注册方式错误1. 访问应用的/actuator/prometheus端点,确认指标已暴露。
2. 检查指标名称是否包含非法字符(只允许[a-zA-Z_:][a-zA-Z0-9_:]*)。
使用 Micrometer 的 API 规范注册指标。
告警无法触发或无法送达告警规则表达式条件永远不满足、Alertmanager 未配置或路由错误1. 在 Prometheus 的Alerts页查看告警规则状态。
2. 检查 Alertmanager 的日志和配置(route,receivers)。
3. 测试告警的静默(Silence)功能是否开启。
调试 PromQL,修正 Alertmanager 配置,检查接收端(如钉钉机器人)配置。

8. 最佳实践与工程建议

1. 指标设计遵循“USE”和“RED”方法

  • USE(Utilization, Saturation, Errors):适用于基础设施(如CPU、内存、磁盘)。关注使用率、饱和度和错误数。
  • RED(Rate, Errors, Duration):适用于服务(如微服务、API)。关注请求速率、错误率和耗时。 为你的核心服务定义清晰的 RED 指标看板,是监控的起点。

2. 告警分级与降噪

  • P0(致命):影响核心业务,需立即响应。如:核心接口成功率<99%,数据库主节点宕机。
  • P1(严重):影响部分用户或功能,需尽快处理。如:非核心接口成功率<95%,响应时间P99>2s。
  • P2(警告):潜在风险或需要关注。如:磁盘使用率>80%,内存使用率持续增长。 为不同级别配置不同的通知渠道和响应SLA。避免“告警疲劳”,确保每一条告警都是可行动的。

3. 建立“黄金信号”看板为每个微服务创建一个统一的概览看板,至少包含:

  • 请求量(QPS/RPS)趋势图
  • 错误率(4xx, 5xx)趋势图
  • 响应时间(平均,P50, P90, P99)趋势图
  • 服务依赖健康状态(上游/下游) 这个看板应作为团队每日站会或线上值班的第一屏。

4. 追踪与日志的协同

  • 强制在日志中注入 TraceID 和 SpanID。这是打通追踪和日志的桥梁。
  • 在 Jaeger/Grafana Tempo 中配置 Loki 数据源链接,实现从 Trace 一键跳转到关联日志。
  • 结构化日志(JSON格式)比纯文本日志更利于分析和过滤。

5. 生产环境部署考量

  • Prometheus:考虑使用 Thanos 或 Cortex 实现长期存储和高可用。
  • 存储:评估数据保留周期,监控存储容量。指标数据通常可聚合后长期保存,原始链路数据保留期较短(如7天)。
  • 安全:为监控组件(尤其是Grafana、Alertmanager)配置认证和授权。避免将监控端点暴露在公网。
  • 资源隔离:为监控栈单独规划服务器或K8s命名空间,避免与业务争抢资源。

9. 总结:从“拥有工具”到“善用数据”

搭建一套包含 Prometheus、Grafana、Jaeger、Loki 的监控栈,只是“懂表”的第一步,相当于你拥有了一个装满名表的表盒。真正的功力,体现在你如何解读这些“表盘”上的数据。

  • 初级阶段:看仪表盘,知道系统现在是否健康。
  • 中级阶段:通过对比历史曲线、关联不同指标,能判断问题的类型和影响范围。
  • 高级阶段:在故障发生前,通过趋势预测风险(如容量预警);在故障发生时,能通过预设的关联分析(指标+日志+追踪)在1分钟内定位到代码行或配置项。

建议你以本文的本地沙箱为起点,将这套模式逐步推广到你的测试和生产环境。先从最重要的一个服务开始,定义好它的 RED 指标,配置好关键告警,打通日志和追踪。当你和你的团队习惯了基于数据做决策,而不是凭感觉猜问题时,你会发现自己对系统的掌控力达到了一个全新的层次。

技术人的“腕表”,不是为了炫耀,而是为了在关键时刻,能精准、冷静地诊断问题。现在,你的表盒已经备好,是时候开始你的“读表”训练了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询