为什么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<YOUR_BOT_TOKEN>/sendMessage'
http_config:
headers:
Content-Type: 'application/json'
url_param: "chat_id=<CHAT_ID>&parse_mode=Markdown&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配置文件中添加
1table_manager: retention_deletes_enabled: true; retention_period: 168h
(保留7天)。
监控效果与运维建议
搭建完成后,这套系统能帮你做到:
- 实时感知:Grafana仪表盘每秒刷新,VPS状态一目了然
- 秒级告警:从故障发生到收到微信通知,通常不超过30秒
- 历史回溯:Prometheus保留30天历史数据,出现问题时可以回看事发前后的指标变化
- 日志分析:通过Loki在Grafana中直接搜索日志,告别SSH登录后
1grep
的繁琐操作
- 容量规划:根据持续监控的趋势数据,提前判断是否需要升级VPS配置
最后给几个生产建议:监控系统本身也要监控——定期检查Prometheus和Alertmanager的运行状态;告警阈值要逐步调优,避免”狼来了”效应;刚开始可以只配置关键告警,逐步增加覆盖范围。毕竟,好的监控系统应该让你忘记它的存在,只在真正需要的时候才提醒你。

图:Grafana监控仪表盘示例

图:VPS运维管理示意图
汤不热吧