资讯动态

Ubuntu 24.04 conda环境Python服务开机自启:Ollama依赖管理实践

发布时间:2026/10/9 8:17:39 来源:尧图企业网站定制
搞了一台Ubuntu 24.04的机器做本地大模型应用推理服务用Ollama跑业务逻辑是放在conda虚拟环境里的Python程序。需求很简单机器重启后两个东西都要自动起来而且Python程序必须在Ollama之后启动。听起来挺常规实际动手才发现坑比想象中多最典型的就是conda activate这招在systemd里直接失灵卡了我快两个小时。这篇文章把完整的解决路径、踩过的坑和最后的稳定方案都写出来给需要在Ubuntu 24.04上让conda虚拟环境里的Python程序开机自启、同时依赖外部服务的朋友做个参考。1. 开机启动方案选型为什么最后留在systemd上先说结论开机自启动方案花样不少我一个个试过最后老实回到systemd上。简单对比一下几个主流的方案优点缺点适合场景rc.local配置简单一行命令搞定Ubuntu 24.04默认没有此文件需自行创建执行环境极简PATH不全无法控制与其他服务的依赖顺序一次性脚本不依赖外部服务crontab reboot用起来快root权限下直接写环境变量缺失严重command not found是家常便饭没有依赖管理程序崩溃不会自动重启快速临时方案不推荐生产systemd服务原生依赖管理、崩溃自动重启、日志统一管理、官方推荐配置需要理解几个关键字段学习成本略高长期稳定运行的服务尤其是需要控制启动顺序的场景我一开始图省事先试了rc.local。在/etc/rc.local里写了两行一行启动ollama服务一行用conda run -n myenv python main.py启动脚本。结果重启之后Ollama起来了Python程序完全没动静。查了半天日志问题出在rc.local的执行环境太干净conda run需要的环境变量没加载解释器都找不到程序压根没跑到。后来又试了crontab的reboot倒是能跑但没法说等Ollama起来了再跑我的程序纯靠脚本里硬编码sleep 30这种碰运气的做法不踏实。最后老老实实写systemd服务单元。Ubuntu 24.04的启动流程本身就跑在systemd上用它管服务是天经地义的事。依赖管理用After和Wants就能精确表达我要在Ollama之后启动程序崩了还能配Restarton-failure自动拉起日志直接进journald排查问题一条命令的事。折腾了两天后我的体会是能用systemd解决的就别在rc.local和crontab上浪费时间。2. 动手前的环境摸底找到conda环境里Python的真实路径很多教程一上来就教你写service文件结果一堆人卡在第一步systemd单元文件里没法用conda activate。这不是你操作不对是机制决定的——systemd执行服务时用的是最精简环境不会加载你的shell配置文件conda命令和虚拟环境激活状态都带不过来。所以在写配置之前先把几件事摸清楚。2.1 确认conda安装位置和虚拟环境列表先看一眼conda装在哪、有哪些环境which conda conda env list我的机器上输出是这样的which conda /home/xxx/miniconda3/bin/conda conda env list # conda environments: # base * /home/xxx/miniconda3 ollama-app /home/xxx/miniconda3/envs/ollama-app重点记住虚拟环境的根路径/home/xxx/miniconda3/envs/ollama-app。这个路径在配置里要反复用到。2.2 找到虚拟环境里Python解释器的绝对路径这就是很多人忽略的细节写systemd单元文件时ExecStart里不能用python不能用conda activate xxx python最干净的方式是直接用虚拟环境里Python解释器的绝对路径。确认它的真实路径/home/xxx/miniconda3/envs/ollama-app/bin/python -c import sys; print(sys.executable)输出是/home/xxx/miniconda3/envs/ollama-app/bin/python就对了。这一步看似多余但它能避免你后面纠结为什么我配了conda activate还是报错。顺带验证一下虚拟环境能不能正常导入项目的依赖比如requests、httpx这些/home/xxx/miniconda3/envs/ollama-app/bin/python -c import requests; print(deps ok)能打印deps ok说明环境没问题是干净的、可以直接被systemd调用的。如果有模块导入失败先在命令行里排查依赖别带着问题去写服务配置。2.3 确认Ollama的当前状态因为我们要做依赖排序必须先知道Ollama在这台机器上是怎么跑的。标准安装的话检查systemctl status ollama如果输出显示Active: active (running)说明存在ollama.service我们的Afterollama.service才能生效。如果提示Unit ollama.service could not be found说明Ollama不是以systemd服务方式安装的比如是手动下载的二进制包、或者放在自定义目录里跑那依赖方式就得换思路这个我留到第四章详细讲。3. 核心编写systemd服务单元文件以及Afterollama.service为什么还不够环境摸完写配置。我最终用的service文件长这样放在/etc/systemd/system/ollama-app.service[Unit] DescriptionOllama Python App Service Wantsnetwork-online.target ollama.service Afternetwork-online.target ollama.service [Service] Typeexec Userxxx WorkingDirectory/home/xxx/app EnvironmentPATH/home/xxx/miniconda3/envs/ollama-app/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin EnvironmentLD_LIBRARY_PATH/home/xxx/miniconda3/envs/ollama-app/lib:/usr/local/lib:/usr/lib:/usr/lib/x86_64-linux-gnu ExecStart/home/xxx/miniconda3/envs/ollama-app/bin/python /home/xxx/app/main.py Restarton-failure RestartSec5 [Install] WantedBymulti-user.target逐行拆解一下每个字段都有讲究。3.1 [Unit] 段的依赖声明Wantsnetwork-online.target ollama.service Afternetwork-online.target ollama.serviceWantsollama.service的意思是系统启动时如果ollama.service存在且能启用就尽量把它拉起来但这不是强依赖就算Ollama起不来我的服务也照样启动。Afterollama.service则告诉systemd在启动顺序上必须排在Ollama之后。这里有个很容易踩的认知误区After只保证systemd层面认为Ollama服务已启动不保证Ollama真正对外可用。systemd把ollama.service标记为active指的是主进程被拉起并运行了至于Ollama内部的模型有没有加载完、端口有没有开始监听systemd不管。所以我在ExecStart前做了一个ollama就绪检测见3.3。3.2 [Service] 段的几个关键点Typeexec而不是Typesimple。Typesimple是默认值systemd认为服务已经启动不会做额外检查。Typeexec会等systemd确认ExecStart里的可执行文件能被找到、能成功执行如果路径写错了会立刻失败并报错排查问题非常直观。实测下来这个区别能省很多冤枉时间。Userxxx。我用普通用户跑Python程序不用root。一个是安全考虑另一个是很多模型应用会写缓存到用户目录用root跑反而会遇到权限路径不一致的问题。注意如果User字段指定的用户不对后面你会看到各种Permission denied。EnvironmentPATH。表面上看ExecStart已经用了绝对路径为什么还要配PATH因为Python程序运行时很多依赖库尤其是含C扩展的包会通过subprocess调用系统命令或者查找共享库。如果你只给Python绝对路径、不配PATH程序内部调ffmpeg、curl这类外部命令时可能直接找不到。把虚拟环境的bin目录放PATH最前面就是让程序默认优先使用虚拟环境里的工具链。Restarton-failure和RestartSec5。这是兜底策略。即使前面所有依赖判断都失效比如Ollama在启动Python后才崩溃Python程序连不上Ollama抛异常退出了systemd会在5秒后重新拉起。配合后面的检测脚本基本能做到只要Ollama最终可用Python就一定会起来。3.3 ExecStartPre真正的等Ollama就绪方案Afterollama.service只解决启动顺序不够。我的做法是加一个ExecStartPre在启动主程序前先等Ollama的端口真正响应。我先写了个等待脚本/home/xxx/app/wait_for_ollama.sh#!/bin/bash # wait_for_ollama.sh # 等待 ollama 的 API 就绪最多等 60 秒 for i in $(seq 1 60); do if curl -sf http://127.0.0.1:11434/api/version /dev/null 21; then echo ollama is ready exit 0 fi sleep 1 done echo ollama not ready after 60s 2 exit 1给执行权限chmod x /home/xxx/app/wait_for_ollama.sh然后修改service文件在[Service]里加一行ExecStartPre/home/xxx/app/wait_for_ollama.sh这样整个服务的生命周期就非常清晰了systemd先按After把Ollama拉起来然后执行ExecStartPre脚本脚本确认http://127.0.0.1:11434/api/version能访问了才放行让主程序启动。即使Ollama加载模型要花十几秒、甚至因为模型体积大要更久程序也不会因为连接被拒而崩溃。关于Ollama默认端口11434以及健康检查端点/api/version这是Ollama的固定约定不同版本都适用。如果你的Ollama改了端口记得同步改脚本里的URL。4. 如果Ollama不是systemd服务怎么保证先后顺序上面说Afterollama.service成立的前提是系统里存在ollama.service。实际中我见过两种情况会打破这个前提4.1 Ollama是手动下载的二进制包没注册成systemd服务很多人在Linux上装Ollama是直接下载官方脚本或者二进制包跑起来靠的是命令行ollama serve甚至把模型存储路径改到其他盘去了比如把模型放到数据盘OLLAMA_MODELS环境变量指向/data/models。这种情况下没有ollama.service可用。我的建议是先给Ollama补一个systemd服务单元让它的生命周期也归systemd管。一个可用的/etc/systemd/system/ollama.service大概是这样的[Unit] DescriptionOllama Service Afternetwork-online.target [Service] Typesimple Userxxx EnvironmentOLLAMA_HOST127.0.0.1:11434 EnvironmentOLLAMA_MODELS/data/models ExecStart/usr/local/bin/ollama serve Restarton-failure RestartSec3 [Install] WantedBymulti-user.target注意ExecStart路径要换成你实际的ollama二进制位置OLLAMA_MODELS按需配置。配置好后systemctl daemon-reload systemctl enable ollama.service systemctl start ollama.service之后你那个Python服务的Wantsollama.service和Afterollama.service就都能生效了。这样做的好处是无论是先启动Ollama再启动Python还是反过来systemd都能把依赖链管清楚。为了让顺序更稳妥我用的是Afternetwork-online.target作为Ollama的网络依赖确保网络就绪后再启动服务。4.2 不想给Ollama写服务想用更通用的等待方案如果你不想动Ollama的启动方式或者Ollama跑在别的容器里/别的机器上那After这条路就不通了。这时可以完全移除Afterollama.service和Wantsollama.service只保留3.3里那个等待脚本Restart兜底。这个方案的好处是不关心Ollama怎么启动的只关心它什么时候能用。只要你的Python程序能通过curl访问到/api/version就说明Ollama准备好了。这套逻辑对Ollama跑在远程服务器的场景同样适用——把等待脚本里的URL改成远程地址就行。我实测过这个等待脚本更通用而且它本质上是在服务可用性层面做判断比systemd层面的状态判断靠谱得多。5. 部署、启用、验证一条命令链条搞定开机自启配置文件写好、等待脚本就位后剩下的就是标准的systemd操作流程。5.1 放置配置并加载把服务文件放到/etc/systemd/system/下然后重载systemd配置sudo cp ollama-app.service /etc/systemd/system/ sudo systemctl daemon-reload5.2 启动并查看状态先手动启动一次别急着重启验证sudo systemctl start ollama-app.service sudo systemctl status ollama-app.service看到Active: active (running)是第一步还要看日志确认程序真的连上了Ollamajournalctl -u ollama-app.service -f实时日志里如果出现ollama is ready然后程序开始正常打印业务日志说明整套流程通了。如果状态是failed直接看最后的日志journalctl -u ollama-app.service -n 50 --no-pager5.3 启用开机自启确认手动启动没问题后启用开机自启sudo systemctl enable ollama-app.service输出一般是Created symlink /etc/systemd/system/multi-user.target.wants/ollama-app.service → /etc/systemd/system/ollama-app.service.到这里理论上重启就能自动跑了。但一定要实测重启。我见过手动启动一切正常、一重启就出幺蛾子的情况原因通常是Afternetwork-online.target在某些场景下并不会等待所有网卡就绪或者conda环境里的某个包需要额外环境变量而重启时没带进去。重启后按顺序检查systemctl status ollama.service systemctl status ollama-app.service journalctl -u ollama-app.service -n 30 --no-pager如果ollama-app.service显示failed日志里会有具体报错对着下面的章节排查。6. 实战中踩过的坑conda init、PATH、以及服务active但不可用最后这部分把我实际踩过的坑全部列出来都是常规文档不会提的。6.1 千万别在ExecStart里写conda activate网上很多教程教你这样写ExecStart/bin/bash -c source /home/xxx/miniconda3/etc/profile.d/conda.sh conda activate ollama-app python /home/xxx/app/main.py我试过能跑但问题非常多shell层级的信号转发不干净、日志里会混入大量activate输出、字符串转义稍不注意就出错。更常见的是报condaerror: run conda init before conda activate——原因是bash非交互模式下没有加载~/.bashrcconda脚本没被source进来conda activate自然失效。如果你非要用这种shell方式正确姿势是先source conda的profile脚本再activateExecStart/bin/bash -c source /home/xxx/miniconda3/etc/profile.d/conda.sh conda activate ollama-app exec python /home/xxx/app/main.py但我的建议很明确直接省略conda激活这层用绝对路径。虚拟环境的bin目录下的python本身就已经绑定了这个虚拟环境的site-packages根本不需要conda activate来激活。少一层绕路少一堆坑。6.2 PATH配了还是报动态库找不到要查LD_LIBRARY_PATH有个Python包在import时一直报undefined symbol排查半天最后发现是动态库路径问题。原因conda环境里的包编译时链接了某个.so文件运行时要在/home/xxx/miniconda3/envs/ollama-app/lib里找但systemd环境里LD_LIBRARY_PATH是空的找不到就崩了。解决办法就是在[Service]里加上EnvironmentLD_LIBRARY_PATH/home/xxx/miniconda3/envs/ollama-app/lib:/usr/local/lib:/usr/lib:/usr/lib/x86_64-linux-gnu如果你不确定具体要加哪些路径可以先在命令行里执行python -c import 你的包测试能通过就用ldd查一下这个包关联的.so文件来源把对应目录加进LD_LIBRARY_PATH。6.3 服务active但不可用After给了你假安全感这是我这次经历中最典型、也最容易让人抓狂的问题。第一次配置好后重启ollama-app.service状态显示active (running)但程序日志里全是Connection refused然后进程崩了。一看时间Ollama服务虽然标记为active但模型还没加载完端口根本没监听。如果你没加等待脚本纯靠Afterollama.service就会遇到这个假active陷阱。处理方式有两种在ExecStartPre里做端口就绪检测我在3.3写的方案这是最稳的。不想写脚本的话用Restartalways配合RestartSec10做个无脑兜底让程序崩了自己重启直到Ollama可用。缺点是启动初期会有一小段时间的反复重启日志会有点乱。我个人推荐方案1因为它是主动探测、一次到位而不是被动崩溃、重复拉起。加了这个脚本之后我重启了四五次每次都是等Ollama就绪后Python程序一次启动成功再没出现过连接被拒的情况。6.4 一个小技巧写unit文件时先加ExecStartPre测试依赖如果你后面还要给别的程序配置开机自启我建议先别急着写完整unit而是把最关心的依赖判断放到ExecStartPre里单独测sudo systemd-run --unittest-wait --propertyExecStartPre/home/xxx/app/wait_for_ollama.sh /bin/true journalctl -u test-wait -n 20 --no-pagersystemd-run能临时起一个服务测试你的ExecStartPre逻辑是否正常不影响现有服务。我后来给别人排查开机启动问题时都是用这招先定位是ExecStartPre没过还是主程序本身起不来比反复改配置文件高效得多。最后分享一点实际操作的体会这套配置跑了两周中间重启过好几次Python程序都能稳定地在Ollama就绪后自动拉起。整个过程回头看最关键的并不是某个高深的命令而是想清楚一件事systemd里说的依赖是启动顺序层面的依赖不是服务可用性层面的依赖。把等Ollama真正就绪这个逻辑明确写进ExecStartPre比在After里反复纠结管用得多。另外提醒一句给服务写配置之前一定先手动跑一遍那条ExecStart命令确认你的绝对路径、环境变量在正常shell里就能工作。我在命令行里执行得好好的Python程序放到systemd里就各种找不到东西十有八九是环境变量没带全。把环境变量整理成Environment写清楚能省一整天的排查时间。如果你的程序还依赖其他外部服务比如数据库、消息队列完全可以照着同一套思路再加几个After和检测脚本逻辑是一样的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