欢迎光临

VPS监控与告警系统搭建完全指南:Prometheus+Grafana+Loki生产级部署实战

为什么VPS监控如此重要

拥有VPS的朋友们一定经历过这样的场景:好好的网站突然打不开了,SSH连不上,一查才发现磁盘早已爆满,或者内存被某个进程吃光。更糟的是,你可能是从用户投诉中才得知这一切。对于单机运行的VPS而言,缺乏监控意味着每一次故障都是一次”盲人摸象”式的排查过程。

生产环境的监控体系不是大厂的专利。即便是单台VPS,搭建一套完善的监控告警系统也完全可行,而且投入产出比极高。本文将从零开始,教你在一台VPS上搭建完整的生产级可观测性体系,涵盖基础设施监控、告警通知、日志管理和可视化仪表盘。

我们将使用以下开源工具栈:Prometheus + Node Exporter + Grafana + Alertmanager + Loki。这套组合在业界被称为”PLG Stack”(Prometheus, Loki, Grafana),被几千家公司的生产环境验证过,完全可以在单台VPS上跑起来。

监控架构概览

在动手之前,先理解整体架构是有必要的。我们的监控系统分为四个层次:

层次 组件 功能
数据采集层 Node Exporter 收集CPU、内存、磁盘、网络等系统指标
数据存储层 Prometheus 时序数据存储 + 告警规则评估
可视化层 Grafana 仪表盘展示 + 告警通知
告警通知层 Alertmanager 告警路由、去重和发送
日志层 Loki + Promtail 日志采集、聚合和查询

这四个层次通过Docker Compose编排在一起,部署和维护都非常方便。数据流向是:Node Exporter暴露指标 → Prometheus拉取 → Grafana查询展示;Prometheus触发告警 → Alertmanager路由 → 推送到微信/钉钉/邮件。

第一步:环境准备与Docker部署

几乎所有现代VPS都支持Docker。如果你的VPS还没安装,先执行以下命令:


1
2
3
4
5
6
7
8
# 更新系统并安装Docker
apt update && apt upgrade -y
apt install -y docker.io docker-compose-v2
systemctl enable --now docker

# 验证安装
docker --version
docker compose version

建议使用Ubuntu 22.04或Debian 12作为宿主机系统。2GB内存的VPS即可流畅运行整套监控栈,如果只有1GB内存,建议增加512MB Swap。

创建项目目录结构


1
2
mkdir -p ~/monitoring/{prometheus,grafana,loki,promtail,alertmanager}
cd ~/monitoring

接下来创建核心的

1
docker-compose.yml

文件:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
version: '3.8'

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

  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
      - '--path.rootfs=/rootfs'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    ports:
      - "9100:9100"
    restart: unless-stopped

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=***
      - GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource
    volumes:
      - ./grafana/datasources:/etc/grafana/provisioning/datasources
      - ./grafana/dashboards:/etc/grafana/provisioning/dashboards
      - grafana_data:/var/lib/grafana
    ports:
      - "3000:3000"
    depends_on:
      - prometheus
    restart: unless-stopped

  alertmanager:
    image: prom/alertmanager:latest
    container_name: alertmanager
    volumes:
      - ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml
    ports:
      - "9093:9093"
    restart: unless-stopped

  loki:
    image: grafana/loki:latest
    container_name: loki
    volumes:
      - ./loki/loki-config.yml:/etc/loki/loki-config.yml
      - loki_data:/loki
    ports:
      - "3100:3100"
    command: -config.file=/etc/loki/loki-config.yml
    restart: unless-stopped

  promtail:
    image: grafana/promtail:latest
    container_name: promtail
    volumes:
      - ./promtail/promtail-config.yml:/etc/promtail/promtail-config.yml
      - /var/log:/var/log:ro
    command: -config.file=/etc/promtail/promtail-config.yml
    depends_on:
      - loki
    restart: unless-stopped

volumes:
  prometheus_data:
  grafana_data:
  loki_data:

第二步:配置Prometheus抓取指标

Prometheus通过拉取(Pull)模式获取指标数据。创建

1
prometheus/prometheus.yml


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
global:
  scrape_interval: 15s
  evaluation_interval: 15s

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - alertmanager:9093

rule_files:
  - "alert_rules.yml"

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'node'
    static_configs:
      - targets: ['node-exporter:9100']

  - job_name: 'docker'
    static_configs:
      - targets: ['10.0.0.1:9323']

注意:Docker本身的指标需要额外开启。在Docker daemon配置文件

1
/etc/docker/daemon.json

中添加:


1
2
3
4
{
  "metrics-addr": "0.0.0.0:9323",
  "experimental": true
}

然后重启Docker:

1
systemctl restart docker

。这样Prometheus就能监控Docker守护进程本身的运行状态了。

第三步:定义告警规则

高可用不只是采集数据,更重要的是在异常发生时第一时间通知你。创建

