NAS打造私有云Agent算力中枢:vLLM与Ollama混搭架构实战

2次阅读
没有评论

共计 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 智能底座。

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