部署配方 · 监控
Prometheus:把数字留到足够有用为止
Uptime Kuma 告诉你某个服务挂了。Prometheus 告诉你这个服务从三月起就在漏内存——后者是更有用的一句话。它是这台机架上最重的单个服务,760MB,也是保留策略决定它到底是留在一 GB 以内、还是把固态盘吃掉的那一个。
| 容器镜像 | prom/prometheus:v3.1.0 |
|---|---|
| 宿主端口 | :9090/tcp |
| 数据目录 | /srv/homelab/prometheus/data |
| 内存预留 | 760 MB |
| CPU 上限 | 0.50 vCPU (cpus: "0.50") |
| 更新节奏 | twice a year on the LTS releases. The storage format is not backward compatible across major versions, so a downgrade means losing the history |
| ARM 兼容 | arm64 native; the ingestion path is single-threaded per target and the Pi handles a few hundred series without trouble |
| 宿主 | 容器 | proto | 暴露 | 用途 |
|---|---|---|---|---|
| :9090 | 9090 | tcp | 仅局域网 | query UI and the target health page, mostly used to check what stopped being scraped |
部署步骤
按正确属主建好配置和数据目录
Prometheus 在容器里以 uid 1000 运行,数据目录不可写就不会启动。配置目录可以保持 root 所有,只要可读即可。
run sudo mkdir -p /srv/homelab/prometheus/{config/rules,data} sudo chown -R 1000:1000 /srv/homelab/prometheus/data sudo chmod -R a+rX /srv/homelab/prometheus/config写抓取配置,并带上外部标签
外部标签是以后区分这个 Prometheus 和另一个的方式,也是远程写入目标用来路由数据的依据。即使现在只有一个实例,也先设好。
run cat > /srv/homelab/prometheus/config/prometheus.yml <<'EOF' global: scrape_interval: 30s evaluation_interval: 30s external_labels: rack: home tls_config: insecure_skip_verify: false scrape_configs: - job_name: prometheus static_configs: [{targets: ['localhost:9090']}] - job_name: node static_configs: [{targets: ['node-exporter:9100']}] - job_name: cadvisor static_configs: [{targets: ['cadvisor:8080']}] rule_files: - /etc/prometheus/rules/*.yml EOF启动并确认每个目标都是 UP
报 DOWN 且是 context deadline 的,是网络问题;报 DOWN 且是 404 的,是路径问题。改配置之前先看清是哪一种。
run cd /srv/homelab/prometheus && docker compose up -d docker logs --tail 30 prometheus # 打开 http://192.168.10.20:9090/targets 确认三个都是 UP加上记录规则,并且查一次验证
从没被查过的记录规则,就是一条你不知道能不能用的规则。等一分钟后在查询框里输入记录出来的序列名。
run docker compose restart prometheus # 查询:instance:node_cpu:rate5m # 一个评估周期内图上就应该有线让 Grafana 指向它,并设好保留上限
数据源已经在 Grafana 的配置里备好了;确认连接测试通过,然后观察一周磁盘占用,再决定十五天是否负担得起。
run docker exec grafana wget -qO- http://prometheus:9090/-/ready docker exec prometheus du -sh /prometheus
保留策略才是关键设置
Prometheus 压缩率不错——大约每个样本 1.7 字节——但四十个容器、每个容器一百五十条序列,每天会产生约两百万个样本。十五天大约占 250MB 磁盘,所以这里把保留期显式设成十五天,而不是留给「永久」这个默认值。
除了时间上限还设了大小上限。时间上限是你画图想要的;大小上限则是给「某天某个容器突然导出十倍序列」这种情况准备的安全带。
# 看实际占用,而不是靠猜
docker exec prometheus du -sh /prometheus
curl -s localhost:9090/api/v1/status/tsdb | python3 -c 'import json,sys; d=json.load(sys.stdin)["data"]; print(d["headStats"])'记录规则把慢查询变成快查询
每一个对十五天窗口跑 rate() 的面板,都会在每次打开页面时重新计算。记录规则把这些预先算成新序列,这正是树莓派能把一块仪表盘在一秒内画出来的原因。
四条记录规则覆盖了这里全部三块仪表盘:每实例五分钟 CPU 速率、内存占用比例、五分钟容器内存、以及磁盘使用比例。其它一切按需查询。
# /srv/homelab/prometheus/config/rules/recording.yml
groups:
- name: rack-recording
interval: 30s
rules:
- record: instance:node_cpu:rate5m
expr: 1 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m]))
- record: instance:node_memory:used_fraction
expr: 1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes
- record: container:memory:rate5m
expr: sum by (name) (rate(container_memory_working_set_bytes[5m]))该对什么告警,该放过什么
三条告警规则,而且全部是慢性的,而不是瞬时阈值。磁盘超过百分之八十持续两小时。根据六小时线性趋势预测七天内写满。以及一小时内重启超过五次的容器。
刻意不告警的:CPU 尖峰、温度超过七十、服务短暂不可达。这些会产生那种让人学会忽略通知的通知。温度是仪表盘上的一块面板加一个人,而不是一条规则加一部手机。
# /srv/homelab/prometheus/config/rules/alerts.yml
groups:
- name: rack-alerts
rules:
- alert: DiskFillingUp
expr: predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 7*86400) < 0
for: 2h
- alert: ContainerRestartLoop
expr: increase(container_start_time_seconds[1h]) > 5
for: 10m导出器,以及该小心哪一个
node exporter 以 pid: host 运行、并把根文件系统只读挂在 /host,这正是让宿主机指标(含温度区)可用的原因。这是标准做法,同时也意味着对宿主机文件系统的一次宽泛读取——这是在容器平台上获取宿主机指标所要付出的代价。
需要留意的是 cAdvisor:它以特权运行,并读取 Docker 套接字目录来枚举容器,这实际上等于对守护进程所知的一切拥有读权限。它绑定在局域网接口上,绝不发布到别处。
compose 文件
整份文件放在 /srv/homelab/prometheus/compose.yaml。标签固定版本号,不用 latest——树莓派上回滚比升级麻烦得多。
services:
prometheus:
image: prom/prometheus:v3.1.0
container_name: prometheus
restart: unless-stopped
ports:
- "9090:9090/tcp"
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
- --storage.tsdb.retention.time=15d
- --storage.tsdb.retention.size=4GB
- --web.enable-lifecycle=false
- --web.enable-admin-api=false
- --web.listen-address=0.0.0.0:9090
volumes:
- /srv/homelab/prometheus/config:/etc/prometheus
- /srv/homelab/prometheus/data:/prometheus
user: "1000:1000"
networks: [rack]
node-exporter:
image: prom/node-exporter:v1.8.2
container_name: node-exporter
restart: unless-stopped
pid: host
volumes:
- /:/host:ro,rslave
command:
- --path.rootfs=/host
- --collector.thermal_zone
- --collector.hwmon
networks: [rack]
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.50.0
container_name: cadvisor
restart: unless-stopped
privileged: true
devices:
- /dev/kmsg
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
networks: [rack]
networks:
rack:
external: true加固清单
- 查询界面只在局域网内;它能在机架上对任何指标执行任意查询,绝不该出现在公网上
- 关闭管理 API 和生命周期端点,外部没有任何 HTTP 调用能删除时序或触发重载
- 在导出器支持的情况下,抓取目标使用只读凭据;导出器绑定在局域网接口而不是 0.0.0.0
- 容器指标来自绑定在局域网上的 cAdvisor,它是机架上攻击面最大的导出器
备份方案
时序数据库被当作可丢弃的东西:丢了它丢的是图表,不是采集能力。真正持久的是配置、告警规则和记录规则,它们在 git 里。唯一值得复制的是告警状态文件,而那也只是图个方便,不算数据。
上线后验证
- 目标页面里每个 job 都是 UP,抓取耗时远小于三十秒的间隔
- 按名字查询某条记录规则序列能返回数据
- 跑一周后磁盘占用低于 300MB,说明保留设置起作用了
- 停掉某个容器后,两个抓取周期内容器内存面板上出现可见的空隙
踩过的坑
- 存储格式在大版本之间不向后兼容。一旦跑了一个月,降级就意味着删掉数据目录。锁死版本,大版本升级前先读发布说明。
- 抓取间隔设成 15 秒,序列数和磁盘占用相比 30 秒翻倍,而对家庭机架来说多出来的分辨率什么都换不来。保持 30 秒。
- 能读 Docker 套接字的 cAdvisor,能看到每个容器的环境变量,包括 compose 文件里的密码。它待在局域网里,且不挂任何代理规则。
- 从没被查询过的记录规则,依然会在每次评估时消耗 CPU。本机架上每一条规则的存在都是因为某块面板要用它,这里没有推测性的规则编写。
硬件与选型问答
- 用 Prometheus 还是 VictoriaMetrics 还是 InfluxDB?
- 如果生态里的导出器和 Grafana 仪表盘比长期保留更重要,就用 Prometheus。如果想要在同一套硬件上保留几年,用 VictoriaMetrics——它压缩更好、查询更快,而且能直接吃 Prometheus 的抓取配置。对保留十五天的家庭机架来说,这点差别不值得做一次迁移。
- 树莓派能扛多少个目标?
- Pi 4 能扛几十万条活跃序列;按四十个容器、每个一百五十条算大约是六千条——还有两个数量级的余量。你会先撞上的限制是固态盘的写入寿命,不是 CPU。
- 已经有 Grafana 和 Uptime Kuma 了,还需要这个吗?
- 如果只想知道服务在不在,不需要。如果想知道内存占用是不是在往上走,那就需要——而且在机架上没有别的办法回答这个问题:Uptime Kuma 不存指标,而 Grafana 没有数据源就没东西可画。