跳转至

Ollama 部署 Qwen2.5-0.5B 实验总结

〇. 全局思路:用 Docker 类比理解 Ollama

先看这段,建立整体认知再往下读细节。

Ollama ≈ 管理模型的 Docker:Docker 管理"镜像",Ollama 管理"模型"——都是"从仓库拉到本地 → 按名字管理 → 跑起来对外服务"。

Docker 步骤 Ollama 对应步骤 本实验实际做法
装 Docker 装 Ollama 官方脚本下载慢 → gh-proxy.com 代理下载 1.4G 包,bin+lib 都要装
起服务 ollama serve(前台) 默认前台跑
Docker Hub 拉镜像 Ollama 官方源拉模型 海外源拉不动
国内镜像站下 tar 包 魔搭直链下 GGUF wget 魔搭直链,469MB
docker load 导入 ollama create 导入 写 Modelfile → ollama create qwen05
docker run 启动 ollama run qwen05 对话 / API 调用
systemd 托管 Docker systemd 托管 Ollama 写 service 文件 → systemctl enable --now

完整链路一句话:装 Ollama → 下 GGUF → ollama create 导入 → ollama run 启动 → 写 systemd 转后台常驻。

唯一差异点:Ollama 模型没有 Docker 那种"分层镜像"概念,模型就是一堆文件 + 一个 Modelfile 描述,没有构建复杂度。这套"下文件 → 起服务 → 暴露 API"的心智模型以后学 vLLM / SGLang 同样适用。

一. 实验说明

1. 实验目的

在纯 CPU 的阿里云服务器上把"本地部署大模型"整条链路走通:装推理框架 → 下载模型 → 导入 → 对话 → API 调用。选择 0.5B 模型是因为它体积小(约 500MB)、CPU 就能跑,适合当练手材料。

注:0.5B 是玩具级模型,做正经翻译/推理质量很差。本实验的价值是低成本把部署链路跑通,为以后部署 7B/13B 或 GPU 推理(vLLM)打基础。

2. 实验环境

项目 值
服务器 阿里云 ECS(主机名 my-yun),纯 CPU 无 GPU
系统 RockyLinux 系,包管理 dnf
Python 3.6(过老,导致 modelscope 工具不可用,见 四.1)
模型 Qwen2.5-0.5B-Instruct-GGUF(q4_k_m 量化,约 469MB)
推理框架 Ollama 0.33.3

3. 结论

  • ✅ 整条链路跑通:模型能对话、API 能调用、systemd 托管成功
  • ✅ 模型下载走魔搭直链,阿里云服务器上速度飞快,无墙问题
  • ⚠️ 全程踩了 6 个坑(海外源慢、缺 zstd、Python 过老、段错误……),见 九. 踩坑记录汇总

二. 关键概念(先搞懂再动手)

名词 解释
推理框架 把模型"跑起来"并提供接口的软件,如 Ollama / llama.cpp / vLLM
Ollama LLM 界的 "Docker":负责加载模型、对外提供 OpenAI 兼容 API,本身不生产模型
魔搭 ModelScope LLM 界的 "Docker Hub":模型仓库,Qwen/DeepSeek 都有,只负责存和下载
GGUF 适合 CPU 加载的模型文件格式,支持量化
量化(Q4_K_M) 把权重从 16bit 压到 4bit,体积小、速度快,精度略降
OpenAI 兼容 API 各框架统一模仿 OpenAI 的 /v1/chat/completions 接口,客户端可通用对接

一句话流程:魔搭(仓库)下载 GGUF → Ollama(运行时)加载 → 11434 端口提供 API。 用 Docker 类比:Docker Hub 拉不动 → 从国内镜像下 tar 包 → docker load 导入再跑,ollama create 干的就是 docker load 的活。

三. 安装 Ollama

1. 坑①:官方脚本报缺 zstd

[root@my-yun ~]# curl -fsSL https://ollama.com/install.sh | sh
>>> Installing ollama to /usr/local
ERROR: This version requires zstd for extraction. Please install zstd and try again:
  - RHEL/CentOS/Fedora: sudo dnf install zstd

原因:安装包是 zstd 压缩格式,系统缺解压工具。解决:装 zstd 后重跑(以下为纯执行命令,可整段复制粘贴):

dnf -y install zstd

2. 坑②:官方脚本卡死在海外下载源

[root@my-yun ~]# curl -fsSL https://ollama.com/install.sh | sh
>>> Cleaning up old version at /usr/local/lib/ollama
>>> Installing ollama to /usr/local
>>> Downloading ollama-linux-amd64.tar.zst
                                                                          0.1%

注:阿里云服务器访问 ollama.com/GitHub 走国际出口,速度约 0.1%~25KB/s(下载包 1.4GB 要十几个小时且随时断)。跟本地网络无关,服务器能通也下不动。解决:走 GitHub 加速代理。

3. 代理测速,挑最快的

