零基础搞懂监控工具:从架构思维到实战落地

吴浩天
2026-07-29 15:03
阅读 353

作者说:我写这篇教程,是因为五年前我刚入行时,被线上故障搞得焦头烂额,却连日志都不知道去哪找。那时候我就想,如果有人能早点告诉我"监控到底是个什么东西",我大概能少掉几根头发。今天,我用最直白的话,把这件事讲清楚。


一、监控工具到底是什么?

我当初学的时候,最大的困惑不是"怎么用 Prometheus",而是"为什么要用它"。

打个比方:你开了一家餐厅,你不可能只坐在收银台等顾客投诉菜不好吃。你会在厨房里装温度计、在冰柜里放传感器、在出菜口装摄像头、在门口放计数器。这些东西加在一起,就是"监控"。

监控工具,就是给你的软件系统装上"温度计"和"摄像头"。

它回答三个核心问题:

问题 对应监控维度 举例
系统现在健不健康? 健康检查 / 存活探针 服务是否在线、端口是否通
系统跑得慢不慢? 性能指标 接口响应时间、QPS、CPU使用率
哪里出了问题? 日志 / 链路追踪 错误日志、请求在哪个环节超时

从架构设计的角度来看,监控不是一个"附加功能",它是系统的神经系统。没有监控的系统,就像蒙着眼睛开车——能跑,但随时可能撞墙。


二、监控工具的架构全景

在动手之前,我们先在脑子里画一张图。一个完整的监控体系,通常由四层组成:

┌─────────────────────────────────────────────────────┐
│                  展示层(Grafana)                     │
│            仪表盘、告警规则、可视化                      │
├─────────────────────────────────────────────────────┤
│                  存储与查询层                           │
│       Prometheus / InfluxDB / Elasticsearch           │
├─────────────────────────────────────────────────────┤
│                  采集与传输层                           │
│      Exporter / Telegraf / Filebeat / OTel Collector  │
├─────────────────────────────────────────────────────┤
│                  数据源(你的应用)                      │
│         业务代码 / 中间件 / 操作系统 / 数据库            │
└─────────────────────────────────────────────────────┘

我用一个表格把每一层的职责说清楚:

层级 职责 常见工具 类比
数据源 产生原始数据 你的Java/Python/Go应用 厨房里的温度计
采集层 把数据拉走或推走 Prometheus Exporter、Fluentd 服务员把数据抄到本子上
存储层 把数据存起来,支持查询 Prometheus、InfluxDB 档案柜
展示层 把数据画成图表、触发告警 Grafana、Alertmanager 老板看的大屏

这里有一个新手常问的问题:Prometheus 和 Grafana 是什么关系?

简单说:Prometheus 负责"存"和"查",Grafana 负责"画"。Prometheus 是仓库管理员,Grafana 是展示设计师。它们是两个独立的工具,但经常一起用。


三、环境准备

我们用一个最小可运行的环境来上手。你需要准备:

  • 一台 Linux 机器(CentOS 7+ / Ubuntu 18+),或者用 Docker
  • Docker 和 Docker Compose(推荐用 Docker,省去安装的痛苦)
  • 一个能跑起来的小 Web 应用(后面我会给你一个 Python 示例)

我当初学的时候,在 CentOS 上手动装 Prometheus 装了两天,配置文件写错一个缩进就报错。后来发现用 Docker 十分钟就搞定了。所以,新手请务必用 Docker

3.1 创建项目目录

mkdir monitoring-demo
cd monitoring-demo

3.2 编写 docker-compose.yml

这个文件会帮我们一键启动 Prometheus 和 Grafana:

version: '3.8'

services:
  prometheus:
    image: prom/prometheus:v2.47.0
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'

  grafana:
    image: grafana/grafana:10.1.0
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin123
    volumes:
      - grafana-data:/var/lib/grafana

volumes:
  grafana-data:

3.3 编写 Prometheus 配置文件

创建 prometheus.yml

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'my-app'
    static_configs:
      - targets: ['host.docker.internal:8000']

这段配置的意思是:每隔 15 秒,去 host.docker.internal:8000/metrics 端点拉一次数据。host.docker.internal 是 Docker 里访问宿主机的特殊域名。


四、核心概念:用最简单的话讲清楚

4.1 指标(Metric)是什么?

指标就是监控里的"数据点"。它有三个要素:

  • 指标名:比如 http_request_duration_seconds
  • 标签(Label):用来区分维度,比如 method="GET"status="200"
  • :当前的数值

举个例子:

http_request_duration_seconds{method="GET", path="/api/users", status="200"} 0.035

