03 | 更新:Ollama 换成 llama.cpp构建更小的离线镜像

之前的方案是 FastAPI 调用 Ollama,把 qwen2:0.5b 打进 Ollama 镜像做离线部署。问题在于 Ollama 镜像太大 (基础镜像约 2.6G;上一篇里打包好的 llm-serving-ollama:0.1.0 为 4.58GB),而实际只需要"加载一个 0.5B 的 GGUF 模型并提供推理接口"。

llama.cpp 自带的 llama-server 正好满足这个需求:

  • 直接加载 GGUF 文件,不需要 Ollama 的模型库、拉取、管理那一整套机制;
  • 原生提供 OpenAI 兼容接口(/v1/chat/completions、/v1/models)和 /health;
  • 模型文件复制进镜像即可离线运行。

这一次:下载模型、准备基础镜像、编写 Dockerfile、构建 llm-serving-llama:0.1.0。镜像的运行验证、Helm 配置和 Kubernetes 部署后面更新。

过程概览

步骤 操作 关键内容
1 下载 GGUF 模型 下载 Qwen2-0.5B-Instruct 的 Q4_K_M 模型至 llama/models/
2 下载基础镜像 docker pull ghcr.io/ggml-org/llama.cpp:server
3 编写 Dockerfile 配置模型复制、llama-server 启动参数和监听端口 8080
4 构建本地镜像 构建 llm-serving-llama:0.1.0,使用 --pull=false

步骤 4 有原始日志;步骤 1~3 在日志中没有直接输出,下文按"补充"处理。

验证结果

  • 镜像 llm-serving-llama:0.1.0 构建成功,首次构建 96.5s(含拉取基础镜像),镜像 ID bb93e849f185...。
  • 基础镜像已在本地时,用 --pull=false 重新构建只需 1.4s,所有层命中缓存。
  • 模型文件复制进镜像仅用 1.0s,构建上下文约 397.90MB,即模型文件的大小。

环境信息(来自日志)

  • 宿主机:MacBook,Docker Desktop(docker:desktop-linux)
  • 项目目录:llm-serving
  • 基础镜像:ghcr.io/ggml-org/llama.cpp:server(digest sha256:fc9b506e...8ed2f6)
  • 模型文件:qwen2-0_5b-instruct-q4_k_m.gguf
  • 目标镜像:llm-serving-llama:0.1.0

一、准备模型与基础镜像

1.1 下载 GGUF 模型(补充)

日志中没有记录下载过程,只有构建时的结果:模型文件位于 llama/models/qwen2-0_5b-instruct-q4_k_m.gguf,构建上下文传输了约 397.90MB。该文件通常来自 Hugging Face 上 Qwen 官方发布的 GGUF 仓库(例如 Qwen/Qwen2-0.5B-Instruct-GGUF)。下面是一个示例做法,请按你实际的下载方式调整:

bash 复制代码
mkdir -p llama/models
huggingface-cli download Qwen/Qwen2-0.5B-Instruct-GGUF \
  qwen2-0_5b-instruct-q4_k_m.gguf \
  --local-dir llama/models
ls -lh llama/models

选择 Q4_K_M 量化是体积与质量的折中:约 390MB 的模型文件,CPU 上就能运行。

1.2 下载基础镜像

bash 复制代码
docker pull ghcr.io/ggml-org/llama.cpp:server

这是 llama.cpp 官方发布在 GitHub Container Registry 上的 server 镜像(只含 llama-server)。从日志看,如果本地没有这个镜像,docker build 会在构建过程中自动拉取(见 2.2 的构建输出)。


二、构建 llama.cpp 离线镜像

2.1 编写 Dockerfile(补充)

日志中没有贴出 llama/Dockerfile。根据构建步骤(WORKDIR /app、COPY models/... /models/...,大小 322B),以及后续容器日志中的信息(模型路径 /models/qwen2-0_5b-instruct-q4_k_m.gguf、监听 0.0.0.0:8080、启动命令 /app/llama-server、模型 id 为 qwen2:0.5b),可以推断它大致如下,仅供参考:

dockerfile 复制代码
FROM ghcr.io/ggml-org/llama.cpp:server

WORKDIR /app

COPY models/qwen2-0_5b-instruct-q4_k_m.gguf /models/qwen2-0_5b-instruct-q4_k_m.gguf

EXPOSE 8080

ENTRYPOINT ["/app/llama-server"]
CMD ["-m", "/models/qwen2-0_5b-instruct-q4_k_m.gguf", \
     "--host", "0.0.0.0", \
     "--port", "8080", \
     "--alias", "qwen2:0.5b"]

几点说明:

  • 基础镜像里预设了 LLAMA_ARG_HOST、LLAMA_ARG_PORT 环境变量,而启动命令里显式传了 --host 和 --port ,以命令行参数为准(容器日志里有对应的 warn 提示)。
  • 监听端口定为 8080,后续容器端口映射和 Kubernetes Service 都要与它对应。
  • 用 --alias 把模型 id 设为 qwen2:0.5b,与之前 Ollama 方案中的模型名一致,上层 FastAPI 的 DEFAULT_MODEL 不用改。