[root@my-yun ~]# for p in ghfast.top gh-proxy.com mirror.ghproxy.com ghproxy.net gh.llkk.cc; do
  echo "== $p =="
  curl -sL -o /dev/null --max-time 8 -w '速度: %{speed_download} B/s\n' \
    "https://$p/https://github.com/ollama/ollama/releases/latest/download/ollama-linux-amd64.tar.zst"
done

测速结果汇总:

代理 速度 结论
gh-proxy.com 24.4 MB/s ✅ 选它
ghfast.top 41 KB/s 太慢
ghproxy.net 26 KB/s 太慢
mirror.ghproxy.com 0 不通
gh.llkk.cc 0 不通

4. 用 gh-proxy.com 下载安装包(24MB/s,约 1 分钟下完)

cd /tmp
wget -c "https://gh-proxy.com/https://github.com/ollama/ollama/releases/latest/download/ollama-linux-amd64.tar.zst" -O ollama.tar.zst

注:-c 断点续传,中断后重跑同一条命令接着下。GitHub 加速代理是社区公益的,域名常换,失效就重新测速。

下载完校验是"真包"不是错误页:

[root@my-yun tmp]# ls -lh ollama.tar.zst
-rw-r--r-- 1 root root 1.4G Sep  4 01:15 /tmp/ollama.tar.zst
[root@my-yun tmp]# file ollama.tar.zst
/tmp/ollama.tar.zst: Zstandard compressed data (v0.8+), Dictionary ID: None

5. 坑③:系统 tar 不支持 --zstd,分两步解压

zstd -d /tmp/ollama.tar.zst -o /tmp/ollama.tar
mkdir -p /opt/ollama-pkg
tar -xf /tmp/ollama.tar -C /opt/ollama-pkg

zstd 解压会打印解压后大小(2.2GB 正常,解压后比压缩包大):

[root@my-yun tmp]# zstd -d /tmp/ollama.tar.zst -o /tmp/ollama.tar
/tmp/ollama.tar.zst : 2262210560 bytes

6. 坑④:只装 bin 导致 Segfault(段错误)

第一次只把 bin/ollama(误以为 12M 残缺文件就是全部)install 到系统,结果 ollama create / ollama list 全部段错误:

[root@my-yun ~]# ollama create qwen05 -f /data/models/qwen05/Modelfile
Segmentation fault

排查 file 发现是残缺的 ELF:

[root@my-yun tmp]# file /usr/local/bin/ollama
/usr/local/bin/ollama: ELF 64-bit LSB shared object, x86-64, ..., missing section headers

原因:Ollama 是"外壳 + 引擎库"结构——bin/ollama 只是主程序(38M),真正干推理的引擎在 lib/ollama/ 目录下几百 MB 的 .so 共享库。只装 bin 不装 lib,程序运行到一半找不到自己的代码就崩。

正确安装:bin 和 lib 都要装(纯执行命令):

install -m 755 /opt/ollama-pkg/bin/ollama /usr/local/bin/ollama
mkdir -p /usr/local/lib/ollama
cp -a /opt/ollama-pkg/lib/ollama/. /usr/local/lib/ollama/

验证版本(第一条 Warning 只是提示服务没启动,能出版本号 = 装好了):

[root@my-yun tmp]# ollama --version
Warning: could not connect to a running Ollama instance
Warning: client version is 0.33.3

四. 下载模型(魔搭直链,国内飞快)

1. 坑⑤:modelscope 工具跑不起来(Python 3.6 太老)

魔搭官方下载工具装上了但报语法错误:

[root@my-yun ~]# modelscope download --model Qwen/Qwen2.5-0.5B-Instruct-GGUF
  File "/usr/local/lib/python3.6/site-packages/modelscope/utils/file_utils.py", line 238
    while byte_chunk := f.read(buffer_size):
                      ^
SyntaxError: invalid syntax

注::= 叫海象运算符,Python 3.8 才引入,系统的 Python 3.6 直接语法报错。不要为了它升级系统 Python(容易搞坏系统),绕开它用 wget 直链下载。

2. 用 wget 下魔搭直链

魔搭每个文件都有直链,格式:仓库地址/resolve/master/文件名(纯执行命令):

mkdir -p /data/models/qwen05
cd /data/models/qwen05
wget -c "https://modelscope.cn/models/Qwen/Qwen2.5-0.5B-Instruct-GGUF/resolve/master/qwen2.5-0.5b-instruct-q4_k_m.gguf" -O qwen2.5-0.5b-instruct-q4_k_m.gguf

魔搭是阿里系,服务器上速度飞快,几十秒下完。校验大小(几 KB = 下到错误页,几百 MB 才对):

[root@my-yun qwen05]# ls -lh qwen2.5-0.5b-instruct-q4_k_m.gguf
-rw-r--r-- 1 root root 469M May 21 09:01 qwen2.5-0.5b-instruct-q4_k_m.gguf

注:模型仓库页面文件列表里给的是字节数(如 428730208 ≈ 409MB)而不是"409MB"这种写法;不同量化版本(q2_k/q4_0/q4_k_m/q8_0/fp16)体积差异大,下载前在网页核对文件名,别下错版本也别下整个仓库。