这行数据的意思是:GET 请求 /api/users 接口、返回 200 状态码的耗时是 0.035 秒。

4.2 四种指标类型

Prometheus 定义了四种指标类型,我用一个表格来对比:

类型 特点 适用场景 举例
Counter 只增不减 累计次数 总请求数、错误总数
Gauge 可增可减 瞬时值 当前内存使用量、在线人数
Histogram 分桶统计分布 耗时分布 请求耗时的百分位
Summary 客户端计算分位 耗时分布 请求耗时的 P99

我当初学的时候,Counter 和 Gauge 老搞混。后来记住一句话:能回退的是 Gauge,不能回退的是 Counter。比如内存用量可以升可以降,是 Gauge;总请求数只能往上加,是 Counter。

4.3 Pull 模型 vs Push 模型

这是 Prometheus 架构设计里最核心的一个决策。

Pull 模型(Prometheus 的方式):Prometheus 主动去目标机器上拉数据。

Prometheus  ──── 请求 /metrics ────>  你的应用
   <─────── 返回指标数据 ───────────

Push 模型(传统方式):你的应用主动把数据推给监控中心。

你的应用  ──── 推送指标数据 ────>  监控中心

为什么 Prometheus 选 Pull?因为 Pull 模型有几个好处:

  1. 你能知道目标是不是活着——拉不到数据,说明目标挂了
  2. 配置集中管理——在 Prometheus 端配置要监控谁,而不是在每个应用里配置推给谁
  3. 调试方便——你可以手动 curl 目标的 /metrics 端点看数据对不对

五、实战项目:给你的 Python 应用加上监控

我们用一个 Flask 应用来演示。这个应用有两个接口:一个正常接口,一个偶尔超时的接口。

5.1 安装依赖

pip install flask prometheus_client

5.2 编写应用代码

创建 app.py

import time
import random
from flask import Flask, jsonify
from prometheus_client import Counter, Gauge, Histogram, generate_latest

app = Flask(__name__)

# 定义指标
REQUEST_COUNT = Counter(
    'http_requests_total',
    'Total HTTP requests',
    ['method', 'endpoint', 'status']
)

REQUEST_DURATION = Histogram(
    'http_request_duration_seconds',
    'HTTP request duration in seconds',
    ['method', 'endpoint'],
    buckets=[0.01, 0.05, 0.1, 0.5, 1.0, 5.0]
)

ONLINE_USERS = Gauge(
    'online_users',
    'Number of online users'
)

# 模拟在线用户数波动
ONLINE_USERS.set(42)

@app.route('/api/users')
def get_users():
    start_time = time.time()
    
    # 模拟业务逻辑
    time.sleep(0.02)
    
    # 记录指标
    REQUEST_COUNT.labels(method='GET', endpoint='/api/users', status='200').inc()
    REQUEST_DURATION.labels(method='GET', endpoint='/api/users').observe(time.time() - start_time)
    
    return jsonify({"users": ["Alice", "Bob", "Charlie"]})

@app.route('/api/slow')
def slow_endpoint():
    start_time = time.time()
    
    # 模拟偶尔超时
    delay = random.uniform(0.01, 2.0)
    time.sleep(delay)
    
    status = '200' if delay < 1.0 else '504'
    REQUEST_COUNT.labels(method='GET', endpoint='/api/slow', status=status).inc()
    REQUEST_DURATION.labels(method='GET', endpoint='/api/slow').observe(time.time() - start_time)
    
    if delay >= 1.0:
        return jsonify({"error": "timeout"}), 504
    return jsonify({"result": "ok", "delay": round(delay, 3)})

@app.route('/metrics')
def metrics():
    return generate_latest()

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000)

5.3 代码解读

这段代码做了三件事:

  1. 定义了三个指标REQUEST_COUNT(计数器)、REQUEST_DURATION(直方图)、ONLINE_USERS(仪表盘)
  2. 在业务逻辑中埋点:每次请求进来,计数器加一,直方图记录耗时
  3. 暴露 /metrics 端点:Prometheus 来拉数据时,就访问这个端点

5.4 启动服务

# 先启动 Prometheus 和 Grafana
docker-compose up -d

# 再启动你的应用
python app.py

5.5 验证数据

手动访问一下你的应用,然后看看 /metrics 端点:

curl http://localhost:8000/api/users
curl http://localhost:8000/api/slow
curl http://localhost:8000/metrics

你会看到类似这样的输出:

# HELP http_requests_total Total HTTP requests
# TYPE http_requests_total counter
http_requests_total{endpoint="/api/users",method="GET",status="200"} 3.0
http_requests_total{endpoint="/api/slow",method="GET",status="200"} 1.0
http_requests_total{endpoint="/api/slow",method="GET",status="504"} 1.0

