自建服务的体检表:Prometheus 最小可用栈与四条告警

先想清楚要它回答什么问题

很多人装监控的路径是:听说 Prometheus 是标配 → 拉一套 compose → 打开 Grafana 看见曲线 → 结束。半年后磁盘满了,翻看板才发现那条曲线三天前就开始往上爬,只是没人看。

监控真正要回答的是三个问题,按优先级排:服务还活着吗、资源够用到什么时候、刚才那十分钟发生了什么。第一个问题用一个指标就能答,第二个需要预测,第三个才轮到看板和留存。

所以下面这套栈很小:Prometheus 存时序、node_exporter 采主机、cAdvisor 采容器、Alertmanager 负责把「有事」推到你手机上、Grafana 只在你要查历史时才打开。

compose:四个服务加一个看板

services:
  prometheus:
    image: prom/prometheus:v3.15.1
    command:
      - --config.file=/etc/prometheus/prometheus.yml
      - --storage.tsdb.path=/prometheus
      - --storage.tsdb.retention.time=30d
      - --web.enable-lifecycle
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - ./rules:/etc/prometheus/rules:ro
      - prom-data:/prometheus
    ports:
      - "127.0.0.1:9090:9090"
    restart: unless-stopped

  node-exporter:
    image: prom/node-exporter:v1.12.1
    command:
      - --path.procfs=/host/proc
      - --path.sysfs=/host/sys
      - --path.rootfs=/host/root
      - --collector.filesystem.mount-points-exclude=^/(dev|proc|sys|run|host)(/.+|)$$
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/host/root:ro,rslave
    pid: host
    restart: unless-stopped

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:v0.60.6
    privileged: true
    devices:
      - /dev/kmsg
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    restart: unless-stopped

  alertmanager:
    image: prom/alertmanager:v0.34.1
    volumes:
      - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
    ports:
      - "127.0.0.1:9093:9093"
    restart: unless-stopped

volumes:
  prom-data:

两个端口都绑在 127.0.0.1,这是故意的:Prometheus 的接口默认没有认证,暴露到公网等于把主机名、容器名、内网拓扑一起送出去。要看,就走 SSH 隧道或者塞进反向代理加一道登录。

注意 node-exporter 那行 mount-points-exclude 里的 $$——compose 会把它解析成单个 $,正好是正则里的行尾锚点。写成 $ 会被 compose 当成变量插值,报一个很难看懂的错。

prometheus.yml:只抓三个目标

global:
  scrape_interval: 15s
  evaluation_interval: 15s

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

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

scrape_configs:
  - job_name: node
    static_configs:
      - targets: ['node-exporter:9100']

  - job_name: cadvisor
    static_configs:
      - targets: ['cadvisor:8080']

  - job_name: prometheus
    static_configs:
      - targets: ['localhost:9090']

先把 Prometheus 自己也抓上。它挂掉的时候,你至少还有它自己的 up 归零的痕迹——虽然这条曲线也没人看了。真正的保险在下一步。

告警规则:四条就够开工

groups:
  - name: host-basics
    rules:
      - alert: InstanceDown
        expr: up == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "{{ $labels.instance }} 抓不到了"
          description: "job={{ $labels.job }} 已持续 2 分钟无响应"

      - alert: DiskWillFillIn4Hours
        expr: |
          predict_linear(
            node_filesystem_avail_bytes{fstype!~"tmpfs|overlay|squashfs"}[6h], 4*3600
          ) < 0
        for: 1h
        labels:
          severity: warning
        annotations:
          summary: "{{ $labels.instance }} {{ $labels.mountpoint }} 预计 4 小时内写满"

      - alert: MemoryPressure
        expr: |
          node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.10
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "{{ $labels.instance }} 可用内存低于 10%"

      - alert: ContainerMemoryNearLimit
        expr: |
          container_memory_working_set_bytes{name!=""}
            / container_spec_memory_limit_bytes{name!=""} > 0.9
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "{{ $labels.name }} 内存逼近上限,随时可能被 OOM"

四条规则分别对应三种死法:服务没了、磁盘满了、内存爆了。predict_linear 那条是整套里最值钱的——它不看当前百分比,而是拿最近 6 小时的斜率往后推 4 小时,能提前半天给你留出挪数据的时间。刚重启或刚清理过的机器斜率是负的,不会误报。

第四条要注意 name!=""。cAdvisor 会把 cgroup 层级里的每一层都报出来,不过滤的话父 cgroup 没有 limit,除出来是 NaN,图上会出现一堆断点。

Alertmanager:把「响」送到人手上

route:
  receiver: default
  group_by: ['alertname', 'instance']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h

receivers:
  - name: default
    webhook_configs:
      - url: 'http://your-notifier:8080/hook'
        send_resolved: true

没有现成的通知服务,就用 webhook 转发到自己的脚本、钉钉/飞书机器人或者 Telegram——格式各家不同,Alertmanager 只负责把 JSON 推出去。改完先校验再重启:

amtool check-config /etc/alertmanager/alertmanager.yml
docker compose restart alertmanager

repeat_interval: 4h 是个平衡点。设太短,一个没修的告警每十分钟响一次,三天后你就开始无视它;设太长,真忘了修的时候没人提醒。group_wait 则是给同一批故障的告警留一个合并窗口,避免磁盘满的时候收到十条消息。

Grafana:只做数据源,不做入口

# /etc/grafana/provisioning/datasources/prom.yml
apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true

数据源用文件声明,别在界面里手点——重装一次就丢的配置,等于没有配置。看板同理,导出的 JSON 放进 provisioning 目录,跟 compose 一起进 git。

Grafana 的定位要摆正:它是「事后查证」的工具,不是告警通道。真出事的时候,人不会盯着看板,只会看手机。

四个坑,踩过都记得

  • node-exporter 挂载不全。只挂 /proc 不挂 /host/root,文件系统指标全是容器自己的 overlay,看着永远不满;pid: host 不加,进程数指标也不对。
  • cAdvisor 在 cgroup v2 上不全。要新版本,还要 privileged: true 和 /dev/kmsg。跑在 macOS 的 Docker Desktop 上更别指望,那边根本没有真正的 cgroup。
  • 时序数据的磁盘账。15 秒抓一次、几百个序列,一天大约几百 MB。30 天保留意味着几十 G。估算方法:序列数 × 抓取频率 × 保留天数,宁可按两倍留余量。
  • 时间不同步。Prometheus 靠时间戳对齐,宿主机的 NTP 漂了几十秒,图上就会出现阶梯状断点,排查半天以为是采集丢了。

先让 up == 0 响起来

这套栈最容易走偏的地方,是把时间花在挑看板主题上。四条规则里有三条可以慢慢调,唯独 InstanceDown 应该第一天就通——它不关心你用什么仪表盘,只回答一个最原始的问题:东西还活着吗。

顺序建议是这样:先让 up == 0 真的能推到你手机上(拿 docker compose stop node-exporter 试一次,看两分钟内有没有收到),再补磁盘预测,最后才折腾看板和留存策略。反过来做,通常的结局是一套漂漂亮亮的仪表盘,配一个从来没响过的告警通道。

参考来源

延伸阅读

© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
评论 抢沙发
头像/SUENCA
欢迎您留下宝贵的见解!
提交
头像/SUENCA

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容