五. 导入模型并对话

1. 写 Modelfile 并导入(ollama create 等价于 docker load)

cat > /data/models/qwen05/Modelfile <<'EOF'
FROM /data/models/qwen05/qwen2.5-0.5b-instruct-q4_k_m.gguf
EOF

导入前需要 ollama 服务在运行,另开终端执行:

[root@my-yun ~]# ollama create qwen05 -f /data/models/qwen05/Modelfile
gathering model components
copying file sha256:74a4da8c9fdbcd15bd1f6d01d621410d31c6fc00986f5eb687824e7b93d7a9db 100%
parsing GGUF
verifying conversion
creating new layer sha256:969a560cd5d289a304ed169d293a9c6687c088ac5390077690d7e567cc938ee7
writing manifest
success

[root@my-yun ~]# ollama list
NAME             ID              SIZE      MODIFIED
qwen05:latest    42660c488ef5    491 MB    3 minutes ago

注:ollama create 里的 qwen05 是自定义的本地名字,以后 run/API 调用都用它,与官方命名无关。模型数据默认存在 /root/.ollama 下。

2. 对话测试

[root@my-yun ~]# ollama run qwen05
>>> 你好,介绍一下你自己
我是Qwen,一种人工智能模型。我被设计用于处理各种问题和回答问题。……

坑⑥:从网页/聊天窗口复制命令会带上隐藏的控制字符(如 11;rgb:2828/2a2a/3636),粘贴后要么被当成命令执行(-bash: 11: command not found),要么混进输入内容。对策:粘贴命令先放到纯文本编辑器过一遍;终端被弄乱执行 reset 恢复。

六. API 验证(部署实验的核心)

Ollama 自动提供 OpenAI 兼容接口,这是以后接一切客户端(OpenWebUI、翻译工具等)的入口:

[root@my-yun ~]# curl -s http://127.0.0.1:11434/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen05","messages":[{"role":"user","content":"1+1等于几"}],"stream":false}'

返回 JSON(标准 OpenAI 格式):

{
  "id": "chatcmpl-950",
  "object": "chat.completion",
  "created": 1788915799,
  "model": "qwen05",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "1+1等于2。" },
      "finish_reason": "stop"
    }
  ],
  "usage": { "prompt_tokens": 34, "completion_tokens": 7, "total_tokens": 41 }
}

注:关键字段——choices[0].message.content 是模型回答;finish_reason: stop 表示正常结束;usage 是 token 统计。返回这个格式 = 任何 OpenAI 客户端把 base_url 指向 http://<服务器IP>:11434/v1 即可对接。

1. 这一步到底在做什么

本质是亲手扮演一次"客户端程序",向模型服务发了一个 HTTP 请求并收到回复。

用 Docker 类比:容器起来后 curl http://localhost:8080 验证网站通不通,那个 curl 是在模拟浏览器访问。这里的 curl 就是模拟任何 AI 客户端(OpenWebUI/翻译工具/agent 内部替你发的都是这种请求)。

请求里带了三样东西:

参数 含义
model 调哪个模型(qwen05)
messages 对话内容(role=user 表示用户说的话)
stream 是否流式返回(false = 一次给完整回答)

意义:证明模型已不是"能聊天的程序",而是"按标准协议对外提供服务的接口"——这是部署与"自己跑着玩"的分水岭。

2. 需要生成 API Key 吗?——不需要

Ollama 不生成 API Key,也不校验 Key。 原因:

OpenAI 官网 本地 Ollama
为什么有 Key 用别人的服务器,要验证身份 + 计费 服务只监听 127.0.0.1(自己的机器)
Key 的作用 你是谁、给你算钱 不需要——本地已经信任你

在 agent / AI 客户端里对接本地模型时,配置项长这样——api_key 填任意占位符即可:

base_url = http://<服务器IP>:11434/v1    # 指向谁(关键)
model    = qwen05                         # 用哪个模型(关键)
api_key  = ollama                         # 任意占位符,Ollama 不校验

什么时候才真的需要 Key:Key = 身份验证 + 配额管理,只有"多人/跨网络使用"才需要。以后要把模型开放给别人用,正确姿势是在前面加一层 API 网关(如开源 new-api/one-api,负责发 Key、限流、审计),再转发给后端 Ollama——思路跟 nginx 反代 + 鉴权是一路的。

七. systemd 托管(后台常驻 + 开机自启)

手动安装的 ollama 没有注册 systemd,前台 ollama serve 一关终端就没了。写服务文件并启用(纯执行命令):

pkill -f "ollama serve"
cat > /etc/systemd/system/ollama.service <<'EOF'
[Unit]
Description=Ollama Service
After=network-online.target

