跳转到内容

Prometheus + Grafana 监控系统搭建实战 2026

Prometheus + Grafana 监控

📊 为什么需要监控? 你花几小时搭好的代理服务、博客、数据库,一旦半夜挂掉,没有监控你根本不知道。等到第二天用户来反馈,黄金恢复时间早就错过了。Prometheus + Grafana 是业界标准的开源监控组合——一个负责采集存储指标,一个负责可视化告警,配合起来几乎零成本就能拥有企业级监控能力。

本文将带你从零搭建一套完整的 VPS 监控系统:

  • ✅ 监控 vs 告警:为什么两者缺一不可
  • ✅ Prometheus 核心原理(时序数据库 / Pull 模型 / Exporter)
  • ✅ Node Exporter 采集 CPU/内存/磁盘/网络
  • ✅ Docker Compose 一键部署整套监控栈
  • ✅ Grafana 看板导入与自定义
  • ✅ Alertmanager 邮件 / Telegram 告警
  • ✅ Nginx、SSL 证书、网站可用性进阶监控
  • ✅ 多 VPS 集中管控与常见问题

一、为什么需要 VPS 监控

1.1 典型痛点

痛点没有监控有监控
服务宕机用户反馈才知道告警秒级推送
磁盘写满数据库崩溃才发现80% 容量就预警
流量异常月底才发现被刷实时流量曲线可见
性能劣化凭感觉优化数据驱动定位瓶颈
安全入侵事后溯源困难异常进程/连接立即可见

1.2 监控 vs 告警

很多人把监控等同于「看个图表」,这是片面的。两者职责不同:

  • 监控(Monitoring):持续采集指标、可视化展示,让你「看得见」系统状态。
  • 告警(Alerting):当指标突破阈值时主动通知你,让你「被提醒」。

💡 只有看板没有告警,等于装了摄像头却没人看;只有告警没有看板,出问题了却没法分析原因。两者必须配套。

1.3 开源方案对比

方案组成优点缺点适用
Prometheus + Grafana采集+可视化生态最完善、查询强大、免费组件稍多通用首选
Zabbix一体化开箱即用、Agent 丰富配置偏重、UI 老旧传统企业
Netdata一体化安装极简、实时性强长期存储弱、定制差单机快速看
Nagios告警为主告警逻辑成熟可视化弱、配置繁琐纯告警场景
VictoriaMetrics替代 Prometheus性能更高、存储更省生态较新超大规模

结论: 个人 VPS / 中小团队首选 Prometheus + Grafana,生态成熟、资料多、迁移成本低。


二、Prometheus 核心原理

2.1 时序数据库(TSDB)

Prometheus 存储的是时序数据——带时间戳的指标序列:

<指标名>{<标签1>=<值1>, <标签2>=<值2>} <数值> <时间戳>
node_memory_MemAvailable_bytes{instance="server-1:9100", job="node"} 3.2e+09 1750000000
  • 指标名:如 node_cpu_seconds_total
  • 标签(Label):区分维度,如 instancejobmode
  • 数值:浮点数
  • 时间戳:毫秒级

2.2 Pull 模型

Prometheus 采用主动拉取(Pull)模式:服务端定时去各 Exporter 的 HTTP 接口抓取指标。

Prometheus Server
   │  (每 15s 拉取 /metrics)
   ├──> Node Exporter :9100/metrics
   ├──> Nginx Exporter :9113/metrics
   └──> Blackbox Exporter :9115/metrics

Pull vs Push 对比:

维度Pull(Prometheus)Push(如 StatsD)
服务发现天然支持需额外注册
故障定位谁挂了一目了然推送失败难排查
防火墙服务端主动出客户端主动出
短任务需 Pushgateway 中转天然支持

2.3 Exporter 生态

Exporter 是「指标翻译器」,把各系统的内部状态暴露成 Prometheus 格式:

Exporter监控对象
Node Exporter主机 CPU/内存/磁盘/网络
cAdvisorDocker 容器
Nginx ExporterNginx 连接/请求
MySQL Exporter数据库状态
Blackbox ExporterHTTP/DNS/TCP 探测
Redis Exporter缓存状态
Process Exporter指定进程

三、Node Exporter 部署

3.1 Docker 方式

bash
docker run -d \
  --name node-exporter \
  --restart=always \
  --network host \
  --pid host \
  -v "/proc:/host/proc:ro" \
  -v "/sys:/host/sys:ro" \
  -v "/:/rootfs:ro" \
  prom/node-exporter:latest \
  --path.procfs=/host/proc \
  --path.sysfs=/host/sys \
  --path.rootfs=/rootfs

3.2 二进制方式(Systemd)

