双卡 A100 + Ollama 生产交付文档

3 阅读10分钟

1. 文档说明

本文档用于指导团队在 Ubuntu 20.04 + 双卡 A100 环境中完成 Ollama 的部署、启停、巡检、故障排查、升级与回滚,形成一套可直接执行的生产落地手册。

适用对象:

  • 运维工程师
  • 平台工程师
  • AI 应用研发工程师
  • 项目负责人 / 交付负责人

本文档目标不是“介绍 Ollama 是什么”,而是确保团队可以完成以下事情:

  • 正确部署双实例 Ollama
  • 让 GPU0、GPU1 分别承载一个推理实例
  • 保证模型常驻与自动预热
  • 支持 Python 侧轮询分发与失败切换
  • 能完成日常巡检、故障定位、升级和回滚

2. 交付目标

本次交付最终目标如下:

2.1 服务目标

  • ollama-gpu0.service 正常运行
  • ollama-gpu1.service 正常运行
  • ollama-prewarm.service 可正常执行
  • 默认 ollama.service 停用,避免端口冲突

2.2 资源目标

  • GPU0 对应端口 11434
  • GPU1 对应端口 11435
  • 两张 A100 可同时接收推理请求
  • 模型保持常驻,减少冷启动抖动

2.3 调用目标

  • Python 客户端支持双实例轮询
  • 客户端支持实例探活
  • 客户端支持失败切换
  • 客户端支持预热与常驻调用

2.4 运维目标

  • 服务可开机自启
  • 支持日志追踪
  • 支持健康检查
  • 支持升级与回滚
  • 支持日常巡检

3. 交付架构说明

整体采用 双实例双端口 模式,而不是一个实例同时管理两张 GPU。

架构如下:

  • ollama-gpu0

    • 绑定 CUDA_VISIBLE_DEVICES=0
    • 监听 0.0.0.0:11434
  • ollama-gpu1

    • 绑定 CUDA_VISIBLE_DEVICES=1
    • 监听 0.0.0.0:11435
  • ollama-prewarm

    • 在两个实例启动完成后自动执行预热
    • 确保模型提前加载进显存
  • Python 调用层

    • 1143411435 作为实例池
    • 按轮询方式分发请求
    • 失败时自动切换实例

这样设计的原因很直接:

  • 边界清晰
  • 便于定位问题
  • 更利于吞吐优先场景
  • 一张卡异常不会直接拖垮另一张卡

4. 目录规划

推荐目录结构如下:

/data/ollama/models                # 模型目录
/etc/systemd/system/               # systemd 服务目录
/usr/local/bin/                    # 运行脚本目录

最终落地文件如下:

/etc/systemd/system/ollama-gpu0.service
/etc/systemd/system/ollama-gpu1.service
/etc/systemd/system/ollama-prewarm.service
/usr/local/bin/ollama-prewarm.sh
/usr/local/bin/ollama-healthcheck.sh
/usr/local/bin/ollama_bi_gpu_client.py
/data/ollama/models

5. 环境前提

部署前需确认以下前提:

5.1 系统环境

  • Ubuntu 20.04
  • systemd 可正常使用
  • 网络正常,可访问模型下载源或已有离线模型

5.2 GPU 环境

执行:

nvidia-smi
nvidia-smi -L

确认:

  • 两张 A100 可正常识别
  • 驱动工作正常
  • GPU 编号或 UUID 可获取

5.3 Ollama 已安装

执行:

which ollama
ls -l /usr/local/bin/ollama

预期路径:

/usr/local/bin/ollama

如果实际路径不是这个,后续 ExecStart 需要同步修改。

5.4 ollama 用户存在

执行:

id ollama

如果不存在,需先创建服务用户。

6. 部署步骤

6.1 创建模型目录并赋权

sudo mkdir -p /data/ollama/models
sudo chown -R ollama:ollama /data/ollama
sudo chmod 755 /data
sudo chmod 755 /data/ollama
sudo chmod 755 /data/ollama/models

验证目录链路:

namei -l /data/ollama/models

验证 ollama 用户可写:

sudo -u ollama bash -lc 'cd /data/ollama/models && touch .perm_test && rm -f .perm_test && echo ok'

如果返回 ok,说明目录权限已打通。

6.2 写入 ollama-gpu0.service

[Unit]
Description=Ollama GPU0 Service
After=network-online.target

[Service]
ExecStart=/usr/local/bin/ollama serve
User=ollama
Group=ollama
Restart=always
RestartSec=3

Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Environment="OLLAMA_HOST=0.0.0.0:11434"
Environment="CUDA_VISIBLE_DEVICES=0"
Environment="OLLAMA_MODELS=/data/ollama/models"
Environment="OLLAMA_KEEP_ALIVE=-1"
Environment="OLLAMA_FLASH_ATTENTION=1"
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"
Environment="OLLAMA_MAX_LOADED_MODELS=1"
Environment="OLLAMA_NUM_PARALLEL=4"
Environment="OLLAMA_MAX_QUEUE=1024"
Environment="OLLAMA_CONTEXT_LENGTH=8192"

