家庭NAS魔改AI推理中心:Docker直通GPU部署Ollama与向量知识库排坑指南

3次阅读
没有评论

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

我手头这台跑了三年的自建 NAS(Intel 12 代 i5 + 64GB 内存 + Debian 12 底层 + ZFS 存储池),过去 90% 的时间都在充当纯粹的冷备份仓库与媒体服务器。看着常年个位数的 CPU 占用和巨大的空闲内存,总觉得是极大的算力浪费。正好手里有一张闲置的 RTX 3060 12G 显卡,我索性将它插进了 NAS 的第二条 PCIe 插槽,目标非常明确:在不破坏原有 NAS 存储稳定性的前提下,将其打造成局域网内的私有化大模型推演与 RAG(检索增强生成)知识库中心。

但在真正的 NAS 系统上跑大模型,远不是敲一行 docker run 那么简单。你会遭遇驱动签名、Docker 容器无法调用显卡、ZFS 的 ARC 内存缓存机制与模型载入争抢内存导致的 OOM Panic 等一系列恶心的问题。这篇文章把我上周踩平的所有深坑整理成一套标准流水线,供大家抄作业。

一、底层打通:NVIDIA Container Toolkit 与无头环境配置

许多人在 NAS 环境(尤其是 Debian/Ubuntu Server 或 TrueNAS SCALE)里搞 GPU 直通时,最容易死在第一步。因为 NAS 通常是没有接显示器的“Headless”无头机器,显卡经常会因为电源策略或显示协商失败导致驱动状态异常。

1. 彻底禁用 Nouveau 开源驱动并安装专有驱动

首先必须干掉开源的 Nouveau 驱动,否则它会悄无声息地抢占硬件通道:

# 写入黑名单配置
sudo bash -c "cat <<EOF > /etc/modprobe.d/blacklist-nouveau.conf
blacklist nouveau
options nouveau modeset=0
EOF"

# 更新 initramfs 并重启
sudo update-initramfs -u
sudo reboot

重启后,安装标准的 NVIDIA 无头驱动(这里推荐使用官方生产分支驱动,兼顾稳定性):

sudo apt-get update
sudo apt-get install -y nvidia-driver nvidia-smi
# 验证驱动,查看显卡状态
nvidia-smi

2. 部署 NVIDIA Container Toolkit 并配置 Docker 运行时

要让 Docker 容器识别到物理 GPU,仅仅宿主机有驱动是不够的,必须通过 NVIDIA 提供的容器工具包进行资源映射:

# 添加软件源
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/stable/deb/nvidia-container-toolkit.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

# 关键步骤:重置 docker 默认运行时为 nvidia
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

测试 GPU 能否正常被容器抓取:

docker run --rm --gpus all nvidia/cuda:12.2.0-base-ubuntu22.04 nvidia-smi

如果终端能正确输出 GPU 拓扑信息与温度显存,底层的路才算真正铺平。

二、核心服务编排:Ollama + Qdrant + Open-WebUI

我们搭建的这套架构由三部分组成:Ollama 负责模型加载与 GPU 加速推理;Qdrant 作为高性能向量数据库,用来挂载 NAS 大容量机械硬盘做长期文档向量索引;Open-WebUI 提供类似 ChatGPT 的沉浸式操作界面及完整的文档 RAG 流程。

在你的 NAS 高速 SSD 存储池(建议使用 NVMe 盘做 Docker 目录,机械盘做数据挂载)上创建编排目录:

mkdir -p /mnt/fast-pool/ai-stack && cd /mnt/fast-pool/ai-stack
nano docker-compose.yml

写入以下经过生产级调优的 docker-compose.yml 配置文件:

version: '3.8'

services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama-engine
    restart: unless-stopped
    environment:
      - OLLAMA_KEEP_ALIVE=24h        # 模型常驻显存,杜绝首次请求的冷启动加载延迟
      - OLLAMA_NUM_PARALLEL=2        # 允许局域网 2 个并发会话同时推理
      - OLLAMA_FLASH_ATTENTION=1     # 开启 Flash Attention 降低显存占用并提升生成速度
    volumes:
      - /mnt/fast-pool/ai-stack/ollama:/root/.ollama
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    ports:
      - "11434:11434"

  qdrant:
    image: qdrant/qdrant:latest
    container_name: qdrant-vector
    restart: unless-stopped
    volumes:
      - /mnt/hdd-pool/ai-vector-data:/qdrant/storage
    ports:
      - "6333:6333"

  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui
    restart: unless-stopped
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
      - VECTOR_DB=qdrant
      - QDRANT_HOST=qdrant
      - QDRANT_PORT=6333
      - WEBUI_SECRET_KEY=generate_your_own_secure_key_here
    volumes:
      - /mnt/fast-pool/ai-stack/webui:/app/backend/data
    ports:
      - "3000:8080"
    depends_on:
      - ollama
      - qdrant

