TrueNAS 监测:数据库焕新

阅前注意

TrueNAS 系列文章涉及可能影响生产数据的危险操作,仅供测试参考,切勿模仿。 ⚠️

丢掉臃肿的 Influx,迎接 AI 时代可观测性平台的基石——Victoria Metrics。原因很简单: 性能更佳,资源使用更少,查询语句更简洁(看着 SQL 我真的会当场宕机)。我还发现获取硬盘温度无需再依赖 TrueNAS 的报告导出接口了。好!

改造过程的关键点,是学习 PromQL(虽然 V Metrics 有自己的 MetricsQL,但这实际上是 PromQL 的超集)。很幸运的是,V Metrics 为 Grafana 提供了专用插件(貌似是借用了 Prometheus 接口),也有现成的面板模板可供参考。然后,基本上实现了对原有面板的复刻。

目前的面板

使用的查询语句

V Metrics 插件提供了直观的构建界面,一般无需手打查询语句,就算要手打,一行字也便解决了;但个别特殊情况除外。这里我举几个例子:

处理器使用率

根据文档,V Metrics 将一切监测项目以近乎“扁平化”的方式进行管理:原来泾渭分明的分类 measurementsfield 被熨平了,每条指标都是一张大表下独立的一列;数据类型统一为 Float64;时间段的查询则整体转移到 HTTP 请求头上。于是,原来臃肿的 Flux 语句:

from(bucket: "${bucket}")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "cpu")
|> filter(fn: (r) => r["cpu"] == "cpu-total")
|> filter(fn: (r) => r["_field"] =~ /usage_(iowait|nice|softirq|system|user)/)
|> filter(fn: (r) => r["host"] == "${host}")
|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
|> yield(name: "mean")

变成了简简单单一句 PromQL:

{__name__=~"cpu_usage_(iowait|nice|softirq|system|user)",host="$hostname",cpu="cpu-total"}

题外话,貌似 Flux 语句和 MongoDB 查询语言大同小异;本博客代码的高亮以 Prism.js 模块实现,该高亮的地方都高亮了。

ARC 命中率

该指标未被直接记录,需要从一定间隔内的“命中次数”和“未命中次数”两项记录计算出来。原来在 InfluxDB 整的那套极度冗杂,堪称灾难:

from(bucket: "${bucket}")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "zfs")
|> filter(fn: (r) => r["host"] == "${host}")
|> filter(fn: (r) => r["_field"] == "arcstats_hits" or r["_field"] == "arcstats_misses")
|> difference()
|> pivot(rowKey:["_time"], columnKey:["_field"], valueColumn:"_value")
|> map(fn: (r) => ({
  _time: r._time,
  _measurement: r._measurement,
  _field: "hit_rate",
  _value: float(v: r.arcstats_hits) / (float(v: r.arcstats_hits) + float(v: r.arcstats_misses))
  }))
|> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false)

在 V Metrics 这边,三下五除二解决了:

WITH (
hits = rate(zfs_arcstats_hits{host="$hostname"}[$__interval]),
misses = rate(zfs_arcstats_misses{host="$hostname"}[$__interval])
)
hits / (hits + misses)

记录精度和准确性

基本和 InfluxDB 相同。但个别方面存在区别:

V MetricsInfluxDB
硬盘存取用时(I/O Time)最低精度 0.8 ms精度可达亚微秒级
网络吞吐(Network Transfer)记录值接近于实际同单位下,记录值偏大

性能和资源占用

这方面而言,资深 DevOps 冯若航写的文章更有说服力,负载比我这小不点儿大了好几个数量级。但与此同时,他采用的平台算力也是我家服务器的成百上千倍,其结论不一定能套用到这里来。我在 2 月 3 日切换到 V Metrics,所以到撰稿为止还没有长期观察的条件,但短期收益依然立竿见影:

V MetricsInfluxDB
Grafana 面板刷新速度有少许延迟有明显延迟,半秒以上
Grafana 显示前后,服务端处理器使用率4%→5%,曲线平滑4%→7%,呈锯齿状波动
存储使用量8 日存储量:约 88 MB90 日存储量:约 2 GB
日均存储需求11 MB/天22.75 MB/天