[Install]
WantedBy=multi-user.target

6.3 写入 ollama-gpu1.service

[Unit]
Description=Ollama GPU1 Service
After=network-online.target

[Service]
ExecStart=/usr/local/bin/ollama serve
User=ollama
Group=ollama
Restart=always
RestartSec=3

Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Environment="OLLAMA_HOST=0.0.0.0:11435"
Environment="CUDA_VISIBLE_DEVICES=1"
Environment="OLLAMA_MODELS=/data/ollama/models"
Environment="OLLAMA_KEEP_ALIVE=-1"
Environment="OLLAMA_FLASH_ATTENTION=1"
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"
Environment="OLLAMA_MAX_LOADED_MODELS=1"
Environment="OLLAMA_NUM_PARALLEL=4"
Environment="OLLAMA_MAX_QUEUE=1024"
Environment="OLLAMA_CONTEXT_LENGTH=8192"

[Install]
WantedBy=multi-user.target

6.4 写入预热脚本 ollama-prewarm.sh

#!/usr/bin/env bash
set -e

MODEL="${1:-gemma3}"

for port in 11434 11435; do
  echo "[prewarm] checking ${port}"
  curl -sf "http://127.0.0.1:${port}/api/version" > /dev/null

  echo "[prewarm] loading ${MODEL} on ${port}"
  curl -sf "http://127.0.0.1:${port}/api/generate" \
    -d "{\"model\":\"${MODEL}\",\"keep_alive\":-1}" > /dev/null

  echo "[prewarm] verifying ${MODEL} on ${port}"
  curl -sf "http://127.0.0.1:${port}/api/ps"
done

赋权:

sudo chmod +x /usr/local/bin/ollama-prewarm.sh

6.5 写入预热服务 ollama-prewarm.service

[Unit]
Description=Prewarm Ollama Models
After=ollama-gpu0.service ollama-gpu1.service
Wants=ollama-gpu0.service ollama-gpu1.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/ollama-prewarm.sh gemma3

[Install]
WantedBy=multi-user.target

6.6 停用默认单实例服务

sudo systemctl disable --now ollama || true

目的:

  • 避免占用 11434
  • 避免与双实例方案混用
  • 保持服务治理边界清晰

6.7 重新加载并启动服务

sudo systemctl daemon-reload
sudo systemctl enable --now ollama-gpu0
sudo systemctl enable --now ollama-gpu1
sudo systemctl enable ollama-prewarm
sudo systemctl start ollama-prewarm

7. 参数设计说明

本次推荐参数如下:

OLLAMA_KEEP_ALIVE=-1
OLLAMA_FLASH_ATTENTION=1
OLLAMA_KV_CACHE_TYPE=q8_0
OLLAMA_MAX_LOADED_MODELS=1
OLLAMA_NUM_PARALLEL=4
OLLAMA_MAX_QUEUE=1024
OLLAMA_CONTEXT_LENGTH=8192

参数解释

OLLAMA_KEEP_ALIVE=-1

作用:

  • 模型常驻内存
  • 避免频繁卸载/重载
  • 降低冷启动对时延的影响

OLLAMA_FLASH_ATTENTION=1

作用:

  • 降低大上下文场景的内存开销
  • 提高上下文扩展场景的稳定性

OLLAMA_KV_CACHE_TYPE=q8_0

作用:

  • 降低 KV Cache 占用
  • 在质量与显存之间取得平衡

OLLAMA_MAX_LOADED_MODELS=1

作用:

  • 每实例只维持一个主服务模型
  • 避免多模型竞争显存
  • 避免行为复杂化

OLLAMA_NUM_PARALLEL=4

作用:

  • 提高单实例并发承载能力
  • 适合双实例吞吐优先场景

注意:

并发和上下文不是独立关系,而是联动关系。
实例压力近似与:

OLLAMA_NUM_PARALLEL × OLLAMA_CONTEXT_LENGTH

共同相关。

OLLAMA_MAX_QUEUE=1024

作用:

  • 给短时高峰提供缓冲
  • 降低瞬时尖峰导致的快速失败

注意:

队列不是性能放大器,只是缓冲区。

OLLAMA_CONTEXT_LENGTH=8192

作用:

  • 控制单请求上下文预算
  • 更适合吞吐优先型服务

如果业务是超长上下文任务,再单独为该类服务调整,而不要默认全局拉太高。

8. 启停与运维命令