# HELP http_request_duration_seconds HTTP request duration in seconds
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{endpoint="/api/users",method="GET",le="0.01"} 0.0
http_request_duration_seconds_bucket{endpoint="/api/users",method="GET",le="0.05"} 3.0
http_request_duration_seconds_bucket{endpoint="/api/users",method="GET",le="0.1"} 3.0
...

5.6 在 Grafana 中查看

打开浏览器访问 http://localhost:3000,用户名 admin,密码 admin123

第一步:添加数据源。进入 Configuration → Data Sources → Add data source → 选 Prometheus → URL 填 http://prometheus:9090 → Save & Test。

第二步:创建 Dashboard。进入 Dashboards → New Dashboard → Add Panel → 在查询框里输入:

rate(http_requests_total[1m])

这条 PromQL 的意思是:计算过去 1 分钟内请求速率的变化。你会看到一条实时变化的曲线。


六、PromQL:监控世界的查询语言

PromQL 是 Prometheus 的查询语言,类似于 SQL 之于数据库。你不需要精通,但几个最常用的必须会。

6.1 常用函数速查表

函数 作用 示例
rate() 计算每秒平均增长率 rate(http_requests_total[5m])
increase() 计算时间段内的增量 increase(http_requests_total[1h])
histogram_quantile() 计算分位值 histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
sum() 求和 sum(rate(http_requests_total[5m])) by (endpoint)
avg() 求平均 avg(node_cpu_seconds_total) by (instance)

6.2 实战 PromQL

我列出几个你上线后一定会用到的查询:

# 1. 当前 QPS(每秒请求数)
sum(rate(http_requests_total[1m]))

# 2. 按接口分组的 QPS
sum(rate(http_requests_total[1m])) by (endpoint)

# 3. P99 响应时间
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, endpoint))

# 4. 错误率(5xx 占比)
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))

# 5. 在线用户数
online_users

我当初学 PromQL 的时候,最大的坑是 rate()increase() 搞混。记住:rate() 返回的是"每秒"的速率,increase() 返回的是"整个时间段"的增量。如果你要看"过去一小时多了多少请求",用 increase();如果你要看"现在的请求速率是多少",用 rate()


七、告警:监控的灵魂

监控如果不告警,那就只是一块好看的屏幕。告警才是监控真正发挥价值的地方。

7.1 配置告警规则

prometheus.yml 中添加告警规则文件引用:

global:
  scrape_interval: 15s

rule_files:
  - 'alert_rules.yml'

scrape_configs:
  - job_name: 'my-app'
    static_configs:
      - targets: ['host.docker.internal:8000']

创建 alert_rules.yml

groups:
  - name: my-app-alerts
    rules:
      - alert: HighErrorRate
        expr: |
          sum(rate(http_requests_total{status=~"5.."}[5m])) 
          / sum(rate(http_requests_total[5m])) > 0.05
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "错误率超过 5%"
          description: "当前错误率为 {{ $value | humanizePercentage }}"

      - alert: SlowResponse
        expr: |
          histogram_quantile(0.99, 
            sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
          ) > 1.0
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "P99 响应时间超过 1 秒"

7.2 告警规则解读

每条告警规则有三个关键部分:

  • expr:触发条件,用 PromQL 写
  • for:持续时间,满足条件多久后才真正告警(防止瞬时抖动误报)
  • labelsannotations:告警的标签和描述信息

这里有一个架构设计上的思考:for 字段非常重要。如果接口偶尔有一个慢请求,P99 可能瞬间飙高,但几秒钟就恢复了。如果不设 for,你会收到一堆无意义的告警,最后把所有告警都屏蔽掉——这就叫"告警疲劳",是监控体系里最危险的事情。


八、日志监控:别忘了另一半

指标监控告诉你"系统慢了",日志监控告诉你"为什么慢"。两者缺一不可。

8.1 日志收集架构

你的应用(写日志到文件)
    │
    ▼
Filebeat / Fluentd(采集日志)
    │
    ▼
Elasticsearch(存储和检索)
    │
    ▼
Kibana(查看和搜索)

这个组合叫 EFK Stack(Elasticsearch + Fluentd/Filebeat + Kibana),是日志监控的事实标准。

8.2 用 Docker 快速搭建 EFK

docker-compose.yml 中追加:

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.10.0
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
    ports:
      - "9200:9200"

  kibana:
    image: docker.elastic.co/kibana/kibana:8.10.0
    ports:
      - "5601:5601"
    depends_on:
      - elasticsearch