bash
# 下载
cd /tmp
wget 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 服务
sudo tee /etc/systemd/system/node-exporter.service > /dev/null <<'EOF'
[Unit]
Description=Node Exporter
After=network.target

[Service]
User=node_exporter
ExecStart=/usr/local/bin/node_exporter
Restart=always

[Install]
WantedBy=multi-user.target
EOF

# 创建专用用户并启动
sudo useradd -rs /bin/false node_exporter
sudo systemctl daemon-reload
sudo systemctl enable --now node-exporter

3.3 验证指标

bash
curl http://localhost:9100/metrics | head -20
# 应看到 node_cpu_seconds_total、node_memory_MemAvailable_bytes 等指标

3.4 安全加固(重要)

Node Exporter 默认无认证,切勿直接暴露公网。推荐用 Nginx 反向代理 + Basic Auth 或仅监听内网:

nginx
# /etc/nginx/conf.d/node-exporter.conf
location /metrics {
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_pass http://127.0.0.1:9100;
}

四、Prometheus 服务端部署

4.1 Docker Compose 完整配置

创建 docker-compose.yml

yaml
version: '3.8'

services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: always
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=30d'
      - '--web.enable-lifecycle'

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: always
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=your_strong_password
    volumes:
      - grafana_data:/var/lib/grafana

  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    restart: always
    network_mode: host
    pid: host
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro

volumes:
  prometheus_data:
  grafana_data:

4.2 Prometheus 配置文件

prometheus.yml

yaml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  # 监控 Prometheus 自己
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  # 监控本机
  - job_name: 'node'
    static_configs:
      - targets: ['node-exporter:9100']
        labels:
          instance: 'server-main'

  # 监控另一台 VPS(通过内网/隧道 IP)
  - job_name: 'node-remote'
    static_configs:
      - targets: ['10.0.0.2:9100']
        labels:
          instance: 'server-edge'

4.3 数据保留与性能

参数说明建议
retention.time数据保留时长15~30d(个人)
retention.size数据体积上限2GB 防止写满磁盘
wal-compressionWAL 压缩开启节省空间

磁盘占用估算:

每目标 ≈ 1~2 MB/天(默认采集频率)
30 天 × 5 目标 ≈ 150~300 MB

4.4 启动与验证

bash
docker compose up -d
# 访问 http://你的IP:9090
# 在 Graph 页面执行: up  → 应看到所有 target 的 up=1

五、Grafana 可视化

5.1 首次登录

地址: http://你的IP:3000
默认账号: admin / admin(首次登录强制改密码)

5.2 添加 Prometheus 数据源

  1. 左侧菜单 Connections → Data sources → Add data source
  2. 选择 Prometheus
  3. URL 填 http://prometheus:9090(同一 Docker 网络用服务名)
  4. 点击 Save & test,显示绿色对勾即成功

5.3 导入 Node Exporter 看板

Grafana 社区有成熟的现成看板,无需从零画:

  1. 左侧 Dashboards → New → Import
  2. 输入官方 Dashboard ID:1860(Node Exporter Full)或 8919(新版)
  3. 选择刚才添加的 Prometheus 数据源
  4. 点击 Import

💡 推荐常用 Dashboard ID:

  • 1860 — Node Exporter Full(主机监控经典)
  • 11074 — Node Exporter 轻量版
  • 893 — 主机硬件温度(需额外 exporter)

5.4 核心指标 PromQL 查询

如果你想自己画面板,以下是常用查询:

promql
# CPU 使用率(非 idle)
100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# 内存使用率
100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)

# 磁盘使用率(根分区)
100 * (1 - node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})

# 网络流入速率(Mbps)
rate(node_network_receive_bytes_total{device="eth0"}[5m]) * 8 / 1024 / 1024

# 1 分钟负载
node_load1 / count by(instance)(node_cpu_seconds_total{mode="idle"})

六、核心 Dashboard 构建

6.1 推荐面板布局

面板指标
概览系统状态总览up
CPU使用率 / 核心分布node_cpu_seconds_total
内存已用 / 可用 / Swapnode_memory_*
磁盘分区使用率 / IO 速率node_filesystem_* / node_disk_*
网络入站 / 出站流量node_network_*
进程运行进程数 / 负载node_procs_running / node_load*

6.2 示例面板 JSON 片段

Grafana 面板是 JSON 结构,以下是一个「CPU 使用率」面板核心字段:

json
{
  "title": "CPU 使用率",
  "type": "timeseries",
  "gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 },
  "targets": [
    {
      "expr": "100 - (avg by(instance)(rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)",
      "legendFormat": "{{instance}}"
    }
  ],
  "fieldConfig": {
    "defaults": {
      "unit": "percent",
      "max": 100
    }
  }
}

6.3 阈值与单位

