结论
Linux 宿主机跑 llama.cpp Docker 不需要传统显卡直通(PCIe / VFIO)。标准做法是容器 GPU 透传:宿主机装驱动 + NVIDIA Container Toolkit,容器经 --gpus all 获得设备与驱动库视图,镜像自带 CUDA 用户态运行时与编译好的 llama-server,模型与配置经数据目录映射进入。
两种直通的本质区别
| 维度 | PCIe 直通 / VFIO | 容器 GPU 透传 |
|---|---|---|
| 对象 | 虚拟机 | 容器进程 |
| 归属 | 整卡从宿主机夺走独占,需 IOMMU 配置 | 宿主机照常持有管理,仅暴露设备节点与驱动库视图 |
| 内核 | 客户机自带内核 | 与宿主机共享同一内核,直接调用宿主机已加载驱动 |
| llama.cpp 场景 | 不需要 | 需要此种 |
驱动层位置与职责划分
- 宿主机:只装 NVIDIA Driver + NVIDIA Container Toolkit。内核模块与
libcuda.so、libnvidia-ml.so由工具包经 OCI Hook 自动挂载进容器。 - 容器内:无需完整内核驱动,但必须自带用户态 CUDA 运行时(
libcudart.so、cuBLAS、cuDNN)。缺失则回退 CPU。 - 判据:容器内
nvidia-smi能跑只证明设备透传成功,不等于 llama.cpp CUDA 可用。 - 宿主机无需另装 CUDA Toolkit,那是冗余操作。原则:宿主机管驱动,容器管运行时。
- 唯一兼容规则:容器 CUDA 版本不得高于宿主机驱动支持上限(
nvidia-smi右上角可查);旧 CUDA 镜像跑在新驱动上通常可行,反之可能报错。 - 澄清:工具包注入的是驱动层
libcuda.so(操控硬件),不是运行时层libcudart.so(供应用调用内存分配与核函数启动,后者由镜像自带)。
分层架构
宿主层:NVIDIA Driver · CUDA Driver API (libcuda.so) · /dev/nvidia*
| nvidia-container-toolkit(挂载设备 / 驱动库 / 注入 CUDA_VISIBLE_DEVICES)
v
Docker 容器
服务层 llama-server:server_http_context -> server_routes -> server_queue -> server_context -> slot -> llama_decode -> server_response
推理层 libllama + ggml:ggml_backend_sched -> ggml-cuda backend -> CUDA kernels(cuBLAS / custom kernels)
运行时依赖:CUDA Runtime (libcudart) · cuBLAS镜像构建(多阶段)
build 阶段用完整 CUDA devel 镜像编译,runtime 阶段只保留运行库与产物。runtime 基镜像必须是 nvidia/cuda:*-runtime,不可换成普通 ubuntu,否则缺 libcudart 导致 ggml-cuda 无法加载。
FROM nvidia/cuda:12.4.1-devel-ubuntu22.04 AS build
RUN apt-get update && apt-get install -y cmake build-essential git curl
COPY llama.cpp/ /app/llama.cpp/
WORKDIR /app/llama.cpp
RUN cmake -B build -DGGML_CUDA=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_CUDA_ARCHITECTURES="${CUDA_DOCKER_ARCH}" && cmake --build build --config Release -j"$(nproc)"
FROM nvidia/cuda:12.4.1-runtime-ubuntu22.04
COPY --from=build /app/llama.cpp/build/bin/llama-server /app/llama-server
WORKDIR /app
ENTRYPOINT ["/app/llama-server"]CUDA_DOCKER_ARCH须匹配目标 GPU 计算能力;llama-server 通常静态链接 libllama / libggml / ggml-cuda,动态依赖libcudart.so、libcublas.so来自 runtime 基镜像。- llama API 不直接调用 CUDA,中间隔
ggml_backend_sched:llama_decode -> ggml_backend_sched_graph_compute切分子图分发,GPU 子图进 ggml-cuda(实现位于ggml/src/ggml-cuda/),经 CUDA stream 异步执行。
容器启动与 GPU 挂载调用链
docker run --gpus all -> Docker daemon -> nvidia-container-runtime
|- 挂载 /dev/nvidia0, /dev/nvidiactl, /dev/nvidia-uvm
|- bind-mount 宿主驱动库 libcuda.so 与 CUDA 兼容库
|- 注入 CUDA_VISIBLE_DEVICES
-> llama-server 启动 -> ggml_backend_load_all() 加载 ggml-cuda 后端 -> 初始化 CUDA 上下文 -> 分配显存llama-server 内部调用链(单次推理)
API 客户端 --HTTP--> server_http_context (cpp-httplib:路由/CORS/API Key/线程池)
-> server_routes (JSON 解析 / chat template / tokenization -> 构造 server_task)
-> server_queue (线程安全 FIFO,HTTP 线程 push,推理线程消费)
-> server_context (专用单线程:持有 llama_context,管理 slot 池,update_slots() 主循环组 batch)
-> server_slot (每并行序列一个:IDLE/STARTED/PROCESSING_PROMPT/GENERATING/DONE_PROMPT,持 sampler 与 KV cache 片段)
-> llama_decode(ctx, batch) -> ggml_backend_sched 切图分发 -> ggml-cuda (CUDA stream / cuBLAS / kernels) -> GPU
-> common_sampler_sample() 采样 -> server_response 队列 -> HTTP 线程经 SSE/JSON 返回核心循环 update_slots():收集各 slot 待处理 token 聚合成统一 llama_batch,一次 llama_decode 后各 slot 分别采样;连续批处理允许不同长度请求共享 batch,有 slot 空闲即可拉入新任务。
INI 预加载约定
llama.cpp 原生支持 --models-preset 加载 INI,load-on-startup 控制启动时是否预加载进显存。
version = 1
[*]
c = 8192
n-gpu-layers = 99
parallel = 1
[my-model]
model = /models/model.gguf
load-on-startup = 1
temp = 0.7load-on-startup = 1:服务启动即载入显存预热,开箱即用;为 0 或缺省则按需加载。- INI 分区:全局
[*]对所有模型生效,模型专属分区覆盖全局。 - 参数优先级由高到低:命令行参数 > INI 模型专属 > INI 全局 > 环境变量(如
LLAMA_ARG_CTX_SIZE)> 内置默认值。--host、--port属路由器级,建议直接写命令行。 - 备选:参数简单固定时可用
LLAMA_ARG_*环境变量(如-e LLAMA_ARG_MODEL=/models/xxx.gguf);多模型复杂参数用 INI 更清晰。
Docker 运行与 Compose 等价配置
docker run --gpus all -p 8080:8080 -v /host/models:/models -v /host/config/my-models.ini:/config/my-models.ini llama-server-cuda:latest --models-preset /config/my-models.ini --host 0.0.0.0 --port 8080services:
llama-server:
image: llama-server-cuda:latest
container_name: llama-server
ports: ["8080:8080"]
volumes:
- /host/models:/models
- /host/config/my-models.ini:/config/my-models.ini
environment:
- CUDA_VISIBLE_DEVICES=0
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
command: --models-preset /config/my-models.ini --host 0.0.0.0 --port 8080
restart: unless-stopped对应关系:--gpus all 对应 Compose 的 deploy.resources.reservations.devices;-p 对应 ports;-v 对应 volumes;-e 对应 environment;镜像后参数对应 command。docker run 与 Compose 二选一,生产推荐 Compose(docker compose up -d)。
关键约束与常见问题
| 问题 | 根因 | 解决方向 |
|---|---|---|
| 容器内 nvidia-smi 正常但报 CUDA 不可用 | runtime 镜像缺 libcudart / cuBLAS,或未以 CUDA 后端编译 | 用 server-cuda 标签或自建 CUDA runtime 阶段镜像 |
| ggml_backend_load_all 未加载 CUDA 后端 | 编译未开 -DGGML_CUDA=ON 或架构不匹配 | 确认 CMake 参数与 CUDA_DOCKER_ARCH |
| 显存不足 | -ngl 过高或 KV cache 超显存 | 降 -ngl,减少 parallel 或上下文长度 |
| 多卡未识别 | 只透传单卡或 CUDA_VISIBLE_DEVICES 限制 | --gpus all 或显式设置可见卡 |
| 首次推理延迟高 | CUDA kernel JIT / cuBLAS 初始化 | 预热请求或编译时实例化对应架构 |
调用关系总览:nvidia-container-toolkit -> CUDA runtime -> ggml-cuda 后端 -> llama_decode -> server_slot -> server_context -> server_queue -> server_routes -> server_http_context。上层并发靠 slot 池 + 连续批处理,下层加速靠 ggml 插件式后端,二者以 llama_batch 与 llama_decode 为边界。
来源
DeepSeek 蒸馏(Linux + NVIDIA + Docker 部署场景:镜像构建、容器启动、服务组件、单次推理调用链、INI 预加载、Compose 编排)。原文附件为会话粘贴内容。