开头不好写,直接用从业者的场景引入:我自己在内网服务器上搭过几套本地大模型服务,踩了不少坑,最后稳定下来就是这套docker+ollama的组合拳。这篇文章把从零到能跑通、能对外提供服务的完整过程讲清楚,包括每一步为什么这么做、参数怎么定、出现问题怎么排查。
1. 部署思路与整体环境准备
1.1 为什么选docker加ollama这套组合
先说结论:如果你要在Linux服务器上部署本地大模型,docker+ollama是目前最省心的方案,没有之一。
ollama本身是一个大模型运行时管理工具,负责模型下载、加载、推理调度,对用户暴露一个简单的HTTP API,默认跑在11434端口。它是用Go写的,原生支持Linux,理论上直接安装也可以。但实际用下来,裸装方式有几个问题:
- 模型文件和ollama程序混在系统目录里,升级、卸载、换版本都容易留一堆残留。
- 服务器上可能同时跑着其他服务,依赖环境一旦冲突就得折腾半天。
- 团队协作时,每个人的服务器环境不一样,复制一套环境成本很高。
docker把这些全部封装掉。镜像里自带运行时和依赖,宿主机只需要有GPU驱动和nvidia-container-toolkit(NVIDIA显卡场景下),容器一拉起来就是一致的环境。换机器、换机房、扩容节点,体验基本等同于拉一个镜像再启动,几乎没有适配成本。
还有一个关键点:ollama的镜像本身就内置了适合容器化运行的配置,默认下载模型到容器内路径,官方文档也推荐用docker部署。这属于工具链本身就为容器场景做了优化,不是硬套。
1.2 部署前的硬件与系统检查清单
在动手前先把硬件和系统情况摸清楚,避免装到一半发现不兼容。我在实际项目中总结出下面这张检查表,按顺序过一遍基本不会翻车:
| 检查项 | 最低要求 | 推荐配置 | 备注 |
|---|---|---|---|
| CPU | x86_64 / ARM64均可 | 8核以上 | 模型推理主要靠GPU,但加载模型和并发请求CPU也有压力 |
| 内存 | 16GB | 32GB以上 | 等于最大模型体积加上运行开销,建议至少为最大模型预留1.5倍内存 |
| 显卡 | NVIDIA GPU 8GB显存 | 24GB以上 | 没有GPU也能跑,但只能跑小模型,速度慢一个数量级 |
| 显存 | 8GB | 16-24GB | 决定能跑多大的模型,预算有限优先保证显存 |
| 系统 | Ubuntu 20.04+ / CentOS 7+ / Debian 11+ | Ubuntu 22.04 LTS | CentOS 7的docker老版本兼容性略麻烦 |
| 磁盘 | 20GB可用 | 100GB以上SSD | 模型动辄几个GB到几十个GB,建议用SSD盘 |
| 网络 | 能访问镜像仓库 | 国内服务器建议配好加速 | 拉取ollama镜像和模型文件都需要外网 |
这里补充一个判断逻辑:能不能用GPU跑,主要看显存和模型参数量之间的比例。比如7B模型(70亿参数)的fp16精度权重大约是14GB,8B模型约16GB,量化版本(q4)可以压到4-6GB。所以如果你只有8GB显存,老老实实选7B或8B的q4量化版本;16GB显存可以跑13B-14B的量化版;24GB以上才能比较舒服地跑全精度或更大参数量。
没有NVIDIA GPU的机器也不是不能玩。ollama支持纯CPU推理,只是速度慢很多。我试过用一台4核8G的云服务器跑7B模型,一个简单问题要转20到30秒,体验一般,但用来学习调试流程、验证API调用是足够的。
系统方面还有一个容易忽略的点:如果用的是国产化Linux系统(比如统信UOS、麒麟等),docker安装方式和Ubuntu略有差异,但核心逻辑一样。遇到报错优先看是不是缺依赖包,其次是内核版本是否过旧。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 服务器docker环境搭建
2.1 docker引擎安装的三种方式
docker的安装方式分场景选择。Ubuntu/Debian系服务器我一般用官方脚本方式,CentOS/RHEL系用yum源方式,离线环境用rpm包方式。给大家说下具体操作和适用场景。
方式一:官方一键脚本(适合能访问外网的测试环境)
bash复制curl -fsSL https://get.docker.com | bash -s docker
这个脚本会自动识别系统版本、配置好软件源、安装docker-ce和containerd,装完再启动服务:
bash复制systemctl enable --now docker
docker version
能正常输出client和server版本信息就说明装成功了。脚本方式最快,但有两个注意点:第一,从海外官方源拉取安装脚本,国内部分服务器会超时,这种情况考虑方式二;第二,脚本执行得root权限,生产环境注意审计。
方式二:国内镜像源安装(推荐国内服务器使用)
国内服务器推荐直接配置阿里云或清华的镜像源,避免官方源连接不稳定。以Ubuntu为例:
bash复制# 替换为阿里云docker-ce源
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | apt-key add -
echo "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list
这里有个小坑需要注意:$(lsb_release -cs)获取的是系统代号,Ubuntu 22.04返回的是jammy,如果服务器源里没有这个版本目录,需要手动改成可用的代号。
接下来:
bash复制apt update && apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
注意docker-compose-plugin这个包,后面用docker compose管理ollama容器时会用到。
方式三:离线安装(适合内网隔离环境)
内网环境没有外网访问权限,只能在能联网的机器上下载好rpm或deb包,拷贝进去安装。下载地址在官方releases页面,需要下载docker-ce、docker-ce-cli、containerd.io三个包,版本号要对应。然后:
bash复制yum localinstall -y docker-ce-*.rpm docker-ce-cli-*.rpm containerd.io-*.rpm
这种方式虽然麻烦,但可靠性最高,不受网络波动影响。
2.2 配置镜像加速器的完整过程
docker装好后第一件事不是启动容器,而是配置镜像加速。原因很简单:默认的docker hub源在国内访问速度非常不稳定,拉取镜像经常卡在几十KB每秒甚至超时。而ollama镜像本身有好几百MB,不配置加速会很痛苦。
修改/etc/docker/daemon.json:
json复制{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://docker.mirrors.ustc.edu.cn"
],
"data-root": "/data/docker"
}
这里我多配置了一个data-root参数,把docker的存储目录迁移到大容量磁盘。这个参数容易被忽略,但非常重要——模型文件体积大,如果系统盘只有40GB,跑两个模型就满了。
修改完重启:
bash复制systemctl daemon-reload
systemctl restart docker
docker info | grep -A 5 "Registry Mirrors"
能列出配置的镜像加速地址就生效了。实测下来,daocloud的加速效果相对稳定,中科大的在某些地区偶尔失效,所以多配置几个做冗余。
这里要特别提醒,网上很多人说配了加速还是慢,原因主要出在两个地方:第一,daemon.json写错了格式,json语法稍微错一格就整个不生效;第二,docker服务没有真正重启。检查格式可以用docker info看实际加载状态,别只看文件内容。
3. ollama容器部署与模型拉取
3.1 拉取ollama镜像与版本选择
镜像加速配置好之后,拉取ollama镜像就是一条命令:
bash复制docker pull ollama/ollama:latest
当然实际生产环境不建议直接latest,而是锁定具体版本。我的习惯是先看一眼官方tags列表,选一个最近稳定发布的版本:
bash复制docker pull ollama/ollama:0.1.44
镜像版本的选择逻辑很简单:大版本升级通常伴随着模型库目录结构变化或API调整,锁定版本可以避免升级带来的兼容性问题。等确认新版本没问题再统一升级。
拉取完成后验证一下:
bash复制docker images | grep ollama
看到ollama镜像就有了。这一步如果速度很慢,回到2.2节检查镜像加速配置。
3.2 启动容器的关键参数解释
启动ollama容器是整篇文章的核心环节。先看完整命令,我再逐行拆解:
bash复制docker run -d \
--name ollama \
--gpus all \
-v /data/ollama:/root/.ollama \
-p 11434:11434 \
--restart always \
-e OLLAMA_HOST=0.0.0.0 \
-e OLLAMA_MODELS=/root/.ollama/models \
-e OLLAMA_KEEP_ALIVE=-1 \
--gpus all \
ollama/ollama:0.1.44
各参数的实际作用:
-d:后台运行,终端关掉容器不退出。--name ollama:给容器起名字,后续docker logs和docker exec都用这个名字。--gpus all:把宿主机所有GPU透传给容器。这一步的前提是宿主机安装了nvidia-container-toolkit,否则会报could not select device driver "" with capabilities: [[gpu]]。后面5.3节会讲怎么排查。-v /data/ollama:/root/.ollama:数据卷挂载,把容器内的模型存储目录映射到宿主机。模型文件都存到宿主机磁盘上,后续重建容器、升级版本,模型不会被删掉。-p 11434:11434:端口映射,宿主机11434端口映射到容器内部11434端口。--restart always:服务器重启后容器自动拉起,这个在生产环境非常关键,不然一次断电所有的服务就全挂了。-e OLLAMA_HOST=0.0.0.0:监听所有网卡的11434端口。默认情况下ollama只监听127.0.0.1,意味着只能本机访问,这样设置才能让其他机器远程调用。-e OLLAMA_KEEP_ALIVE=-1:模型加载后保持常驻内存,不自动卸载。这个按需设置,如果你希望模型在一定空闲时间后释放显存给其他任务,可以改为OLLAMA_KEEP_ALIVE=30m这类时间值。
启动后检查状态:
bash复制docker ps | grep ollama
docker logs -f ollama
日志正常会显示监听端口的信息,没有报错说明容器起来了。
3.3 拉取模型并完成首次对话测试
容器起来后,拉取模型有两种方式。第一种是进入容器执行ollama命令:
bash复制docker exec -it ollama ollama pull qwen2.5:7b
第二种是直接用宿主机的curl调用ollama的HTTP API:
bash复制curl http://localhost:11434/api/pull -d '{
"name": "qwen2.5:7b"
}'
两种方式等价。第一种适合在命令行交互测试,第二种适合写脚本自动化。
模型选择上,国内环境我首推通义千问的qwen系列,中文理解能力强,社区活跃,量化版本多。跑通流程用最小体量的模型最合适,比如qwen2.5:3b或qwen2.5:7b。以下是不同参数量模型对硬件的要求参考:
| 模型 | 参数量 | 量化版本体积 | 最低显存 | 适合场景 |
|---|---|---|---|---|
| qwen2.5:0.5b | 5亿 | 400MB | 1GB | 测试环境、CPU运行 |
| qwen2.5:3b | 30亿 | 2.4GB | 4GB | 入门体验、低配服务器 |
| qwen2.5:7b | 70亿 | 4.7GB | 8GB | 效果与资源均衡 |
| qwen2.5:14b | 140亿 | 9GB | 16GB | 需要更强推理能力 |
| qwen2.5:32b | 320亿 | 19GB | 24GB | 较高要求的生产环境 |
拉取模型时,网络不好的时候经常中断。ollama不支持断点续传的完整保证,但实测下来,偶尔中断后重新执行pull命令会从断点继续,底层有分片拉取机制。如果反复失败,可以考虑设置代理或换一个网络时段。
模型拉取完成后直接测试对话:
bash复制docker exec -it ollama ollama run qwen2.5:7b "你好,请介绍一下你自己"
看到正常回复基本就成功了。要注意进入交互模式后,如果想退出,输入/bye退出,不是Ctrl+C直接杀进程。
4. 模型管理与API调用实战
4.1 模型文件的存储规划与备份策略
ollama把所有模型文件都存在一个目录里,容器内路径是/root/.ollama/models,通过数据卷映射到了宿主机的/data/ollama。整体结构是这样的:
text复制/data/ollama/
├── models/
│ ├── blobs/ # 模型分片文件,实际的大文件
│ └── manifests/ # 模型清单信息
└── id_ed25519 # 密钥文件
重点说一下blobs目录。这是模型真正存放的地方,在宿主机上看到的是一个哈希命名的分片文件。所以如果你用du命令统计大小,会发现模型实际占用比ollama list显示的更大,因为有些分片会被多个模型共享。
这个特性也带来一个好处:当你拉取不同版本的相同模型时,相同分片不会重复下载,比较省空间。但也意味着不能简单地把某个model名称对应的文件复制到另一台机器,因为缺少分片关联信息。
如果要在多台服务器之间迁移模型,最稳妥的方式有几种:
- 在源机器上把整个/models目录打包,拷贝到目标机器对应目录,然后重启ollama服务识别。
- 或者直接把模型重新pull一遍,利用内网带宽。
- 也可以用ollama自带的导出机制,
ollama show可以查看模型详情,但不支持直接导出单文件。
备份方面,我的建议是至少把blobs目录里占用最大的几个文件定期同步到另一块磁盘或对象存储。真的把容器删了不要紧,只要模型文件还在,重新拉个镜像启动就恢复,这个就是挂载数据卷的价值。
4.2 通过API方式调用本地模型的完整示例
部署本地大模型不只是为了在服务器上聊天,更重要的是把模型能力集成到自己的应用里。ollama对外提供的HTTP API非常简洁,我实际开发中主要用三个接口。
生成对话补全:
bash复制curl http://localhost:11434/api/generate -d '{
"model": "qwen2.5:7b",
"prompt": "用一句话解释什么是docker",
"stream": false
}'
返回结果里response字段就是生成的内容。stream参数设为false时等全部生成完毕返回,设为true则启用流式输出,适合对接前端打字机效果。
多轮会话模式:
bash复制curl http://localhost:11434/api/chat -d '{
"model": "qwen2.5:7b",
"messages": [
{"role": "system", "content": "你是一个只会用简洁中文回答的助手"},
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好,请问有什么可以帮你?"},
{"role": "user", "content": "帮我总结一下docker的优点"}
]
}'
messages数组里维护了会话上下文,这个模式更适合做聊天机器人或客服系统。
获取已加载模型列表:
bash复制curl http://localhost:11434/api/tags
返回所有已经拉取到本地的模型名称和大小。这个接口在做模型管理界面的时候很常用。
Python调用也就十几行代码:
python复制import requests
import json
url = "http://localhost:11434/api/chat"
payload = {
"model": "qwen2.5:7b",
"messages": [
{"role": "user", "content": "写一个python函数判断字符串是否为回文"}
],
"stream": False
}
resp = requests.post(url, json=payload)
data = resp.json()
print(data["message"]["content"])
官方还提供了ollama-python库,可以pip install ollama直接使用,API风格差不多的体感,本质都是走HTTP接口。
4.3 docker compose方式统一管理部署配置
随着配置项越来越多,用docker run命令管理会变得很麻烦。换成docker compose文件把配置固化下来,后续改参数只需要编辑YAML然后重启服务。
创建/data/ollama/docker-compose.yml:
yaml复制version: '3.8'
services:
ollama:
image: ollama/ollama:0.1.44
container_name: ollama
restart: always
ports:
- "11434:11434"
volumes:
- /data/ollama:/root/.ollama
environment:
- OLLAMA_HOST=0.0.0.0
- OLLAMA_KEEP_ALIVE=-1
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
其实deploy.resources.reservations.devices这部分就是compose中传递GPU的方式。不过很多新版本的docker compose直接支持gpus: all字段,写法更简洁:
yaml复制 gpus: all
启动和更新的命令:
bash复制cd /data/ollama
docker compose up -d
docker compose logs -f ollama
用compose管理最大的好处是版本控制。compose文件放到git仓库里,团队成员拉下来docker compose up -d就能得到一致的环境。这个思路在后面的模型服务扩展、多模型调度场景下特别有用。
5. 常见问题与排查技巧实录
5.1 镜像下载慢、模型拉取超时的处理方案
这是几乎每个人都会遇到的第一道坎。现象有两种:拉取ollama镜像慢,或者拉取模型文件超时。
镜像慢的解决方案在2.2节已经写过:配置registry-mirrors。如果配置完docker info确认生效但还是慢,可以尝试重启docker守护进程,有些情况下docker service是运行中的,但配置只在重启后才应用。
模型拉取慢的问题和镜像加速不一样。模型文件实际是从ollama官方的registry下载的,不走docker镜像加速器。常见的解决办法有这几种:
一是为ollama配置HTTP代理。如果服务器本身能访问外网但速度慢,可以在启动容器时加环境变量:
bash复制docker run -d \
--name ollama \
--gpus all \
-e HTTPS_PROXY=http://proxy.example.com:port \
-e HTTP_PROXY=http://proxy.example.com:port \
-e OLLAMA_HOST=0.0.0.0 \
-v /data/ollama:/root/.ollama \
-p 11434:11434 \
--restart always \
ollama/ollama:0.1.44
二是换更小体积的量化模型。因为带宽有限,拉不动14B就换7B,拉不动7B就换3B,先把链路打通再逐步升级硬件和模型。
三是设置遇到单文件过大失败时,换一个模型版本。比如qwen2.5:7b-instruct-q4_K_M和qwen2.5:7b,执行pull命令的模型名称不同,实际拉取分片也不完全相同,有时换个tag就能绕开损坏缓存,重新拉取。
这里要说明一点,在国内如何使用合适的代理需要符合相关法律法规要求,我这里提供的技术方案是针对有合规网络条件的场景,请读者遵循相关规定。
5.2 GPU不可用、显存不足的快速判断方法
启动容器后第一件事永远是确认GPU是否真的被识别到。执行:
bash复制docker exec ollama nvidia-smi
如果输出正常的GPU信息表,说明GPU透传成功。如果提示nvidia-smi command not found,说明容器内没有NVIDIA工具,通常问题出在宿主机没装nvidia-container-toolkit。
装这个工具链的步骤(Ubuntu):
bash复制distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list > /etc/apt/sources.list.d/nvidia-docker.list
apt update && apt install -y nvidia-container-toolkit
systemctl restart docker
装完再启动容器。
显存不足的典型报错是CUDA error: out of memory或payload size exceeded。处理方式:
- 切换更小的模型或量化版本。
- 关闭OLLAMA_KEEP_ALIVE常驻,让它空闲后自动释放显存。
- 在容器内设置OLLAMA_MAX_LOADED_MODELS=1,限制同时加载的模型数量。
- 用ollama stop命令主动卸载某个模型:
docker exec ollama ollama stop qwen2.5:7b。
我实际遇到过一个有意思的场景:两张4090显卡,每张24GB显存,理论上可以跑qwen2.5:32b,但一直OOM。排查了半天发现是两块卡显存没有做聚合,ollama默认只加载到第一块卡上。解决方式是把模型先删除再重新pull,ollama底层用LLM库的多卡策略来分配,有时候残留状态会导致单卡加载。这个问题在升级版本后有所缓解,但后端的多卡调度策略仍值得关注,建议生产环境用大模型前专门做一个启动后的显存占用来校验。
5.3 端口冲突与数据卷恢复的实战经验
端口冲突是容器启动失败的第二大原因。OLLAMA默认端口11434,如果服务器上已经有其他服务占用,启动时会报address already in use。处理方式:
bash复制netstat -tlnp | grep 11434
看到占用进程的PID后,要么停掉那个服务,要么换端口。换端口的操作是同时修改-p参数和OLLAMA_HOST环境变量:
bash复制docker run -d \
--name ollama \
-p 11435:11435 \
-e OLLAMA_HOST=0.0.0.0:11435 \
...
注意这里-p的右侧容器端口也要改成11435,因为容器内监听的是这个端口,两边要一致。
数据卷的问题集中在误删容器后模型如何恢复。我自己的标准操作流程是:
- 删除容器前先确认挂载目录还在:
ls /data/ollama/models。 - 容器删掉后用同样挂载参数的run命令重新创建。
- 启动后执行
docker exec ollama ollama list,看到之前的模型就说明恢复成功。
如果list为空但models目录有内容,多半是权限问题或启动用户不一致。旧版本镜像默认以root运行,新版本有些镜像改成非root用户,导致挂载目录没有读取权限。解决方式是给挂载目录授权:
bash复制chmod -R 755 /data/ollama
chown -R 1000:1000 /data/ollama
具体uid要根据镜像内用户来确定,可以通过docker exec查阅。
5.4 网络配置与防火墙开放要点
容器跑通了,模型也能聊了,但其他机器访问不了,这是最后一个高频问题。原因基本都在防火墙或安全组。
Linux服务器上的iptables或firewalld需要放行11434端口:
bash复制firewall-cmd --permanent --add-port=11434/tcp
firewall-cmd --reload
如果是iptables:
bash复制iptables -A INPUT -p tcp --dport 11434 -j ACCEPT
systemctl save iptables
云服务器的话还要去控制台的安全组入方向规则添加11434端口,这一步很多人会忽略。
验证远程访问是否正常:
bash复制curl http://<服务器IP>:11434/api/tags
返回JSON格式的模型列表就说明网络通了。
这里我个人还有一个习惯:生产环境不会直接把11434端口对公网完全开放,而是通过Nginx反代加一层访问控制,用API Key或IP白名单限制调用来源。大模型服务成本主要在算力上,开放给不相关的人调用既浪费资源也有安全隐患。
写在最后的几个实用心得
这个部署方案我在三台不同规格的服务器上反复验证过,整个过程最耗时间的其实不是部署本身,而是前期的模型选型和硬件匹配。我的建议是先把最小可用链路跑通,用0.5b或3b模型验证环境,再逐步上大规模模型,这样排查问题最快。
另外一个小技巧:给容器命名时加上项目标识,比如ollama-prod、ollama-test,多个环境共存时一眼就能认出哪个是哪个。数据卷也要按环境隔离,不然测试环境拉了一堆小模型,生产环境做容量规划时磁盘占用就混乱了。
最后再分享一个我在实际运维中积累的经验:定期执行docker exec ollama ollama list检查模型列表,清理不再使用的模型,命令是docker exec ollama ollama rm <model>。模型文件体积很大,留着不用的模型既占磁盘又增加备份成本。部署完成后把docker-compose.yml和部署文档一起收到项目的git仓库里,后面无论换人还是换机器,都能按文档快速复原整套环境。
说到底,docker+ollama这套方案真正的价值不是省了那几条命令,而是让本地大模型变成了一项标准化的、可复制的基础设施能力。这套部署方式在不同机器上的一致性,才是它最值得投入时间的原因。