2.2 构建镜像

bash 复制代码
(.venv) (base) igwanglyang@MacbookProdahouzicn llm-serving % docker build \
  -t llm-serving-llama:0.1.0 \
  -f llama/Dockerfile \
  llama
[+] Building 96.5s (9/9) FINISHED                                                         docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                    0.0s
 => => transferring dockerfile: 322B                                                                    0.0s
 => [internal] load metadata for ghcr.io/ggml-org/llama.cpp:server                                      3.3s
 => [auth] ggml-org/llama.cpp:pull token for ghcr.io                                                    0.0s
 => [internal] load .dockerignore                                                                       0.0s
 => => transferring context: 2B                                                                         0.0s
 => [1/3] FROM ghcr.io/ggml-org/llama.cpp:server@sha256:fc9b506e89c1a1cfdd7bc056480f54dc657c1c04d76839a08642dc280b8ed2f6   91.4s
 => => resolve ghcr.io/ggml-org/llama.cpp:server@sha256:fc9b506e89c1a1cfdd7bc056480f54dc657c1c04d76839a08642dc280b8ed2f6    0.0s
 => => sha256:8cd4a0822fc4b025faa5ad678bc5076727907e00997ba8fcf5ead1a40a587723 27.59kB / 27.59kB       1.1s
 => => sha256:fc9b506e89c1a1cfdd7bc056480f54dc657c1c04d76839a08642dc280b8ed2f6 1.42kB / 1.42kB         0.0s
 => => sha256:16fc96974cd6c0e7c8f9a2a5085446279dff2c00dda73a7b64a4ef66ab2eb379 1.75kB / 1.75kB         0.0s
 => => sha256:147880ee65ff2c9b6ddff83c26f4ec71dd4eb191b1c269eec4d5dbf7af8a17ea 4.89kB / 4.89kB         0.0s
 => => sha256:3dd7229bb5739a4d9511ab52141929d26651f52c5e9edb82e2af9c8d33e88d8a 255.44MB / 255.44MB    86.5s
 => => sha256:b3b8bb43fdfa2740e7c7310376ed90a66f6898d8654c2430b597bd655fd434f6 13.41MB / 13.41MB       9.0s
 => => sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1 32B / 32B               1.9s
 => => extracting sha256:3dd7229bb5739a4d9511ab52141929d26651f52c5e9edb82e2af9c8d33e88d8a              4.6s
 => => extracting sha256:b3b8bb43fdfa2740e7c7310376ed90a66f6898d8654c2430b597bd655fd434f6              0.1s
 => => extracting sha256:8cd4a0822fc4b025faa5ad678bc5076727907e00997ba8fcf5ead1a40a587723              0.0s
 => => extracting sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1              0.0s
 => [internal] load build context                                                                       3.3s
 => => transferring context: 397.90MB                                                                   3.1s
 => [2/3] WORKDIR /app                                                                                  0.2s
 => [3/3] COPY models/qwen2-0_5b-instruct-q4_k_m.gguf /models/qwen2-0_5b-instruct-q4_k_m.gguf          1.0s
 => exporting to image                                                                                  0.5s
 => => exporting layers                                                                                 0.5s
 => => writing image sha256:bb93e849f185ccdaecf908cb67cbe7e667d4ce44872992e1a8c9a5408d4f336b            0.0s
 => => naming to docker.io/library/llm-serving-llama:0.1.0                                              0.0s

View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/wxxd6lzdc6ne72uzbi344w0mm

原始输出的列间距很宽,这里压缩了空白,内容没有改动。

构建输出里的要点:

  • 基础镜像拉取占了大头 :主要层是一个 255.44MB 的层加一个 13.41MB 的层,[1/3] FROM 一步就耗时 91.4s。
  • 模型复制很快 :COPY models/...gguf 只用了 1.0s;构建上下文 397.90MB 传输用了 3.1s。
  • 镜像最终命名为 docker.io/library/llm-serving-llama:0.1.0。

2.3 基础镜像已在本地时,用 --pull=false 重新构建

基础镜像已经下载过,再次构建时可以加 --pull=false,避免构建时去检查/拉取基础镜像(在网络受限、镜像源不稳定的环境下尤其有用):

bash 复制代码
(.venv) (base) igwanglyang@MacbookProdahouzicn llm-serving % docker build \
  --pull=false \
  -t llm-serving-llama:0.1.0 \
  -f llama/Dockerfile \
  llama