8.3 应用端日志规范

日志不是随便写就行的。好的日志应该包含:

  • 时间戳
  • 日志级别(INFO / WARN / ERROR)
  • 请求 ID(用于链路追踪)
  • 关键上下文(用户 ID、接口路径等)
import logging
import uuid

logging.basicConfig(
    format='%(asctime)s [%(levelname)s] [request_id=%(request_id)s] %(message)s',
    level=logging.INFO
)

class RequestIDFilter(logging.Filter):
    def filter(self, record):
        record.request_id = getattr(record, 'request_id', str(uuid.uuid4())[:8])
        return True

logger = logging.getLogger(__name__)
logger.addFilter(RequestIDFilter())

@app.route('/api/users')
def get_users():
    request_id = str(uuid.uuid4())[:8]
    logger.info("Handling request", extra={'request_id': request_id})
    # ... 业务逻辑
    logger.info("Request completed", extra={'request_id': request_id})
    return jsonify({"users": ["Alice", "Bob"]})

九、新手常见问题

Q1:Prometheus 数据能存多久?

默认情况下,Prometheus 本地存储保留 15 天。可以通过启动参数 --storage.tsdb.retention.time=30d 改成 30 天。但 Prometheus 不适合做长期存储,长期数据建议用 Thanos 或 VictoriaMetrics。

Q2:我的应用是 Java/Go/Node.js,怎么接入?

每种语言都有对应的 Prometheus Client 库:

语言 库名 安装方式
Python prometheus_client pip install prometheus_client
Java micrometer Maven 依赖
Go prometheus/client_golang go get github.com/prometheus/client_golang
Node.js prom-client npm install prom-client

接入思路完全一样:定义指标 → 在代码中埋点 → 暴露 /metrics 端点。

Q3:指标太多了,Prometheus 扛不住怎么办?

这是架构设计中常见的问题。几个解决思路:

  1. 减少标签组合:不要把用户 ID 这种高基数字段作为标签
  2. 用 Recording Rules 预计算:把复杂的 PromQL 提前算好,存成新的指标
  3. 分片部署:用 Thanos 或 Prometheus Federation 做水平扩展

Q4:Grafana 面板太多,看不过来怎么办?

按照"总—分—细"的层级设计 Dashboard:

  • L1 总览面板:一眼看到系统整体健康状态(绿/黄/红)
  • L2 模块面板:按服务或模块分组,看各模块的核心指标
  • L3 详情面板:排查问题时下钻,看具体接口、具体机器的详细数据

十、学习建议与避坑指南

10.1 我踩过的坑

  1. 一开始就想搞全套:Prometheus + Grafana + Alertmanager + Thanos + Loki + Tempo... 新手千万别这么干。先把 Prometheus + Grafana 跑通,能监控一个接口,就已经超过 80% 的团队了。

  2. 告警规则写得太敏感:接口超时一次就告警,结果一天收 500 条告警,最后全忽略了。记住:宁可漏报,不可误报。误报多了,人会麻木。

  3. 只监控不演练:监控搭好了,但从没验证过告警能不能发出来。建议定期做"混沌工程"演练,主动制造故障,验证监控是否真的能发现。

10.2 下一步学习路径

入门:Prometheus + Grafana 跑通(你现在在这里)
    │
    ▼
进阶:接入 Alertmanager,配置钉钉/企微/邮件告警
    │
    ▼
中级:学习日志监控(EFK Stack)和链路追踪(Jaeger / SkyWalking)
    │
    ▼
高级:学习 Thanos / VictoriaMetrics 做长期存储和高可用
    │
    ▼
专家:设计完整的可观测性体系(Metrics + Logs + Traces 三支柱)

10.3 推荐资源


总结

监控工具的本质,是给系统装上"神经系统"。它由四层组成:数据源、采集层、存储层、展示层。Prometheus 用 Pull 模型主动拉取指标,Grafana 负责可视化,Alertmanager 负责告警。

从架构设计的角度,监控体系有三个核心原则:

  1. 可观测性优先于可监控性——先让系统"可被观察",再谈"可被监控"
  2. 告警要少而精——每一条告警都应该是需要人工介入的
  3. 监控要像代码一样管理——配置文件版本化,Dashboard 代码化

我当初花了半年才把这些概念串起来。希望这篇文章能帮你把这条路缩短到一周。

去跑通你的第一个 Dashboard 吧。当你第一次看到自己写的接口 QPS 曲线在 Grafana 上跳动的时候,你会觉得这一切都值得。

评论 0

最热最新
暂无评论
吴浩天Lv.1
0
影响力
0
文章
0
粉丝