// LATEST

【技术分享】Prometheus & Grafana 部署

为什么是 Prometheus + Grafana

K8s 时代之前,监控选型百花齐放(Zabbix / Nagios / Cacti / InfluxDB+Telegraf...)。Prometheus 出现后把这件事重新定义了一遍 —— Pull 模型 + 多维度 label + PromQL,几乎成了 Cloud Native 监控的事实标准。Grafana 则是它最佳搭档,只负责一件事:把数据画好看

Prometheus 的核心理念

  • Pull 模型(server 主动抓 target 的 /metrics
  • 多维度 label 数据模型
  • PromQL 查询语言
  • 自带 TSDB(单机 1.5 亿样本/s)
  • 服务发现(K8s / Consul / DNS / 静态)

Grafana 的核心理念

  • 数据源无关(Prom / Loki / InfluxDB / MySQL / ...)
  • 强大的面板编辑器 + transform
  • Dashboard as Code(JSON / Provisioning)
  • 内建告警引擎(Unified Alerting)
  • 多用户 / Org / Folder 权限

两者的边界很清晰:Prometheus 负责采集 + 存储 + 计算,Grafana 负责展现 + 告警 UI

整体架构

[Exporters] ── scrape ──▶ [Prometheus] ──▶ [Grafana] ──▶ 浏览器
                              │
                              ▼
                        [Alertmanager] ──▶ 告警通道(钉钉/邮件/PagerDuty)

最小生产级部署需要 4 类组件:

组件分工


  • 01
    Prometheus —— 时序库 + 抓取调度器 + 规则引擎

  • 02
    Alertmanager —— 告警去重 / 分组 / 路由 / 静默 / 抑制

  • 03
    Grafana —— 可视化 + 用户系统 + 告警 UI(也内置告警引擎,但通常只用前者)

  • 04
    若干 Exporter —— 把目标系统的指标转成 Prometheus 格式(Node / MySQL / Redis / Blackbox / ...)

准备工作


前置要求

  • Linux 主机一台(建议 4C8G 起步,Prometheus 内存随 series 数量线性增长)
  • Docker 24.x + Docker Compose v2
  • ≥ 100 GB 数据盘(监控数据 retention 默认 15 天)
  • 公网域名(强烈建议,便于配 TLS)
  • 80 / 443 端口空闲

下面以一台 Ubuntu 22.04 LTS 为例,所有组件跑在 Docker 里。

一、目录结构

mkdir -p /opt/monitoring/{prometheus/config,prometheus/rules,alertmanager/config,grafana/provisioning,grafana/dashboards,data/prometheus,data/grafana,data/alertmanager}
cd /opt/monitoring

# 数据目录权限 —— 容器里跑的 uid 各不相同
sudo chown -R 65534:65534 data/prometheus       # nobody:nogroup
sudo chown -R 472:472     data/grafana          # grafana
sudo chown -R 65534:65534 data/alertmanager

二、Prometheus 配置

prometheus/config/prometheus.yml

global:
  scrape_interval:     15s
  evaluation_interval: 15s
  external_labels:
    cluster: prod
    replica: 0

rule_files:
  - /etc/prometheus/rules/*.rules.yml

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']

scrape_configs:
  # Prometheus 自监控
  - job_name: prometheus
    static_configs:
      - targets: ['localhost:9090']

  # 主机指标
  - job_name: node
    static_configs:
      - targets: ['node-exporter:9100']

  # 黑盒探测(HTTP 健康检查)
  - job_name: blackbox-http
    metrics_path: /probe
    params:
      module: [http_2xx]
    static_configs:
      - targets:
          - https://yantao.wiki
          - https://example.com/healthz
    relabel_configs:
      - source_labels: [__address__]
        target_label:  __param_target
      - source_labels: [__param_target]
        target_label:  instance
      - target_label:  __address__
        replacement:   blackbox-exporter:9115

为什么 retention 默认是 15 天?

Prometheus 的设计定位是 短期热存储,长期保留靠 remote_write 转到 Mimir / Thanos / VictoriaMetrics。15 天的默认值平衡了:

  • 单实例硬盘成本
  • 大多数运维场景的回溯需求(P90 故障在 1 周内能定位)
  • WAL replay 时间(保留越长重启越慢)

需要更长期可加 --storage.tsdb.retention.time=90d,但同时要扩磁盘并接受重启变慢的代价。

三、告警规则

prometheus/rules/node.rules.yml

groups:
  - name: node
    rules:
      - alert: NodeDown
        expr: up{job="node"} == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "节点 {{ $labels.instance }} 失联超过 2 分钟"
          description: "Prometheus 抓取 {{ $labels.job }}/{{ $labels.instance }} 失败"

      - alert: HighCPU
        expr: 100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "{{ $labels.instance }} CPU 持续超过 85%"

      - alert: DiskFull
        expr: |
          (node_filesystem_avail_bytes{mountpoint="/"}
           / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 10
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "{{ $labels.instance }} 根分区剩余 < 10%"
告警的 for: 字段非常重要 —— 它决定了「连续异常多久才真正 firing」。设得太短会被毛刺打爆 oncall 群,设得太长又延迟响应。一般规律:metric 异常时延 < for < 业务可容忍的延迟。

四、Alertmanager 配置

alertmanager/config/alertmanager.yml

global:
  resolve_timeout: 5m

route:
  receiver: default
  group_by: ['alertname', 'cluster', 'service']
  group_wait:      30s
  group_interval:  5m
  repeat_interval: 4h
  routes:
    - matchers:
        - severity = critical
      receiver: critical-pager
      continue: true       # 让 critical 也走 default 一份(双通道)

receivers:
  - name: default
    webhook_configs:
      - url: https://oapi.dingtalk.com/robot/send?access_token=YOUR_DD_TOKEN

  - name: critical-pager
    webhook_configs:
      - url: https://oapi.dingtalk.com/robot/send?access_token=YOUR_DD_TOKEN
    email_configs:
      - to: oncall@example.com
        smarthost: smtp.example.com:587
        auth_username: alert@example.com
        auth_password: '<smtp-pass>'
        from: alert@example.com
        require_tls: true

钉钉 / 飞书 / Slack 都用 webhook 接入,但 Alertmanager 默认 payload 格式不是它们的。中间通常加一个转换器,常用的是 prometheus-webhook-dingtalk

五、Grafana 数据源 & 仪表盘自动导入

Grafana 推荐用 provisioning 把数据源和仪表盘做成代码,避免点点点。

grafana/provisioning/datasources/prometheus.yml

apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    jsonData:
      timeInterval: 15s
      httpMethod: POST

grafana/provisioning/dashboards/default.yml

apiVersion: 1
providers:
  - name: default
    folder: ''
    type: file
    options:
      path: /var/lib/grafana/dashboards

把社区精品仪表盘的 JSON 下载到 grafana/dashboards/

DashboardID来源
Node Exporter Full1860rfmoz
Prometheus Stats2grafana
Blackbox Exporter7587grafana

Grafana 启动时会自动加载,重启容器即生效

六、Docker Compose 编排

docker-compose.yml

version: '3.8'

services:
  prometheus:
    image: prom/prometheus:v2.55.0
    container_name: prometheus
    restart: unless-stopped
    user: '65534:65534'
    command:
      - --config.file=/etc/prometheus/prometheus.yml
      - --storage.tsdb.path=/prometheus
      - --storage.tsdb.retention.time=15d
      - --web.enable-lifecycle              # POST /-/reload 热加载
      - --web.external-url=https://prom.yantao.wiki
    volumes:
      - ./prometheus/config:/etc/prometheus
      - ./prometheus/rules:/etc/prometheus/rules
      - ./data/prometheus:/prometheus
    networks: [monitoring]

  alertmanager:
    image: prom/alertmanager:v0.27.0
    container_name: alertmanager
    restart: unless-stopped
    user: '65534:65534'
    command:
      - --config.file=/etc/alertmanager/alertmanager.yml
      - --storage.path=/alertmanager
      - --web.external-url=https://am.yantao.wiki
    volumes:
      - ./alertmanager/config:/etc/alertmanager
      - ./data/alertmanager:/alertmanager
    networks: [monitoring]

  grafana:
    image: grafana/grafana:11.3.0
    container_name: grafana
    restart: unless-stopped
    user: '472:472'
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=changeme-now
      - GF_SERVER_ROOT_URL=https://grafana.yantao.wiki
      - GF_AUTH_ANONYMOUS_ENABLED=false
      - GF_USERS_ALLOW_SIGN_UP=false
    volumes:
      - ./grafana/provisioning:/etc/grafana/provisioning
      - ./grafana/dashboards:/var/lib/grafana/dashboards
      - ./data/grafana:/var/lib/grafana
    networks: [monitoring]

  node-exporter:
    image: prom/node-exporter:v1.8.2
    container_name: node-exporter
    restart: unless-stopped
    pid: host
    network_mode: host
    volumes:
      - /:/host:ro,rslave
    command:
      - --path.rootfs=/host
      - --collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($|/)

  blackbox-exporter:
    image: prom/blackbox-exporter:v0.25.0
    container_name: blackbox-exporter
    restart: unless-stopped
    networks: [monitoring]

networks:
  monitoring:
    name: monitoring

启动:

docker compose up -d
docker compose ps

七、首次访问验证

  • 访问 http://<host>:9090
  • 顶部菜单 Status → Targets:所有 endpoint 应显示 UP
  • Status → Configuration:检查解析后的最终配置
  • Graph 面板执行 up:应该看到所有 target 的状态
  • 执行 rate(prometheus_tsdb_head_samples_appended_total[5m]):当前的样本写入速率

八、生产化清单

部署能跑只是第一步,下面是上生产前必补的 6 层:

生产级补丁层(按优先级)


  • L1
    TLS + 反代 —— Caddy / Nginx 给三个 UI 都套上 HTTPS。Caddy 自动 Let's Encrypt 最省心。Prometheus / Alertmanager 的 --web.external-url 必须设对,否则 redirect / link 会回到 localhost。

  • L2
    鉴权 —— Prometheus / Alertmanager 默认裸奔,对外必须加 basic auth(在反代层配);Grafana 内置用户系统就够用,记得关掉 GF_AUTH_ANONYMOUS_ENABLEDGF_USERS_ALLOW_SIGN_UP

  • L3
    数据持久化 —— ./data 子目录映射做对就行。生产建议挂独立云盘,定期 snapshot;Prometheus 的 /prometheus/snapshots API 可以触发热备。

  • L4
    长期存储 —— 单机 Prometheus 撑 1-2 个月数据,超出走 remote_writeVictoriaMetrics / Mimir / Thanos。VM 单机性能比原生 Prom 高 10x,迁移成本低。

  • L5
    HA —— Prometheus 双副本(同 config,不同 external_labels.replica)→ Alertmanager 集群(gossip 协议)→ 任意一台挂了告警不丢、查询走 Thanos Querier 自动去重。

  • L6
    备份 —— Grafana 仪表盘走 provisioning + git;Prometheus 数据 snapshot via API;Alertmanager silence 也是状态,值得备份。

九、常见踩坑


踩坑 1 :Targets 显示 DOWN 但目标服务正常

90% 是 Docker 网络问题。先验证容器间能不能 talk:

docker exec prometheus wget -qO- http://node-exporter:9100/metrics | head

如果 node-exporter 用了 network_mode: host,Prometheus 容器要用 宿主机的 docker0 IP(在 Linux 上通常是 172.17.0.1),不是 host.docker.internal(那个只在 Docker Desktop 上有)。


踩坑 2 :Grafana 提示 data source proxy: dial tcp ... connection refused

数据源 URL 用了 http://localhost:9090 —— Grafana 容器里的 localhost 不是宿主机!必须用 docker compose 的 service name:

url: http://prometheus:9090   # ✅
url: http://localhost:9090    # ❌ 在容器里指向自己
url: http://127.0.0.1:9090    # ❌ 同上

并且两个容器要在同一个 networks 下。


踩坑 3 :Alertmanager 告警没发出

按下面的顺序排查,一般卡在第 4 步:

  1. Prometheus → Status → Targets:up{job="alertmanager"} == 1 吗?
  2. Prometheus → Alerts:规则在 firing 吗?
  3. Alertmanager → Alerts:告警传到了吗?
  4. Alertmanager → Status → Config:route 匹配到了吗?常见问题是 matcher 写错(用了 severity == "critical" 而不是 severity = critical
  5. Alertmanager 容器 log:webhook 调用是否 200?


踩坑 4 :内存爆涨 / OOM

Prometheus 内存随 active series 数量线性增长,估算公式:

内存(GB) ≈ active_series × 3.5 KB / 1024 / 1024

百万 series 大约吃 3.5 GB heap。如果你的 metrics 里有高基数 label(比如把 user_id / request_id 当 label),会迅速爆掉。

排查:topk(20, count by(__name__)({__name__=~".+"})) 看哪个 metric 占了最多 series。

十、写在最后

下一步可以扩展的方向:

  • Loki + Promtail —— 日志聚合,和 metrics 在 Grafana 里跨源关联
  • Tempo —— 分布式追踪,OpenTelemetry-native
  • Pyroscope —— 持续性能剖析(Continuous Profiling)
  • Mimir / Thanos —— 多租户 + 长期存储

完整 LGTM Stack(Loki + Grafana + Tempo + Mimir)把 Logs / Metrics / Traces / Profiles 全部串起来,下一篇细讲。

📖Prometheus 官方文档 📊Grafana 官方文档 K8s Helm Charts

还没有评论

欢迎留下你的观点,保持交流的清晰和友好。

写下评论