执行 docker compose up -d 拉起服务。通过浏览器访问 http://NAS_IP:3000,注册首个管理员账号,整个基础服务就跑起来了。

三、避坑核心:ZFS ARC 与大模型 OOM 抢内存调优

这套系统上线第一天,我就遇到了致命事故:在上传一份 300MB 的技术白皮书做向量嵌入时,整个 NAS 突然卡死,SSH 掉线,几分钟后系统触发 Kernel Panic 重启。

查日志后发现是内存打架:ZFS 的自适应替换缓存(ARC)默认会用尽系统多达 50% 的物理内存;而 Ollama 在加载 qwen2.5:14b-instruct-q4_K_M 模型时,除了需要 9GB 显存,还会向系统请求部分 RAM 承载上下文 KV Cache;再加上向量化任务疯狂消耗宿主机内存,直接触发了 Linux 系统的 OOM Killer,甚至误杀了系统守护进程。

实战解决方案:手动锁死 ZFS ARC 内存上限

在运行大模型的 NAS 节点上,绝不能允许 ZFS 任意挥霍物理内存。如果机器是 64GB 内存,建议最多给 ARC 分配 16GB,留出充足的系统和模型冗余空间:

# 编辑 ZFS 配置文件
sudo nano /etc/modprobe.d/zfs.conf

# 限制 ARC 最大为 16GB (16 * 1024 * 1024 * 1024 字节)
options zfs zfs_arc_max=17179869184

# 限制 ARC 最小为 4GB
options zfs zfs_arc_min=4294967296

动态生效该参数(无需重启):

sudo sh -c 'echo 17179869184 > /sys/module/zfs/parameters/zfs_arc_max'
# 检查当前生效的 ARC 大小
arcstat

锁死 ARC 之后,我用 qwen2.5:14b 与 bge-m3 嵌入模型连续跑了 10 个 PDF 的并发向量化,宿主机内存占用稳定在 48GB 左右,再也没有发生过系统雪崩。

四、模型选型与实测表现对比

基于 12GB 显存(RTX 3060)的边界,模型选型必须卡在参数与精度的甜点位上。实测下来的几款主力模型表现如下:

1. 综合对话与编码:qwen2.5:14b-instruct-q4_K_M

通过命令行拉取模型:

docker exec -it ollama-engine ollama run qwen2.5:14b-instruct-q4_K_M
  • 显存占用: 约 9.2 GB(完全装进显存,未触发 CPU 卸载)。
  • 推理速度: 首字延迟(TTFT)约 420ms,稳态生成速度达到 34 tokens/s,体感非常丝滑。
  • 中文表现: 语义理解和代码生成能力极佳,完全能胜任日常全栈开发的代码重构与文档提炼。

2. 嵌入向量模型:bge-m3:latest

用于本地文档的向量化,支持多语言、多粒度匹配:

docker exec -it ollama-engine ollama pull bge-m3

在 Open-WebUI 的“文档”设置中,将嵌入模型指定为 bge-m3。实测解析一本 500 页的《Linux 内核设计与实现》PDF,切分为 1200 个 chunk 并存入 Qdrant 耗时不到 3 分钟,召回命中率极高。

总结与避坑心得

把 NAS 变成 AI 推理中枢,本质上是一场 系统级资源调度管理 的游戏:

  1. 存储分级: 必须把 Ollama 的模型权重目录(/root/.ollama)放在 NVMe SSD 存储池上。如果放在机械硬盘阵列上,每次切换模型时巨额的冷启动 IO 会让整个 NAS 产生令人抓狂的 IO Wait。
  2. 守护机制: 务必把 OLLAMA_KEEP_ALIVE 设为 -1 或较长时间(如 24h),家用推理场景最怕每次发消息还要等 10 秒钟模型载入显存。
  3. 安全边界: 如果你需要通过反向代理(如 NPM 或 Traefik)把 WebUI 映射到公网,请务必关闭 Open-WebUI 的公开注册开关(在环境变量中加入 ENABLE_SIGNUP=False),并强制开启两步验证(2FA)。私有知识库里存放的往往是极度敏感的个人或业务数据,暴露在外网裸奔无异于自寻死路。

现在,我家里所有设备在编写代码、阅读技术论文、整理碎片灵感时,都在调用这台默默运转的 NAS,数据不出内网,响应飞快且没有 token 费用焦虑。这才是极客 NAS 的终极打开方式。

正文完
 0
评论(没有评论)