非常厉害,不愧是苏联地区斯拉夫人的杰作。

设置、面板等相关资源

感谢你看到这里,奖励来了!Docker Compose 配置、Telegraf 配置、Grafana 面板配置一网打尽!

Docker-Compose.yml

请注意将 <池名称>/<Docker数据集名称> 替换为你的服务器上相应存放 Docker 容器数据的池和一级数据集名称。

services:
  telegraf:
    container_name: telegraf
    hostname: telegraf
    image: telegraf:alpine
    networks:
      vmetrics:
        aliases:
    ports:
      - 19040:19040/tcp
    restart: unless-stopped
    user: 0 # 给予 root 权限调用内核接口,root-less 的情况下可能报错;可用 privileged 取代
    environment:
      - HOST_MOUNT_PREFIX=/hostfs
      - HOST_ETC=/hostfs/etc
      - HOST_PROC=/hostfs/proc
      - HOST_RUN=/hostfs/run
      - HOST_SYS=/hostfs/sys
      - HOST_VAR=/hostfs/var
    volumes:
      - /mnt/<池名称>/<Docker数据集名称>/telegraf/telegraf.conf:/etc/telegraf/telegraf.conf:ro
      - /:/hostfs:ro
      - /run/udev:/run/udev:ro
  victoriametrics:
    container_name: vmetrics
    hostname: vmetrics
    image: victoriametrics/victoria-metrics:latest
    networks:
      vmetrics:
        aliases:
    ports:
      - 8428:8428/tcp
    restart: always
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - /mnt/<池名称>/<Docker数据集名称>/vmetrics:/vmetrics
    command:
      - "-storageDataPath=/vmetrics" # 永久化存储
      - "-retentionPeriod=3M" # 记录保存三个月
      - "-search.latencyOffset=1s" # 查询结果中最后一个记录点距离当前时间的最小间隔,默认 30 秒
  grafana:
    container_name: grafana
    hostname: grafana
    image: grafana/grafana:latest
    networks:
      vmetrics:
        aliases:
    ports:
      - 3000:3000/tcp
    restart: unless-stopped
    user: 950 # 创建了宿主机 grafana 目录的用户
    volumes:
      - /mnt/<池名称>/<Docker数据集名称>/grafana:/var/lib/grafana

Telegraf 配置文件

注:保存于 /mnt/<池名称>/<Docker数据集名称>/telegraf/telegraf.conf
另注意:我不确定限定需要监测的网络接口 interfaces 和硬盘 devices 的设置是否可覆盖你的情况,请在 TrueNAS 管理界面检查以后自行调整。其中

  • 硬盘:SATA/SAS 接口硬盘,最多 26 块(sd[a-z]);M.2 PCIe 接口固态硬盘,最多 10 块(nvme[0-9]n1)。
  • 网卡:物理网卡(eno* enp0s[0-1]);本机回路接口(lo)。
[global_tags]
[agent]
  interval = "5s"
  round_interval = true
  metric_batch_size = 1000
  metric_buffer_limit = 10000
  collection_jitter = "0s"
  flush_interval = "10s"
  flush_jitter = "0s"
  precision = "1ms"
  debug = false
  quiet = false
  logfile = ""
  hostname = "truenas"
  omit_hostname = false
[[outputs.influxdb]]
  urls = ["http://localhost:8428"]
  skip_database_creation = true
  exclude_retention_policy_tag = true
[[inputs.cpu]]
  percpu = true
  totalcpu = true
  collect_cpu_time = false
  report_active = false
  core_tags = false
[[inputs.system]]
[[inputs.diskio]]
  devices = ["sd[a-z]","nvme[0-9]n1"]
[[inputs.sensors]]
[[inputs.net]]
  interfaces = ["eno*", "enp0s[0-1]", "lo"]
[[inputs.zfs]]
[[inputs.mem]]
[[inputs.processes]]
[[inputs.upsd]]
[[inputs.kernel]]

Grafana 面板配置

由于配置文件较长,这里不再贴出,可由此下载