8.1 启动

sudo systemctl start ollama-gpu0
sudo systemctl start ollama-gpu1
sudo systemctl start ollama-prewarm

8.2 停止

sudo systemctl stop ollama-prewarm
sudo systemctl stop ollama-gpu0
sudo systemctl stop ollama-gpu1

8.3 重启

sudo systemctl restart ollama-gpu0
sudo systemctl restart ollama-gpu1
sudo systemctl restart ollama-prewarm

8.4 查看状态

systemctl status ollama-gpu0 --no-pager -l
systemctl status ollama-gpu1 --no-pager -l
systemctl status ollama-prewarm --no-pager -l

8.5 查看日志

journalctl -u ollama-gpu0 --no-pager --follow --pager-end
journalctl -u ollama-gpu1 --no-pager --follow --pager-end
journalctl -u ollama-prewarm --no-pager --follow --pager-end

9. 日常巡检手册

建议每天巡检一次,重点检查以下内容。

9.1 服务状态巡检

systemctl is-active ollama-gpu0
systemctl is-active ollama-gpu1

预期都应返回:

active

9.2 API 可达性巡检

curl http://127.0.0.1:11434/api/version
curl http://127.0.0.1:11435/api/version

9.3 模型运行状态巡检

curl http://127.0.0.1:11434/api/ps
curl http://127.0.0.1:11435/api/ps

确认:

  • 两个实例都有运行模型
  • 模型未异常卸载
  • 上下文配置符合预期

9.4 GPU 巡检

ollama ps
watch -n 1 nvidia-smi

关注点:

  • ollama ps 中模型是否为 100% GPU
  • 两张卡是否都有显存占用
  • 两张卡是否都有推理活动

9.5 目录权限巡检

namei -l /data/ollama/models

确认目录链路权限未被变更。

10. 健康检查手册

可直接执行:

/usr/local/bin/ollama-healthcheck.sh

也可手工检查:

curl http://127.0.0.1:11434/api/version
curl http://127.0.0.1:11435/api/version
curl http://127.0.0.1:11434/api/ps
curl http://127.0.0.1:11435/api/ps

判断标准:

  • version 接口可返回
  • ps 接口能看到模型
  • 模型状态正常,无异常卸载

11. 故障排查手册

下面列出最常见问题及处理方法。

11.1 服务启动失败,提示端口被占用

典型报错:

bind: address already in use

处理步骤:

sudo ss -lntp | grep 11434
sudo ss -lntp | grep 11435
sudo lsof -i:11434 -P -n
sudo lsof -i:11435 -P -n

如果默认 ollama.service 在运行:

sudo systemctl disable --now ollama

必要时释放端口:

sudo fuser -k 11434/tcp
sudo fuser -k 11435/tcp

11.2 服务启动失败,提示目录权限不足

典型报错:

permission denied: ensure path elements are traversable

处理步骤:

sudo mkdir -p /data/ollama/models
sudo chown -R ollama:ollama /data/ollama
sudo chmod 755 /data
sudo chmod 755 /data/ollama
sudo chmod 755 /data/ollama/models
namei -l /data/ollama/models

验证:

sudo -u ollama bash -lc 'cd /data/ollama/models && touch .perm_test && rm -f .perm_test && echo ok'

11.3 模型首包很慢

排查方向:

  • 是否未预热
  • 是否未设置 keep_alive=-1
  • 模型是否反复被卸载

检查:

curl http://127.0.0.1:11434/api/ps
curl http://127.0.0.1:11435/api/ps

必要时手工预热:

/usr/local/bin/ollama-prewarm.sh gemma3

11.4 压测时吞吐不高

排查方向:

  • Python 客户端是否真的轮询了两个实例
  • 是否所有请求都只打了一个端口
  • OLLAMA_NUM_PARALLEL 是否过低
  • OLLAMA_CONTEXT_LENGTH 是否过高
  • 是否发生 CPU offload

检查:

ollama ps
watch -n 1 nvidia-smi

11.5 出现 503 overloaded

原因:

  • 请求超出当前实例承载能力
  • 队列已满或实例过载

处理建议:

  • 检查调用侧是否做了均衡分流
  • 检查并发是否过高
  • 检查上下文是否过大
  • 必要时适当调整 OLLAMA_NUM_PARALLELOLLAMA_MAX_QUEUE

12. 升级手册

12.1 升级前准备

先记录当前版本:

ollama --version

备份服务文件:

sudo cp /etc/systemd/system/ollama-gpu0.service /etc/systemd/system/ollama-gpu0.service.bak
sudo cp /etc/systemd/system/ollama-gpu1.service /etc/systemd/system/ollama-gpu1.service.bak
sudo cp /etc/systemd/system/ollama-prewarm.service /etc/systemd/system/ollama-prewarm.service.bak

