共计 4524 个字符,预计需要花费 12 分钟才能阅读完成。
很多朋友把淘汰下来的 RTX 3060/4060 Ti 甚至二手 Tesla P40 插进 NAS,打算搭一个 24 小时开机的“私人 AI 中枢”。但真上手跑起 Agent 工作流(比如 Dify 知识库检索、多轮代码重构)时,八成会遇到两个致命问题:一是单用 Ollama 并发能力太弱,两个请求同时触发推理,延迟直接呈断崖式下跌;二是用纯原生 vLLM 部署 Qwen2.5-7B/14B 这类大模型时,默认配置直接吃满 100% 显存,导致 NAS 上原本跑着的 Plex 转码或轻量级 Embedding 容器直接被 OOM-Killer 无情干掉。
上个月我把工作室的自建 NAS(TrueNAS SCALE 宿底,加装单卡 RTX 4090 24G + 单卡 Tesla P4 8G)推倒重做了一套兼顾吞吐与低开销的异构架构: 高吞吐主模型交由 vLLM 提供 OpenAI 规范接口,Embedding 与多模态小模型交给 Ollama 管理,外层通过 LiteLLM 做动态路由与 Token 流控 。实测下来,显存利用率稳定在 88% 以内,多 Agent 压测下首字延迟(TTFT)降低了 62%。
一、架构设计:为什么不建议单一推理引擎?
在折腾 NAS 大模型节点时,必须理清不同框架的底层设计差异:
- Ollama 的优势与软肋: 底层是封装良好的 llama.cpp,擅长 GGUF 动态量化加载,支持 CPU/GPU 混合分层卸载,但默认并发能力和 PagedAttention 机制对长上下文 Batch 任务的调度非常简陋。
- vLLM 的杀伤力与陷阱: 专为高吞吐设计,PagedAttention 能将显存碎片率从 30% 压缩到 4% 以下,但它的致命缺点是“显存霸权主义”——启动时默认会抢占 90% 物理显存用于 KV Cache 预分配,导致 NAS 系统完全失去显存弹性。
因此,对于单卡 16G/24G 的家用 NAS,最稳妥的拓扑是:vLLM 锁定 75% 显存专门吞吐 4-bit/8-bit 量化后的主力 Instruct 模型,留出 20% 空间由 Ollama 动态唤醒运行 bge-m3、nomic-embed 或轻量视觉模型 。二者统一由 LiteLLM 汇聚成一个聚合端点,供 Open WebUI 或 Dify 无缝调用。
二、NAS 宿主机 NVIDIA 驱动与容器透传
无论你是用 Debian 12 裸机还是 TrueNAS SCALE,容器调用 GPU 必须确保 NVIDIA Container Toolkit 安装干净。很多同学在 Docker 里执行 nvidia-smi 报错,通常是忽略了运行时配置。
# 1. 验证宿主机驱动(NAS 终端)nvidia-smi
# 2. 如果缺少 nvidia-container-toolkit,配置 Debian 源并安装
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
三、核心实战:docker-compose 编排与显存严控
我在 NAS 数据池(/mnt/storage/ai-stack)下统一管理模型权重和持久化配置。关键在于:在启动 vLLM 时强制限制 --gpu-memory-utilization,并在 Ollama 中配置模型卸载超时时间,防止它一直赖在显存里不走。
version: '3.8'
services:
# 核心推理引擎:负责高负载主模型(以 Qwen2.5-7B-Instruct-GPTQ-Int4 为例)vllm:
image: vllm/vllm-openai:latest
container_name: vllm-core
runtime: nvidia
restart: unless-stopped
ports:
- "8000:8000"
volumes:
- /mnt/storage/ai-stack/models:/root/.cache/huggingface
environment:
- HUGGING_FACE_HUB_TOKEN=hf_your_token_here
command: >
--model Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4
--quantization gptq
--served-model-name qwen-instruct
--max-model-len 8192
--gpu-memory-utilization 0.70
--max-num-seqs 16
--trust-remote-code
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
# 轻量嵌入 / 辅助引擎:负责向量计算,闲置自动释放
ollama:
image: ollama/ollama:latest
container_name: ollama-aux
restart: unless-stopped
ports:
- "11434:11434"
volumes:
- /mnt/storage/ai-stack/ollama:/root/.ollama
environment:
- NVIDIA_VISIBLE_DEVICES=all
- OLLAMA_KEEP_ALIVE=5m # 关键:5 分钟无调用自动释放显存
- OLLAMA_NUM_PARALLEL=2
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
# 聚合路由代理:统一协议与统一鉴权
litellm:
image: ghcr.io/berriai/litellm:main-latest
container_name: litellm-gateway
restart: unless-stopped
ports:
- "4000:4000"
volumes:
- ./litellm-config.yaml:/app/config.yaml
command: ["--config", "/app/config.yaml", "--port", "4000"]
depends_on:
- vllm
- ollama
LiteLLM 路由配置文件解析
在同目录下新建 litellm-config.yaml。这样你的前端无论请求大模型还是 Embedding,都只需要访问 NAS 的 4000 端口:
model_list:
- model_name: gpt-4o-mini # 映射名称,兼容已有开发工具
litellm_params:
model: openai/qwen-instruct
api_base: http://vllm:8000/v1
api_key: "none"
- model_name: text-embedding-3-small
litellm_params:
model: ollama/bge-m3
api_base: http://ollama:11434
general_settings:
master_key: sk-nassky-master-key-2026
四、关键避坑与深水区排障
1. 为什么 vLLM 启动报 CUDA out of memory?
即便设置了 --gpu-memory-utilization 0.70,vLLM 有时还是在构建计算图阶段崩掉。 这是因为 --max-model-len 默认值常常高达 32768。 每个并发序列的上下文长度直接与 KV Cache 占用的显存挂钩。家用 NAS 跑 Agent,设定 8192 或 12288 就完全够用,盲目拉满只会挤爆缓存池。
2. 机械硬盘与 NVMe 缓存池的巨大性能差异
我在测试时踩过一个物理层面的深坑:千万不要把模型权重(~/.cache/huggingface)放在 NAS 的机械硬盘 RAID 阵列上!以一个 15GB 的模型为例,机械硬盘冷启动加载足足花了 2 分 40 秒,期间 SATA 队列直接堵死,导致运行在同一阵列上的 Nextcloud 连带假死超时。请务必在 NVMe M.2 缓存池或专门的 SSD 数据池上划出独立的 Dataset 存放模型权重,冷启动加载时间直接缩短至 12 秒以内。
3. 锁死 Linux 内存 Swappiness
Linux 默认在内存压力稍大时就会将部分匿名内存换出到 Swap。推理模型如果部分数据被踢到了 NAS 的 Swap 分区(尤其是机械盘 Swap),一次推理就会产生数以千计的 Page Fault,直观表现就是 Agent 回答卡顿 10 秒以上。修改宿主机配置:
# 查看当前交换倾向
cat /proc/sys/vm/swappiness
# 临时修改为 10(优先保留内存)sudo sysctl vm.swappiness=10
# 写入持久化配置
echo "vm.swappiness = 10" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
五、实测表现与资源压测
在单张 RTX 4090 (24GB VRAM) 机器上,使用 Locust 模拟 8 个并发 Agent 工作流同时调用,实测对比数据如下:
- 单纯 Ollama 方案: 并发达到 4 之后,首字响应时间(TTFT)从 480ms 飙升至 3.8s,总吞吐仅为 34 tokens/s,偶发 context 截断错误。
- vLLM + PagedAttention(本文方案):8 并发状态下,TTFT 稳定在 650ms 左右,总吞吐稳定维持在 210 tokens/s,显存稳固锚定在 16.8GB(vLLM)+ 3.2GB(Ollama 跑 bge-m3),剩余 4GB 显存留作安全缓冲,连续压测两小时系统未出现任何抖动。
总结与避坑心得
把 NAS 变成 AI 算力节点并不是简单地拉一个 Docker 镜像跑起来就完事。真正的工程考量在于: 如何给高吞吐模型以最大化的稳定空间,同时给轻量碎片任务留出弹性的资源退出通道。 通过限制 vLLM 显存边界、利用 Ollama 的 Keep-Alive 机制进行弹性腾挪、再配合 LiteLLM 做集中管控,你的 NAS 才能真正从一台“只能存电影的铁盒子”,进化为随叫随到、稳如磐石的私有 Agent 智能底座。