llama.cpp docker本地算力部署

结论

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.7
  • load-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 8080
services:
  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 编排)。原文附件为会话粘贴内容。