如果你还不清楚 Prometheus 和 Grafana 到底是什么,可以先读我们这篇文章: Prometheus 与 Grafana 对比。如果你已经清楚两者的区别,下面就是把它们一起跑起来的方法。
本文是配套的实战篇。它用 Docker Compose 在单台 VPS 上部署 Prometheus、Grafana 和 Node Exporter,用 Caddy 为 Grafana 加上 HTTPS,导入 Node Exporter Full 仪表板(ID 1860),并演示如何通过 WireGuard 抓取第二台 VPS 的指标。
最初的测试在 2026 年 4 月进行,环境是 Ubuntu 24.04 LTS,搭配 Docker Engine 27.x 和 Docker Compose v2.30。下面配置中锁定的版本此后已更新为当前受支持的版本。在那次五台主机的测试中,中心节点空闲时约占用 300 MB 内存,持续抓取时为 530 到 650 MB。
TL;DR(太长不看版)
- 中心 VPS 用一个 Compose 栈运行 Prometheus、Grafana 和 Node Exporter,Caddy 则直接装在宿主机上作为反向代理。
- Prometheus 和 Grafana 只监听 127.0.0.1,公网访问一律经过启用自动 HTTPS 的 Caddy。
- 要监控更多服务器,只需在每台上安装 Node Exporter,并把对应条目追加到
prometheus.yml. - 中心与各节点之间推荐走 WireGuard。如果你的服务器本来就在同一个私有网络里,直接用私有网络也很合适。
- 下面这次五台主机的测试用 2 GB 的 VPS 就够了,但请把它当作参考基线,而不是按主机数量套用的固定规则。
你将搭建什么
下面是完成之后整套架构的大致样子。
+-------------------+
| Your laptop |
+---------+---------+
| HTTPS
v
+--------------------------+----------------------------+
| MONITORING HUB VPS (2 GB RAM starting point) |
| Caddy (reverse proxy, auto HTTPS) |
| Grafana (port 3000, only via Caddy) |
| Prometheus (port 9090, internal only) |
| Node Exporter (port 9100, scraped on localhost) |
+----+---------------------------------+----------------+
| |
| scrape over WireGuard | scrape over WG
v v
+----+--------+ +-----+-------+
| App VPS 1 | | App VPS 2 |
| Node Expo. | | Node Expo. |
+-------------+ +-------------+
整套监控栈只放在一台中心 VPS 上。其他每台需要监控的 VPS 只跑 Node Exporter。中心上的 Prometheus 从各台拉取指标,Grafana 负责展示,Caddy 负责 HTTPS。
你需要的一切
要照搬这套配置,你需要一台运行 Ubuntu 的 VPS 作中心节点、一个域名,以及 sudo 权限。
- 一台内存至少 2 GB、运行 Ubuntu 24.04 LTS 的 VPS。
- 一个域名,并把 A 记录指向 VPS 的公网 IP(例如 grafana.example.com)。启用 HTTPS 必须要有。
- 通过 SSH 获得 root 或 sudo 权限。
本文中所有出现的 grafana.example.com,都请替换成你实际指向监控 VPS 的那个子域名。Grafana 环境变量、DNS 检查和 Caddyfile 里要用同一个域名。
如果你只想监控一台服务器、暂时也不需要 HTTPS,那就可以跳过反向代理那一节,直接在现有 VPS 上跑这套 Compose 栈。其余内容依然适用。
立即启动拥有 root 权限和 NVMe 存储的 Ubuntu VPS。
部署 Ubuntu VPS第 1 步:服务器准备
用具备 sudo 权限的用户 SSH 登录中心 VPS,先做一次系统更新。
sudo apt update && sudo apt upgrade -y
从 Docker 官方仓库安装 Docker Engine 和 Compose 插件。
# Install prerequisites
sudo apt install -y ca-certificates curl gnupg lsb-release
# Add Docker's official GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add the Docker repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
确认两者都已安装。
docker --version
docker compose version
两条命令都应能正常输出版本号。Docker Engine 和 Compose 的具体版本,取决于你安装时官方仓库提供的是哪一版。
sudo usermod -aG docker $USER
继续之前请先注销再重新登录,让新的用户组成员身份生效。docker 组实际上等同于 root 权限,因此只把可信的管理员加进去。
配置 UFW,在监控中心上放行 SSH、HTTP 和 HTTPS。80 端口负责 HTTP 到 HTTPS 的跳转和 ACME 验证,Grafana 本身依旧待在 Caddy 后面。
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status
不要把 3000、9090、9100 端口暴露到公网。这些监控服务只监听 127.0.0.1,是有原因的。
第 2 步:Compose 栈
为这套栈和 Prometheus 配置文件创建一个目录。
mkdir -p ~/monitoring/prometheus
cd ~/monitoring
编写 Compose 文件。
# ~/monitoring/docker-compose.yml
services:
prometheus:
image: prom/prometheus:v3.13.2
container_name: prometheus
restart: unless-stopped
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=15d'
- '--web.enable-lifecycle'
- '--web.listen-address=127.0.0.1:9090'
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
network_mode: host
node-exporter:
image: prom/node-exporter:v1.12.1
container_name: node-exporter
restart: unless-stopped
pid: host
network_mode: host
command:
- '--path.rootfs=/host'
- '--web.listen-address=127.0.0.1:9100'
- '--collector.filesystem.mount-points-exclude=^/(dev|proc|sys|var/lib/docker/.+|var/lib/kubelet/.+)($$|/)'
volumes:
- '/:/host:ro,rslave'
grafana:
image: grafana/grafana:13.1.3
container_name: grafana
restart: unless-stopped
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}
- GF_USERS_ALLOW_SIGN_UP=false
- GF_SERVER_ROOT_URL=https://grafana.example.com
- GF_SERVER_HTTP_ADDR=127.0.0.1
volumes:
- grafana_data:/var/lib/grafana
network_mode: host
depends_on:
- prometheus
volumes:
prometheus_data:
grafana_data:
接着编写 Prometheus 的配置文件。
# ~/monitoring/prometheus/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
monitor: 'monitoring-hub'
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['127.0.0.1:9090']
- job_name: 'node'
static_configs:
- targets: ['127.0.0.1:9100']
labels:
host: 'monitoring-hub'
创建一个 .env 文件存放 Grafana 管理员密码。密码要够强,并且不要把这个文件提交到 git。
cat > ~/monitoring/.env <<'EOF'
GRAFANA_ADMIN_PASSWORD='replace-with-a-strong-password'
EOF
chmod 600 ~/monitoring/.env
上面有几个参数值得各说一句。
--web.listen-address=127.0.0.1:9090让 Prometheus 只监听宿主机的回环接口。这样 9090 端口就不会暴露在公网上,同时 Grafana 和本地管理操作仍然访问得到。--web.enable-lifecycle会启用POST /-/reload,这样修改 Prometheus 配置后无需重启容器即可生效。pid: host,network_mode: host、宿主机根目录的 bind 挂载,以及--path.rootfs=/host让容器里的 Node Exporter 拿到它需要的宿主机上下文,而不是只监控自己所在的容器环境。GF_USERS_ALLOW_SIGN_UP=false可以阻止访客自行注册 Grafana 账号,但这并不等于把 Grafana 变成私有的,登录页面通过 Caddy 依然是公开可访问的。
小贴士: 如果你不想手动装,Cloudzy 提供一键部署的 Grafana 和 Prometheus 。其中 Prometheus 的部署还可以顺带装上 Node Exporter。
第 3 步:首次启动与验证
把这套栈跑起来。
cd ~/monitoring
docker compose up -d
等几秒钟,然后检查三个容器是否都在运行。
docker compose ps
三个服务都应显示为运行中。
从你的笔记本开一条 SSH 隧道,去查看 Prometheus 的 targets 页面。
ssh -L 9090:localhost:9090 your-user@your-vps-ip
然后在浏览器里打开 http://localhost:9090/targets。你应该能看到两个目标,状态都是 UP:
prometheus UP http://127.0.0.1:9090/metrics
node UP http://127.0.0.1:9100/metrics
如果有任何一个是 DOWN,先跳到「常见问题」一节。两个都变成 UP 之前不要继续往下做。
检查完目标后就把隧道关掉。从 VPS 外部看,Prometheus 依然只能通过这条 SSH 隧道访问。下一步我们把 Grafana 通过 HTTPS 暴露出去。
第 4 步:用 Caddy 做带 HTTPS 的反向代理
Caddy 只是一个二进制文件,并且会自动处理 HTTPS。对于单站点的反向代理,它的配置比等效的 Nginx 配置块更短。
从官方仓库安装 Caddy。
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | \
sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | \
sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg
sudo chmod o+r /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install -y caddy
进行下一步之前,先确认 grafana.example.com 的 DNS A 记录指向了 VPS 的公网 IP。否则 ACME 验证会失败。
dig +short grafana.example.com
# Should print the VPS public IP
编辑 Caddyfile。
# /etc/caddy/Caddyfile
grafana.example.com {
reverse_proxy 127.0.0.1:3000
encode gzip
}
重新加载 Caddy。
sudo systemctl reload caddy
只要域名解析到你的服务器、80 和 443 端口可达,Caddy 就会通过 ACME 自动申请并续期公开受信任的 TLS 证书。在浏览器里访问 https://grafana.example.com,你应该能在有效的 HTTPS 连接上看到 Grafana 登录页。用 admin 和 .env 文件里的密码登录即可。
第 5 步:把 Prometheus 添加为数据源并导入 1860 号仪表板
在 Grafana 界面中,依次进入 连接 > 数据源 > 添加数据源 ,然后选择 Prometheus.
把 URL 设置为:
http://127.0.0.1:9090
在这套配置里,Grafana 和 Prometheus 共用宿主机网络,而 Prometheus 只监听回环地址。点击 Save & test ,确认 Grafana 能够查询 Prometheus API。你应该会看到「Successfully queried the Prometheus API」。
现在导入仪表板。Node Exporter Full(rfmoz 制作,ID 1860)是社区里非常常用的 Node Exporter 指标仪表板,覆盖 CPU、内存、磁盘 I/O、网络、文件描述符,以及主机有暴露时的硬件温度。它还带有 job 和 instance 变量,因此不用改动就能直接用于中心辐射式架构。
前往 仪表板 > 新 > 导入仪表板,填入仪表板 ID 1860,选择你的 Prometheus 数据源,然后导入。
只要 Node Exporter 目标处于 UP 状态,仪表板就应该开始有数据。1860 号仪表板的部分面板还会用到可选的 systemd 和 processes 采集器的指标,所以个别面板空白,并不一定说明目标出了问题。
第 6 步:加入第二台 VPS(中心辐射式)
启动 Node Exporter 之前,先确认你打算绑定的那个私有 IP 在第二台 VPS 上确实已经存在。如果用的是 WireGuard,请先把 WireGuard 配好。
在第二台 VPS 上,按第 1 步中安装 Docker 的部分把 Docker 装好,但不要仅仅为了监控就开放 80 或 443 端口。然后只运行 Node Exporter。
mkdir -p ~/node-exporter
cd ~/node-exporter
cat > docker-compose.yml <<'EOF'
services:
node-exporter:
image: prom/node-exporter:v1.12.1
container_name: node-exporter
restart: unless-stopped
pid: host
command:
- '--path.rootfs=/host'
- '--web.listen-address=10.10.0.2:9100' # bind to the private monitoring IP, never 0.0.0.0
volumes:
- '/:/host:ro,rslave'
network_mode: host
EOF
docker compose up -d
把 10.10.0.2 换成这台服务器用于监控的私有 IP。用 WireGuard 就填它的 WireGuard 地址;用 Cloudzy 私有网络就填该 VPS 私有网卡的地址。如果这台 VPS 上已经启用了 UFW,只放行监控中心访问 Node Exporter 即可。比如中心的私有 IP 是 10.10.0.1:
sudo ufw allow proto tcp from 10.10.0.1 to any port 9100
sudo ufw status
把 10.10.0.1 换成中心节点真实的私有 IP。即便 UFW 没启用,显式指定的 --web.listen-address 依然能让 Node Exporter 不出现在公网接口上,但其他能进入那个私有网络的机器同样可以访问 9100 端口。
中心与各节点之间的网络路径,有两个不错的选择:
- WireGuard 网络。 让每台 VPS 都加入同一个 WireGuard 网络,Prometheus 直接抓取私有 IP。初次配好 WireGuard 之后,这是最安全的方案。我们提供 WireGuard 一键部署,另外还有一篇 现成的配置教程 。
- Cloudzy 私有网络。 同一区域内的 Cloudzy VPS 实例都会分配一个用于东西向流量的私有网卡,因此你可以直接抓取该地址,而不必另外搭一条 WireGuard 隧道。
等 Node Exporter 在第二台 VPS 上跑起来、并且能通过私有 IP 访问之后,把 prometheus.yml 中心节点上的这个文件里原有的 node job,替换成下面这段。
# Update the existing 'node' job in ~/monitoring/prometheus/prometheus.yml
- job_name: 'node'
static_configs:
- targets: ['127.0.0.1:9100']
labels:
host: 'monitoring-hub'
- targets: ['10.10.0.2:9100']
labels:
host: 'app-vps-1'
tier: 'production'
在不重启容器的情况下重新加载 Prometheus。
curl -X POST http://localhost:9090/-/reload
重新打开第 3 步里的 SSH 隧道,然后访问 http://localhost:9090/targets。新目标应该显示为 UP。打开 Node Exporter Full 仪表板,把顶部的 instance 变量切换到新服务器,就能看到它的图表了。
资源占用与何时升级
这些数字来自 2026 年 4 月的最初测试,环境是一台 2 GB 的 Ubuntu VPS,监控五台主机。请把它们当作这类负载的参考基线,而不是对更新版本的固定配置保证。
| 组件 | 空闲内存 | 工作时内存(1 台主机) | 5 台主机时内存 | 磁盘(保留 15 天,5 台主机) |
|---|---|---|---|---|
| Prometheus | ~100 MB | ~150 MB | ~250-350 MB | ~500 MB 到 1.5 GB |
| Grafana | ~150 MB | ~180 MB | ~200 MB | ~50 MB |
| Node Exporter | ~15 MB | ~20 MB | 不适用 | 可忽略不计 |
| Caddy | 约 30 MB | ~40 MB | ~40 MB | 不适用 |
| 中心节点合计 | ~300 MB | ~390 MB | ~530-650 MB | 工作集约 1.5 GB |
当中心节点的内存或磁盘余量开始吃紧时就该升级了。单看主机数量并不是好的判断依据,因为序列基数、启用的采集器、抓取间隔和保留时长都会改变实际占用。若需要更长期的集中存储,Prometheus 支持远程存储集成,VictoriaMetrics 就是一个兼容 Prometheus 的选择。
常见问题
最常出现的七个问题及其解决办法。
- Prometheus 可以从公网访问。 直接写成 9090:9090 的端口映射,会把 Prometheus 暴露给全世界。解决办法:在 Prometheus 的 command 中保留上面那样的
--web.listen-address=127.0.0.1:9090参数。这样 Prometheus 就只监听宿主机的回环接口。 - Grafana 数据源连不上 Prometheus。 这套栈用的是宿主机网络,所以 Grafana 可以通过 http://127.0.0.1:9090 访问 Prometheus。请检查 Prometheus 是否还在运行、是否仍在回环地址上监听。
- Node Exporter 仪表板显示「No data」。 通常有三种原因。(a) Prometheus 根本没在抓取该目标,去看看
/targets。(b) 仪表板变量里的 job 标签,和抓取配置中的job_name对不上。(c) 防火墙挡住了中心与目标之间的 9100 端口。 - 生产环境里 Grafana 管理员密码还是
admin/admin。 设置GF_SECURITY_ADMIN_PASSWORD。解决办法:在首次启动前,通过 .env 文件设置 GF_SECURITY_ADMIN_PASSWORD。如果已经漏掉了,就在第一次登录时改掉密码,并关闭自助注册。 - Caddy 报「ACME challenge failed」。 要么 DNS 的 A 记录还没生效,要么 80 端口被挡住了。执行
ufw allow 80,443/tcp,等待 DNS 生效,再执行sudo systemctl reload caddy。可以用dig +short来确认解析是否已经生效。 - Prometheus 的磁盘被写满。 高基数的标签,或者主机很多时抓取间隔又太短,都会很快把卷写满。注意观察
prometheus_tsdb_head_series以及卷的大小。缓解办法:关掉用不到的 Node Exporter 采集器,把scrape_interval调到 30s,或者缩短保留时长。 - bind 挂载出现权限错误。 如果你挂载的是宿主机目录而不是具名卷,那么容器的 UID(Prometheus 是 65534,Grafana 是 472)必须有写权限。像上面 Compose 文件里那样使用具名卷,就不会遇到这个问题。
什么情况下这套栈是杀鸡用牛刀
如果你只是想在某个 URL 无响应时收到告警,这套栈就有点杀鸡用牛刀了。 Uptime Kuma 要轻量得多,如果你只需要基本的可用性检查,用它就够了。
常见问题
Prometheus 和 Grafana 有什么区别?
Prometheus 是一个时序数据库:它按你设定的间隔从配置好的目标抓取指标,写入磁盘,并响应 PromQL 查询。Grafana 则是可视化层,连接 Prometheus(以及其他许多数据源)并渲染仪表板。绝大多数情况下两者都需要:Prometheus 负责采集和存储,Grafana 负责展示。
Prometheus + Grafana 这套需要多少内存?
在单台 VPS 上同时运行 Prometheus、Grafana、Node Exporter 和一个反向代理的监控中心,空闲时约占用 300 MB 内存;以 15 秒间隔持续抓取五台主机时为 530 到 650 MB。
能用一个 Grafana 实例监控多台服务器吗?
可以。标准做法就是中心辐射式:中心 VPS 上的一个 Prometheus 实例,去抓取你想监控的每台其他服务器上运行的 Node Exporter;中心上的 Grafana 只查询这一个 Prometheus。Node Exporter Full 仪表板(ID 1860)支持 instance 变量,所以在同一个仪表板里就能切换查看不同服务器。
Node Exporter 用哪个 Grafana 仪表板最好?
对这套配置来说,Node Exporter Full(rfmoz 制作,仪表板 ID 1860)是一个很稳妥的默认选择。它覆盖了 Node Exporter 的主要主机指标,并支持 job 和 instance 变量以便监控多台服务器。部分面板依赖 Node Exporter 的可选采集器,因此个别面板空白,并不一定意味着抓取目标出了问题。
如何通过 HTTPS 暴露 Grafana?
让 Grafana 绑定在 127.0.0.1:3000 上运行,再在它前面放一个负责 HTTPS 的反向代理。最简单的选择是 Caddy:一个四行的 Caddyfile,里面写上 reverse_proxy 127.0.0.1:3000 和一个域名区块,就能把包括自动管理 TLS 证书在内的所有事情都办好。
Prometheus + Grafana 商用是免费的吗?
Prometheus 采用 Apache 2.0 许可证,Grafana OSS 采用 AGPLv3。内部使用和商业使用未经修改的 Grafana OSS 是允许的,但如果你修改、分发它,或者通过网络提供修改后的版本,就可能触发 AGPL 下的源码共享义务。Grafana Enterprise 和 Grafana Cloud 则适用另外的商业条款。
什么时候该从 Prometheus 换成 VictoriaMetrics?
当更长的保留时间、很高的序列基数,或者要跑多个 Prometheus 实例,使得在服务器的内存和磁盘预算内维护本地 Prometheus TSDB 变得吃力时,就可以考虑 VictoriaMetrics 了。它能接收 Prometheus 的数据,并提供兼容 Prometheus 的查询 API,因此既可以充当长期存储,也可以作为 Grafana 的指标后端。请拿你自己的实际负载去实测,而不是卡在某个固定的内存阈值上换。
