跳过正文
  1. 博客文章/

用 Docker Compose 部署 Scrutiny:NVMe 健康监控的最小可用方案

·772 字·4 分钟·
DevOps SRE Scrutiny Docker-Compose NVMe SMART DevOps SRE
Zayn
作者
Zayn
专注 Kubernetes、CI/CD、可观测性等云原生技术栈,记录生产环境中的实战经验与踩坑复盘。
目录
生产变更复盘 - 这篇文章属于一个选集。
5: 本文
这台 Docker 主机用 Scrutiny 监控 5 块 NVMe 盘。部署方式很轻:一个 master-omnibus 容器同时跑 Web UI、Collector 和 InfluxDB,宿主机只负责暴露设备、端口和持久化目录。
文中使用 192.0.2.10registry.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/configScrutiny 配置和 SQLite 元数据
./influxdb2/opt/scrutiny/influxdbInfluxDB 数据
/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 的 devicescollector.yaml。这一步不能只改一边。

5. Review Gate:启动前先检查什么
#

Scrutiny 这类服务真正容易出问题的地方不在 Web UI,而在设备路径、容器权限和持久化目录。启动前先过一遍检查。

检查项命令继续条件停止条件
Dockerdocker --version && docker compose version命令正常返回Docker/Compose 不可用
设备路径ls /dev/nvme*n1目标设备都存在有设备缺失或路径变化
smartctlsmartctl --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 psscrutinyUp
Web 服务curl -I http://127.0.0.1:18080返回 200 OK
端口监听ss -lntup | grep ':18080'存在 Docker proxy 或监听记录
设备存在test -e /dev/nvme0n1所有配置设备都存在
Collectordocker 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 页面能打开,但没有新数据
#

按顺序检查:

  1. docker logs --tail 200 scrutiny 是否有 Collector 执行日志。
  2. collector.yaml 中的设备路径是否存在。
  3. Compose devices 是否包含同一批设备。
  4. 容器是否有 SYS_RAWIO 和必要设备权限。
  5. 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
生产变更复盘 - 这篇文章属于一个选集。
5: 本文

相关文章

华为 UPS2000 接入 NUT Server:编译 huawei-ups2000 驱动并完成联动
··2156 字·11 分钟
SRE NUT UPS Linux SRE +4
生产级 DNS 集群从零部署:dnsdist 与 Technitium 三节点架构
·2755 字·13 分钟
SRE DNS DDNS SRE DevOps +4
生产 DNS 集群平滑升级复盘:dnsdist 与 Technitium 的滚动变更
·989 字·5 分钟
SRE DNS SRE DevOps Dnsdist +3