2. 监控数据采集:Metrics、Logs、Traces 三大支柱

聊到监控数据采集,圈子里有个共识——光看指标不行,光看日志也不行。你得把 Metrics、Logs、Traces 这三样东西捏在一起,才能看清系统的全貌。我刚开始做调度系统监控那会儿,只盯着 CPU 和内存,结果线上出了故障,指标一切正常,业务却已经崩了半小时。嗯,从那以后,我再也不敢偏科了。

核心观点:Metrics 告诉你「出问题了」,Logs 告诉你「出了什么问题」,Traces 告诉你「问题出在哪一环」。三者缺一不可。

2.1 Metrics(指标)—— 系统的体温计

Metrics 说白了就是数字。它描述的是系统在某个时刻的状态,比如 QPS、延迟 P99、错误率、CPU 使用率。我习惯把 Metrics 分成三类:

  • 工作负载指标:请求量、并发数、队列深度。调度系统里我最关注的是「待调度任务积压量」,这个数一涨,准没好事。
  • 资源指标:CPU、内存、磁盘、网络。别只看平均值,要看 P99 和最大值。我曾经被「平均 CPU 50%」骗过,实际上有一台机器已经 100% 了。
  • 业务指标:任务成功率、调度延迟、租户配额使用率。这些才是老板关心的。

采集 Metrics 的协议,现在主流是 Prometheus 的 Pull 模式。调度系统每个组件暴露一个 /metrics 端点,Prometheus Server 定期来拉。为什么不用 Push?你想想看,调度系统本身节点就多,如果每个节点都主动推数据,网络风暴先把你打趴下。

我的习惯:每个 Metrics 都要带三个标签——service(服务名)、instance(实例 ID)、region(区域)。没有标签的指标,排查问题时基本等于废纸。

2.2 Logs(日志)—— 系统的黑匣子

Metrics 告诉你「出问题了」,但说不出原因。这时候就得翻日志。日志是结构化的文本,每一行记录一个事件。我见过太多团队把日志当垃圾桶,什么乱七八糟的都往里写。结果真出故障时,几十 GB 的日志里捞不出一句有用的话。

好的日志应该长这样:

{
  "timestamp": "2025-03-21T14:32:10.123Z",
  "level": "ERROR",
  "service": "scheduler-worker",
  "trace_id": "abc123def456",
  "message": "Task execution failed",
  "context": {
    "task_id": "task-98765",
    "worker_id": "worker-03",
    "error": "Connection timeout to executor"
  }
}

看到了吗?关键信息全在:时间、级别、服务名、链路 ID、具体错误。我建议所有日志都采用 JSON 格式,方便后续接入 ELK 或 Loki 做全文检索。

避坑指南:我曾经遇到一个团队,日志里打了大量 DEBUG 级别的信息,结果磁盘写满,导致调度系统直接 OOM。记住:生产环境只开 WARN 和 ERROR,DEBUG 日志必须动态开关。

2.3 Traces(链路追踪)—— 系统的 X 光

Traces 解决的是「请求到底慢在哪」的问题。一个调度任务从提交到执行完成,中间可能经过 API 网关、调度器、队列、Worker、执行器。如果每个环节都慢 10ms,用户感知就是 50ms。没有链路追踪,你根本不知道瓶颈在哪。

我常用的 Trace 模型是 OpenTelemetry,它把一次请求抽象成一个 Trace,里面包含多个 Span。每个 Span 记录一个操作,比如「从队列拉取任务」「调用执行器接口」。Span 之间通过 parent_span_id 形成树状结构。

调度系统里,我建议至少给以下操作加 Span:

  • 任务提交入口
  • 任务排队等待
  • 任务调度决策
  • 任务执行调用
  • 任务结果回写

这样一旦某个任务超时,你打开 Jaeger 或 Zipkin,一眼就能看出是调度器决策慢了,还是执行器响应慢了。

2.4 三大支柱的关系与整合

单独看 Metrics、Logs、Traces 都不够。你得把它们关联起来。怎么关联?靠 标签 和 ID。

我举个真实例子:

  1. Prometheus 告警:task_latency_p99 > 5s(Metrics 发现问题)
  2. 点开告警,看到异常的任务 ID 是 task-98765
  3. 去 Loki 搜 task_id=task-98765,发现日志里报了 Connection timeout(Logs 定位原因)
  4. 再拿 trace_id 去 Jaeger 查,看到执行器调用阶段耗时 4.8s(Traces 定位环节)

你看,三步下来,问题根因就锁定了。这就是三大支柱整合的价值。

关键原则:Metrics 负责「告警」,Logs 负责「排查」,Traces 负责「定位」。不要指望一个工具干所有事。

2.5 数据采集架构总览

下面这张图是我自己总结的调度系统监控数据采集架构。你可以把它当成一个参考模板:

调度系统监控数据采集架构 调度系统应用层 Scheduler | Worker | Executor | API Gateway | Queue Metrics 采集 Prometheus Client /metrics 端点暴露 Logs 采集 Filebeat / Fluentd JSON 结构化日志 Traces 采集 OpenTelemetry SDK Span 上报 Prometheus TSDB 指标存储 Loki / Elasticsearch 日志存储 Jaeger / Tempo 链路存储 Grafana | AlertManager | 告警平台 | 值班系统 统一可视化 & 告警消费 说明:应用层通过三种不同协议暴露数据,采集层分别收集后写入对应存储,最终统一在 Grafana 等平台消费。 关键关联字段:service、instance、trace_id、task_id

2.6 采集策略与避坑

数据采集不是把数据扔进去就完事了。我总结了几条铁律:

维度 推荐做法 常见坑
采集频率 Metrics 15s 一次,Logs 实时,Traces 采样 10% 频率太高把系统打满,频率太低错过故障
数据量控制 Metrics 限制标签基数,Logs 设置轮转策略 标签爆炸导致 Prometheus OOM
采样策略 Traces 头部采样 + 错误全采样 全量采样成本高,尾部采样丢失关键链路
关联字段 统一 trace_id、service、instance 字段名不统一,排查时对不上

我曾经踩过的坑:有一次调度系统频繁告警,我查了 Metrics 发现 Worker 的 CPU 飙升,但 Logs 里没有任何 ERROR。折腾了两小时才发现,是日志采集器 Filebeat 把磁盘 IO 打满了,导致 Worker 写日志都卡住。从那以后,我坚持把采集器进程和业务进程做资源隔离,并且给采集器单独设 CPU 限额。

2.7 小结

Metrics、Logs、Traces 这三样东西,你单独拎出来任何一个,都只能看到系统的一个侧面。只有把它们串起来,才能形成完整的可观测性。我个人习惯在 Grafana 里做一个统一的「故障排查面板」,左边放 Metrics 曲线,右边放关联的 Logs 和 Traces 入口。这样值班人员收到告警后,点一下就能开始排查,不用在多个系统之间来回跳。

嗯,数据采集这块就聊到这儿。记住:采集是基础,基础不牢,后面告警和自动化全是空中楼阁。