master-omnibus 容器同时跑 Web UI、Collector 和 InfluxDB,宿主机只负责暴露设备、端口和持久化目录。192.0.2.10、registry.example.internal 和 /dev/nvme0n1 这类示例值。落地时替换为自己的地址、镜像仓库、设备路径和访问控制策略。0. 结论和使用边界#
这份 runbook 适合单机磁盘健康监控,重点是把 Scrutiny 跑稳,而不是搭一套复杂的可观测平台。
当前部署形态:
| 项 | 配置 / 状态 |
|---|---|
| 服务 | Scrutiny |
| 部署方式 | Docker Compose |
| 部署目录 | /ccache/docker-compose/scrutiny |
| 镜像 | registry.example.internal/ghcr.io/analogj/scrutiny:master-omnibus |
| 容器名 | scrutiny |
| Web 端口 | 18080:8080 |
| 持久化目录 | ./config、./influxdb2 |
| 监控设备 | /dev/nvme0n1 到 /dev/nvme4n1 |
| 巡检状态 | 容器运行 5 周以上,HTTP 返回 200 OK |
主要覆盖四件事:
- 用
master-omnibus单容器部署 Scrutiny。 - 显式映射 NVMe 设备,避免容器扫描到不该采集的磁盘。
- 用最小 capability 替代
privileged: true。 - 给出变更前检查、验证矩阵和回滚步骤。
边界也要先收住:
- Scrutiny 只能提前暴露磁盘健康信号,不能替代 RAID 冗余、备份和容量规划。
- RAID0 场景没有冗余,任何成员盘异常都可能影响整个缓存盘。
- 示例使用浮动标签
master-omnibus,长期运行更建议固定版本或 digest。 - Web UI 暴露在宿主机
18080,上线前需要通过防火墙、反向代理或 ACL 限制访问来源。
1. 为什么要单独部署 Scrutiny#
这台机器上有多块 NVMe 盘,其中 4 块盘组成 /ccache 对应的 RAID0 缓存盘,另 1 块盘承载系统盘。文件系统、RAID 和业务监控能告诉我们“服务是否还能跑”,但不一定能提前看到盘的寿命、温度和错误计数变化。
Scrutiny 补的是这部分信号:
- 定时读取 SMART / NVMe 健康信息。
- 把指标写入内置 InfluxDB。
- 通过 Web UI 展示温度、寿命、错误计数等趋势。
- 在磁盘彻底不可用前,给运维留出观察窗口。
对 RAID0 缓存盘来说,这个窗口很有用。RAID0 没有冗余,监控不能消除风险,但能减少“坏了才开始查”的概率。
2. 部署目录和数据边界#
部署目录保持简单,所有配置和数据都在 Compose 目录下,便于备份和迁移。
/ccache/docker-compose/scrutiny/
├── compose.yaml
├── config/
│ ├── collector.yaml
│ ├── scrutiny.yaml
│ └── scrutiny.db
├── influxdb2/
│ ├── config.yaml
│ ├── influxd.bolt
│ ├── influxd.sqlite
│ └── engine/
└── scrutiny/
└── scrutiny.db
实际生效的持久化目录:
| 宿主机路径 | 容器路径 | 用途 |
|---|---|---|
./config | /opt/scrutiny/config | Scrutiny 配置和 SQLite 元数据 |
./influxdb2 | /opt/scrutiny/influxdb | InfluxDB 数据 |
/run/udev | /run/udev | 只读挂载,帮助容器识别设备信息 |
这里有一个需要后续清理的小问题:历史配置里曾同时出现 ./scrutiny:/opt/scrutiny/config 和 ./config:/opt/scrutiny/config。两个挂载目标相同时,后面的 ./config:/opt/scrutiny/config 会覆盖前面的配置。从当前 Compose 渲染结果看,真正生效的是 ./config。./scrutiny 目录可以先保留,等确认没有历史依赖后再删。
3. Compose 配置#
核心配置如下。它没有使用 privileged: true,只把 Scrutiny 需要的设备和 capability 显式列出来。
services:
scrutiny:
image: registry.example.internal/ghcr.io/analogj/scrutiny:master-omnibus
container_name: scrutiny
restart: always
ports:
- "18080:8080"
volumes:
- ./influxdb2:/opt/scrutiny/influxdb
- /run/udev:/run/udev:ro
- ./config:/opt/scrutiny/config
cap_add:
- SYS_RAWIO
- SYS_ADMIN
devices:
- /dev/nvme0n1
- /dev/nvme1n1
- /dev/nvme2n1
- /dev/nvme3n1
- /dev/nvme4n1
几个关键点:
| 配置 | 作用 | 取舍 |
|---|---|---|
master-omnibus | 单容器运行 Web、Collector 和 InfluxDB | 单机部署简单,后续扩展能力有限 |
18080:8080 | 宿主机 18080 暴露 Web UI | 需要额外限制访问来源 |
/run/udev:/run/udev:ro | 读取设备元数据 | 只读挂载,避免容器改宿主机 udev 状态 |
SYS_RAWIO | 允许 smartctl 读取底层磁盘健康信息 | 需要明确这是硬件访问能力 |
SYS_ADMIN | 兼容部分设备扫描和底层操作需求 | 权限较大,后续可按环境验证是否能去掉 |
devices | 显式暴露目标 NVMe 块设备 | 设备路径变化时必须同步改配置 |
这个配置的重点不是“权限最少”,而是“权限可审查”。相比 privileged: true,显式列 capability 和设备更容易在 review 时看出容器到底能碰什么。
4. Scrutiny 和 Collector 配置#
config/scrutiny.yaml 负责 Web、数据库和 InfluxDB 参数:
version: 1
web:
listen:
port: 8080
host: 0.0.0.0
basepath: ''
database:
location: /opt/scrutiny/config/scrutiny.db
influxdb:
host: 0.0.0.0
port: 8086
retention_policy: true
log:
file: ''
level: INFO
limits:
nvme:
critical: true
standard: true
config/collector.yaml 显式列出 5 块 NVMe:
version: 1
devices:
- device: /dev/nvme0n1
type: nvme
- device: /dev/nvme1n1
type: nvme
- device: /dev/nvme2n1
type: nvme
- device: /dev/nvme3n1
type: nvme
- device: /dev/nvme4n1
type: nvme
显式列设备比自动扫描更适合生产机器。它能避免采集范围失控,也让换盘、新增磁盘这类动作在 Git diff 或变更单里看得见。
缺点也很直接:设备路径变了,要同步改 Compose 的 devices 和 collector.yaml。这一步不能只改一边。
5. Review Gate:启动前先检查什么#
Scrutiny 这类服务真正容易出问题的地方不在 Web UI,而在设备路径、容器权限和持久化目录。启动前先过一遍检查。
| 检查项 | 命令 | 继续条件 | 停止条件 |
|---|---|---|---|
| Docker | docker --version && docker compose version | 命令正常返回 | Docker/Compose 不可用 |
| 设备路径 | ls /dev/nvme*n1 | 目标设备都存在 | 有设备缺失或路径变化 |
| smartctl | smartctl --version | 能正常执行 | 宿主机缺少 smartmontools |
| 端口 | ss -lntup | grep ':18080' | 未被未知进程占用 | 端口冲突 |
| 目录 | test -d config && test -d influxdb2 | 持久化目录存在 | 目录缺失或权限异常 |
| 配置 | docker compose config | 渲染结果符合预期 | volume、device 或端口不符合预期 |
设备缺失时不要直接启动。Compose 里的 devices 指向不存在的设备,常见结果是容器启动失败,或者 Collector 少采一部分数据。
6. 启动和验证#
进入部署目录:
cd /ccache/docker-compose/scrutiny
先看 Compose 渲染结果:
docker compose config
确认端口、volume 和 devices 都符合预期后启动:
docker compose up -d
查看容器状态:
docker compose ps
期望看到:
NAME IMAGE STATUS
scrutiny registry.example.internal/.../scrutiny:omnibus Up
检查端口:
ss -lntup | grep ':18080'
检查 Web:
curl -I http://127.0.0.1:18080
正常结果类似:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
确认设备都在:
for d in /dev/nvme0n1 /dev/nvme1n1 /dev/nvme2n1 /dev/nvme3n1 /dev/nvme4n1; do
test -e "$d" && echo "OK $d" || echo "MISSING $d"
done
看 Collector 日志:
docker logs --tail 200 scrutiny
正常采集时能看到类似日志:
Loading configuration file: /opt/scrutiny/config/collector.yaml
Executing command: smartctl --scan --json
Executing command: smartctl --xall --json --device nvme /dev/nvme0n1
POST /api/devices/register 200
POST /api/device/.../smart 200
Main: Completed
7. 备份、升级和回滚#
变更前先备份配置和 InfluxDB 数据。配置很小,InfluxDB 数据会随采集周期增长,备份耗时和空间要按现场数据量评估。
cd /ccache/docker-compose/scrutiny
tar -czf "/tmp/scrutiny-config-$(date +%Y%m%d%H%M%S).tar.gz" compose.yaml config
tar -czf "/tmp/scrutiny-influxdb2-$(date +%Y%m%d%H%M%S).tar.gz" influxdb2
升级镜像:
docker compose pull
docker compose up -d
docker compose ps
回滚配置:
cd /ccache/docker-compose/scrutiny
tar -xzf /tmp/scrutiny-config-YYYYMMDDHHMMSS.tar.gz -C .
docker compose up -d
如果是镜像升级失败,优先回退到升级前记录的镜像 tag 或 digest。当前示例使用 master-omnibus,它不是固定版本标签。长期运行时建议改成明确版本或 digest,减少下一次拉取的不确定性。
8. 验证矩阵#
下面这张表适合放到变更单里。每一项都能对应到一条命令和一个通过标准。
| 验证项 | 命令 | 通过标准 |
|---|---|---|
| Compose 渲染 | docker compose config | 无报错,挂载和设备符合预期 |
| 容器状态 | docker compose ps | scrutiny 为 Up |
| Web 服务 | curl -I http://127.0.0.1:18080 | 返回 200 OK |
| 端口监听 | ss -lntup | grep ':18080' | 存在 Docker proxy 或监听记录 |
| 设备存在 | test -e /dev/nvme0n1 等 | 所有配置设备都存在 |
| Collector | docker logs --tail 200 scrutiny | 出现 Main: Completed 和 API 200 |
| 数据目录 | du -sh config influxdb2 | 目录存在且持续可写 |
9. 常见问题#
9.1 为什么需要 SYS_RAWIO#
Scrutiny 通过 smartctl 读取底层硬盘健康信息。普通容器权限通常不够,SYS_RAWIO 用来允许这类底层设备访问。
9.2 为什么要挂载 /run/udev#
/run/udev 提供设备元数据。只读挂载可以满足识别需求,同时避免容器修改宿主机 udev 状态。
9.3 为什么显式列设备,而不是自动扫描#
显式列设备更适合生产机器。它能避免容器扫描到不该采集的设备,也让变更审查更直接:新增或替换磁盘时,Compose 和 collector.yaml 都会出现明确 diff。
9.4 页面能打开,但没有新数据#
按顺序检查:
docker logs --tail 200 scrutiny是否有 Collector 执行日志。collector.yaml中的设备路径是否存在。- Compose
devices是否包含同一批设备。 - 容器是否有
SYS_RAWIO和必要设备权限。 influxdb2目录是否可写。
9.5 能不能直接使用 privileged: true#
能跑不代表适合长期放在生产机器上。privileged: true 会显著扩大容器权限面,排障时也很难说明容器到底依赖哪些能力。优先使用显式 devices 和必要 capability,除非已经证明当前内核、设备或驱动组合必须依赖更大的权限。
10. 遗留项#
| 项 | 当前状态 | 建议 |
|---|---|---|
| 重复挂载 | 历史上 ./scrutiny 和 ./config 都指向 /opt/scrutiny/config,实际只使用 ./config | 确认无历史依赖后删除 ./scrutiny 目录和旧挂载 |
| 镜像版本 | 使用 master-omnibus | 固定到明确版本或 digest |
| 通知告警 | notify 未启用 | 需要告警时配置 SMTP、Webhook 或其他通知渠道 |
| 访问控制 | Web 端口监听 0.0.0.0:18080 | 通过防火墙、反向代理或内网 ACL 限制访问来源 |
| RAID0 风险 | /ccache 位于 RAID0 | 监控只能提前发现风险,不能替代备份和冗余 |
11. 可复用原则#
Scrutiny 本身不难部署,容易踩坑的是周边边界:
- 磁盘更换后设备路径变化,Compose 和 Collector 配置没有同步。
- 为了省事使用
privileged: true,容器权限面被放大。 - 用浮动镜像标签升级,下一次拉取行为不可预测。
- Web UI 能打开就以为采集正常,但 Collector 没有真正写入新数据。
把设备路径、容器权限和持久化目录写清楚,Scrutiny 就可以作为一个轻量、可复现的磁盘健康监控入口。
12. 参考#
- Scrutiny 项目:
https://github.com/AnalogJ/scrutiny - Scrutiny 示例配置:
https://github.com/AnalogJ/scrutiny/blob/master/example.scrutiny.yaml - Collector 示例配置:
https://github.com/AnalogJ/scrutiny/blob/master/example.collector.yaml
