共计 3506 个字符,预计需要花费 9 分钟才能阅读完成。
家里那台配了 64GB 内存的 x86 NAS 如果只用来挂 PT 和跑 Plex,电费交得实在肉疼。上个月我把整套本地 LLM 服务与个人 RAG(检索增强生成)知识库完整搬进了 NAS,作为家庭内网随时调用的技术文档助手和私人 Agent 网关。全天候待机、低功耗、数据不出局域网,体验相当扎实。
但在折腾过程中,我也踩了不少“NAS 特色”的深坑:从 Intel 核显驱动在容器内的权限断层,到 SQLite 放在网络存储池直接爆锁,再到内存命中率低导致的吐字卡顿。这篇指南把这套经过压测验证的生产级部署方案和排障细节完整整理出来,省去大家反复翻日志查报错的时间。
一、技术选型与硬件负载评估
很多朋友第一反应是:NAS 孱弱的 CPU 跑得动大模型?实测下来,只要不盲目追求 32B 以上的大参数模型,目前的轻量量化模型在普通 x86 平台上表现完全可用:
- 算力核心:主流 12 代及以后的 Intel CPU(如 1235U/12600K/N100)搭载的 UHD/Iris Xe 核显,或加装单槽半高卡(如 Tesla P4、RTX 3050 6G、A2000)。我这台使用的是 Intel i5-12400 + 64GB DDR4 + 2TB NVMe 固态。
- 大模型推理引擎:选用
Ollama。内存管理优秀,RESTful 接口标准化,对 CPU AVX2 指令集和 Intel 核显(via OpenVINO)都有良好支持。 - 模型选型:
- 通用问答:
qwen2.5:7b-instruct-q4_K_M(实测占用约 4.7GB 内存 / 显存,CPU 推理速度可达 12~16 tokens/s)。 - 向量嵌入:
bge-m3:latest(中文语义理解极强,多语言混合检索准确率表现优异)。
- 通用问答:
- 向量存储与交互:
Qdrant(Rust 编写,内存控制精准,相比 Chroma 对系统资源极度友好)+Open WebUI(目前交互最成熟的开源大模型 Web 前端)。
二、Docker Compose 生产级编排架构
不要在终端里敲冗长易错的 docker run。我们采用模块化的 docker-compose.yml 统一管理。这里以宿主机直通 Intel 核显并启用 GPU 加速为例,如果是纯 CPU 运行,删掉 devices 节即可:
version: '3.8'
services:
# 1. Ollama 模型推理核心
ollama:
image: ollama/ollama:latest
container_name: ollama
restart: unless-stopped
ports:
- "11434:11434"
environment:
- OLLAMA_KEEP_ALIVE=24h # 模型常驻显存 / 内存,避免频繁冷启动 I /O
- OLLAMA_NUM_PARALLEL=2 # 允许双并发请求,NAS 多核优势拉满
- OLLAMA_FLASH_ATTENTION=1 # 开启闪光注意力机制减少内存占用
volumes:
# 必须映射到高速 NVMe 缓存池,绝对禁止映射到机械硬盘或慢速阵列
- /mnt/nvme-pool/docker/ollama:/root/.ollama
devices:
- /dev/dri:/dev/dri # 挂载核显节点
networks:
- ai-network
# 2. Qdrant 向量数据库
qdrant:
image: qdrant/qdrant:latest
container_name: qdrant
restart: unless-stopped
ports:
- "6333:6333"
volumes:
- /mnt/nvme-pool/docker/qdrant/storage:/qdrant/storage:z
environment:
- QDRANT__SERVICE__GRPC_PORT=6334
networks:
- ai-network
# 3. Open WebUI 交互前端与 RAG 中枢
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
restart: unless-stopped
ports:
- "3000:8080"
environment:
- OLLAMA_BASE_URL=http://ollama:11434
- VECTOR_DB=qdrant
- QDRANT_URL=http://qdrant:6333
- RAG_EMBEDDING_MODEL=bge-m3:latest
- CHUNK_SIZE=1024
- CHUNK_OVERLAP=128
- WEBUI_AUTH=true # 局域网访问强烈建议开启鉴权
volumes:
- /mnt/nvme-pool/docker/open-webui:/app/backend/data
depends_on:
- ollama
- qdrant
networks:
- ai-network
networks:
ai-network:
name: ai-network
driver: bridge
启动服务集群:
# 后台启动并检查拉取状态
docker compose up -d
# 预先拉取核心模型
docker exec -it ollama ollama run qwen2.5:7b-instruct-q4_K_M
docker exec -it ollama ollama pull bge-m3:latest
三、三个实战踩坑与性能调优排障
1. 踩坑第一击:网络存储挂载引发 SQLite 死锁
在最初配置时,为了省事,我把 open-webui 的持久化目录直接丢在了通过 NFS 挂载的机械硬盘大存储池(ZFS RaidZ2)上。部署不到两小时,Web 端开始疯狂报 500 内部错误,容器日志显示:
sqlite3.OperationalError: database is locked
原因复盘 :Open WebUI 的元数据、聊天记录以及临时向量索引使用了 SQLite 数据库。SQLite 的写入锁定依赖 POSIX 文件锁,而 NFS、SMB 等网络文件协议对文件锁的支持极其拉胯,高频检索分块时必然发生死锁。 解决方案:所有容器的配置目录和数据库路径(特别是 SQLite 和 Qdrant),必须存放在宿主机本地的 NVMe 驱动盘(通过 Ext4 或本地 ZFS 单盘)上,绝不可跨网络文件系统挂载。
2. 核显硬解加速与 Render GID 权限断层
很多使用 Debian、Ubuntu Server 或 TrueNAS SCALE 的朋友,挂载了 /dev/dri 之后发现 Ollama 根本不走 GPU 推理,intel_gpu_top 纹丝不动,CPU 占用却瞬间飙到 100%。
检查宿主机的渲染组 ID:
ls -l /dev/dri/renderD128
# 通常会输出类似:crw-rw---- 1 root render 226, 128 ...
查看宿主机 render 组的 GID:
getent group render | cut -d: -f3
# 假设输出为 107
容器内部的默认运行用户若未加入该 GID,就无法调用核显。需要在 docker-compose.yml 的 Ollama 服务中加入参数:
group_add:
- "107" # 替换为宿主机实际的 render GID
3. 吞吐量优化:ZFS ARC 缓存冲突与 Hugepages 调优
当 NAS 运行 ZFS 时,ZFS 默认会吃掉 50% 甚至更多的物理内存作为 ARC(自适应替换缓存)。当 Ollama 突然需要加载一个 7B 模型(申请 5GB~8GB 连续内存空间)时,Linux 内核会陷入剧烈的 kswapd 内存回收震荡,导致 NAS 整体 I/O 瞬间假死近 10 秒。
解决策略是强制限制 ZFS ARC 的最大占用,并开启大页内存(Transparent Hugepages):
# 限制 ZFS ARC 最大占用为 16GB(根据你的总内存调整,示例为 64G 机器)echo "options zfs zfs_arc_max=17179869184" >> /etc/modprobe.d/zfs.conf
# 临时生效
echo 17179869184 > /sys/module/zfs/parameters/zfs_arc_max
# 确认开启透明大页,减少页表开销
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
四、实测表现与 RAG 检索调优
配置完成后,通过局域网设备访问 http://NAS-IP:3000 进入 Open WebUI。在「文档」模块中上传了 120 份共计 45MB 的技术 PDF 与 Markdown 架构文档进行切片测试。
| 硬件模式 | 模型参数 | Prefill 速度 (Prompt 处理) | Eval 速度 (生成吐字) | 首字延迟 (TTFT) |
|---|---|---|---|---|
| 纯 CPU (i5-12400 6C/12T) | Qwen2.5-7B-Q4 | 58
正文完
评论(没有评论)
|