Grafana 支持在面板中设置阈值变色:

指标单位警告阈值严重阈值
CPU 使用率%7090
内存使用率%8095
磁盘使用率%8090
网络流量MB/s按带宽定

七、告警规则配置

7.1 告警规则文件

alerts.yml

yaml
groups:
  - name: node-alerts
    rules:
      - alert: 实例宕机
        expr: up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "{{ $labels.instance }} 已离线"
          description: "实例 {{ $labels.instance }} 持续 1 分钟无法抓取"

      - alert: 高CPU使用率
        expr: 100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "{{ $labels.instance }} CPU 过高"
          description: "CPU 使用率持续 5 分钟超过 85%"

      - alert: 磁盘即将写满
        expr: 100 * (1 - node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) > 85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "{{ $labels.instance }} 磁盘空间不足"
          description: "根分区使用率超过 85%"

      - alert: 内存不足
        expr: 100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 90
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "{{ $labels.instance }} 内存紧张"
          description: "内存使用率超过 90%"

prometheus.yml 中引用:

yaml
rule_files:
  - /etc/prometheus/alerts.yml

7.2 Alertmanager 部署

yaml
# 在 docker-compose.yml 中追加
  alertmanager:
    image: prom/alertmanager:latest
    container_name: alertmanager
    restart: always
    ports:
      - "9093:9093"
    volumes:
      - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
    command:
      - '--config.file=/etc/alertmanager/alertmanager.yml'

prometheus.yml 添加告警路由:

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

7.3 邮件告警通道

alertmanager.yml

yaml
global:
  smtp_smarthost: 'smtp.gmail.com:587'
  smtp_from: 'monitor@gmail.com'
  smtp_auth_username: 'monitor@gmail.com'
  smtp_auth_password: '应用专用密码'
  smtp_require_tls: true

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

receivers:
  - name: 'email-alert'
    email_configs:
      - to: 'admin@example.com'
        subject: '[监控告警] {{ .CommonAnnotations.summary }}'
        body: |
          告警: {{ .CommonAnnotations.summary }}
          详情: {{ .CommonAnnotations.description }}
          时间: {{ .StartsAt }}

7.4 Telegram 告警通道

Telegram 更适合移动端即时推送。先找 @BotFather 创建 Bot 获取 Token,再找 @userinfobot 获取 Chat ID。

yaml
receivers:
  - name: 'telegram-alert'
    telegram_configs:
      - bot_token: '你的BOT_TOKEN'
        chat_id: '你的CHAT_ID'
        message: |
          🔥 *{{ .CommonAnnotations.summary }}*
          {{ .CommonAnnotations.description }}
          🖥 实例: {{ .CommonLabels.instance }}

八、进阶监控

8.1 Nginx 访问统计

启用 Nginx stub_status,再用 nginx-prometheus-exporter 采集:

nginx
# nginx.conf
location /nginx_status {
    stub_status on;
    allow 127.0.0.1;
    deny all;
}
bash
docker run -d --name nginx-exporter \
  -p 9113:9113 \
  nginx/nginx-prometheus-exporter:latest \
  -nginx.scrape-uri=http://你的IP/nginx_status

Prometheus 添加 job:

yaml
  - job_name: 'nginx'
    static_configs:
      - targets: ['nginx-exporter:9113']

看板 ID:11199(Nginx 请求/连接/状态码)。

8.2 SSL 证书过期监控

Blackbox Exporter 探测 HTTPS 并解析证书剩余天数:

yaml
# blackbox.yml
modules:
  https:
    prober: http
    http:
      preferred_ip_protocol: ip4
      tls: true

Prometheus 添加探测 job:

yaml
  - job_name: 'ssl-check'
    metrics_path: /probe
    params:
      module: [https]
    static_configs:
      - targets:
          - https://y-m.top
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: blackbox-exporter:9115

证书剩余天数告警:

yaml
  - alert: SSL证书即将过期
    expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 14
    for: 1h
    labels:
      severity: warning
    annotations:
      summary: "SSL 证书将在 14 天内过期"
      description: "域名 {{ $labels.instance }} 证书剩余不足 14 天"

8.3 网站可用性探测

Blackbox Exporter 还能探测网站是否可访问、响应时间:

yaml
  - alert: 网站不可达
    expr: probe_success{job="blackbox-http"} == 0
    for: 2m
    labels:
      severity: critical
    annotations:
      summary: "{{ $labels.instance }} 访问失败"
      description: "HTTP 探测连续失败超过 2 分钟"
yaml
  - alert: 响应过慢
    expr: probe_duration_seconds{job="blackbox-http"} > 3
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "{{ $labels.instance }} 响应缓慢"
      description: "HTTP 响应时间持续超过 3 秒"

