3.12 Linux 系统监控与告警方案
预计阅读时间:13 分钟
📖 目录
学习目标
学完本章后,你将能够:
- 使用命令行工具实时监控 CPU、内存、磁盘、网络四大维度
- 理解 Prometheus + Grafana 监控架构的核心组件和工作流程
- 编写自定义监控脚本并集成到告警体系
- 定义 SLO/SLI 并基于 Error Budget 设计告警规则
- 配置 Alertmanager 告警路由与通知
核心知识
- 监控四维度——CPU(使用率/负载)、内存(可用/交换)、磁盘(使用率/I/O)、网络(带宽/连接数)
- Prometheus——开源的监控和告警工具,通过拉取(Pull)模式采集指标,使用 PromQL 查询
- Node Exporter——Prometheus 的官方主机指标采集器,暴露 CPU、内存、磁盘等系统指标
- Grafana——指标可视化平台,支持 Prometheus、InfluxDB、Elasticsearch 等多种数据源
- SLI(服务级别指标)——衡量服务质量的具体指标(如请求成功率、P99 延迟)
- SLO(服务级别目标)——SLI 的目标值(如"月度请求成功率 ≥ 99.9%")
- Error Budget(错误预算)——SLO 允许的故障时间,超出后应放缓新功能上线
- Alertmanager——Prometheus 生态的告警管理组件,负责去重、分组、路由和静默
知识关联
- 前置知识:4.5:网络故障排查 网络故障排查、2.4:进程管理 进程管理、2.7:日志与故障排查 日志与故障排查
- 后续影响:监控是 4.6:Linux 性能调优 Linux 性能调优和 4.8:集中式日志管理 集中式日志管理的基础
- 配套技术:Prometheus Operator(K8s 部署)、Grafana Loki(日志聚合)
原理讲解
1. 命令行实时监控
htop / btop / glances # 综合仪表盘
# 输出: (进入交互式仪表盘,按 q 退出)
top / mpstat -P ALL 1 / sar -u 1 10 # CPU
# 输出: (CPU 实时监控,按 q 退出)
free -h / vmstat 1 / sar -r 1 10 # 内存
# 输出: total used free
# 输出: Mem: 31G 8.2G 16G
df -h / iostat -x 1 / iotop # 磁盘
# 输出: Filesystem Size Used Avail Use% Mounted on
# 输出: /dev/sda2 931G 234G 697G 26% /
iftop / nethogs / sar -n DEV 1 10 # 网络
ss -s # Socket 统计
# 输出: Total: 345 (kernel 0)
# 输出: TCP: 12 (estab 8, closed 0, orphaned 0, synrecv 0, timewait 0/0)
2. Prometheus + Grafana
架构:Prometheus(采集+存储)→ Node Exporter(主机指标)→ Grafana(可视化+告警)。
为什么推荐 Prometheus 而不是 Zabbix?
Zabbix 是老牌的企业级监控工具,功能全面但架构较重(需要数据库后端、复杂的模板系统)。Prometheus 是云原生时代的监控标准,核心优势在于:
- Pull 模型:Prometheus 主动从目标拉取指标,而不是目标主动推送。好处是:监控目标不需要知道 Prometheus 的地址,配置简单;Prometheus 可以主动探测目标是否存活;不存在推送失败导致数据丢失的问题。
- 服务发现:Prometheus 原生支持 Kubernetes、Consul、DNS 等服务发现机制,自动发现新实例并开始采集。Zabbix 需要手动注册主机。
- PromQL:Prometheus 的查询语言 PromQL 功能强大且直观,可以实时计算复杂的聚合指标。Zabbix 的查询能力相对有限。
- 云原生生态:Prometheus 是 CNCF 毕业项目,与 Kubernetes、Grafana、Alertmanager 无缝集成。Zabbix 更适合传统基础设施。
- 轻量:Prometheus 单二进制部署,无外部数据库依赖。Zabbix 需要 MySQL/PostgreSQL + 前端 + Agent + Server 多组件。
选型建议:云原生/Kubernetes 环境用 Prometheus;传统企业环境(大量物理服务器、需要现成模板)可以考虑 Zabbix。新项目默认选 Prometheus + Grafana 是业界共识。
为什么监控要做 Pull 而不是 Push?
Push 模式(如 StatsD、InfluxDB Telegraf)由被监控端主动上报指标,Pull 模式(Prometheus)由监控端主动拉取。Pull 的核心优势是解耦:被监控端不需要知道监控服务器的地址,只需暴露一个 HTTP 端点。这意味着你可以随时更换监控后端而不修改任何应用代码。另外,Pull 模式下监控端可以主动探测目标是否存活——如果拉取失败,说明目标已经挂了。Push 模式下,目标挂了就停止推送,监控端可能很久才发现。
3. 自定义监控脚本
#!/bin/bash
CPU=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
MEM=$(free | grep Mem | awk '{print $3/$2 * 100.0}')
if (( $(echo "$CPU > 90" | bc -l) )); then echo "WARNING: CPU ${CPU}%"; fi
if (( $(echo "$MEM > 90" | bc -l) )); then echo "WARNING: Memory ${MEM}%"; fi
df -h | awk 'NR>1 {if ($5+0 > 85) print "WARNING: Disk " $6 " is " $5 " full"}'
for svc in nginx mysql docker; do
systemctl is-active --quiet $svc || echo "ERROR: $svc is down"
done
4. Grafana 面板设计
好的面板设计让故障排查时间从小时级降到分钟级。核心原则是"一图一指标,关键信息一目了然"。
# Grafana 面板 JSON 模型核心结构
{
"title": "CPU 使用率",
"type": "timeseries",
"datasource": "Prometheus",
"targets": [{
"expr": "100 - (avg by(instance) (rate(node_cpu_seconds_total{mode='idle'}[5m])) * 100)",
"legendFormat": "{{ instance }}"
}],
"fieldConfig": {
"defaults": {
"unit": "percent",
"thresholds": {
"mode": "absolute",
"steps": [
{"color": "green", "value": null},
{"color": "yellow", "value": 80},
{"color": "red", "value": 95}
]
}
}
}
}
# 变量模板(让面板在不同环境间切换)
# Grafana Settings → Variables → Add variable
# Name: env
# Type: Query
# Query: label_values(node_uname_info, env)
# 在 PromQL 中使用: $env 变量
# 推荐的面板布局
# 行1: CPU + 内存 + 磁盘(TimeSeries 面板,每个一行)
# 行2: 网络速率(TimeSeries,分进/出双向)
# 行3: 服务状态(Stat 面板,绿色=正常/红色=异常)
# 行4: 进程列表(Table 面板,显示 CPU/MEM Top N)
5. 告警规则编写
Prometheus 告警分两种:记录规则(recording rules) 提前聚合常用指标,告警规则(alerting rules) 触发告警通知。
# /etc/prometheus/rules/alerts.yml
groups:
- name: node_alerts
interval: 30s
rules:
# 记录规则:预计算 CPU 使用率(提高查询性能)
- record: node:cpu_usage:avg5m
expr: |
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode='idle'}[5m])) * 100)
# 告警规则:CPU 持续高负载
- alert: HighCpuUsage
expr: node:cpu_usage:avg5m > 90
for: 10m
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }} CPU 使用率 > 90%"
description: "{{ $labels.instance }} CPU 已达 {{ $value | humanizePercentage }},持续超过 10 分钟"
# 告警规则:磁盘即将满
- alert: DiskSpaceLow
expr: |
(node_filesystem_avail_bytes{fstype!="", mountpoint!="/boot"} /
node_filesystem_size_bytes{fstype!="", mountpoint!="/boot"}) * 100 < 15
for: 5m
labels:
severity: warning
annotations:
summary: "{{ $labels.mountpoint }} 磁盘空间不足 15%"
# 告警规则:节点不可达
- alert: InstanceDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "实例 {{ $labels.instance }} 已下线"
- name: service_alerts
rules:
# 服务质量告警:HTTP 错误率
- alert: HighHttpErrorRate
expr: |
sum(rate(nginx_http_requests_total{status=~"5.."}[5m])) /
sum(rate(nginx_http_requests_total[5m])) * 100 > 5
for: 5m
labels:
severity: critical
annotations:
summary: "HTTP 5xx 错误率超过 5%"
# 检查告警规则语法
promtool check rules /etc/prometheus/rules/alerts.yml
# 输出: Checking /etc/prometheus/rules/alerts.yml
# 输出: SUCCESS: 5 rules found
# 查看当前触发的告警
kubectl port-forward svc/prometheus-k8s 9090:9090 -n monitoring
# 输出: Forwarding from 127.0.0.1:9090 -> 9090
# 打开: http://localhost:9090/alerts
6. SLO / SLI 定义
SLO(Service Level Objective)是服务的可靠性目标,SLI(Service Level Indicator)是衡量这一目标的具体指标。没有 SLO 的监控就是"不知道什么算好什么算坏"。
| 服务 | SLI | SLO 目标 |
|---|---|---|
| Web API | 请求成功率(非 5xx) | 99.9%(月度) |
| API 延迟 | P99 请求延迟 | < 500ms(月度) |
| 数据库 | 查询成功率 | 99.95%(月度) |
| CDN | 缓存命中率 | ≥ 90%(月度) |
# Error Budget 计算:SLO 允许的"犯错时间"
# 99.9% SLO → 月度 Error Budget = 30天 × 24小时 × 60分钟 × 0.001 = 43.2 分钟
# 当前 Error Budget 消耗 = 停机时间 / 总时间 × 1000000 (单位:ppm)
# PromQL 计算 Error Budget
# 过去 30 天的请求成功率(用于计算 error budget 消耗)
sum(rate(http_requests_total{status!~"5.."}[30d])) /
sum(rate(http_requests_total[30d])) * 100
# 用 Burn Rate 判断是否需要立即响应
# 如果错误率突然高出 SLO 目标的 10 倍以上 → 需要立即响应
# < 3x burn rate → 可以等到工作时间处理
7. 告警路由与静默
# Alertmanager 配置文件:/etc/alertmanager/alertmanager.yml
route:
receiver: 'default'
group_by: ['alertname', 'cluster'] # 按告警名+集群分组
group_wait: 30s # 组内第一条告警等待聚合
group_interval: 5m # 组内新告警的发送间隔
repeat_interval: 4h # 已发送告警的重复间隔
# 路由树:不同级别走不同通知渠道
routes:
- receiver: 'critical-pager'
match:
severity: critical
repeat_interval: 30m # 关键告警每 30 分钟重发
- receiver: 'warning-email'
match:
severity: warning
repeat_interval: 4h
- receiver: 'team-db'
match:
service: database
repeat_interval: 1h
receivers:
- name: 'critical-pager'
webhook_configs:
- url: 'https://hooks.example.com/pagerduty'
- name: 'warning-email'
email_configs:
- to: 'team@example.com'
headers:
subject: '{{ .GroupLabels.alertname }}'
- name: 'team-db'
webhook_configs:
- url: 'http://db-team-bot:8080/alert'
# 静默规则(Alertmanager WebUI):维护窗口关闭告警
# 或者通过 amtool 命令行
amtool silence add alertname=HighCpuUsage --duration=2h --comment="维护窗口 2026-07-30 14:00-16:00"
# 输出: silence "silence-xxxxx" created
# 告警分级策略
# P0 (Critical) — 服务完全不可用 → 立即通知 Oncall → 15 分钟响应
# P1 (Warning) — 服务质量下降 → 工作时间处理 → 2 小时响应
# P2 (Info) — 需要关注但非紧急 → 记录到工单系统
# Alertmanager WebUI
kubectl port-forward svc/alertmanager-main 9093:9093 -n monitoring
# 输出: Forwarding from 127.0.0.1:9093 -> 9093
# 打开: http://localhost:9093 → 查看/管理告警
示例代码
以下是一套完整的 Node Exporter 文本采集器脚本,将自定义指标暴露给 Prometheus:
#!/bin/bash
# /usr/local/bin/custom_metrics.sh
# Node Exporter textfile collector — 每 5 分钟由 cron 调用
# 输出到 /var/lib/node_exporter/textfile/custom.prom
OUTPUT_DIR="/var/lib/node_exporter/textfile"
mkdir -p "$OUTPUT_DIR"
# 自定义服务健康检查
for svc in nginx mysql docker; do
systemctl is-active --quiet "$svc" \
&& echo "custom_service_up{service=\"$svc\"} 1" \
|| echo "custom_service_up{service=\"$svc\"} 0"
done > "$OUTPUT_DIR/custom.prom.$$"
mv "$OUTPUT_DIR/custom.prom.$$" "$OUTPUT_DIR/custom.prom"
# 输出:
# custom_service_up{service="nginx"} 1
# custom_service_up{service="mysql"} 0
# custom_service_up{service="docker"} 1
# cron 配置(每 5 分钟执行)
*/5 * * * * /usr/local/bin/custom_metrics.sh
# 验证 Prometheus 是否采集到
curl -s http://localhost:9090/api/v1/query?query=custom_service_up | jq .
# 输出: {
# 输出: "status": "success",
# 输出: "data": {
# 输出: "result": [
# 输出: {"metric": {"__name__": "custom_service_up"}, "value": [1785393000, "1"]}
# 输出: ]
# 输出: }
# 输出: }
常见错误
| 错误表现 | 根因 | 正确做法 |
|---|---|---|
| Grafana 面板显示 "No data" | Prometheus 未能从 Node Exporter 采集到数据 | 检查 systemctl status prometheus prometheus-node-exporter(Debian 包的服务名是 prometheus-node-exporter,不是 node_exporter),确认 prometheus.yml 中的 target 地址正确 |
| 告警规则从未触发过 | 阈值设置不合理(过高或过低)或 PromQL 语法错误 | 在 Prometheus WebUI 的 Graph 页面手动执行告警 PromQL 验证表达式;用 promtool check rules 检查语法 |
| 告警太多,团队麻木 | 没有区分告警级别,CPU>80% 和 服务宕机走同一通道 | 按 severity 路由,P0 走即时通知(电话/短信),P1 走邮件,P2 走工单系统 |
| 磁盘监控没有覆盖关键挂载点 | node_filesystem_* 默认包含 /boot、/dev 等非关键路径 | 用 PromQL 过滤:node_filesystem_avail_bytes{mountpoint=~"/|/data|/var"} |
| Prometheus 内存 OOM | 采集指标基数过大且没有合理设置存储限制 | 设置 storage.tsdb.retention.time=15d 和 storage.tsdb.retention.size=50GB |
最佳实践
| 实践 | 原理 | 示例 |
|---|---|---|
| 先设 SLO,再写告警 | 没有目标的监控无法判断优先级 | 定义"API P99 延迟 < 500ms"为 SLO,用 Error Budget 消耗速率决定告警级别 |
| 用 USE 和 RED 方法选择指标 | 避免监控一切:USE=利用率/饱和度/错误数,RED=速率/错误/延迟 | USE 适合基础设施(CPU 利用率+排队数+硬件错误),RED 适合服务(请求率+错误率+延迟) |
| 告警要有 Actionable 信息 | 收到告警的人应知道做什么,而非只是一个警告 | 告警描述包含:当前值、阈值、持续时长、Runbook 链接 |
| 定期 Review 告警规则 | 减少噪音告警,保持告警体系健康 | 每季度统计告警触发率,沉默/合并/删除从未触发或总是触发的规则 |
| 仪表盘设计遵循"一图一指标" | 混合多个指标在一张图上难以快速解读 | CPU 一张图、内存一张图,而非将 CPU/内存/网络混在一个 Panel |
练习题
- (概念)监控的四大维度是什么?每个维度至少列出两个常用的命令行排查工具。
- (概念)什么是 SLO 和 Error Budget?如果一个服务的 SLO 是 99.9%,月度 Error Budget 是多少分钟?
- (实操)安装
htop、sysstat、iotop,运行stress命令(需安装)模拟 CPU 压力:stress --cpu 4 --timeout 60,同时用htop和mpstat -P ALL 1观察 CPU 变化。 - (实操)编写一个简单的 Node Exporter 文本文件采集:在
/var/lib/node_exporter/textfile/目录下创建一个脚本,每 5 分钟输出自定义指标(如custom_up 1),在 Prometheus 中验证该指标能被采集到。 - (🔍 挑战)在本地或测试环境部署 Prometheus + Node Exporter + Grafana,连接到任意一台服务器。设计一个 SLO 为"磁盘使用率 < 80%"的告警规则,配置 Alertmanager 发送邮件通知。然后模拟磁盘使用率超过 80%,验证告警链路的完整流程(采集→规则评估→告警触发→通知到达)。
点击查看答案
- (概念)四大维度:CPU(
top、mpstat、htop)、内存(free、vmstat)、磁盘 I/O(iostat、iotop)、网络(ss、iftop、nload)。 - (概念)SLO(服务等级目标)是期望的可靠性目标(如 99.9% 可用性);Error Budget(错误预算)是 SLO 允许的不可用时间。99.9% 的月度 Error Budget = 30 天 × 24 小时 × 60 分钟 × 0.1% ≈ 43.2 分钟。超过 Error Budget 意味着应暂缓发布新功能,优先提升可靠性。
- (实操)
sudo apt install htop sysstat iotop stress→stress --cpu 4 --timeout 60会创建 4 个工作进程占满 CPU。同时htop显示 4 个 CPU 核心接近 100%,mpstat -P ALL 1显示每个核心的%usr飙升。注意stress命令可能不在默认源中,需sudo apt install stress。 - (实操)创建脚本
/usr/local/bin/custom-metrics.sh:#!/bin/bash echo "custom_metric_up $(date +%s)" > /var/lib/node_exporter/textfile/custom.prom。chmod +x 后在 crontab 中设置*/5 * * * * /usr/local/bin/custom-metrics.sh。Prometheus target 页面(/targets)验证 node 的textfilecollector 已采集该指标。 - (🔍 挑战)部署步骤:①
docker-compose.yml定义 Prometheus + Node Exporter + Grafana + Alertmanager;② 告警规则示例:ALERT DiskUsageHigh IF node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"} < 0.2 FOR 5m;③ Alertmanager 配置 email 接收者;④ 模拟:dd if=/dev/zero of=/tmp/bigfile bs=1M count=10240填充磁盘。验证 Grafana Alerting 页面和邮件收件箱。
学习检查点
学完本章后,请检验自己是否掌握以下内容:
| 检查项 | 自测问题 | 验证方法 |
|---|---|---|
| 概念理解 | 能用自己的话解释监控系统的架构和组件作用 | 尝试向他人讲解 |
| 命令操作 | 能不查文档完成 Prometheus 配置、Grafana 面板创建 | 在终端实际执行 |
| 原理掌握 | 能说出 Prometheus 的拉取模型和时序数据库原理 | 画出流程图 |
| 故障排查 | 能独立排查监控数据丢失、告警不触发、面板显示异常 | 模拟故障并修复 |
| 最佳实践 | 能说明为什么需要设置合理的告警阈值和通知渠道 | 对比不同方案 |
本章总结
监控是运维的"眼睛"。从命令行快速排查(top、free、df、ss)到 Prometheus + Grafana 集中监控,再到基于 SLO 的告警体系,三层架构覆盖了"问题发生了→怎么快速发现→怎么确定严重程度→怎么通知对的人"全流程。好的监控不是指标越多越好,而是选择正确的 SLI、设定合理的 SLO、编写 actionable 的告警规则。
速查表
| 命令/组件 | 用途 |
|---|---|
htop / btop / glances | 综合系统监控仪表盘 |
mpstat -P ALL 1 | 按 CPU 核心查看使用率 |
free -h / vmstat 1 | 内存使用情况 |
iostat -x 1 / iotop | 磁盘 I/O 监控 |
ss -tlnp / ss -s | Socket 统计和监听端口 |
| Prometheus | 指标采集和 PromQL 查询引擎 |
| Node Exporter | 主机指标暴露(CPU/内存/磁盘等) |
| Grafana | 仪表盘可视化和告警 |
| Alertmanager | 告警去重、分组、路由、静默 |
promtool check rules | 检查告警规则语法 |
学习路径建议
- 监控是 4.6:Linux 性能调优 Linux 性能调优的输入数据来源
- 日志监控可看 4.8:集中式日志管理 集中式日志管理(ELK 或 Loki)
- Kubernetes 监控可看 6.2:Kubernetes 入门 Kubernetes 入门
延伸阅读
- Prometheus 官方文档
- Grafana 官方文档
- Google SRE — 基于 SLO 的告警
- Awesome Prometheus Alerts — 常用告警规则合集
- 推荐书籍:《Google SRE 运维解密》、《Prometheus 实战》