共计 3527 个字符,预计需要花费 9 分钟才能阅读完成。
很多朋友把 NAS 组起来后,除了挂 PT、跑 Plex 串流和做 Time Machine 备份,算力常年处于闲置状态。我手头这台自建的 8 盘位 TrueNAS SCALE 机器,配备了一张半高单槽的 Tesla P4(8GB 显存)和 64GB ECC 内存,平时待机功耗只有 35W 上下。上个月我尝试把它彻底改造成一台能 7×24 小时值守的私有 AI 助理,跑本地 LLM 和针对本地代码库 / 合同文档的 RAG 知识库系统。然而把 LLM 搬上 NAS 绝不仅是敲一行 docker run 那么简单——显存爆仓、向量检索卡死 I /O、多轮对话上下文截断,踩了一圈坑下来才算调教妥当。
一、NAS 跑大模型的硬件边界与核心痛点
在 NAS 上部署大模型,首先要打消“用 8 代 i3 核显跑 70B 模型”的不切实际想法。以目前主流的 NAS 环境来看,合理的硬件分级与负载预期如下:
- 纯 CPU/ 核显环境(如 N100、i5-8500):建议上限为 4 -bit 量化的 Qwen2.5-3B 或Llama-3.2-3B,推理速度勉强能维持在 8~12 tokens/s,适合处理轻量级定时任务或通知总结。
- 入门独显环境(GTX 1650 4G / Tesla P4 8G / RTX 3060 12G):甜点区间是 Qwen2.5-7B-Instruct-Q4_K_M 或者DeepSeek-R1-Distill-Qwen-8B。配合 8GB 显存,可以吃下 4k 到 8k 的 Context Window,推理速度能冲到 25~35 tokens/s,日常问答和知识检索体验非常顺滑。
我在早期部署时遇到的第一个痛点是 上下文爆炸引发的 OOM 崩溃。默认配置下,如果知识库检索切片(Chunks)塞得太多,Ollama 在显存不足时会强行把 KV Cache 卸载到系统 RAM,导致推理速度从 30 tokens/ s 断崖式下跌至 1.2 tokens/s,同时 NVMe 缓存盘被狂写。
二、底层运行时与显卡直通踩坑
无论是基于 Debian 的 TrueNAS SCALE 还是群晖 DSM 7.2,要让 Docker 容器调用 Nvidia 显卡,必须确保 Nvidia Container Toolkit 正确就绪。群晖需要通过第三方源或 Container Manager 注入驱动,而通用 Linux NAS 环境建议直接校验容器内部可见性:
# 验证驱动与 nvidia-docker 环境是否正常
docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi
如果输出正常列出了显卡型号和驱动版本,接着解决存储挂载问题。大模型权重文件动辄数 GB 到几十 GB,切忌存放在机械硬盘阵列(HDD Pool)。机械盘随机读写弱,冷启动载入模型时会导致容器假死甚至触发 Docker 健康检查失败重启。建议在 NAS 的 M.2 NVMe SSD 独立存储池开辟专用目录挂载给模型和向量数据库。
三、生产级 Docker Compose 编排实战
一套健壮的本地 AI 服务需要三层架构:推理引擎(Ollama)、向量数据库(Qdrant)和交互前端(Open-WebUI)。相比于让 WebUI 内置 SQLite 保存向量,单独拉起 Qdrant 能大幅减少嵌入检索时的系统阻塞。
以下是我调优后的 docker-compose.yml 配置,重点关注显存锁定、长链接代理以及 Ollama 的并发与常驻时间参数:
version: '3.8'
services:
ollama:
image: ollama/ollama:latest
container_name: ollama-core
restart: unless-stopped
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
volumes:
- /mnt/nvme-pool/docker/ollama:/root/.ollama
environment:
- OLLAMA_KEEP_ALIVE=24h # 避免模型频繁从内存进出,设为 24 小时常驻
- OLLAMA_NUM_PARALLEL=2 # 小显存建议设为 1 -2,防止并发抢占显存导致 OOM
- OLLAMA_FLASH_ATTENTION=1 # 显存支持则开启 Flash Attention,降低显存占用
- OLLAMA_MAX_LOADED_MODELS=1 # 仅常驻一个主模型,向量模型动态挂载
ports:
- "11434:11434"
qdrant:
image: qdrant/qdrant:v1.11.0
container_name: qdrant-vector
restart: unless-stopped
volumes:
- /mnt/nvme-pool/docker/qdrant/storage:/qdrant/storage
ports:
- "6333:6333"
environment:
- QDRANT__SERVICE__GRPC_PORT=6334
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
restart: unless-stopped
ports:
- "3000:8080"
volumes:
- /mnt/nvme-pool/docker/open-webui:/app/backend/data
environment:
- OLLAMA_BASE_URL=http://ollama:11434
- VECTOR_DB=qdrant
- QDRANT_URI=http://qdrant:6333
- RAG_EMBEDDING_MODEL=bge-m3:latest
- CHUNK_SIZE=1000
- CHUNK_OVERLAP=150
depends_on:
- ollama
- qdrant
四、私有知识库(RAG)吞吐与精度调优细节
服务跑起来后,直接进入 http://nas-ip:3000 注册初始管理员账号。搭建 RAG 知识库时,很多新手直接选默认配置,上传几个 PDF 后发现提问要么答非所问,要么卡顿半分钟。这里有三个关键调优要点:
1. Embedding 模型选型与预拉取
在 NAS 端执行终端命令,提前拉取中英文检索表现优异的开源向量模型:
docker exec -it ollama-core ollama run bge-m3
docker exec -it ollama-core ollama run qwen2.5:7b-instruct-q4_K_M
bge-m3支持多语言且支持混合检索,占用显存约 1.2GB。当 Open-WebUI 配置该模型后,向量化文档速度会比调用 CPU 通用算法提升 5 倍以上。
2. 分块(Chunking)与重排参数精细化
在 Open-WebUI 的“管理员面板 -> 文档”设置中:
- Top K 检索条数:设为
3 ~ 5。NAS 小显存经不起拼入十几条长片段,上下文一长,推理延迟成倍增加。 - 分块大小(Chunk Size):对于结构化的技术笔记或 API 手册,建议设为
800,重叠区间(Overlap)设为120。这能确保函数定义或配置参数不会在切片边界被硬性斩断。
五、实操踩坑与故障排查清单
在实际跑了半个月的过程中,我把几个最典型的问题记录成了排障备忘:
坑点 1:容器重启后显卡失去识别
现象:NAS 系统自动更新或重启后,Ollama 日志显示no GPU detected, falling back to CPU。
原因与对策:NAS 系统内核模块更新后,Nvidia 驱动未自动加载。在宿主机添加定时启动脚本,确保在 Docker daemon 启动前执行:
sudo nvidia-modprobe -u -c=0
坑点 2:高频写入导致 SSD 寿命焦虑与内存泄漏
排查:Open-WebUI 自带的文档解析引擎在处理超大 PDF(超 100 页)时,会瞬间生成海量中间缓存文件塞满/tmp。
修复 :在 Docker 配置中将/tmp 挂载为tmpfs(内存临时文件系统),并限制最大大小,例如添加配置:tmpfs: /tmp:size=2G。这样既避免了对固态硬盘颗粒的无效写入消耗,又利用了 NAS 冗余的内存带宽。
总结与低成本部署建议
在 NAS 上搭建完全脱网运行的 AI 助手,核心思想是“量体裁衣、动静分离”。不要贪大求全去上量化损失严重的低精度大模型,7B/8B 精调模型配合精准的 RAG 检索,处理日常代码查询、Markdown 技术文摘以及内网 API 交互完全游刃有余。
把沉重的模型权重与向量索引放在高速 NVMe 固态盘,把冷备份归档推给 ZFS 机械阵列,这样配置下的 NAS 不仅是一台靠谱的数据堡垒,也是真正能 7×24 小时低功耗工作的私人极客算力中枢。
===EOF===