自建服务器监控实战:Prometheus + Grafana 从零搭起生产级告警体系
服务器跑了一堆服务,心里却一直不踏实——哪天…
服务器跑了一堆服务,心里却一直不踏实——哪天磁盘满了、内存爆了、服务挂了,等用户反馈才知道,那太被动了。这大概是每个自部署玩家的必经阶段:从”能跑就行”到”我得知道它现在怎样”。
今天这篇就把我自用的监控方案完整走一遍:Prometheus 采集指标、Grafana 可视化展示、Alertmanager 告警通知,三件套全部 Docker 部署,你只要有一台 Linux 服务器就能跟着搭。
为什么是 Prometheus + Grafana?
市面上监控方案不少,Netdata 开箱即用但数据出不去、Zabbix 功能强大但太重、云厂商的监控服务是付费不说,数据还得往外送。Prometheus 这个组合是我折腾一圈后留下的方案,理由就三条:
- Pull 模型——被监控端不用主动上报,Prometheus Server 按间隔去拉数据,安全可控,单个节点挂了不影响其他节点
- 生态极广——几乎所有主流中间件(Nginx、MySQL、Redis、Docker、Kubernetes)都有现成的 Exporter,装上就能采集
- Grafana 的仪表盘太好看——这不是开玩笑,可视化的美观程度直接影响你巡检的意愿
整体架构
先脑子里过一遍各个组件的关系:
- Prometheus Server:核心,负责拉取和存储指标数据,按标签组织时间序列
- Exporters:部署在各节点/服务上的采集器,把系统指标暴露成 HTTP 端点给 Prometheus 拉
- Grafana:从 Prometheus 读数据,画仪表盘,支持告警面板
- Alertmanager:处理告警规则触发的通知,推送到钉钉、微信、邮件、Telegram
我们要的就是一个 docker-compose.yml 把这几样全部拉起来,后续加节点只需要在被监控机上跑一个 Exporter 容器。
第一步:准备目录结构和配置文件
先在服务器上建个目录,后面所有配置都放这:
mkdir -p /opt/monitoring/{prometheus,grafana,alertmanager}
cd /opt/monitoring
然后写 Prometheus 的配置文件。这是核心,告诉 Prometheus 去哪里拉数据:
# /opt/monitoring/prometheus/prometheus.yml
global:
scrape_interval: 15s # 默认采集间隔
evaluation_interval: 15s # 告警规则评估间隔
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
rule_files:
- "rules/*.yml"
scrape_configs:
# Prometheus 自身监控
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# 本机节点监控 (node_exporter)
- job_name: 'node'
static_configs:
- targets: ['node_exporter:9100']
# Docker 容器监控 (cadvisor)
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
再建一个告警规则文件,定义什么时候触发告警:
mkdir -p /opt/monitoring/prometheus/rules
cat > /opt/monitoring/prometheus/rules/node_alerts.yml << 'EOF'
groups:
- name: node_alerts
rules:
- alert: HighCpuUsage
expr: (1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance)) * 100 > 80
for: 5m
labels:
severity: warning
annotations:
summary: "CPU 使用率超过 80% ({{ $value }}%)"
- alert: DiskSpaceLow
expr: node_filesystem_avail_bytes{mountpoint="/",fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{mountpoint="/",fstype!~"tmpfs|overlay"} * 100 < 10
for: 2m
labels:
severity: critical
annotations:
summary: "磁盘剩余空间不足 10%"
- alert: MemoryPressure
expr: (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 > 90
for: 5m
labels:
severity: warning
annotations:
summary: "内存使用率超过 90% ({{ $value }}%)"
- alert: NodeDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "节点 {{ $labels.instance }} 已离线"
EOF
再配一下 Alertmanager,让它把告警推到钉钉(你也可以换 Telegram 或邮件):
# /opt/monitoring/alertmanager/alertmanager.yml
global:
resolve_timeout: 5m
route:
group_wait: 10s
group_interval: 2m
repeat_interval: 4h
receiver: 'dingtalk'
receivers:
- name: 'dingtalk'
webhook_configs:
- url: 'http://dingtalk-webhook:8060/dingtalk/webhook1/send'
send_resolved: true
如果不用钉钉,换 Telegram 更简单,直接设一个 bot:
# Alertmanager Telegram 示例
receivers:
- name: 'telegram'
telegram_configs:
- bot_token: '你的BOT_TOKEN'
chat_id: 你的CHAT_ID
parse_mode: 'HTML'
send_resolved: true
第二步:Docker Compose 一键拉起
这是最爽的一步,一个 docker-compose.yml 搞定所有组件:
# /opt/monitoring/docker-compose.yml
version: '3.8'
networks:
monitoring:
driver: bridge
volumes:
prometheus_data:
grafana_data:
services:
# Prometheus 核心
prometheus:
image: prom/prometheus:latest
container_name: prometheus
volumes:
- ./prometheus:/etc/prometheus
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=30d'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
ports:
- "9090:9090"
networks:
- monitoring
restart: unless-stopped
# 节点指标采集
node_exporter:
image: prom/node-exporter:latest
container_name: node_exporter
command:
- '--path.rootfs=/host'
pid: host
volumes:
- '/:/host:ro,rslave'
ports:
- "9100:9100"
networks:
- monitoring
restart: unless-stopped
# Docker 容器指标采集
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
devices:
- /dev/kmsg
ports:
- "8080:8080"
networks:
- monitoring
restart: unless-stopped
# 可视化面板
grafana:
image: grafana/grafana:latest
container_name: grafana
volumes:
- grafana_data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin123
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource
ports:
- "3000:3000"
networks:
- monitoring
restart: unless-stopped
# 告警管理
alertmanager:
image: prom/alertmanager:latest
container_name: alertmanager
volumes:
- ./alertmanager:/etc/alertmanager
command:
- '--config.file=/etc/alertmanager/alertmanager.yml'
- '--storage.path=/alertmanager'
ports:
- "9093:9093"
networks:
- monitoring
restart: unless-stopped
直接启动:
cd /opt/monitoring
docker compose up -d
# 查看启动状态
docker compose ps
# 检查日志确认都跑起来了
docker compose logs --tail=20
启动后访问 http://你的服务器IP:9090/targets,应该能看到所有 targets 都是 UP 状态。如果有标红的,说明对应的 Exporter 没起来或者网络不通,检查一下容器日志。
第三步:配置 Grafana 仪表盘
Grafana 默认账号 admin / admin123(刚才环境变量里设的),登录后第一步是加数据源:
- 左侧齿轮 → Data Sources → Add data source → Prometheus
- URL 填
http://prometheus:9090(因为在同一个 Docker 网络里,容器名就是主机名) - 点 Save & Test,看到绿色提示就通上了
然后导入现成的仪表盘。不用自己从头画,Grafana 社区有几千个现成的模板,输入 ID 就直接导入:
- Node Exporter Full(ID: 1860)—— 服务器概览,CPU、内存、磁盘、网络流量一张图全看
- Docker Monitoring(ID: 179)—— 容器级别的 CPU/内存/网络/块 IO 监控
导入方式:左侧 + 号 → Import → 输入模板 ID → 选择 Prometheus 数据源 → Import。几十秒就有一套漂亮的板子。
第四步:给被监控节点装 Exporter(远程节点扩展)
如果你不止一台服务器(比如你有多台 VPS 或者家里还有 NAS),要在每台被监控的机器上也跑一个 node_exporter。每台机器上执行:
docker run -d \
--name node_exporter \
--net="host" \
--pid="host" \
-v "/:/host:ro,rslave" \
--restart unless-stopped \
prom/node-exporter:latest \
--path.rootfs=/host
然后在 Prometheus 的配置文件里加上这个节点(记得防火墙放行 9100 端口):
# 加到 prometheus.yml 的 scrape_configs 里
- job_name: 'node_vps2'
static_configs:
- targets: ['VPS2的IP:9100']
改完配置后不需要重启 Prometheus,它支持热加载:
# 或者直接给 Prometheus 发 SIGHUP
kill -HUP $(docker exec prometheus cat /prometheus/supervisor.pid 2>/dev/null)
# 最简单的热加载方式
curl -X POST http://localhost:9090/-/reload
然后去看 http://localhost:9090/targets,新节点应该会自动出现。
第五步:告警接入钉钉/Telegram
光有仪表盘不够——凌晨三点磁盘写满了你也不会盯着屏幕看。得让告警主动找你。
钉钉机器人:创建一个钉钉群 → 群设置 → 智能群助手 → 添加机器人 → 自定义(Webhook),拿到 Webhook 地址后,用 prometheus-webhook-dingtalk 这个桥接工具:
# 加到 docker-compose.yml
dingtalk-webhook:
image: timonwong/prometheus-webhook-dingtalk:latest
container_name: dingtalk-webhook
command:
- '--ding.profile=webhook1=https://oapi.dingtalk.com/robot/send?access_token=你的TOKEN'
ports:
- "8060:8060"
networks:
- monitoring
restart: unless-stopped
Telegram Bot:找 @BotFather 创建一个 bot 拿到 token,然后找 @userinfobot 拿到你的 chat_id。在 Alertmanager 配置里直接配 telegram_configs 即可(上面已经给出了示例)。
告警一旦触发,你的手机就会弹出”磁盘空间不足 10%”或者”节点离线”这样实实在在的推送,不用再等到用户来骂你才知道。
你可能遇到的坑
这方案我踩了不少坑,列几个高频的:
- 防火墙端口问题:Prometheus、node_exporter、Grafana 各自有不同的端口。如果 targets 老是 DOWN,检查一下防火墙或安全组有没有放行 9100/9090/3000
- 磁盘空间暴涨:Prometheus 默认保留 15 天数据,对于单机监控来说够用。如果节点很多,建议在启动参数里设
--storage.tsdb.retention.time=7d缩减存储周期 - Grafana 忘了密码:执行
docker exec grafana grafana-cli admin reset-admin-password 新密码就能重置 - 容器时间不同步:所有监控容器的时区一定要和宿主机一致,否则时间序列数据会乱。在 docker-compose 里加上
environment: - TZ=Asia/Shanghai
写在最后
这套方案我跑了快一年,三台 VPS 加两台家里的机器,全部通过这一个 Grafana 面板统览。半夜被钉钉叫醒过几次去看磁盘扩容,也靠 NodeDown 告警发现过某台低价 VPS 偷偷重启了。说实话,监控这件事做完了你不会觉得多了什么,但没做的时候,心里总悬着。
Prometheus + Grafana 的好处是”一次搭建,长期受益”。后续你想监控 Nginx 连接数、MySQL 慢查询、Redis 命中率,基本就是加一个 Exporter 容器、配一条 scrape_config 的事,不用改架构。
你的服务器在跑什么服务?欢迎留言聊聊你的监控方案。
