Ubuntu 20 双卡 A100 部署 Ollama 从安装翻车到双实例拉满吞吐的完整避坑
一、为什么双卡 A100 不能只装一个 Ollama 就完事很多人对“双卡”有个天然误区我有两张 A100那我起一个 Ollama 服务它应该自动把两张卡都跑满。现实里这件事没这么简单。在很多推理框架里如果模型本身单卡就能装下服务通常会优先走单卡推理因为这样调度更简单不需要跨卡通信延迟往往更低不会引入额外的同步损耗所以双卡机器想追求“整机总吞吐最大化”很多时候最佳方案不是“一个服务看见两张卡”而是起两个 Ollama 实例每个实例只绑定一张卡分别监听不同端口上层调用时做分流这个思路非常重要。一句话总结单实例双卡更适合超大模型跨卡承载双实例双卡更适合把双卡总吞吐压榨出来。二、环境设定Ubuntu 20 双卡 A100本次场景是操作系统Ubuntu 20.04GPU双 A100Ollama 安装路径/usr/local/bin/ollama目标部署两个独立实例ollama-gpu0→ 使用GPU 0监听11434ollama-gpu1→ 使用GPU 1监听11435先确认ollama实际路径whichollamals-l/usr/local/bin/ollama实际结果显示/usr/local/bin/ollama -rwxr-xr-x1root root431102164月304:15 /usr/local/bin/ollama这一步非常关键因为很多教程里写的是/usr/bin/ollama如果你机器上实际在/usr/local/bin/ollama那systemd里的ExecStart就不能抄错。三、极致性能思路为什么我选择双实例方案如果你的目标是“能跑”一个服务够了。如果你的目标是“性能拉满、吞吐拉满”那就要开始考虑如何让两张卡同时工作如何让模型常驻显存如何减少重复装载如何让并发数和上下文长度处于合理区间如何避免一个实例吞掉全部流量另一张卡在旁边看戏所以这里采用双实例实例 1GPU0CUDA_VISIBLE_DEVICES0OLLAMA_HOST0.0.0.0:11434实例 2GPU1CUDA_VISIBLE_DEVICES1OLLAMA_HOST0.0.0.0:11435这样做的好处很直接两张 A100 各自跑一份模型上层可以按端口做轮询每张卡资源互不干扰更容易把整机吞吐打满四、第一版 systemd 配置先给出最终思路对应的systemd配置。/etc/systemd/system/ollama-gpu0.service[Unit] DescriptionOllama GPU0 Service Afternetwork-online.target [Service] ExecStart/usr/local/bin/ollama serve Userollama Groupollama Restartalways RestartSec3 EnvironmentPATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin EnvironmentOLLAMA_HOST0.0.0.0:11434 EnvironmentCUDA_VISIBLE_DEVICES0 EnvironmentOLLAMA_MODELS/data/ollama/models EnvironmentOLLAMA_KEEP_ALIVE-1 EnvironmentOLLAMA_FLASH_ATTENTION1 EnvironmentOLLAMA_KV_CACHE_TYPEq8_0 EnvironmentOLLAMA_MAX_LOADED_MODELS1 EnvironmentOLLAMA_NUM_PARALLEL4 EnvironmentOLLAMA_MAX_QUEUE1024 EnvironmentOLLAMA_CONTEXT_LENGTH8192 [Install] WantedBymulti-user.target/etc/systemd/system/ollama-gpu1.service[Unit] DescriptionOllama GPU1 Service Afternetwork-online.target [Service] ExecStart/usr/local/bin/ollama serve Userollama Groupollama Restartalways RestartSec3 EnvironmentPATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin EnvironmentOLLAMA_HOST0.0.0.0:11435 EnvironmentCUDA_VISIBLE_DEVICES1 EnvironmentOLLAMA_MODELS/data/ollama/models EnvironmentOLLAMA_KEEP_ALIVE-1 EnvironmentOLLAMA_FLASH_ATTENTION1 EnvironmentOLLAMA_KV_CACHE_TYPEq8_0 EnvironmentOLLAMA_MAX_LOADED_MODELS1 EnvironmentOLLAMA_NUM_PARALLEL4 EnvironmentOLLAMA_MAX_QUEUE1024 EnvironmentOLLAMA_CONTEXT_LENGTH8192 [Install] WantedBymulti-user.target这些参数背后的思路也值得讲清楚。五、性能参数不是越大越猛而是越合理越快很多人调优最容易犯的错就是“把所有参数都拉大”。比如OLLAMA_NUM_PARALLEL直接拉到 8OLLAMA_CONTEXT_LENGTH一上来拉到 65536OLLAMA_MAX_LOADED_MODELS也给大队列长度也拉满看起来很猛实际上常见结果是显存压力飙升上下文缓存占用膨胀模型装载与并发调度互相抢资源吞吐未必提高延迟反而变差这套配置里我选择的是下面这个思路1.OLLAMA_KEEP_ALIVE-1模型常驻避免频繁卸载和重新加载。对生产推理服务来说这个几乎是必选项。2.OLLAMA_FLASH_ATTENTION1长上下文下更省显存也更适合高性能场景。3.OLLAMA_KV_CACHE_TYPEq8_0KV Cache 使用量化形式减少上下文缓存带来的显存消耗。4.OLLAMA_MAX_LOADED_MODELS1一张卡一个实例只让它服务一个主模型避免显存碎片化和多模型争抢。5.OLLAMA_NUM_PARALLEL4这是一个偏吞吐优先、又不至于太激进的值。6.OLLAMA_CONTEXT_LENGTH8192这是速度优先的取舍。如果你主要做问答、RAG 结果生成、普通对话服务8192 通常是一个很实用的平衡点。一句话总结就是高性能不是把参数拉爆而是让每个参数都围绕“稳定吞吐”服务。六、第一次翻车ollama-gpu0.service does not exist刚开始启服务时先遇到的是这种问题sudosystemctlenable--nowollama-gpu0 Failed toenableunit: Unitfileollama-gpu0.service does not exist.而另一个服务却能启sudosystemctlenable--nowollama-gpu1 Created symlink /etc/systemd/system/multi-user.target.wants/ollama-gpu1.service → /etc/systemd/system/ollama-gpu1.service.这说明什么说明不是 Ollama 有问题而是ollama-gpu0.service文件压根没创建成功或者文件名不对或者放错目录了。这类问题其实最简单直接检查ls-l/etc/systemd/system/ollama*.service如果没有就重新创建对应的 unit 文件然后执行sudosystemctl daemon-reloadsudosystemctlenable--nowollama-gpu0sudosystemctlenable--nowollama-gpu1七、第二次翻车服务起不来但不是配置文件问题当两个service文件都创建成功后接下来变成了这种状态Active: activating(auto-restart)(Result: exit-code)ExecStart/usr/local/bin/ollama serve(codeexited,status1/FAILURE)这说明什么说明systemd已经找到配置文件了也能执行ollama serve但进程一启动就退出。这时候最容易误判。很多人会开始怀疑是不是ExecStart路径不对是不是 CUDA 不兼容是不是环境变量写错了是不是 Ollama 装坏了其实最正确的动作不是猜而是看日志。八、第一次定位到真因11434 端口冲突日志里首先暴露出来的问题是Error: listen tcp 0.0.0.0:11434: bind: address already in use这说明ollama-gpu0启动失败的根因不是 GPU也不是路径而是11434 端口已经被别的进程占用了。而且它不是启动一次失败而是在自动重启所以你会看到日志里不停刷Started Ollama GPU0 Servicebind: address already in useMain process exitedScheduled restart job这时候要做的事情非常明确1. 停掉默认的单实例服务sudosystemctl stop ollamasudosystemctl disable ollama2. 查看到底是谁占了 11434sudoss-lntp|grep11434sudolsof-i:11434-P-n3. 必要时直接杀掉占端口进程sudofuser-k11434/tcp这里非常容易踩坑如果你已经决定走“双实例”方案就不要再让默认的ollama.service跑着。否则它会先占住11434你的ollama-gpu0永远起不来。九、第二次定位到真因目录权限问题端口问题解决后新的报错出现了Error: mkdir /data/ollama: permission denied: ensure path elements are traversable而且这个问题不只出现在gpu0gpu1也同样报了CUDA_VISIBLE_DEVICES1OLLAMA_HOSThttp://0.0.0.0:11435OLLAMA_MODELS/data/ollama/models这些参数已经正常生效但进程最终还是因为mkdir /data/ollama: permission denied退出。这一点非常值得展开讲。这个错误到底是什么意思很多人看到这句的第一反应是哦/data/ollama/models目录没有创建我创建一下就好了。其实不够。这句报错里最关键的是最后那个单词traversable它的意思是Ollama 运行用户ollama不仅要对目标目录有权限还必须能“穿过”上层目录。换句话说下面这几个路径只要有一级不能进入都会报这个错//data/data/ollama/data/ollama/modelsLinux 目录权限和文件权限不一样。目录想“进入”需要的是执行权限x。所以正确修复方式不是只chown /data/ollama/models而是要把整条目录链路的可遍历权限打通。正确修复方式sudosystemctl stop ollama-gpu0 ollama-gpu1sudomkdir-p/data/ollama/modelssudochown-Rollama:ollama /data/ollamasudochmod755/datasudochmod755/data/ollamasudochmod755/data/ollama/models然后检查目录链路权限namei-l/data/ollama/models这个命令特别好用它会把每一级目录都拆开给你看。再进一步用ollama用户亲自做一遍写入测试sudo-uollamabash-lccd /data/ollama/models touch .perm_test rm -f .perm_test echo ok如果输出ok说明权限链路确实通了。如果你不想放开/data有些机器上/data不希望全局755那也可以走 ACLsudoapt-getupdatesudoapt-getinstall-yaclsudosetfacl-mu:ollama:rx /datasudosetfacl-R-mu:ollama:rwx /data/ollama但就实战效率来说先把服务跑起来最直接的方法还是chown chmod。十、为什么状态看起来是 running实际上还是失败有个细节也特别容易误导人。比如你看到Active: active(running)since...第一眼以为已经启动成功了。结果往下几行又看到Main process exited,codeexited,status1/FAILURE Failed with resultexit-code为什么会这样因为你看到的是systemd 状态切换的瞬时输出。服务刚启动时可能短暂进入 running但随后进程立刻因为权限问题退出。所以判断服务是否真的正常不能只看最上面一行要结合journalctl -u 服务名systemctl status -l端口监听状态API 是否可调用综合判断。十一、服务真正启动后怎么用当权限问题修好两个服务都正常running之后使用方式其实很清晰。实例一11434对应 GPU0如果本机默认要打这一路可以直接ollama pull gemma3 ollama run gemma3如果 CLI 默认就是11434这是最顺手的一路。实例二11435对应 GPU1这一路更适合直接走 API。例如curlhttp://127.0.0.1:11435/api/generate-d{ model: gemma3, prompt: 你好做个自我介绍, stream: false }看当前模型是否装载成功分别查两个实例curlhttp://127.0.0.1:11434/api/pscurlhttp://127.0.0.1:11435/api/ps如果两个实例都各自加载了一份模型再配合watch-n1nvidia-smi你就能直观看到双卡是否同时在干活。十二、双实例方案该怎么真正发挥双卡优势服务起来只是第一步。真正想把双 A100 用出价值还得会“用”。正确姿势是请求 A 打到11434请求 B 打到11435上层调用程序按轮询、负载均衡或健康检查做分流两边都常驻同一个模型这样两张 A100 各跑一份模型副本吞吐通常比“单实例等它自己调度两张卡”更稳定。尤其适合在线问答服务RAG 结果生成内部推理 API批量文本处理多用户同时请求反过来说如果你部署的是特别大的模型单卡根本装不下那才更适合去研究“单实例跨双卡承载”。十三、一次完整的排障链路应该怎么复盘把整个过程串起来其实非常典型第一步创建双实例 service目标没问题思路也对。第二步gpu0.service不存在不是 Ollama 问题是 unit 文件根本没创建好。第三步两个服务都能执行但进程秒退说明systemd已经没问题重点转向ollama serve本体启动失败。第四步发现11434端口被占用这是gpu0起不来的第一个真实原因。第五步发现/data/ollama权限不足这是gpu0和gpu1的共同根因。第六步修复目录可遍历权限本质不是“目录不存在”而是ollama用户无法穿过上层目录链路。你会发现真正的实战排障往往不是一上来就命中最终答案而是一层一层剥洋葱。这也是为什么看日志能力比背命令更重要。十四、给一份最终可落地的命令清单如果你现在也在做同样的事情可以直接按这个顺序来。1. 创建模型目录并修权限sudosystemctl stop ollama ollama-gpu0 ollama-gpu1sudomkdir-p/data/ollama/modelssudochown-Rollama:ollama /data/ollamasudochmod755/datasudochmod755/data/ollamasudochmod755/data/ollama/models2. 验证目录链路权限namei-l/data/ollama/modelssudo-uollamabash-lccd /data/ollama/models touch .perm_test rm -f .perm_test echo ok3. 确认默认单实例没占端口sudosystemctl disable--nowollamasudoss-lntp|grep11434sudolsof-i:11434-P-n4. 启动双实例sudosystemctl daemon-reloadsudosystemctlenable--nowollama-gpu0sudosystemctlenable--nowollama-gpu15. 查看服务状态systemctl status ollama-gpu0 --no-pager-lsystemctl status ollama-gpu1 --no-pager-l6. 分别验证两个端口curlhttp://127.0.0.1:11434/api/pscurlhttp://127.0.0.1:11435/api/ps7. 监控双卡状态watch-n1nvidia-smi十五、这套方案最容易被忽略的三个点最后再提炼三个最容易忽略但最致命的点。1. 不要想当然以为路径一定是/usr/bin/ollama你的机器实际在哪就写哪。本次实际路径是/usr/local/bin/ollama。2. 不要在双实例模式下保留默认ollama.service否则 11434 很容易被它先占住。3. 目录权限问题往往不是目标目录本身而是父目录链路traversable这个词要记住。目录能不能进去关键看x权限。十六、结尾很多部署文章喜欢把过程写得很丝滑仿佛复制几条命令一切就顺理成章。但真正的线上环境不是这样的。真正的服务器部署常常是路径和文档不一样默认服务已经占了端口目录权限并没有你想的那么简单systemd状态看起来没问题实际进程已经挂了双卡也不会自动按照你想象的方式工作所以比“有没有一条万能命令”更重要的是你有没有建立起一套正确的判断链先看 unit 文件是否存在再看进程是否真正启动再看端口是否冲突再看目录权限是否可遍历最后再谈性能参数怎么拉满