
一台服务器,也需要被看着
单机部署最容易出的事故,不是服务挂了,而是服务挂了三天你才发现。磁盘写满、内存被一个慢查询吃光、证书过期——这些问题在发生前都有明确的先兆指标,只是没人看。
这篇文章给出一套最小可用的单机监控:Prometheus 采集、node_exporter 提供主机指标、Grafana 出图、Alertmanager 发告警。全部用 Docker Compose 起,一台 2C4G 的机器足够,整套内存占用约 300MB。
1. 工具清单与版本假设
四个组件,职责不重叠:
- node_exporter:暴露 CPU、内存、磁盘、网络、文件系统等主机指标,只读,无状态。
- Prometheus:定时拉取(scrape)指标,存本地时序库,负责计算告警规则。
- Alertmanager:接收 Prometheus 推来的告警,做去重、分组、静默、路由。
- Grafana:查询 Prometheus 并出图,本身不采集数据。
关键点:Prometheus 是拉模型,不是推模型。它主动去目标地址抓 /metrics,所以被监控端的 9100 端口必须对 Prometheus 可达。这是后面所有网络问题的根源。
2. 目录结构与 Compose 编排
先建目录,配置文件与数据分开存放,避免容器重建时丢数据:
monitor/
├── docker-compose.yml
├── prometheus/
│ ├── prometheus.yml
│ └── rules/host.yml
├── alertmanager/
│ └── alertmanager.yml
└── data/ # 挂载卷,务必纳入备份
Compose 里四个服务共用一个自定义网络,Prometheus 通过服务名访问 node_exporter,不要写 127.0.0.1——容器内的 127.0.0.1 是容器自己。
services:
node-exporter:
image: prom/node-exporter:v1.8.2
pid: host
volumes:
- /:/host:ro,rslave
command:
- --path.rootfs=/host
restart: unless-stopped
prometheus:
image: prom/prometheus:v2.54.1
volumes:
- ./prometheus:/etc/prometheus:ro
- ./data/prom:/prometheus
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.retention.time=30d
- --web.enable-lifecycle
ports: ["9090:9090"]
restart: unless-stopped
--web.enable-lifecycle 一定要加。没有它,改完规则只能重启容器;有了它,一条 curl -X POST localhost:9090/-/reload 就能热加载。
3. 采集配置:三个必须设的参数
global:
scrape_interval: 30s
evaluation_interval: 30s
rule_files:
- /etc/prometheus/rules/*.yml
alerting:
alertmanagers:
- static_configs:
- targets: ["alertmanager:9093"]
scrape_configs:
- job_name: node
static_configs:
- targets: ["node-exporter:9100"]
scrape_interval 设 30 秒,不要设 5 秒。单机监控不需要秒级精度,5 秒间隔会让时序库体积翻六倍,而告警的响应速度并不会因此变快——真正的瓶颈在告警规则的 for 持续时间。
保留期按磁盘倒推。经验值:单目标 30 秒间隔,每天约产生 3~6MB 数据。30 天保留期配 2GB 卷足够,但要把卷的实际可用空间算进去。
4. 四条告警规则,覆盖八成事故
规则不必多,但每一条都要能在半夜叫醒你时有意义。以下四条按优先级排列:
| 告警 | 表达式要点 | 阈值 | 持续时间 |
|---|---|---|---|
| 磁盘将满 | 根分区可用率 | < 15% | 10 分钟 |
| 内存压力 | 可用内存占总内存 | < 10% | 10 分钟 |
| 目标失联 | up == 0 | — | 2 分钟 |
| 负载异常 | 5 分钟负载 / CPU 核数 | > 2 | 15 分钟 |
规则文件示例,注意 for 的作用:它表示「条件持续满足多久才真正触发」,是压制抖动最有效的手段。
groups:
- name: host
rules:
- alert: DiskWillFillSoon
expr: |
node_filesystem_avail_bytes{fstype!~"tmpfs|overlay",mountpoint="/"}
/ node_filesystem_size_bytes{fstype!~"tmpfs|overlay",mountpoint="/"} < 0.15
for: 10m
labels:
severity: warning
annotations:
summary: "根分区可用空间低于 15%"
description: "当前可用 {{ $value | humanizePercentage }},预计 24 小时内写满。"
必须排除 tmpfs 和 overlay 类型。不排除的话,容器环境里的临时文件系统会频繁触发误报,第一次配监控的人几乎都会踩这一条。
5. 三个必然踩到的坑
坑一:node_exporter 读到的是容器自己的磁盘。 不加 pid: host 和 --path.rootfs=/host,它报的磁盘使用率是容器层的,永远显示 5%,看起来一切正常。这是最危险的一类错误——监控在说谎。
坑二:告警发了但没人收到。 Prometheus 的 alerting 段指向 alertmanager:9093,而 Alertmanager 的配置文件里 route.receiver 必须和 receivers 里的名字完全一致。名字差一个字母,告警会被静默丢弃,日志里只有一行不起眼的 warning。
坑三:时区。 Prometheus 内部全部用 UTC 存储。Grafana 面板要显式设成 Asia/Shanghai,否则你会看到「凌晨三点 CPU 飙高」,而实际发生时间是上午十一点——排查方向直接跑偏。
6. 上线后的验收动作
配完不要等事故发生才验证。三个动作,十分钟做完:
- 打开
/targets页面,确认目标状态为 UP,且Last Scrape在 30 秒内。 - 在 Prometheus 表达式框输入
up,返回值为 1 才算通。 - 手动造一次告警:把磁盘阈值临时改成 99%,看是否在
for时间后进入 Firing 状态,并确认消息真的到达了接收端。
第三步最重要,也最常被跳过。没有验证过送达链路的告警,等于没有告警。
写在最后
这套东西的价值不在于图好看,而在于把「服务是否正常」从感觉变成数字。四条规则、三百兆内存,换的是一个可以安心睡觉的夜晚。
下一步可以加 blackbox_exporter 做探活,或者把应用自身的业务指标接进来。但先把主机这一层看住,比什么都重要。
你的服务器上装监控了吗?是从来没出过事,还是出过事之后才装的?

评论 (0)