备份脚本:

sudo cp /usr/local/bin/ollama-prewarm.sh /usr/local/bin/ollama-prewarm.sh.bak
sudo cp /usr/local/bin/ollama-healthcheck.sh /usr/local/bin/ollama-healthcheck.sh.bak
sudo cp /usr/local/bin/ollama_bi_gpu_client.py /usr/local/bin/ollama_bi_gpu_client.py.bak

12.2 升级步骤

停止服务:

sudo systemctl stop ollama-prewarm
sudo systemctl stop ollama-gpu0
sudo systemctl stop ollama-gpu1

替换 ollama 二进制后,执行:

sudo systemctl daemon-reload
sudo systemctl start ollama-gpu0
sudo systemctl start ollama-gpu1
sudo systemctl start ollama-prewarm

12.3 升级后验证

ollama --version
systemctl status ollama-gpu0 --no-pager -l
systemctl status ollama-gpu1 --no-pager -l
/usr/local/bin/ollama-healthcheck.sh
ollama ps

13. 回滚手册

如果升级后异常,按如下步骤回滚。

13.1 停止当前服务

sudo systemctl stop ollama-prewarm
sudo systemctl stop ollama-gpu0
sudo systemctl stop ollama-gpu1

13.2 恢复旧版配置与脚本

sudo cp /etc/systemd/system/ollama-gpu0.service.bak /etc/systemd/system/ollama-gpu0.service
sudo cp /etc/systemd/system/ollama-gpu1.service.bak /etc/systemd/system/ollama-gpu1.service
sudo cp /etc/systemd/system/ollama-prewarm.service.bak /etc/systemd/system/ollama-prewarm.service

sudo cp /usr/local/bin/ollama-prewarm.sh.bak /usr/local/bin/ollama-prewarm.sh
sudo cp /usr/local/bin/ollama-healthcheck.sh.bak /usr/local/bin/ollama-healthcheck.sh
sudo cp /usr/local/bin/ollama_bi_gpu_client.py.bak /usr/local/bin/ollama_bi_gpu_client.py

13.3 恢复旧版 Ollama 二进制

用备份的可执行文件覆盖当前版本。

13.4 重新加载并启动

sudo systemctl daemon-reload
sudo systemctl start ollama-gpu0
sudo systemctl start ollama-gpu1
sudo systemctl start ollama-prewarm

13.5 验证回滚结果

ollama --version
systemctl status ollama-gpu0 --no-pager -l
systemctl status ollama-gpu1 --no-pager -l
/usr/local/bin/ollama-healthcheck.sh

14. Python 接入说明

Python 侧推荐使用实例池方式,不要把调用写死到单一端口。

启动示例:

from ollama_bi_gpu_client import ProductionOllamaPool

pool = ProductionOllamaPool(
    endpoints=[
        "http://127.0.0.1:11434",
        "http://127.0.0.1:11435",
    ]
)

pool.prewarm("gemma3")
resp = pool.generate("gemma3", "用中文介绍一下向量数据库。")
print(resp.get("response", ""))

接入原则:

  • 所有业务统一走实例池
  • 禁止直接写死单端口
  • 探活失败后自动切换实例
  • 所有关键请求建议保留 keep_alive=-1

15. 上线前最终检查清单

服务层

  • ollama-gpu0.service 正常运行
  • ollama-gpu1.service 正常运行
  • ollama-prewarm.service 可执行成功
  • 默认 ollama.service 已停用

路径层

  • which ollama 路径确认无误
  • /data/ollama/models 存在
  • ollama 用户具备读写权限
  • 父目录链路可遍历

API 层

  • 11434/api/version 正常
  • 11435/api/version 正常
  • 11434/api/ps 可见模型
  • 11435/api/ps 可见模型

GPU 层

  • ollama ps 显示 100% GPU
  • 双卡都有显存占用
  • 双卡都有推理活动

调用层

  • Python 客户端具备轮询
  • Python 客户端具备探活
  • Python 客户端具备失败切换
  • 压测时两个实例都收到流量

调优层

  • load_duration 在预热后明显下降
  • prompt_eval_duration 无异常飙升
  • 未出现持续性 503 overloaded
  • 参数收敛在稳定区间

16. 结语

到这里,这套 双卡 A100 + Ollama 的交付手册已经完整闭环了。

它不是一份“命令汇总”,而是一套真正可执行的落地方案,涵盖了:

  • 部署
  • 配置
  • 权限
  • 启停
  • 预热
  • 巡检
  • 故障处理
  • 升级
  • 回滚
  • Python 接入

真正的交付价值,不在于你把某个服务“拉起来了”,而在于:

这套东西交到别人手里,对方也能按文档把它稳定接住。