1
prometheus/alert_rules.yml


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
groups:
  - name: node_alerts
    interval: 30s
    rules:

      - alert: NodeDown
        expr: up{job="node"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "节点离线({{ $labels.instance }})"
          description: "{{ $labels.instance }} 已离线超过1分钟"

      - alert: HighCpuUsage
        expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 90
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "CPU使用率超过90%({{ $value }}%)"
          description: "{{ $labels.instance }} CPU使用率已达 {{ $value }}%"

      - alert: HighMemoryUsage
        expr: (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 > 90
        for: 3m
        labels:
          severity: warning
        annotations:
          summary: "内存使用率超过90%({{ $value }}%)"
          description: "{{ $labels.instance }} 内存使用率已达 {{ $value }}%"

      - alert: DiskSpaceLow
        expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 10
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "磁盘剩余空间不足10%({{ $value }}%)"
          description: "{{ $labels.instance }} 根分区剩余 {{ $value }}%"

      - alert: DiskInodeExhaustion
        expr: (node_filesystem_files_free{mountpoint="/"} / node_filesystem_files{mountpoint="/"}) * 100 < 5
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "inode即将耗尽"
          description: "{{ $labels.instance }} 根分区inode仅剩 {{ $value }}%"

      - alert: HighLoadAverage
        expr: node_load15 > (count(node_cpu_seconds_total{mode="idle"}) by (instance)) * 0.8
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "系统负载过高({{ $value }})"
          description: "{{ $labels.instance }} 15分钟负载平均值 {{ $value }}"

这里我定义了6个最关键的告警规则:节点离线、CPU过载、内存吃紧、磁盘将满、inode耗尽和系统负载过高。这些都是VPS最常见的故障场景,覆盖了95%的实际问题。

第四步:配置Alertmanager告警通知

告警要能送到你手上才有意义。Alertmanager支持多种通知渠道,我们配置微信企业机器人(最推荐)和邮件作为兜底:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# alertmanager/alertmanager.yml
route:
  receiver: 'default'
  group_wait: 10s
  group_interval: 2m
  repeat_interval: 4h
  routes:
    - receiver: 'wechat'
      match:
        severity: critical
      continue: true
    - receiver: 'email'

receivers:
  - name: 'wechat'
    webhook_configs:
      - url: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY'
        send_resolved: true

  - name: 'email'
    email_configs:
      - to: 'admin@yourdomain.com'
        from: 'alert@yourdomain.com'
        smarthost: 'smtp.yourdomain.com:587'
        auth_username: 'alert@yourdomain.com'
        auth_password: 'YOUR_PASSWORD'
        send_resolved: true

  - name: 'default'
    webhook_configs:
      - url: 'http://localhost:8080/webhook'

如果不方便用微信,Telegram Bot也是很好的选择。只需创建一个Bot,获取Token和Chat ID,用Telegram的webhook API即可:


1
2
3
4
5
6
7
  - name: 'telegram'
    webhook_configs:
      - url: 'https://api.telegram.org/bot&lt;YOUR_BOT_TOKEN&gt;/sendMessage'
        http_config:
          headers:
            Content-Type: 'application/json'
        url_param: "chat_id=&lt;CHAT_ID&gt;&amp;parse_mode=Markdown&amp;text={{ template "telegram.default" . }}"

第五步:配置Grafana仪表盘

Grafana是监控数据的”脸面”。初始化配置时,我们通过provisioning机制自动配置数据源和仪表盘,省去手动操作:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# grafana/datasources/datasources.yml
apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    editable: false

  - name: Loki
    type: loki
    access: proxy
    url: http://loki:3100
    editable: false

Grafana官方提供了非常成熟的Node Exporter仪表盘,ID为1860。我们可以在Grafana UI中导入,也可以预先下载JSON文件放到provisioning目录。推荐先启动服务,然后在Grafana UI中导入(Dashboard → Import → 输入1860),这样能得到最新版本。

登录Grafana:浏览器访问

1
http://你的VPS_IP:3000

,默认账号 admin / admin123(密码在docker-compose中配置)。首次登录会提示修改密码,也可以跳过。

自定义监控面板

除了导入官方面板,我建议你按实际需求创建几个自定义面板:

  • VPS概览面板:CPU、内存、磁盘、网络流量四合一,一眼看清VPS整体状态
  • 进程监控面板:显示Top 10 CPU/内存消耗进程,方便排查资源异常
  • Docker容器面板:展示每个容器的资源使用情况,特别适合跑多个服务的VPS
  • 网络流量面板:入站/出站流量趋势图,对带宽敏感的VPS非常有用

第六步:配置Loki日志聚合

指标监控能告诉你”什么出了问题”,但日志分析才能告诉你”为什么出问题”。Loki是一套轻量级日志聚合系统,与Prometheus风格一脉相承:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# loki/loki-config.yml
auth_enabled: false

server:
  http_listen_port: 3100

common:
  path_prefix: /loki
  storage:
    filesystem:
      chunks_directory: /loki/chunks
      rules_directory: /loki/rules
  replication_factor: 1
  ring:
    kvstore:
      store: inmemory

schema_config:
  configs:
    - from: 2020-10-24
      store: boltdb-shipper
      object_store: filesystem
      schema: v11
      index:
        prefix: index_
        period: 24h

Promtail负责将日志文件发送到Loki:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# promtail/promtail-config.yml
server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: system
    static_configs:
      - targets:
          - localhost
        labels:
          job: varlogs
          __path__: /var/log/*.log

  - job_name: nginx
    static_configs:
      - targets:
          - localhost
        labels:
          job: nginx
          __path__: /var/log/nginx/access.log

  - job_name: auth
    static_configs:
      - targets:
          - localhost
        labels:
          job: auth
          __path__: /var/log/auth.log

配置完成后,启动所有服务:


1
2
3
4
5
6
7
8
cd ~/monitoring
docker compose up -d

# 检查所有服务状态
docker compose ps

# 查看日志确认启动正常
docker compose logs prometheus | tail -5

第七步:安全加固

监控系统本身也是攻击面。暴露在公网的Prometheus和Grafana必须做好安全防护:

Nginx反向代理 + HTTPS


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# /etc/nginx/sites-available/monitoring.yourdomain.com
server {
    listen 443 ssl http2;
    server_name monitoring.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/monitoring.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/monitoring.yourdomain.com/privkey.pem;

    # Grafana
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Prometheus(需要Basic Auth)
    location /prometheus/ {
        proxy_pass http://127.0.0.1:9090/;
        auth_basic "Prometheus";
        auth_basic_user_file /etc/nginx/.htpasswd;
    }
}

创建认证用户:

1
htpasswd -c /etc/nginx/.htpasswd admin

。然后用Let’s Encrypt申请SSL证书:

1
certbot --nginx -d monitoring.yourdomain.com

防火墙规则


1
2
3
4
5
6
7
8
9
10
11
12
13
# 只允许本地访问监控端口
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p tcp --dport 9090 -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport 3000 -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport 9093 -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport 9100 -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport 3100 -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport 9323 -s 127.0.0.1 -j ACCEPT
iptables -P INPUT DROP

# 开放Nginx端口
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT

注意:以上规则只是一个基础模板。生产环境建议使用ufw或firewalld进行更精细的管理,并且将规则持久化(

1
apt install iptables-persistent

)。

第八步:常见问题排查

搭建过程中你可能会遇到以下问题,这里列出最常见的解决方案:

  • Grafana显示”Data source not found”:检查Prometheus容器是否正常运行,以及datasources配置文件中URL是否正确(Docker Compose环境中应使用服务名而非IP)。
  • Node Exporter没有数据:确认Prometheus的targets配置中job_name为”node”,且target指向”node-exporter:9100″。在Prometheus UI的Status → Targets页面可查看各target的连接状态。
  • 告警收不到通知:先检查Alertmanager的Status页面确认告警是否已到达。如果已到达但未发送,通常是通知渠道配置问题。微信webhook需要先在企业微信群中添加机器人。
  • 磁盘告警误报:Docker的overlay2文件系统可能导致监控数据不准确,在Node Exporter的启动参数中增加
    1
    --collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)

    过滤掉虚拟文件系统。

  • Loki日志占用磁盘过大:设置日志保留期限,在loki配置文件中添加
    1
    table_manager: retention_deletes_enabled: true; retention_period: 168h

    (保留7天)。

监控效果与运维建议

搭建完成后,这套系统能帮你做到:

  • 实时感知:Grafana仪表盘每秒刷新,VPS状态一目了然
  • 秒级告警:从故障发生到收到微信通知,通常不超过30秒
  • 历史回溯:Prometheus保留30天历史数据,出现问题时可以回看事发前后的指标变化
  • 日志分析:通过Loki在Grafana中直接搜索日志,告别SSH登录后
    1
    grep

    的繁琐操作

  • 容量规划:根据持续监控的趋势数据,提前判断是否需要升级VPS配置

最后给几个生产建议:监控系统本身也要监控——定期检查Prometheus和Alertmanager的运行状态;告警阈值要逐步调优,避免”狼来了”效应;刚开始可以只配置关键告警,逐步增加覆盖范围。毕竟,好的监控系统应该让你忘记它的存在,只在真正需要的时候才提醒你。

服务器监控仪表盘

图:Grafana监控仪表盘示例

服务器运维管理

图:VPS运维管理示意图

【本站文章皆为原创,未经允许不得转载】:汤不热吧 » VPS监控与告警系统搭建完全指南:Prometheus+Grafana+Loki生产级部署实战
分享到: 更多 (0)