没有监控的服务器就像没装仪表盘的车:能开,但你不知道油量、水温、转速,只能等它冒烟了才发现问题。更现实的问题是——故障发生之后,没有历史数据,你连"当时到底怎么了"都回答不了,复盘只能靠猜。
这篇文章讲怎么从零搭一套可用的监控体系:监控要分哪几层、Prometheus 的拉取模型和数据模型是什么、node_exporter 部署与常用指标、prometheus.yml 的关键配置、记录规则与告警规则怎么写、Alertmanager 的路由与抑制思路、Grafana 怎么接数据源和导面板、存储与保留期怎么算、单机小规模有什么轻量替代、以及最容易被忽视的"告警风暴治理"。
覆盖面偏向"中小企业和个人站长能落地"的方案,不需要 Kubernetes,一台 2 核 4G 的小机器就够跑起来监控几十台服务器。
一、监控要分四层
很多人的监控只有"CPU、内存、磁盘"这一层,结果业务已经挂了监控还是绿的——因为机器没事,有事的是应用。完整的监控应该覆盖四层,每层回答不同的问题。
| 层级 | 关注对象 | 典型指标 | 回答的问题 |
|---|---|---|---|
| 基础设施层 | 服务器、网络设备 | CPU、内存、磁盘、网络、内核事件 | 机器还活着吗?资源够吗? |
| 中间件层 | Nginx、数据库、缓存、消息队列 | 连接数、慢查询、命中率、队列积压 | 依赖组件健康吗? |
| 应用层 | 自己写的程序 | QPS、响应时间、错误率、JVM GC、线程池 | 我的程序运行得怎么样? |
| 业务层 | 业务结果 | 注册数、订单量、支付成功率、库存 | 用户能正常完成他要的事吗? |
四层之间越往上越接近用户感受。基础设施层全绿而业务层告急的情况非常常见(比如 DNS 解析失败、第三方接口挂了、某个业务分支逻辑错误),这也是为什么监控不能只做最底层。
分层的另一个价值是告警定级:基础设施告警通常可以等几分钟,业务层告警(支付成功率掉零)必须立刻响应。把四层混在一起同等对待,必然导致告警疲劳。
部署顺序建议:先做基础设施层,因为它最标准化、工具最成熟;然后补业务层,因为它最能直接反映损失;中间件和应用层穿插进行,按遇到的痛点逐步加。不要一开始就追求大而全,一套跑不通的完美方案不如一套跑着的最小方案。
二、Prometheus 的两个核心设计
Prometheus 之所以成为云原生监控的事实标准(CNCF 毕业项目),主要因为它有两个克制而清晰的设计:拉取模型和多维数据模型。
拉取模型(Pull vs Push)
传统监控(如 Zabbix agent 主动上报、StatsD)是推模型:被监控端主动把数据发给服务端。Prometheus 反过来:服务端主动去拉取被监控端暴露的 HTTP 接口。
Prometheus Server --HTTP GET /metrics--> node_exporter:9100
--HTTP GET /metrics--> app:8080/metrics
拉模型的好处很实际:
- 调试极其方便:任何指标都能用
curl http://target:9100/metrics直接看,不需要专门的客户端。 - 目标状态由服务端掌握:Prometheus 知道每个 target 是否在 scrape 成功,并自动生成
up指标。up == 0本身就是一条最基础的告警。 - 不会因为被监控端乱推而打爆服务端:采集节奏由服务端统一管理。
- 天然适配动态环境:结合服务发现(file_sd、consul、k8s SD),target 列表自动更新。
拉模型的代价是需要 target 可达。对于短生命周期任务(比如批处理 Job,可能几秒就结束了,来不及被拉),Prometheus 提供了 Pushgateway 作为补充——但这属于例外机制,滥用 Pushgateway 会破坏拉模型的简洁性(比如多个实例推同一个 job 时的指标冲突和数据不过期问题)。
数据模型:时间序列 + 标签
Prometheus 存储的每条数据是一个时间序列(time series),由两部分唯一确定:指标名(metric name)+ 一组标签(labels,键值对)。
node_cpu_seconds_total{cpu="0",mode="user",instance="web01:9100",job="node"} 12345.67
这里 node_cpu_seconds_total 是指标名,花括号里是四个标签。查询时按标签做过滤、聚合和分组,这是 Prometheus 表达力的来源。
四种指标类型:
| 类型 | 特点 | 典型例子 | 查询注意事项 |
|---|---|---|---|
| Counter | 只增不减的累计值,重启归零 | node_cpu_seconds_total、HTTP 请求总数 |
必须用 rate()/increase() 算速率 |
| Gauge | 可增可减的瞬时值 | node_memory_MemAvailable_bytes、当前连接数 |
可直接用,也可做 avg/max |
| Histogram | 采样到预置 bucket 的分布 | 请求耗时分布 | 用 histogram_quantile() 算分位数 |
| Summary | 客户端直接算好的分位数 | 部分老 exporter | 不能跨实例聚合,不如 Histogram 灵活 |
Counter 必须用 rate() 处理是新手最常见的坎。直接看 node_cpu_seconds_total 的原始值毫无意义(它是开机以来累计的秒数),正确算法:
# 每秒 user 态 CPU 时间增长量,除以总 CPU 时间再乘 100 得百分比
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
rate(metric[5m]) 计算过去 5 分钟的平均每秒增长率,它会自动处理 Counter 归零(重启)的情况。区间选择有讲究:至少要大于采集间隔的 4 倍(比如 15 秒采集一次,至少用 [1m]),太短会因为样本太少而抖动。
PromQL 的几个常用查询(这些是运维日常必会的):
# CPU 使用率(排除 idle)
100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# 内存可用率
100 * node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes
# 磁盘剩余空间百分比
100 * node_filesystem_avail_bytes{mountpoint="/",fstype!="rootfs"} / node_filesystem_size_bytes
# 磁盘空间多久后耗尽(基于 6 小时增长率的线性预测,单位秒)
predict_linear(node_filesystem_avail_bytes{fstype!~"tmpfs|squashfs"}[6h], 4*3600)
# 网络入带宽
rate(node_network_receive_bytes_total{device!~"lo|veth.*|docker.*"}[5m])
# 目标存活探测
up{job="node"} == 0
最后一条 预测磁盘多久耗尽 是特别值得加的一条告警,它比"磁盘使用率 > 85%"更有用——因为它给出的是明确的时间窗口,能提前安排扩容而不是半夜被叫起来。
三、node_exporter 部署
node_exporter 采集主机的硬件和内核指标,是基础设施层的主力采集器。它刻意不做进程级监控——那个职责属于 process-exporter 或 cAdvisor,这是 Unix 哲学的体现。
# 下载(版本号请在官方 Releases 页面确认,这里是示例格式)
cd /tmp
curl -LO https://github.com/prometheus/node_exporter/releases/download/v1.8.2/node_exporter-1.8.2.linux-amd64.tar.gz
tar xzf node_exporter-1.8.2.linux-amd64.tar.gz
sudo cp node_exporter-1.8.2.linux-amd64/node_exporter /usr/local/bin/
创建 systemd 单元:
# /etc/systemd/system/node_exporter.service
[Unit]
Description=Prometheus Node Exporter
Documentation=https://github.com/prometheus/node_exporter
After=network-online.target
[Service]
Type=simple
User=nodeexp
Group=nodeexp
ExecStart=/usr/local/bin/node_exporter \
--web.listen-address=:9100 \
--collector.filesystem.mount-points-exclude=^/(dev|proc|sys|var/lib/docker)($|/) \
--collector.netclass.ignored-devices=^(veth|br-|docker|lo)$ \
--collector.ethtool --collector.tcpstat
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
ReadOnlyPaths=/
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
sudo useradd -rs /bin/false nodeexp
sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter
sudo systemctl status node_exporter
# 验证:直接拉取指标
curl -s http://localhost:9100/metrics | head -20
curl -s http://localhost:9100/metrics | grep -c '^node_'
从发行版仓库安装也可以,但版本通常较旧:
sudo dnf install -y node_exporter # RHEL、Rocky(部分仓库提供)
sudo apt install -y prometheus-node-exporter # Debian、Ubuntu
防火墙(风险提示:9100 端口不应暴露到公网,它包含了大量主机信息,应该只允许 Prometheus 服务器访问):
# RHEL、Rocky、CentOS(firewalld 富规则,限定来源 IP)
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.1.100/32" port protocol="tcp" port="9100" accept'
sudo firewall-cmd --reload
# 或者使用 zone 方式
sudo firewall-cmd --permanent --new-zone=prom --set-target=ACCEPT
sudo firewall-cmd --permanent --zone=prom --add-source=10.0.1.100/32
sudo firewall-cmd --permanent --zone=prom --add-port=9100/tcp
sudo firewall-cmd --reload
# Debian、Ubuntu(ufw)
sudo ufw allow from 10.0.1.100 to any port 9100 proto tcp comment 'prometheus scrape'
sudo ufw reload
更稳妥的做法是只允许内网网段,并且 Prometheus 与被监控机之间走内网或 VPN。
node_exporter 提供几十个 collector,默认开启的是通用集合。常用的有:cpu、meminfo、diskstats、filesystem、netdev、loadavg、vmstat、systemd、uname。按需启停:--collector. 和 --no-collector.。
一个实用建议:启用 --collector.systemd(systemd collector)可以监控每个服务的状态,但要注意它会拉取所有 unit,数量多时有性能开销,用 --collector.systemd.unit-include 限定范围。
四、Prometheus 配置
# /etc/prometheus/prometheus.yml
global:
scrape_interval: 15s # 全局默认采集间隔
scrape_timeout: 10s # 单次采集超时,必须小于 interval
evaluation_interval: 15s # 告警规则评估间隔
external_labels:
env: production
region: cn-beijing
alerting:
alertmanagers:
- static_configs:
- targets:
- 127.0.0.1:9093
rule_files:
- /etc/prometheus/rules/*.yml
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node'
scrape_interval: 15s
file_sd_configs:
- files:
- /etc/prometheus/targets/nodes.yml
refresh_interval: 30s
relabel_configs:
- source_labels: [__address__]
target_label: instance
- job_name: 'node_dc2'
scrape_interval: 30s
static_configs:
- targets:
- '10.0.2.11:9100'
- '10.0.2.12:9100'
labels:
dc: dc2
配合文件服务发现(推荐,比把所有目标写死在主配置里更灵活):
# /etc/prometheus/targets/nodes.yml
- targets:
- '10.0.1.11:9100'
- '10.0.1.12:9100'
- '10.0.1.13:9100'
labels:
role: web
dc: dc1
- targets:
- '10.0.1.21:9100'
- '10.0.1.22:9100'
labels:
role: db
dc: dc1
几个配置要点:
scrape_timeout必须小于scrape_interval,否则采集会堆积。external_labels用于在联邦或远程写到外部存储时区分数据来源,多集群场景必配。file_sd_configs优于static_configs:修改目标列表不需要重启 Prometheus,它会按 refresh_interval 自动重载。- 使用 relabel_configs 可以在采集阶段丢弃不需要的目标、改写标签。比如用
__meta_kubernetes_*这类内部标签做过滤。
启动服务:
# /etc/systemd/system/prometheus.service
[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.path=/var/lib/prometheus/data \
--storage.tsdb.retention.time=30d \
--web.enable-lifecycle \
--web.listen-address=0.0.0.0:9090
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
--web.enable-lifecycle 开启后可以用 curl -X POST http://localhost:9090/-/reload 热重载配置,避免频繁重启。
校验配置文件(强烈建议在 reload 前执行):
promtool check config /etc/prometheus/prometheus.yml
promtool check rules /etc/prometheus/rules/*.yml
访问 http:// 打开 Web UI,在 Status → Targets 页面确认所有 target 的状态是 UP。
五、告警规则与记录规则
告警规则
# /etc/prometheus/rules/node.yml
groups:
- name: node.rules
rules:
- alert: HostDown
expr: up{job="node"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "主机失联 {{ $labels.instance }}"
description: "{{ $labels.instance }} 已连续 2 分钟无法采集,请检查主机状态与 node_exporter。"
runbook: "检查主机是否宕机 / node_exporter 是否运行 / 网络是否可达"
- alert: HighCPU
expr: 100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
for: 10m
labels:
severity: warning
annotations:
summary: "CPU 持续偏高 {{ $labels.instance }}"
description: "CPU 使用率 {{ printf \"%.1f\" $value }}% 已持续 10 分钟高于 85%。"
- alert: LowMemory
expr: 100 * node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 10
for: 5m
labels:
severity: warning
annotations:
summary: "可用内存不足 {{ $labels.instance }}"
- alert: DiskWillFillIn4Hours
expr: |
predict_linear(node_filesystem_avail_bytes{fstype!~"tmpfs|squashfs|overlay"}[6h], 4*3600) < 0
for: 15m
labels:
severity: critical
annotations:
summary: "磁盘将在 4 小时内写满 {{ $labels.mountpoint }}"
- alert: HighDiskLatency
expr: rate(node_disk_io_time_seconds_total[5m]) * 100 > 90
for: 10m
labels:
severity: warning
annotations:
summary: "磁盘繁忙 {{ $labels.device }}"
for 字段是告警质量的关键。它表示"条件持续满足多久才真正触发",避免了瞬时抖动造成的误报。不同告警应该有不同时长:主机失联可以短(1~2 分钟),资源类问题应该长(5~15 分钟),因为短暂的高负载大多是正常的。
annotations 里除了 summary 和 description,建议加 runbook(处理手册链接)和 dashboard(对应面板链接)。值班的人半夜收到告警,最需要的是"下一步该看哪里"。
记录规则(Recording Rules)
记录规则把常用且开销大的查询预先算好存成新序列,作用是加速面板加载、简化复杂 PromQL:
# /etc/prometheus/rules/recording.yml
groups:
- name: recording.rules
interval: 30s
rules:
- record: instance:node_cpu_utilisation:rate5m
expr: 100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
- record: instance:node_memory_utilisation:ratio
expr: 100 - (100 * node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)
- record: job:node_load1_per_core:ratio
expr: node_load1 / count without(cpu, mode) (node_cpu_seconds_total{mode="idle"})
命名约定建议采用 level:metric:operations 格式(如 instance:request_rate:avg1m),社区通用。
六、Alertmanager:路由、抑制与静默
Prometheus 只负责产生告警,Alertmanager 负责把告警变成通知:去重、分组、抑制、静默、路由到不同通道。
# /etc/alertmanager/alertmanager.yml
global:
resolve_timeout: 5m
smtp_smarthost: 'smtp.example.com:587'
smtp_from: 'alert@example.com'
smtp_auth_username: 'alert@example.com'
smtp_auth_password: '这里填应用专用口令' # 不要用真实邮箱密码
route:
receiver: 'default-webhook'
group_by: ['alertname', 'instance']
group_wait: 30s # 分组等待,收集同一组的其他告警一起发
group_interval: 5m # 同一组告警再次发送的最小间隔
repeat_interval: 4h # 未解决告警的重复发送间隔
routes:
- matchers:
- severity="critical"
receiver: 'oncall-phone'
continue: false
- matchers:
- alertname=~"HostDown|DiskWillFillIn4Hours"
receiver: 'ops-webhook'
receivers:
- name: 'default-webhook'
webhook_configs:
- url: 'http://127.0.0.1:5001/webhook' # 企业微信/钉钉/Slack 中转
- name: 'ops-webhook'
webhook_configs:
- url: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY'
send_resolved: true
- name: 'oncall-phone'
webhook_configs:
- url: 'https://your-pager-service/hook'
inhibit_rules:
# 主机宕机时,抑制该主机上的所有其他告警
- source_matchers:
- alertname="HostDown"
target_matchers:
- severity="warning"
equal: ['instance']
# critical 抑制同一 instance 的 warning
- source_matchers:
- severity="critical"
target_matchers:
- severity="warning"
equal: ['instance']
三个时间参数决定了告警的节奏感:
| 参数 | 含义 | 建议值 |
|---|---|---|
| group_wait | 第一个告警到达后等待多久,把同组告警合并发送 | 10~30s |
| group_interval | 同组已有告警时,多久后再发一批 | 5m |
| repeat_interval | 未恢复的告警多久重发一次 | 3h~1d |
repeat_interval 设得太短是告警疲劳的主要来源。设成 4 小时意味着同一条未恢复告警每 4 小时打扰一次,配合定期的值班巡检已经足够。真正需要高频打扰的(比如支付成功率掉零)应该有独立的通道和升级机制。
抑制规则(inhibit_rules)的价值极大:一台机器宕机会同时触发"主机失联、CPU 高、内存高、磁盘不上报、所有服务 down"十几条告警。用了抑制规则后,只发一条 "HostDown",其他全部被压掉。这是从"几十条屏,不知道先看哪条"到"一条清晰的根因告警"的关键。
临时静默(maintenance 窗口):在 Alertmanager 的 Web UI 里可以创建 Silence,按标签匹配并设定时间窗口。计划内维护之前先建 Silence,是好习惯。
七、Grafana:数据源与面板
# RHEL、Rocky、CentOS
sudo tee /etc/yum.repos.d/grafana.repo <<'EOF'
[grafana]
name=grafana
baseurl=https://rpm.grafana.com
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://rpm.grafana.com/gpg.key
EOF
sudo dnf install -y grafana
# Debian、Ubuntu
sudo apt install -y apt-transport-https software-properties-common wget
sudo mkdir -p /etc/apt/keyrings/
wget -q -O - https://apt.grafana.com/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/grafana.gpg
echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" \
| sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt update && sudo apt install -y grafana
sudo systemctl enable --now grafana-server
sudo systemctl status grafana-server
# 默认监听 3000 端口,初始账号 admin / admin,首次登录强制改密码
添加数据源:Configuration → Data sources → Add data source → Prometheus,URL 填 http://localhost:9090(同机)或内网地址,其他保持默认。点 Save & test 验证。
导入现成面板:Dashboards → Import,输入 Grafana 官方面板库的 ID 即可。推荐的几个:
| 面板用途 | 说明 |
|---|---|
| Node Exporter Full | 主机全面指标,最经典的一个大盘 |
| Node Exporter Quickstart | 精简版主机概览,适合总量展示 |
| Prometheus 2.0 Overview | 监控 Prometheus 自身 |
| Nginx / MySQL / Redis 官方或社区面板 | 按使用的组件选用 |
不要一开始就自己造轮子。 先用社区面板跑起来,再按自己的需求裁剪和改造。导出的 JSON 可以纳入版本管理,实现面板的版本化。
# Grafana 数据源与面板的 Provisioning(推荐做法,配置即代码)
# /etc/grafana/provisioning/datasources/prometheus.yml
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: http://127.0.0.1:9090
access: proxy
isDefault: true
editable: false
provisioning 配置后重启 Grafana 即可自动生效,适合多实例或多环境复制。
八、存储、保留期与规模估算
Prometheus 的 TSDB 采用本地存储,每条时间序列大约占 1~2 字节/样本(压缩后),这是个很有用的估算基准。
粗略估算公式:
磁盘占用 ≈ 时间序列数 × 每秒样本数 × 保留秒数 × 2 字节
简化版:每百万活跃序列按 15 秒采集间隔保留 15 天,约需 170GB 左右
更实际的规模参考:
| 目标数 | 序列数(约) | 15 天保留所需磁盘 |
|---|---|---|
| 20 台主机 | 15 万 | 15~30 GB |
| 100 台主机 | 75 万 | 80~150 GB |
| 只保留核心指标 | 大幅下降 | 可减一半以上 |
控制增长的两个手段:保留期和丢弃不必要的序列。
--storage.tsdb.retention.time=30d # 时间保留(优先级高于 size)
--storage.tsdb.retention.size=50GB # 按大小保留,两者可同时配
注意:Prometheus 的保留机制是按块(block)整体删除的,默认 2 小时的块。所以磁盘占用会在清理时刻临时超过 retention.size,要给足余量。修改参数后需要重启生效。
减少序列数的实用做法:
- 在 scrape_config 里用
metric_relabel_configs丢弃不需要的指标(比 Prometheus 端 drop 更省事)。 - node_exporter 侧用
--no-collector.关闭不用的采集器(比如 kube 环境的--no-collector.wifi,或者不需要的arp、entropy),部分采集器还支持--collector.精细化控制。.<子项> - 避免高基数(high cardinality)标签:不要把 user_id、URL path、request_id 这类无限增长的取值做成标签。这是 Prometheus 最常见的失控原因——一个
path="/user/12345"标签能让序列数瞬间暴涨到百万级。
长期存储方案:Prometheus 本地存储不是设计做永久归档的。需要长期留存(数月到数年)时,用 remote_write 接到外部存储(如 VictoriaMetrics、Mimir、Thanos,或者各类时序数据库)。中小企业通常先把本地保留期设到 30~90 天就够了。
九、轻量替代方案的定位
Prometheus + Grafana + Alertmanager 三件套功能强,但也有部署和运维成本。规模小的时候可以考虑替代:
| 方案 | 定位 | 优点 | 局限 |
|---|---|---|---|
| Netdata | 单机实时监控 | 一分钟装好,秒级精度,界面自带,几乎零配置 | 单机视角强,集群聚合弱,长期存储需额外配置 |
| Zabbix | 传统企业监控 | 推模型+Agent,自带完整告警与可视化,模板丰富,中文资料多 | 配置较繁琐,大规模时数据库压力大 |
| Prometheus + Grafana | 云原生标准 | 多维数据模型,生态最广,PromQL 表达力强 | 需自行组装,长期存储与高可用要额外设计 |
| 云厂商监控服务 | 托管方案 | 开通即用,与云平台资源自动关联 | 跨云/自建机房覆盖弱,自定义指标可能受限 |
选型建议:
- 一两台机器、想立刻看到指标 → Netdata,几分钟就有结果。
- 已经有多台机器、希望长期做趋势分析和灵活查询 → Prometheus + Grafana。
- 传统企业 IT、团队熟悉 Zabbix、需要丰富的现成模板和中文生态 → Zabbix 依然合适。
- 全在云上、没有专职运维 → 先开云厂商的监控服务保底,再考虑是否自建补足。
一个常见的误区是"小的配置不够,先用最全的 Prometheus"。其实反过来:先用最轻的把可观测性建立起来,监控体系的价值在于"有"而不在于"全",随着规模增长再迁移成本并不高。
十、监控系统自身的高可用与告警风暴治理
监控是基础设施,它自己也需要被监控。
最低限度要做三件事:
- 监控 Prometheus 自身:
up{job="prometheus"}、prometheus_tsdb_head_series(序列数趋势)、prometheus_notifications_errors_total(告警发送失败)、TSDB 压缩与WAL 是否正常。 - 跨实例互相监控(dead man's switch 思路):两个 Prometheus 实例互相采集对方的
up,任一方挂了另一方能发出告警。如果只有一个实例,可以用 Alertmanager 的心跳机制加外部探活服务。 - Alertmanager 自身要冗余:配置多个实例,Prometheus 的
alertmanagers.static_configs里写多个地址(新版支持自动发现所有实例并去重)。
alerting:
alertmanagers:
- static_configs:
- targets:
- '10.0.1.101:9093'
- '10.0.1.102:9093'
告警风暴治理
这是从"有监控"到"好用的监控"的分水岭。告警风暴的典型场景:一台核心交换机故障 → 50 台主机同时失联 → 每台触发 5 条告警 → 250 条通知同时到达。
治理手段按优先级:
第一,用 for 字段过滤抖动。 绝大多数告警应该是"持续 N 分钟"而不是"瞬时"。这是最有效的一步。
第二,用抑制规则做根因收敛。 如前文所示,用 inhibit_rules 让根因告警压掉衍生告警。这是从"250 条"变成"1 条"的关键。
第三,合理分组。 group_by: ['alertname', 'instance'] 会把同类告警合并成一条消息。可以按 severity 或 cluster 分组以实现不同粒度。
第四,分级。 只有真正需要人立刻处理的才走电话/短信(paging),其余走 IM 群或邮件。severity: critical 才打电话,warning 只记录。
第五,每条告警都要有可执行的处理说明。 收到告警后值班的人应该知道第一步做什么。没有 runbook 的告警等于噪音。
第六,定期清理无效告警。 每季度回顾一次过去三个月的告警:哪些从来没有触发过(可能指标选错了)、哪些触发了但从不需要处理(阈值不对)、哪些重复出现且已自动化处理(应该直接做成自动修复而不是告警)。告警是需要维护的资产,不是配完就完事的。
常见问题
该用多大机器跑 Prometheus?
参考前面估算:每百万活跃序列按 15 秒间隔采集,大约需要 170GB(15 天)。小规模(二三十台主机、十几万序列)用 2 核 4G、100GB SSD 就够,重点是磁盘 IO 要好(SSD),因为 TSDB 的压缩和查询都依赖磁盘。随着规模增长,内存是第二瓶颈(查询时会加载较多的 head block 索引)。
采集间隔设 15 秒还是 30 秒?
15 秒是社区常用默认,足够精度也够省。对故障复盘有帮助。采集间隔要与告警规则里的 rate() 窗口配合:窗口至少是间隔的 4 倍。想要更细的精度可以考虑 5 秒,但存储和查询压力会上升,一般不需要。
为什么 up 是 0 但机器明明是好的?
按顺序查:网络是否可达(从 Prometheus 服务器 curl target:9100/metrics);node_exporter 是否在运行(systemctl status);防火墙是否放行;是否有 relabel_configs 把 target 丢弃了(在 Status → Targets 页面看 Error 列,会有明确原因如 "dropped by relabel")。
告警收不到,但 Prometheus 里能看到告警已经 Firing?
问题在 Alertmanager 链路。依次检查:prometheus.yml 的 alertmanagers 配置; Prometheus Web UI 的 Alerts 页面显示 "FIRING" 说明规则已生效;看 prometheus_notifications_errors_total 指标是否增长;在 Alertmanager UI 里看有没有收到;最后检查接收端的 webhook URL 与密钥是否正确、是否被外部服务限流。
监控本身该怎么备份?
真正重要且无法重建的是配置文件和面板定义,而不是时序数据本身。prometheus.yml、rules/*.yml、alertmanager.yml、Grafana 的 provisioning 目录和导出的面板 JSON,都应纳入 git 版本管理。时序数据丢了可以重新采集,配置丢了整套体系要重建。考虑这个角度看就不会纠结要不要做 TSDB 快照。
容器环境监控用什么?
基础设施层仍用 node_exporter 监控宿主;容器层用 cAdvisor(容器 CPU/内存/网络/文件系统)或 kubelet 自带的 metrics 端点;Kubernetes 集群层还需要 kube-state-metrics 采集资源对象的状态(Deployment 副本数、Pod 状态、节点 Ready 状态)。动态环境下服务发现是关键,用 kubernetes_sd_configs 自动发现 Pod 和 Service。
总结
- 监控分四层:基础设施、中间件、应用、业务。越往上越接近用户真实感受,四层都覆盖才能避免"机器全绿但业务已挂"。部署顺序建议先基础设施层,再补业务层,中间件和应用逐步加。
- Prometheus 采用拉取模型(服务端主动 GET target 的
/metrics)而不是推模型,好处是用 curl 就能调试、服务端掌握 target 存活状态(up指标)、采集节奏统一。短生命周期任务的例外场景才用 Pushgateway。 - 数据模型是"指标名 + 标签"确定的时间序列,四种类型里 Counter 必须用
rate()/increase()才有意义,区间窗口至少是采集间隔的 4 倍。推荐加predict_linear()做磁盘耗尽预测,它比固定使用率阈值更有行动价值。 - 告警质量的关键是
for字段(过滤瞬时抖动,资源类建议 5~15 分钟)、记录规则(预计算加速面板)、以及 Alertmanager 的inhibit_rules抑制(用根因告警压掉衍生告警,把 250 条收敛成 1 条)与repeat_interval(建议 3 小时以上,避免告警疲劳)。 - Grafana 直接导入社区面板先用起来再改造,用 provisioning 目录实现"配置即代码";不要从零自造。
- 存储按"每条序列每样本 1~2 字节"估算,通过
retention.time与--storage.tsdb.retention.size控制。严禁用 user_id、URL path 这类高基数值做标签,那是序列数失控的头号原因。长期留存用 remote_write 接外部存储。 - 选题不必一步到位:一两台机器先用 Netdata,传统企业 IT 用 Zabbix 也完全合适,全托管环境先开云厂商监控。可观测性的价值在于"有"不在于"全"。
- 最后:监控系统自身也需要高可用(互相采集对方的 up、Alertmanager 多实例),并且要把配置文件和面板定义纳入 git 管理——时序数据丢失可以重采,配置丢失要重建整套体系。告警规则要按季度回顾清理,它是需要持续维护的资产。
企业QQ咨询




