ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从零构建可观测性体系:Prometheus+Grafana+Jaeger实战指南

从零构建可观测性体系:Prometheus+Grafana+Jaeger实战指南 如果你在技术社区问“很懂表的人都会戴些什么表”得到的答案大概率不是劳力士、百达翡丽或欧米茄而是一串你从未听过的缩写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 id123, 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.time200h - --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_PASSWORDadmin123 - GF_INSTALL_PLUGINSgrafana-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_ENABLEDtrue 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_ENDPOINThttp://jaeger:4317 - OTEL_SERVICE_NAMEdemo-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:9090Jaeger UI: http://localhost:16686Loki: 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 groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency !-- Micrometer Prometheus 注册表将指标转换为Prometheus格式 -- dependency groupIdio.micrometer/groupId artifactIdmicrometer-registry-prometheus/artifactId /dependency !-- OpenTelemetry Starter自动生成链路追踪 -- dependency groupIdio.opentelemetry.instrumentation/groupId artifactIdopentelemetry-spring-boot-starter/artifactId version2.5.0/version !-- 请使用最新稳定版本 -- /dependency5.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; // 定义一个计数器标签为 methodgetUserById 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 ResponseEntityUser 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为了将链路追踪IDTraceID自动打入日志方便在 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{applicationuser-service, methodgetUserById,} 42.0 # HELP user_query_duration_seconds Time taken to handle user query # TYPE user_query_duration_seconds histogram user_query_duration_seconds_bucket{applicationuser-service, methodgetUserById, le0.005,} 10.0 user_query_duration_seconds_bucket{applicationuser-service, methodgetUserById, le0.01,} 35.0 ...6. 运行结果与效果验证6.1 验证指标采集访问 Prometheus UI (http://localhost:9090)。在 Graph 页面的查询框中输入user_query_requests_total并执行。你应该能看到一条名为user_query_requests_total{applicationuser-service, ...}的指标曲线。访问几次你的应用接口曲线会随之上升。6.2 配置 Grafana 看板登录 Grafana (http://localhost:3000)。点击Configuration-Data Sources-Add data source选择Prometheus。URL 填写http://prometheus:9090点击Save Test应显示Data source is working。点击Create-Dashboard-Add new panel。在 Query 编辑框中输入 PromQL 查询语句例如请求率rate(user_query_requests_total[5m])P95 响应时间histogram_quantile(0.95, rate(user_query_duration_seconds_bucket[5m]))配置图表标题、坐标轴等点击Apply保存面板。6.3 验证链路追踪通过 Jaeger UI (http://localhost:16686) 查看追踪数据。在 Service 下拉框中你应该能看到user-service。点击Find Traces会列出最近的请求追踪。点击任意一条可以看到该请求的完整调用链、各阶段的耗时以及携带的标签如 HTTP 方法、路径、状态码。6.4 验证日志关联在 Grafana 中再添加一个Loki数据源URL 为http://loki:3100。新建一个面板数据源选择 Loki查询语句可以输入{jobvarlogs} | trace_id来查找包含 trace_id 的日志。更强大的用法是在 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%响应时间P992s。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 指标配置好关键告警打通日志和追踪。当你和你的团队习惯了基于数据做决策而不是凭感觉猜问题时你会发现自己对系统的掌控力达到了一个全新的层次。技术人的“腕表”不是为了炫耀而是为了在关键时刻能精准、冷静地诊断问题。现在你的表盒已经备好是时候开始你的“读表”训练了。
返回列表