家用 NAS 爆改本地 AI 算力中心:Docker 容器化、向量检索与 ZFS 内存/IO 调优避坑实录

4次阅读
没有评论

共计 2911 个字符,预计需要花费 8 分钟才能阅读完成。

很多人手里都有一台 7×24 小时低功耗运行的 NAS,不管是自组的 x86 小主机、老志强 E3/E5,还是标准的 4 盘位白群晖、TrueNAS 设备。随着开源模型推理栈(如 Ollama、llama.cpp、vLLM)的成熟,将 NAS 改造成全天候待命的私有 AI 知识库成了顺理成章的选择。但我自己在踩过一堆坑之后发现: 在 NAS 上跑大模型和在开发机上跑,完全是两码事。

你可能会遇到这样的诡异现象:NAS 明明有 32GB 内存,跑一个 7B 模型却频繁被系统 oom-killer 强行干掉;或者一旦触发文档向量化,机械硬盘阵列开始“炒豆子”疯狂狂响,整个 SMB 共享卡死,网页控制台直接超时。本文不谈虚头巴脑的概念,直接从存储分层、底层内核调优、容器化编排三个维度,复盘我这套全天候本地 AI 节点的落地过程。

一、架构分层:为什么机械硬盘阵列跑向量检索必崩?

很多老玩家习惯将 Docker 根目录直接扔在机械硬盘 RAID/ZFS 阵列上,在跑常规服务(如 Plex、Nextcloud)时毫无压力,但在本地 AI(尤其是接入 RAG 知识库)场景下,这种存储拓扑是致命的。

我们先看两类不同的 I/O 负载模型:

  • 模型权重加载(Model Loading):属于典型的大文件顺序读(Sequential Read)。不管是 GGUF 还是 Safetensors,启动时一次性读入 4GB~15GB 内存 / 显存,机械盘顶多慢个几秒钟,无伤大雅。
  • 向量检索(Vector Search)与嵌入计算(Embedding):Qdrant、Milvus 或 pgvector 这类数据库在做高维向量最近邻检索(HNSW 算法)时,会产生极其密集、高度并发的 4KB 随机读写(Random IOPS)。

常规机械硬盘阵列的随机 4K IOPS 通常只有可怜的 150~300。如果你的向量库数据文件放在机械盘上,并发查询或批量灌入文档时,I/O 队列深度会瞬间飙升到 100%(I/O wait 超过 70%),导致 NAS 宿主机所有服务因争抢 I/O 锁而假死。

推荐的存储拓扑方案:

NAS 存储拓扑划分:├── NVMe SSD 池 (高速池)
│   ├── /docker/lib (Docker 运行时与 Rootfs)
│   ├── /opt/qdrant/storage (向量数据库数据目录,抗高并发随机 4K)
│   └── /root/.ollama/models/manifests (模型元数据及高频上下文缓存)
└── 机械盘 ZFS/RAID 池 (冷数据池)
    ├── /mnt/storage/raw_documents (待解析的原始 PDF/Markdown/ 代码库)
    └── /mnt/storage/model_backups (大模型离线 GGUF 归档)

二、致命冲突:ZFS ARC 与 Ollama 争抢内存

如果你用的是 TrueNAS SCALE 或底层基于 Debian/Proxmox VE + ZFS 的系统,这是最容易踩爆的深坑。ZFS 默认会把宿主机 50% 甚至更多的物理内存吃掉作为自适应替换缓存(ARC, Adaptive Replacement Cache)。

假设你的 NAS 有 32GB 内存:

  1. ZFS ARC 慢悠悠吃掉了 16GB。
  2. 你启动 Ollama 载入 qwen2.5:14b-instruct-q4_K_M,模型本身静态占用约 9GB。
  3. 当多轮上下文(Context Window 设定为 8K/16K)打满,以及向量库并发查询到来时,内存瞬间出现峰值抖动。
  4. 虽然 ZFS 理论上会在系统内存吃紧时释放 ARC,但其回收速率远远慢于用户态瞬时分配速度。结果就是:Linux 内核 oom-killer 瞬间触发,优先挑内存占用最高的进程下手——Ollama 容器当场暴毙。

实操优化:给 ZFS ARC 戴上“紧箍咒”

在宿主机上修改 ZFS 内核模块参数,显式限制 ARC 最大占用内存。例如强制将 ARC 限制在 6GB 以内(根据你的总体内存调整,公式: 物理内存 - 大模型开销 - 4GB 预留 = 最大 ARC):

# 编辑或创建 zfs.conf
sudo tee /etc/modprobe.d/zfs.conf << 'EOF'
# 限制最大 ARC 为 6GB (6 * 1024 * 1024 * 1024 = 6442450944)
options zfs zfs_arc_max=6442450944
# 限制最小 ARC 为 1GB
options zfs zfs_arc_min=1073741824
EOF

# 立即应用参数(无需重启生效)echo 6442450944 | sudo tee /sys/module/zfs/parameters/zfs_arc_max

# 验证当前 ARC 状态
arcstat 2

同时,将宿主机的 vm.swappiness 降到最低,避免内核因为短时内存压力将模型的权重页面置换到慢速 Swap 中,导致推理速度出现断崖式下跌:

sudo sysctl -w vm.swappiness=10
echo "vm.swappiness=10" | sudo tee -a /etc/sysctl.d/99-ai-swap.conf

三、算力透传:核显与独显接入 Docker

很多极客的 NAS 采用的是 Intel N100、i3-12100 等集成显卡,或者是插了半高卡(如 Tesla P4、RTX 3060 12G、RTX 4060)。在容器化部署前,必须保证硬件驱动在 Docker 内正确透传。

场景 A:Intel 核显(iGPU)使用 OpenVINO / Vulkan 推理

确保宿主机开启了 /dev/dri 渲染设备。检查设备权限:

ls -l /dev/dri
# 应该能看到 card0 和 renderD128

场景 B:NVIDIA 独立显卡

宿主机安装好驱动后,必须配置 nvidia-container-toolkit,否则 Docker 无法识别 --gpus all:

# 安装容器工具包(以 Debian/Ubuntu 为例)distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
  sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
正文完
 0
评论(没有评论)