[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/ollama serve
Restart=always
RestartSec=3
Environment="OLLAMA_HOST=127.0.0.1:11434"
Environment="PATH=/usr/local/bin:/usr/bin:/bin"

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now ollama

验证服务状态,应看到 Active: active (running):

[root@my-yun ~]# systemctl status ollama

再测一次 API 确认 systemd 托管后一切正常:

[root@my-yun ~]# curl -s http://127.0.0.1:11434/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen05","messages":[{"role":"user","content":"你好"}],"stream":false}'
{"id":"chatcmpl-863","object":"chat.completion","created":1788915920,"model":"qwen05","system_fingerprint":"fp_ollama","choices":[{"index":0,"message":{"role":"assistant","content":"你好! 我是中国阿里云开发的一个AI助手。有什么我可以帮你的好吗?"},"finish_reason":"stop"}],"usage":{"prompt_tokens":30,"prompt_tokens_details":{"cached_tokens":0},"completion_tokens":19,"total_tokens":49}}

⚠️ 两个注意点: ① 实验用了 User=root,因为模型存在 /root/.ollama 下,换用户会找不到模型。个人实验机无所谓,生产环境应建低权限专用用户并迁移模型目录。 ② OLLAMA_HOST 保持 127.0.0.1(只本机访问)。11434 端口没有鉴权,开 0.0.0.0 等于裸奔——云服务器对外提供必须加安全组白名单或 nginx 反代鉴权。

八. 清理临时文件

rm -f /tmp/ollama.tar.zst /tmp/ollama.tar
rm -rf /opt/ollama-pkg

九. 踩坑记录汇总

# 坑 现象 原因 解决
1 缺 zstd 官方脚本报错退出 系统没有 zstd 解压工具 dnf -y install zstd
2 海外源下载慢 0.1%~25KB/s,十几个小时 ollama.com/GitHub 走国际出口 gh-proxy.com 代理 24MB/s
3 tar 不认 zstd tar --zstd 报 unrecognized option 系统 tar 版本老 zstd -d 解出 .tar 再 tar -xf
4 段错误 ollama 命令 Segmentation fault 只装了 bin 缺 lib/ollama 引擎库 补装 /usr/local/lib/ollama
5 modelscope 用不了 Python 语法报错 系统 Python 3.6 < 3.8(海象运算符) 放弃工具,用 wget 魔搭直链
6 粘贴乱码 终端出现 11;rgb:... 或 command not found 复制带隐藏 ANSI 控制字符 纯文本中转;reset 恢复

十. 推理框架全家桶(先认清各自定位)

本实验用的 Ollama 只是推理框架的一种,完整的生态是:

框架 定位 底层 硬件 难度 同类关系
Ollama 开箱即用,个人/玩具 llama.cpp CPU/单卡 一条命令 Docker 之于容器
llama.cpp Ollama 的底层引擎 自研 C++ CPU/GPU 中 containerd 之于 Docker
vLLM 生产主流,高并发 自研调度 必须 GPU 高 SGLang 的竞品
SGLang 生产新锐,R1 部署出名 自研调度 + RadixAttention 必须 GPU 高 vLLM 的竞品

易混点:Ollama 和 SGLang 不是 Docker/containerd 那种"高层包底层"的分层关系,而是同级竞争——都是独立的"跑模型的服务"。Docker/containerd 的分层类比只适用于 Ollama/llama.cpp。 通俗理解:Ollama 是傻瓜相机(举起就拍),vLLM/SGLang 是专业电影机(画质天花板但要会调参)。SGLang 因 DeepSeek 官方用它部署 R1 而火,适合 GPU + 高并发生产场景,0.5B/CPU 场景用不上。

1. Ollama vs SGLang:不是 minikube vs K8s

常见误解:把 Ollama/SGLang 类比成 minikube/K8s(同一个软件的单机版 vs 集群版)。不对:

minikube vs K8s Ollama vs SGLang
关系 同一个软件,单节点版 vs 集群版 两个独立软件,不同团队开发
本质 规模不同 定位、引擎、目标硬件都不同

两者是两套独立引擎,不存在"Ollama 是 SGLang 的单机版"。更像 MySQL vs PostgreSQL:都是数据库,但不是同一款的大小号。

真正区别是天花板不同:Ollama 只能单机跑;SGLang/vLLM 单卡能跑、单机多卡能跑、多机集群也能跑——它们"支持到集群",不是"必须集群"。

2. 大参数模型需要集群吗?——分三档看

不一定。显存需求 ≈ 参数量 × 每参数字节数(fp16 = 2 字节),模型能否放下算一笔账就知道:

模型规模 显存需求(约) 部署形态 例子
≤ 7B / 14B 量化后 < 24GB 单卡就够 租一张 T4/4090
32B / 70B 140GB+(fp16) 单机多卡(2~8 张 80GB 卡) 8×H100 跑 Llama-70B
千亿级 1.3TB+ 多机集群(几十张卡以上) DeepSeek R1(671B)

三个容易误解的点:

  • 单机插 8 张显卡 ≠ 集群:那叫"单机多卡",vLLM/SGLang 用张量并行把它当"一张大卡"用。多机集群只有千亿级模型才真需要——R1 当时轰动的原因之一就是它连单机都放不下。
  • 验证 70B 为什么单卡不行:700亿 × 2 字节 = 140GB,一张 80GB 卡放不下 → 需要 2 张。而 0.5B 量化后 ≈ 5亿 × 0.5 字节 ≈ 250MB,CPU 内存都绰绰有余。
  • 部署规模路线图:0.5B(CPU) → 7B(单张24G卡) → 14B(单张24G卡) → 70B(单机多卡) → 千亿(集群)。集群是终点不是起点,绝大多数公司生产场景(7B~70B)用单机多卡就覆盖了。

3. SGLang 需要 Ollama 当底层吗?——不需要,它是"一体机"

SGLang 自带引擎 + API 服务,不经过 Ollama。 类比错误的根源在于:K8s 是纯"编排层",自己不跑容器所以必须调 containerd;而 SGLang 不是纯编排层——它把"引擎(执行)+ API 服务"做在了一起,扮演的是 containerd + Docker 合体的角色。

容器 vs 推理世界的完整分层对照:

容器世界 AI 推理世界 说明
容器镜像 模型文件(GGUF / safetensors) 都是"数据/产物"
containerd(引擎) llama.cpp / SGLang / vLLM 的引擎 真正把模型跑起来的计算核心
Docker(封装层) Ollama 把引擎包成"一条命令"
K8s(编排层) 多机调度(SGLang 支持的多节点) 大规模时才需要

由此得出三句结论:

  • Ollama 站在"封装层",内部偷偷用 llama.cpp 当引擎(Ollama 的 containerd = llama.cpp)
  • SGLang 站在"引擎层",自己就是干活的那层,不需要任何人包它,只要喂模型文件
  • 模型文件格式也不同:Ollama 吃 GGUF(为 CPU/llama.cpp 优化),SGLang 吃 HuggingFace 原始权重 safetensors(为 GPU/自研引擎优化)——再次印证两者是"两个物种"

实际使用对比:

# 风格一:Ollama(封装层,面向小白)——命令简单,引擎细节全隐藏
ollama run qwen05

# 风格二:SGLang(引擎层,面向工程师)——直接给模型路径,自带 OpenAI 兼容服务
python -m sglang.launch_server \
  --model-path /data/models/Qwen2.5-7B-Instruct \
  --port 30000

4. 通识三件套:这次实验顺带搞懂了什么

部署本身是一次性知识,真正值钱的是顺带搞懂的三个底层通识——剥掉 AI 外壳,它们都是运维老基本功的"换皮":

搞懂的 其实是 在哪见过
显存算账 容量规划 磁盘/内存/机器数量预估
Key 不需要 信任边界与鉴权 端口绑定、防火墙、密码策略
框架分层 抽象分层 Docker/containerd/K8s

① 显存算账 = 容量规划。模型就是一张巨大的数表(权重):fp16 每个数 2 字节 → 70B = 700亿 × 2 = 140GB,一张 80G 卡放不下 → 要多卡。任何系统的第一问都是"资源够不够":数据库问磁盘、模型问显存,同一个本事。

显存算账的完整公式(换任何卡直接套):

能装下的参数量(B) = 可用显存(GB) ÷ 每参数占的字节数
精度 每参数字节 6G 卡理论能装 说明
fp16 2 字节 3B "÷2"只对这一行成立
int8 1 字节 6B 很少用
Q4_K_M ~0.6 字节 ~10B 实际部署主力(÷0.6 ≈ ×1.6)

注意 "能吃满"是伪命题,三个吃不满的因素:

  1. 驱动固定开销:一启动先吃约 0.5~1GB(图形驱动、上下文),是"房租"
  2. KV Cache:上下文越长额外显存越多(长对话比短对话更吃显存)
  3. 必须留余量:满载会 OOM 崩溃,实际留 10~20%

实用预算:模型文件 ≤ 总显存 − 1~1.5GB。以 1060 Ti 6G 为例(实际可用约 4.5~5GB):

模型 量化 文件大小 6G 卡结论
Qwen2.5-3B Q4 ~1.9GB ✅ 舒服,上下文能开大
Qwen2.5-7B Q4_K_M ~4.7GB ⚠️ 极限:能跑但上下文别超 4K,再长 OOM
Qwen2.5-14B Q4 ~9GB ❌ 放不下

老卡特有提醒(Pascal 架构如 1060 Ti 没有 Tensor Core,fp16 性能很弱):别跑 fp16,用 Q4 量化最合适(走 CUDA core 绕开短板)。验证 GPU 真在干活:ollama run 起来后另开终端 nvidia-smi,显存被占、利用率跳动 = 在用 GPU;显示 0% = 在偷偷用 CPU 跑(老卡驱动/支持问题)。

② 本地不需要 Key = 信任边界。Key 存在的唯一理由是"区分陌生人和自己人":OpenAI 全世界都能连 → 必须 Key 计费鉴权;Ollama 只听 127.0.0.1,已经确定是你 → 再要 Key 多此一举(跟 Redis 绑 127.0.0.1 不需要密码一个道理)。运维天天在判断"哪些资源要鉴权",AI 只是把同一问题又问了一遍。

③ 框架分层 = 抽象分层。llama.cpp/SGLang 是引擎层、Ollama 是封装层、多机调度是编排层——AI 把"容器生态的每一层"都重演了一遍。遇到新框架先问"它站在哪一层"(引擎?封装?编排?),这一问能省 80% 的学习弯路。

十一. 后续方向

  1. 换大模型对比:同样流程导入 1.5B/3B(7B 需内存 ≥ 8GB),感受"质量 / 速度 / 内存"三角关系。
  2. 学 llama.cpp:用 llama-server 直接跑同一个 GGUF,理解它和 Ollama 的差异(顺便搞懂 Ollama 的底层引擎)。
  3. 上 GPU 学 vLLM 或 SGLang:按需租抢占式 GPU 实例(T4),两者定位相同二选一。建议先 vLLM(生态成熟、岗位需求大),SGLang 作为 R1 热点可后补。
  4. 接应用闭环:OpenWebUI 或翻译工具把 base_url 指向本机 11434,完成"部署 → 服务 → 应用"。

十二. 附:生产环境认知延伸

这一节是实验之外的延伸话题,从"怎么做"上升到"为什么这么做"。内容独立成块,可按需取用。

1. 隔离谱系:conda / Docker / KVM 是同一招的三个层次

隔离不是只有"容器"一种做法,而是一条从轻到重的谱系——本质都是"让 A 和 B 互不干扰",区别只在颗粒度和代价:

层级 隔离什么 手段 例子
最轻 环境变量 PATH、LD_LIBRARY_PATH Redis 加 PATH
↓ 语言级依赖 独立目录 + 版本管理 conda / venv / pyenv
↓ 系统库依赖 chroot、独立 rootfs 早期隔离
↓ 进程/网络/文件全隔离 namespace + cgroup Docker 容器
↓ 独立内核 虚拟化 KVM 虚拟机
最重 物理隔离 独立硬件 独立服务器

conda vs Docker:

conda Docker
隔离对象 Python 解释器 + 包 整个用户态环境
隔离强度 弱(同内核、同系统库、共享网络) 强(独立文件系统、网络、进程)
能否装 MySQL/nginx ❌ 只能管语言层 ✅
一句话 "Python 界的容器",不是容器 容器

判断标准:隔离需求往上一层够用,就别往下走(两个 Python 项目冲突用 conda 就够,上 Docker 是过度设计)。

为什么要"修改 bash 配置文件"(装 conda 时常见疑惑):

  • 不是改 bash 程序,而是改它的配置文件(/etc/profile、~/.bashrc 等),bash 启动时按顺序读它们
  • 你的 Redis 文档里 echo 'export PATH=...' >> /etc/profile 就是同一类操作
  • conda init 的真正必要性:Linux 规矩是"子进程无法修改父进程的环境变量"。conda activate 要改当前 shell 的 PATH,所以 conda 必须被注入成 shell 函数(跑在当下 shell 里),而不能是普通可执行文件(那只会开子进程,改完就退,白改)
  • conda init 往 ~/.bashrc 追加一段 conda initialize 块,核心是 . .../conda.sh(读入脚本定义函数)
  • 改完要 source ~/.bashrc 或重登 SSH 才生效

为什么提示符前面多了 (base):那段脚本末尾会自动 conda activate base(默认开 auto_activate_base)。(base) 只是提示符前缀,不是命令输出,用来标记"当前在哪个环境"。base 是 conda 自带的基础环境(相当于"官方基础镜像"),别往里装东西,项目都建独立环境:

# 建独立环境(正好绕开系统 Python 3.6 的坑)
conda create -n llm python=3.10 -y
conda activate llm
# 不想每次开终端都自动进 base
conda config --set auto_activate_base false

2. 程序 vs 数据:什么进镜像,什么该挂载

一切文件本质上都是数据,"程序 vs 数据"不是两种实体,而是两种角色:程序回答"怎么算",数据回答"算什么"。

模型权重的双重身份——它有程序的血统(训练 ≈ 编译,权重 ≈ 字节码,推理引擎 ≈ 虚拟机),但工程上按数据处理。因为它完全符合"数据"的工程特征:只读、巨大、独立发布、重建成本极高。

模型和数据库结构居然同构:

数据库 大模型 角色
DBMS 软件(mysqld) 推理引擎(vLLM/Ollama) 程序
数据文件(.ibd) 模型权重(.gguf) 数据
SQL 语句 Prompt 指令

核心区分(最容易混的一点):

数据库数据 模型权重
挂载属于 必须(技术上不该烘焙) 偏好(技术上能烘焙,但不划算)
决定性因素 可变性 + 不可重建 体积 + 分发成本
一句话 因为会变、丢了没救 因为太大、搬着费劲

检验方法:假设库只有 1MB 且一年不改,依然必须挂载(它是可写的唯一状态);模型哪怕 140GB,烘焙依然"安全"(只是不划算)。数据库是"不能",模型是"不划算"。

补充:模型不烘焙的主因是冷启动拉取 + 仓库存储/带宽,而不是"重建不方便"——镜像层是内容寻址的,模型单独占一层的话,升级运行时那层时模型层会被复用、不会重传。

判定四问(遇到任何东西都能自己判断):

  1. 删了能重建吗?能 → 镜像;不能 → 挂载
  2. 谁写它?多久变一次?高频写 → 挂载;只读 → 随意
  3. 多大?巨大 → 别塞镜像
  4. 要活过容器吗?要 → 挂载
东西 可写? 能重建? 结论
引擎/运行时 只读 ✅ 进镜像
模型权重 只读 极贵 挂载(不划算)
数据库数据 可写 不能 挂载(必须)
缓存/会话 可写 无所谓 emptyDir/内存,随便丢
日志 追加写 无所谓 挂载或采集

3. 生产套路:环境进镜像 + 权重做挂载

这就是当前的标准做法(厂商官方文档即如此),本质是数据库"程序与数据分离"的原样平移:

组成 放哪 理由
运行时(CUDA + PyTorch + vLLM/SGLang) 进镜像 只读、可重建、层可缓存复用(5~16GB)
模型权重 挂载(:ro) 只读、巨大(4.7GB~140GB)、独立更新
配置/参数 挂载或环境变量 按环境变,不该重构建

做法 A:官方镜像 + 挂载权重(最主流)

docker run -d --gpus all \
  -v /data/models:/models:ro \
  -p 8000:8000 \
  vllm/vllm-openai:latest \
  --model /models/Qwen2.5-7B-Instruct

做法 B:自建环境镜像 + 挂载权重(需要额外依赖/私有包/自定义脚本时)

# Dockerfile —— 绝不 COPY 权重进来
FROM vllm/vllm-openai:v0.6.3
RUN pip install --no-cache-dir prometheus-client 你的私有包
COPY entrypoint.sh /usr/local/bin/
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
# .dockerignore —— 防止把几十 GB 权重喂进构建上下文(不做会卡死)
models/
*.gguf
*.safetensors

K8s 形态(完全一致,readOnly: true 是关键):

containers:
- name: vllm
  image: registry.example.com/vllm-custom:1.0
  args: ["--model", "/models/Qwen2.5-7B-Instruct"]
  volumeMounts:
  - name: models
    mountPath: /models
    readOnly: true
volumes:
- name: models
  persistentVolumeClaim:
    claimName: models-pvc

五种权重来源:PVC(NFS/Ceph)、hostPath/本地 NVMe(最快)、OSS CSI、initContainer 预下载、KServe 直接声明 storageUri(自动下载缓存)。

五个实操坑:

坑 现象 对策
UID/权限不匹配 容器非 root 跑,挂载目录属 root → Permission denied 挂载前 chown 对齐,或 --user 指定
忘加 :ro 容器意外写坏权重 权重一律只读挂载
路径/环境变量不对 容器里找不到模型 Ollama 用 OLLAMA_MODELS,HF 系用 HF_HOME,或直接给绝对路径
冷启动下载 首次启动卡几分钟拉模型 预置到卷 / initContainer 预热,评估 RTO
权重进了构建上下文 docker build 卡死 .dockerignore 排除 models/、*.gguf

4. GPU 与 CUDA:什么时候需要,容器里怎么用

三者分层(记住这条就不会踩版本坑):

CUDA 程序(vLLM / llama.cpp 的 GPU 版)
    ↓ 需要
CUDA Toolkit(编译器 + 运行时库)
    ↓ 需要
显卡驱动(NVIDIA driver)
    ↓ 需要
NVIDIA 显卡(物理硬件)

判断标准不是"要不要 CUDA",而是"这次计算要不要用 NVIDIA 显卡":

场景 需要 CUDA?
GPU 跑推理(vLLM/SGLang/TensorRT-LLM) ✅ 必须
Ollama/llama.cpp 用 GPU 加速 ✅ 需要(编译时开 CUDA 后端)
训练/微调 ✅ 必须
多卡并行(张量并行) ✅ 必须(NCCL 走 CUDA)
AMD 显卡 ❌ 用 ROCm
国产卡(昇腾等) ❌ 用厂商自家栈(CANN)
纯 CPU 跑模型 ❌ 不需要(本实验即此类)
调云端 API ❌ 不需要

本实验为什么不需要 CUDA:没有显卡,CPU 推理与 CUDA 无关。附带两个有趣结论:

  • Ollama 官方包是全后端打包(CPU/NVIDIA/AMD 引擎全塞进去,运行时自动探测)——这解释了为什么 bin/ollama 只 38M,解压后 2.2G(多出来的多是 GPU 库)
  • 同一份 Ollama 拿到有 N 卡的机器上会自动启用 CUDA,不用重编译
# 看它当前在用 CPU 还是 GPU
[root@my-yun ~]# ollama ps
NAME             ID       SIZE     PROCESSOR    UNTIL
qwen05:latest    xxx      491 MB   100% CPU     ...

PROCESSOR 列显示 100% CPU;在 GPU 机器上会变成 100% GPU 或 xx%/yy% CPU/GPU(部分层跑 GPU)。

Docker 里怎么给显卡(能声明,但要先装桥):

docker run --gpus all nvidia/cuda:12.4.0-base nvidia-smi      # 全部卡
docker run --gpus '"device=0"' ...                            # 只用 0 号卡
docker run --gpus '"device=2,3"' ...                          # 用 2、3 号卡
# 前提:装 NVIDIA Container Toolkit(Docker 原生不认识 GPU)
dnf -y install nvidia-container-toolkit
nvidia-ctk runtime configure --runtime=docker
systemctl restart docker

关键认知——两种"指定"是两个层次:

层面 手段 作用
资源分配(Docker 层) --gpus all / device=0 哪些卡对容器可见
应用选择(应用层) CUDA_VISIBLE_DEVICES、启动参数 应用用可见卡中的哪张

细节:CUDA_VISIBLE_DEVICES=0 在容器内指"容器内视角的第 0 张",不是宿主机物理 0 号卡。--gpus '"device=2,3"' + CUDA_VISIBLE_DEVICES=0 = 实际用宿主机 2 号卡。

K8s 里换了个说法,本质一样:

resources:
  limits:
    nvidia.com/gpu: 1        # 等价于 --gpus device=x

区别是 K8s 不用你写 --gpus——由 NVIDIA GPU Operator 自动装驱动/Toolkit/device plugin,你只声明数量。

铁律:容器里不装显卡驱动,驱动永远由宿主机提供、由 Toolkit 注入;镜像里只带 CUDA 运行时。所以镜像 CUDA 版本必须和宿主驱动兼容——这是容器化 GPU 最常见的翻车点。

5. 数据库为什么通常不上 K8s(附"供奉等级"谱系)

先修正前提:不是"很少有人用",而是"不是默认选择"。现状是争议中成熟——已有 CloudNativePG、KubeBlocks、Crunchy PGO 等成熟 Operator,同时也有大量讨论在讲"什么时候托管 DBaaS 仍更划算":Postgres on Kubernetes 2026: Operator 对比与 DBaaS 何时胜出、 为什么数据库可以放在 K8s 上(成本/控制/扩展对比)、主流 PostgreSQL Operator 生态对比。

场景 主流做法
开发/测试/小项目 Docker Compose 起 MySQL,非常普遍
中小规模生产 云托管 RDS / 数据库跑 VM(最大多数)
大规模云原生公司 K8s + Operator 真实存在(Vitess、TiDB Operator、CloudNativePG)
单实例核心 OLTP、延迟极敏感 物理机/VM

两种"稳定观"根本不同(这是冲突的根源):

无状态服务(容器主场) 数据库
稳定的定义 任何实例随时可挂,整体可用 这一个实例的数据绝对不能错
实现方式 多副本 + 快速重建(牛群模式) 单点数据的正确性 + 持久性(宠物模式)
对失败的假设 假设一定会死,死了就换 假设不能死,死了是灾难
K8s 提供什么 完整保障 只提供机制(PV 会重挂),不保证一致性/failover/性能

关键:容器不是"做不到稳定",而是"被允许不稳定"。同一个"实例消失",在无状态服务里是常态(还有 19 个副本),在数据库里是灾难。

真正原因,按重要性排:

  1. 有更省事的替代品(最大原因):云托管 RDS。你承担全部复杂度和风险,收益却是别人已商品化的东西——投入产出比问题
  2. K8s 的强项数据库用不上:K8s 价值在"海量无状态副本 + 弹性伸缩",而数据库副本数固定、扩副本是主从而非 HPA、还不希望 Pod 乱跑。用不上强项,却要承担复杂度
  3. 数据库对底层苛刻:吃本地 NVMe(网络存储延迟差一个数量级)、需要内核级调参(NUMA/huge pages)、Pod 重调度要经历 PV 解绑→attach→挂载(分钟级),比 VM HA 切换更慢

什么时候上 K8s 是对的:有成熟平台团队 + Operator + 拓扑感知存储;需要大量数据库实例(SaaS 多租户);云原生分布式数据库(TiDB/CockroachDB 天生适合)。

"供奉等级"谱系(数据库该被供着,区别只在供在哪座庙):

等级 形态 谁在用
🏛️ 神殿级 裸金属 + 专线 + 独立机柜 + 门禁 银行核心账务、电信计费、交易所
🏢 自建机房级 自建/托管机房物理机 大厂核心库、有历史包袱的企业
☁️ 云主机级 云 VM(RDS 底层) 当前最主流
📦 容器级 K8s + Operator 云原生公司、SaaS 多租户、分布式数据库
🧪 玩具级 docker compose up 开发测试

神殿级为什么没被取代:合规审计(数据物理可控)、极致性能(微秒级不接受虚拟化开销)、超大规模时自建更便宜、迁移风险大于收益。庙可以换,香火(数据)不能断。

对运维的意义:核心能力不是记住"数据库该装物理机",而是判断一个场景该用哪一档——核心账务 vs 内部工具,合规要求如何,规模多大。面试被问"数据库该不该上云/上 K8s",能答出这层判断就比背答案高一档。