九、常见问题

Q1:Prometheus 内存占用过高怎么办?
  1. 调低采集频率:scrape_interval 从 15s 调到 30s 或 60s
  2. 减少目标数量:只为关键服务建 job
  3. 限制数据保留:--storage.tsdb.retention.time=15d
  4. 设置数据体积上限:--storage.tsdb.retention.size=2GB
  5. 给容器限制内存:deploy.resources.limits.memory: 1g
Q2:数据存哪里?会不会写满磁盘?

默认存在 Docker 卷 prometheus_data(映射在 /var/lib/docker/volumes/)。务必设置 retention.size 上限,并监控宿主机磁盘。建议单独挂一块盘或定期备份:

bash
# 手动备份(停止容器后)
docker run --rm -v prometheus_data:/data -v $(pwd):/backup alpine \
  tar czf /backup/prometheus-backup.tar.gz -C /data .
Q3:多台 VPS 如何集中监控?

三种方案:

方案做法适合
中心拉取监控服务器直接拉各 VPS 的 Exporter(需网络可达)同内网/有隧道
反向代理各 VPS 用 Nginx 暴露 Exporter,中心统一拉有公网 IP
Pushgateway边缘节点定时推送(适合 NAT/动态 IP)无公网 IP

最常用是中心拉取:监控服务器 Prometheus 配置多个 static_configs,targets 填各 VPS 内网 IP + 端口(通过 WireGuard 组网可互通)。

Q4:Grafana 进不去 / 密码忘了?
bash
# 重置 admin 密码
docker exec -it grafana grafana-cli admin reset-admin-password newpassword
Q5:告警不触发?如何排查?
  1. 访问 http://IP:9090/alerts 看规则是否 PENDING/FIRING
  2. 检查 alertmanager.yml 语法:amtool check-config alertmanager.yml
  3. 确认 Prometheus alerting 段指向正确的 Alertmanager 地址
  4. amtool alert query 查看当前告警
  5. Telegram 确认 Bot Token 和 Chat ID 正确(Chat ID 是负数需带负号)
Q6:能监控 Docker 容器吗?

能。部署 cAdvisor 采集容器指标:

bash
docker run -d --name cadvisor \
  -p 8080:8080 \
  -v /:/rootfs:ro -v /var/run:/var/run:ro \
  -v /sys:/sys:ro -v /var/lib/docker/:/var/lib/docker:ro \
  gcr.io/cadvisor/cadvisor:latest

Prometheus 添加 job 指向 cadvisor:8080,看板 ID:893(Docker 监控)。

Q7:生产环境如何保护监控面板?
  1. Grafana/Prometheus 不要直接暴露公网,套一层 Nginx + Basic Auth 或反向代理
  2. Grafana 配置 GF_AUTH_ANONYMOUS_ENABLED=false 禁用匿名访问
  3. 修改默认端口(9090/3000 众所周知易被扫描)
  4. 启用 HTTPS(参考站内 VPS 安全加固实战指南
  5. 定期更新镜像版本
Q8:和 VPS 供应商自带的监控(如阿里云云监控)有什么区别?
维度厂商监控Prometheus+Grafana
数据归属厂商掌握自己掌握
自定义受限完全自由
跨厂商各看各的统一看板
成本高级功能收费免费
告警渠道厂商 App邮件/Telegram/钉钉/Slack

自建监控的最大价值是跨厂商统一数据自主


总结

Prometheus + Grafana 是 VPS 监控的黄金组合。回顾核心要点:

  1. 架构清晰:Prometheus 拉取指标存储,Grafana 可视化,Alertmanager 管告警
  2. 部署简单:Docker Compose 一份配置拉起整套栈
  3. 看板现成:导入社区 Dashboard ID(1860/11199)即可用
  4. 告警灵活:邮件 + Telegram 双通道,PromQL 精准定义阈值
  5. 可扩展:Nginx / SSL / 网站可用性 / 多节点一站式覆盖

搭建顺序建议:

1. Node Exporter(采集主机指标)
2. Prometheus(存储 + 查询)
3. Grafana(可视化看板)
4. 导入 Dashboard 1860
5. Alertmanager(邮件/Telegram 告警)
6. 进阶:Blackbox + Nginx Exporter

延伸阅读:

🚀 监控不是锦上添花,而是系统稳定运行的底线。花一晚上搭好这套监控,未来无数个安稳的夜晚都是它换来的。


相关命令速查:

bash
# 启动整套监控
docker compose up -d

# 查看状态
docker compose ps

# 重启单个服务
docker compose restart prometheus

# 热加载 Prometheus 配置(无需重启)
curl -X POST http://localhost:9090/-/reload

# 查看告警
docker logs alertmanager