[+] Building 1.4s (9/9) FINISHED                                                          docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                    0.0s
 => => transferring dockerfile: 322B                                                                    0.0s
 => [internal] load metadata for ghcr.io/ggml-org/llama.cpp:server                                      1.3s
 => [auth] ggml-org/llama.cpp:pull token for ghcr.io                                                    0.0s
 => [internal] load .dockerignore                                                                       0.0s
 => => transferring context: 2B                                                                         0.0s
 => [1/3] FROM ghcr.io/ggml-org/llama.cpp:server@sha256:fc9b506e89c1a1cfdd7bc056480f54dc657c1c04d76839a08642dc280b8ed2f6   0.0s
 => [internal] load build context                                                                       0.0s
 => => transferring context: 89B                                                                        0.0s
 => CACHED [2/3] WORKDIR /app                                                                           0.0s
 => CACHED [3/3] COPY models/qwen2-0_5b-instruct-q4_k_m.gguf /models/qwen2-0_5b-instruct-q4_k_m.gguf    0.0s
 => exporting to image                                                                                  0.0s
 => => exporting layers                                                                                 0.0s
 => => writing image sha256:0c9f67dd3312101f8d1eab7bf083a764e1f5f184453c2909a63aa45bb31ef46b            0.0s
 => => naming to docker.io/library/llm-serving-llama:0.1.0                                              0.0s

这次构建只花了 1.4s:所有层命中缓存,构建上下文只有 89B(Docker 判断模型文件没有变化)。

注意 :FROM 要写完整的镜像地址 ghcr.io/ggml-org/llama.cpp:server。如果只写成 llama.cpp:server,Docker 会把它当作 Docker Hub 上的 library/llama.cpp,解析失败。


三、确认镜像(补充)

日志里没有列出镜像大小。建议在构建完成后执行下面的命令,确认镜像已生成并记录体积,用来和之前的 Ollama 离线镜像对比:

bash 复制代码
docker images | grep llm-serving

对比参考:上一篇日志中 llm-serving-ollama:0.1.0 为 4.58GB 。llama.cpp 镜像由约 270MB 的基础镜像层(255.44MB + 13.41MB)加上约 390MB 的模型文件组成,体积预期会明显小于 Ollama 方案;具体数字以你机器上 docker images 的实际输出为准,请补进文章。


总结

把 llama.cpp 打包成离线镜像的完整路径:

bash 复制代码
# 1. 准备模型和基础镜像
huggingface-cli download Qwen/Qwen2-0.5B-Instruct-GGUF qwen2-0_5b-instruct-q4_k_m.gguf --local-dir llama/models
docker pull ghcr.io/ggml-org/llama.cpp:server

# 2. 构建镜像(首次)
docker build -t llm-serving-llama:0.1.0 -f llama/Dockerfile llama

# 3. 基础镜像已在本地时重建
docker build --pull=false -t llm-serving-llama:0.1.0 -f llama/Dockerfile llama

# 4. 确认镜像
docker images | grep llm-serving

经验点:

  1. 按需选型 :只需要"加载 GGUF 并提供推理接口"时,llama-server 比 Ollama 精简得多,不用带上模型管理那一套。
  2. 模型直接 COPY 进镜像:一次构建,处处离线可跑;模型复制只需约 1 秒,耗时主要在首次拉取基础镜像。
  3. --pull=false 与完整镜像地址 :基础镜像在本地时用 --pull=false 加快构建;FROM 始终写完整的 ghcr.io/... 地址。
  4. 提前对齐端口和模型名 :Dockerfile 里固定监听 8080、模型别名 qwen2:0.5b,方便后续与 FastAPI、Kubernetes 配置对接。
相关推荐
单向箔1 小时前
08|一套可复制的 AI Coding 流程:从需求到提交怎么走
人工智能·prompt·ai编程
AI棒棒牛1 小时前
YOLO26 改进:融合 LW-DETR 思想的轻量 TokenMixer,让 Neck 从局部堆叠走向局部-全局协同!嘎嘎创新!
人工智能·深度学习·yolo·目标检测
Hello.Reader1 小时前
REA 深度实战让 AI Agent 学会逆向工程,从软件行为分析到二进制原理还原
人工智能
William一直在路上1 小时前
垂直领域大模型训练的一点思考和探索
人工智能·大模型·llm
腾讯云大数据1 小时前
本体立骨架,语义供刻度——DataBuddy对企业AI语义架构的一次完整回答
大数据·人工智能·架构·云计算·腾讯云
Imagination官方博客1 小时前
如何在 E 系列GPU上使用 llama.cpp 和 千问3.5 运行 OpenCode
软件构建·llama·gpu算力
武汉星际互动1 小时前
从“一件事”到“一类事”,政务服务改革的“下一站”在哪里?
大数据·人工智能·政务
朝朝辞暮i1 小时前
VLA 系统学习第 20 课——Diffusion Policy 为什么要先给动作加噪,再学习把噪声去掉?
人工智能·python·深度学习·transformer·vla
七夜zippoe1 小时前
Agent 权限边界:别让 AI 误删你的生产库(安全设计)
数据库·人工智能·安全·ai·权限边界·安全设计