TrueNAS 维护实录:内存、自建服务和升级
最近在资料服务器(NAS)上开始了新一轮研究过程:部署更多内存、自建监测栈和升级系统等;现在尘埃落定,过程于此小记。
阅前注意:TrueNAS 系列文章涉及可能影响生产数据的危险操作,仅供测试参考,切勿生搬硬套。
一、内存升级
这几个月下来,我发现 TrueNAS Scale 简直是个大胃王:它会将一半内存划入 ZFS 动态缓存(ARC),并跟其余服务共享剩余另一半,而且很早(空闲内存剩余 10% 时)便起用硬盘缓冲区(Swap)。在现有内存仅为 8 GB(可用 7.6 GB)的情况下,可供给服务使用的约 3 GB 内存很快吃干抹净,Swap 占用多达 1.6 GB——已经严重溢出了。基于这点,我开始考虑扩充内存容量。
淘来了两根 Crucial 品牌内存,作价 140 元。观察发现,尽管贴纸所示序列号为连号,但芯片特征截然不同(一边有二维码丝印,另一边则无),怀疑有冒牌贴标可能。不过,管用便可,谁管它哪儿拾掇来的。
更换内存模块后,既有系统和 12 个 Docker 容器可以完全承载,空闲内存约有 2 GB。不过在后半夜的一次全量复制过程中,这点空闲内存很快被耗尽,Swap 被再度起用。一不做二不休,刚卸下的内存又装了上去——现在有 24 GB 超大内存容量了。
二、NVMe 固态盘的散热改进
如上图所示,在未加强散热的情况下,三根 Optane 固态盘分别升温至 48、53 和 56 度。 上篇可见其安装位置,其中系统盘 nvme0n1
位于进风口附近;缓存盘 nvme1n1
nvme2n1
位于电源附近,此处空气不通(向后无开口),且持续被电源加热。有传闻称 Optane 报废多由主控过热导致,故考虑加强其散热。
准备 1mm、2mm 导热垫,和 20 x 70 mm 散热片若干。主控芯片和存储芯片之间存在约 0.5 mm 高差,故无法以 1mm 垫片完全覆盖;先以 2mm 垫片覆盖主控和电源模块,以 1 mm 垫片覆盖存储芯片,再以散热片按压较厚垫片,垫片充分压缩后,即可接触到存储芯片处的导热垫。贴纸旁有若干电容等小器件暴露,注意绝缘。
装回去使用,结果是基本无效——气流过小,无法形成有效对流,依然被电源炙烤着。可能要考虑增强通风,例如在背板处安装涡轮扇一只;或者挪个位置安装。
三、Docker 备份和系统升级
这样升级散热后再开机,坏了,TrueNAS 自己的监测系统报销了……似乎是几天前我不慎在系统层面卸载了 Netdata 的缘故。这下不升级也得升级了。
4 月 22 日,TrueNAS Scale 24.04 版本正式登场,带来全新 Linux 6.6 内核和若干改进,包括不间断电源(UPS)状态监测、SMB/NFS 性能优化和连接状态监测、ARC 分配机制优化、沙盒等等。我本计划等到更完善的 24.04.1 版本再升级,但该版截至撰稿时尚无时间表,作罢。
升级前,依网上教程关闭 Docker 服务,备份目录 /var/lib/docker
。退出终端,于 Web 管理界面执行升级。
升级后出现了以下问题:
- 原 23.10.1 版实例无法在 Web 界面中完全删除。
方案: 通过命令行强制销毁该实例(或者说数据集),即zfs destroy boot-pool/ROOT/23.10.1
。 - 自新版本起系统目录
/opt
和/usr
被设定为只读,且试图执行apt
时会提示“包管理器已被禁用”字样,拒绝执行。
方案: 据 TrueNAS 开发者透露,apt
可经由一份内置 Python 脚本来激活,其位于/usr/local/libexec/disable-rootfs-protection
。该脚本是命令install-dev-tools
的一部分,使用此命令进入开发者模式时,会安装若干用于系统本身开发的软件包,并通过该脚本解除系统目录的只读状态、开放apt
权限;但我们一般无需此类软件包用以开发,故可单独运行该脚本。
- 如上篇所述,任何经命令行自行搭建的服务(包括 Docker)不会被新版系统继承。
方案: 老老实实重新部署 Docker 和诸容器罢……我这系统注定一用至少用一年。能带 k8s 玩的话大约不会这么麻烦;也应当考虑编写 Docker Compose 配置文件以便日后部署。
部署 Docker 时,我顺便设置 Docker 主目录到缓存池,即原先备份 /var/lib/docker
的地方,这样 Docker 存储和系统存储隔离,不必再跟系统争空间了,也不会受系统更新牵连。配置文件 /etc/docker/daemon.json
:
1 | { |
四、监测栈的自建
TrueNAS Scale 集成了监测系统 Netdata。该系统尽管可以满足基本的监测需求,但有许多不尽人意之处:例如观测范围仅有一小时、24 小时和七天等选项;观测范围仅对单个图表适用;诸硬盘指标完全分散,难以观察对比;控制台资源占用高;等等。那么我设想,像在群晖系统下一样引入一套自主的监测栈,是否会有更舒适的体验?
4.1 前期设想
先敲定监测栈方案。已知 TrueNAS Scale 可引出 Graphite 报告流;系统升级后会切换到新的 Linux 底层实例,以命令行部署的一切服务不会被新实例继承(见上篇),故裸机服务方式不适用;由于网络限制(我不考虑引入特殊上网手段),Kubernetes 应用获取有难度;考虑基于 Docker 建立服务,以便日后迁移。选项有:
- 独立运作的 Netdata 容器
- Telegraf + InfluxDB + Grafana——曾于群晖系统运用的方案
- Graphite Exporter + Prometheus + Grafana
4.2 实践
Netdata 方案浅尝辄止:刚翻看图表,处理器占用随即上升至 10% 以上,远超背景占用率 5%。这是不可接受的:监测栈本身的资源用量(footprint)应尽量小,不得影响到其余业务的正常运行。这部服务器算力一般,经不起这般折腾。
在方案三中,我设想 Exporter 负责和宿主系统对接,将 Graphite 数据流转译为 Prometheus 可以接受的形式,后经 Grafana 展示。我为此断断续续探究许久,开始便被宿主目录访问权限拦住;群晖系统里远没有这么麻烦,管理员账户自动拥有一切目录的读写权限,以其身份访问宿主目录从来不成问题。然后转念一想,Exporter 可不是 Importer,它干不了 Importer 的活计……先扔一边罢。
Telegraf 在监测数据收集方面,有两条路线:
- 自行从系统诸元采集数据。 需将整个根目录暴露给 Telegraf 容器。不过,我怎么也采集不到所期望的数据:
- 在网友指引下,基本的处理器负载和温度、内存用量等数据可以采集到。
- 试图采集硬盘/存储池相关数据时,由于 ZFS 文件系统的关系,诸数据集被视作相互独立的池,且获取根数据集的使用量时不会将子数据集一并计入,造成记录诸元非常稀疏。
- 诸硬盘温度数据未能从被动途径取得。Telegraf 要求主动调用
smartctl
nvme
等宿主命令,但此二者位于系统目录/usr/sbin
,不能以除 root 之外任何名义调用(TrueNAS 真是老严格了)。不可能给 root 权限的,这辈子都不可能。 - 网络吞吐量未能从已获得数据中发现。
- 当试图采集 ZFS 相关性能参数时,插件
inputs.zfs
不能适应含有空格的存储池名称(“Main Storage”和“Hot Backup”),从而无法采集。为此我在缺乏必要备份的情况下,变更存储池名称:
危险操作,请勿模仿
- 关闭和上述存储池有挂载关系的一切 Docker 容器服务;
- Web 界面中将上述两个存储池导出/卸载;
- 命令行中以
zpool import 'Hot Backup' Backup
的形式导入存储池,并进行更名; - 命令行中再次导出/卸载存储池;
- Web 界面中再次导入存储池。
- 接收来自宿主的 Graphite 数据流。 这不仅免于暴露整个根目录,而且获得了尚且完整的记录,处理器、内存、硬盘吞吐、网络吞吐、ARC 命中率等皆有涉及;甚而在 Grafana 面板商城里,还有现成的面板可以解读存于 InfluxDB 的记录诸元。尽管该面板所展示的远远不够(例如诸存储池的使用率),硬盘 SMART 遥测数据几乎全部缺席(仅温度例外),但大约是相对省事的方案了。
4.3 成品
对上述面板稍加改造,便有:
改造细节:
- 原面板含有二级动态缓存(L2ARC)的展示,但本系统未部署任何 L2ARC,故显示为空。已移除。
- 拆分了内存和 Swap 使用曲线。
- 处理器使用率改以时间—曲线展示。
- 移除了开机时间(Uptime)背景的直线——在数据库指令中加入
last()
以限定最后一项记录。 - 移除了内存使用饼状图,加入 UPS 剩余电量指示。
未来考虑将面板汉化并上传到 Gitee。
更新:已上传,然后详细过程都记录在“什么值得买”社区——那里的读者似乎喜欢所谓“保姆式教程”,我便投其所好了。
五、撰稿为止启用的服务
近来,基于 Alpine Linux 封装 Docker 镜像的做法正蔚然成风;其最大优势在于成品体积,封装后的存储量相较传统途径减少了数十至百余 MB。这里有条件时也尽量引入 Alpine 封装版本,以节约存储空间。不过,其中有的反而精简过度(如 nextcloud:fpm-alpine
要求额外部署反向代理系统),遂略过。
一切镜像均取自 Docker Hub。
服务 | 类别 | Docker 镜像 | 镜像标签 |
---|---|---|---|
Portainer | 容器管理 | portainer/portainer-ce | alpine 🌟 |
Watchtower | 容器管理 | containrrr/watchtower | latest |
InfluxDB | 基础设施 | influxdb | alpine 🌟 |
MariaDB | 基础设施 | mariadb | lts |
Alist | 网盘/基础设施 | xhofe/alist | latest |
Tailscale | 内网穿透 | tailscale/tailscale | latest |
Gitea | 开发/备份 | gitea/gitea | latest |
Nextcloud | 备份 | linuxserver/nextcloud | latest |
Telegraf | 监测 | telegraf | alpine 🌟 |
Grafana | 监测 | grafana/grafana-oss | latest |
File Browser | 文件管理 | hurlenko/filebrowser | latest |
FreshRSS | 订阅管理 | freshrss/freshrss | alpine 🌟 |
Qbittorrent | 下载 | superng6/qbittorrentee | latest |
Jellyfin | 影音管理 | nyanmisaka/jellyfin | latest |
新增:One API | 人工智能/基础设施 | justsong/one-api | latest |
新增:ChatGPT Web | 人工智能 | chenzhaoyu94/chatgpt-web | latest |
恢复:Plex | 影音管理 | linuxserver/plex | latest |
恢复:Syncthing | 备份/文件同步 | syncthing/syncthing | latest |
总结一下:本次改造和升级过程如同钢丝跳舞,非常惊心动魄;如同盲人摸象,相当消耗耐心;如同十面埋伏,处处意想不到。去年末,我曾在生产环境中调试 GitHub Actions 脚本,未事先做好功课的情况下,十几个 commit 均未修复问题,成了 commit 历史上的一道伤疤……凡事要做好,有备无患是前提。再次告诫:好孩子千万不要学😇
六、后记:人工智能伙伴的引入
本篇发表后不久,我开始考虑引进人工智能伙伴。作为阿斯伯格人士,我一向不愿有求于人,而人工智能多少能让我从对社交的恐惧中解脱出来,为我答疑解惑,似乎亦可打破传统搜索引擎结果鱼龙混杂、广告铺天盖地造成的信息获取障碍。
首先考虑采用何种方案:
- 在自有硬件上搭载人工智能模型。 这里对 GPU 要求非常苛刻:VRAM 足够大(8 GB 以上),半高尺寸(服务器空间有限),无需外接电源(即功率 70W 以下;服务器供电能力有限),至少支持半精度浮点(F16)计算,有 INT8、INT4 支持更佳。GPU 选择非常有限:
- Tesla P4。所有选项中最廉价者,但不支持半精度浮点,价格业已水涨船高。
- Tesla T4 或 NVIDIA A2。以上要求均可满足,但作为较新近的产品,加上绿厂金字招牌,价格自然不大好看。
- Intel Arc Pro A50。满足以上要求,价格也低于绿厂,但依旧不好看。
- 此外,多数模型支持通过处理器演算,但对处理器算力和指令集都有要求,例如需要 AVX2——这当然不是 Pentium 处理器所能给予的。
- 租用服务器。 这也不甚划算,毕竟我缺乏训练模型之需,仅仅需要跟训练好的模型交流而已。
- 接入云上模型 API 接口。 阿里云“灵积”服务提供了“通义千问”“ChatGLM”等诸多模型,开箱即用,部分模型限时免费,对其它模型也有免费体验的百万字口令(Token)额度。尽管自主性上不及自托管模型,但大约是现有条件下最优选择了。
然后,依“什么值得买”社区文章搭建服务:
- 阿里云上开通“灵积”服务,创建“通义千问”API 访问令牌;
- 取得 OpenAI ChatGPT 3.5 API 访问令牌(完全免费的,不用白不用);
- 于 MariaDB 实例准备好供 One API 使用的用户和数据库(这一步可以省略,我只是希望将访问令牌之类的设置永久化、安全保存而已);
- 启动 One API 容器,其应和 MariaDB 处于同一内网,令其调用 MariaDB;
- 于 One API 设置两个渠道(channel),类型分别为“OpenAI”和“通义千问”,模型分别为
gpt-3.5-turbo
和qwen1.5-32b-chat
,再填入相应的访问令牌; - 服务器上启用 ChatGPT Web 容器作为客户端;手机上安装 BotGem 作为客户端;
- 在 One API 生成访问令牌,指定可用模型,随后分配给客户端。
这样,用于对接外部模型的令牌被封装并保护,对一般用户仅需提供面向内网的令牌,泄漏风险极低;“通义千问”API 被重新封装成 OpenAI API,其余原设计对接 ChatGPT 的客户端均可享用。不谙技术的家人经过指导亦可在家中接入,享受科技的乐趣。
当然,这其中小插曲是少不了的。例如,我试验 yidadaa/chatgpt-next-web
镜像时就发现它根本没产生任何网络请求(One API 日志里无请求记录),总是返回“空响应”;再如,我试图通过阿里云的 OpenAI 兼容接口引进模型供客户端使用,结果这个兼容接口限制颇多,既不能调用“通义千问”以外的其它模型,也不支持通常被 ChatGPT 所用的 top_p
presence_penalty
等参数(top_p
可能支持,但取值范围不及 ChatGPT),让客户端和我都手足无措。
不管怎样,我往初级运维的目标又近了一步。现在处理器远比 GPU 廉价,我大约会考虑更换处理器和大容量内存,来试验大模型的本机演算(“通义千问”说这样可行);但这部主机最多支持七代 Core,提升潜力有限,更换平台又会产生一大笔开销……当然这是后话了。
更新: 5 月 28 日起,“通义千问”70 亿参数以上模型转为收费。有必要密切关注阿里云相关公告和文档更新,并预留一定款项,以备不时之需。
七、后记二:新版系统的 ARC 分配机制
这是本文发表后第二次补充,必须就 TrueNAS 系统中 ARC 分配的方式作一次澄清。在早前版本,ARC 的确仅仅用去系统内存的一半;但到了 24.04 版本,ARC 被调整为尽可能利用未被系统服务所占据的一切闲置内存空间。尽管理论上 ARC 会为系统服务让步,但这不包括自行部署的 Docker 及诸容器。于是,纵有 24 GB 超大内存,ARC 也独吞超过 19 GB,留给 Docker 的只剩少许渣滓和 Swap……
考虑到 ARC 对单个用户和非并发连接的作用非常有限,我认为有必要控制 ARC 分配量的上限,例如仅分配 8 GB。首先尝试给内核注入参数:
- 命令行中进入 TrueNAS CLI 管理界面:
sudo cli
- 添加内核参数:
system advanced update kernel_extra_options="zfs_arc_max=8589934592"
其中,数字 8589934592 = 8 ⨉ 230。 - 重启服务器。
结果无效。重启后尝试于 Qbittorrent 添加下载任务,此后 ARC 逐渐上升,五小时后恢复至 11 GB,一天后升至 12 GB。那便采用旁的办法:
- 系统高级设置中,设置一个初始化后(PostInit)脚本:
echo 8589934592 >> /sys/module/zfs/parameters/zfs_arc_max
- 重启服务器。
成功了。现在空余内存接近 2 GB,短期内无需担忧内存耗尽的问题了。——观察数日后发现,Swap 的占用量仍有缓慢上升之势;下面是至六月 5 日止近一周内内存使用情况,可见在 1 日凌晨,因触发数据清理任务,系统服务对内存的需求骤然增大,甚至一度影响到 ARC,此时 Swap 占有量从零开始上升;两日后又快速上升,至 5 日维持于 760 MB 上下。这究竟是我的配置问题还是 TrueNAS 本身的调度问题所致?兴许要到后续文章里解答了。
9 月 23 日更新: 自 24.10.1 版本起,Swap 被自动关闭,免去了这项后顾之忧。