前言:当服务器重启时,你的服务还能“活”着吗?
在折腾服务器和 AI 模型部署时,我们经常会写一些 Shell 脚本来一键启动服务。比如最近我在配置一台 GPU 服务器,使用脚本 /www/wwwroot/start_cluster.sh 来一键拉起多张显卡上的 Ollama 模型集群:
正在启动 Ollama 集群...
策略: 闲置 15s 后自动释放显存
- 实例 0 (GPU 0) 端口 11534 启动成功
- 实例 1 (GPU 1) 端口 11535 启动成功
...
集群启动完毕!
看着满屏的“启动成功”,成就感满满。但是,如果机房停电、系统内核升级或者不小心触发了 reboot,会发生什么?
所有服务都会宕机,你必须手动 SSH 连上服务器,再次敲下启动命令。为了实现真正的自动化、做到“无人值守”,我们必须让这个脚本开机自动运行。
本文将以启动 Ollama 集群为例,详细盘点 Linux 下实现脚本开机自启的三种常见方法,并附带新手最容易踩坑的“避坑指南”。
方案一:使用 Systemd 服务(⭐ 强烈推荐,企业级标准)
在现代的 Linux 发行版(如 Ubuntu 16.04+, CentOS 7+, Debian 8+)中,Systemd 已经是管理系统和服务的绝对核心。
使用 Systemd 的最大好处是:它可以接管你的进程,不仅能开机自启,还能在服务崩溃时自动重启,并且统一管理日志。 它是将普通脚本“正规化”为系统服务的最佳方式。
步骤详解:
1. 赋予脚本执行权限
首先,确保你的脚本拥有可执行权限,否则系统无法调用它:
chmod +x /www/wwwroot/start_cluster.sh
2. 创建 Service 配置文件
进入 Systemd 的服务目录,创建一个以 .service 结尾的文件。我们给它起名叫 ollama-cluster.service:
sudo nano /etc/systemd/system/ollama-cluster.service
3. 编写服务配置
将以下内容复制进去。因为我们的集群脚本是执行完一连串命令后就退出的(后台进程依然运行),所以我们将 Type 设置为 oneshot,并开启 RemainAfterExit。
[Unit]
Description=Ollama GPU Cluster Auto-Start
# 确保在网络和基础服务启动后再执行我们的脚本
After=network.target
[Service]
Type=oneshot
# 脚本执行完毕退出后,Systemd 仍认为该服务处于 active 状态
RemainAfterExit=yes
User=root
# 设定工作目录,防止脚本内的相对路径报错
WorkingDirectory=/www/wwwroot
# 执行启动脚本的绝对路径
ExecStart=/bin/bash /www/wwwroot/start_cluster.sh
# 核心亮点:将输出日志保存,方便排错!
StandardOutput=append:/var/log/ollama_cluster.log
StandardError=append:/var/log/ollama_cluster_error.log
[Install]
WantedBy=multi-user.target
4. 激活并设置开机自启
配置写好后,告诉系统重新加载配置,并启用它:
sudo systemctl daemon-reload # 刷新 Systemd 配置
sudo systemctl enable ollama-cluster # 开启开机自启
sudo systemctl start ollama-cluster # 立即启动测试一下
只需这几步,你的脚本就正式拥有了“编制”,以后可以用 systemctl status ollama-cluster 随时查看状态了。
方案二:使用 Crontab 的 @reboot 指令(⚡ 简单粗暴)
如果你觉得写 .service 文件太麻烦,只是想临时让脚本跑起来,那么 Linux 的定时任务管理器 cron 提供了一个非常隐藏但好用的指令:@reboot。
步骤详解:
1. 编辑当前用户的定时任务
在终端输入:
crontab -e
2. 增加 @reboot 规则
在文件的最末尾,另起一行加上:
@reboot cd /www/wwwroot && /bin/bash start_cluster.sh > /var/log/ollama_cluster.log 2>&1
原理解析:@reboot 会在系统启动且 Cron 守护进程启动时执行一次后面的命令。这里的 > /var/log/... 2>&1 是将标准输出和错误输出都重定向到日志文件中,防止程序报错时无迹可寻。
优点:配置极简,一分钟搞定。
缺点:无法精细控制启动顺序(比如无法保证网络就绪后再执行),进程崩溃后不会自动重启。
方案三:使用 /etc/rc.local(🕰️ 经典老派写法)
对于一些老鸟来说,/etc/rc.local 是最熟悉的朋友。在这个文件里的命令,会在系统所有运行级别启动完毕后执行。
注意:在较新的 Ubuntu 等系统中,rc-local 服务默认可能是关闭或不存在的,需要手动开启,因此现代系统不太推荐此方法,但作为经典依然值得了解。
步骤详解:
1. 编辑 rc.local 文件
sudo nano /etc/rc.local
2. 插入你的启动命令
找到 exit 0 这一行,在它之前插入你的启动代码:
#!/bin/bash
# (上面是系统默认的其他配置)
# 启动 Ollama GPU 集群
echo "Starting Ollama Cluster..." > /var/log/sys_boot.log
cd /www/wwwroot && /bin/bash start_cluster.sh >> /var/log/ollama_cluster.log 2>&1
exit 0 # 必须确保以 exit 0 结尾
3. 确保文件可执行
sudo chmod +x /etc/rc.local
⚠️ 高能预警:90% 新手都会踩的“环境变量陷阱”
很多朋友按照上面的方法配置完,满心欢喜地重启服务器,却发现脚本根本没跑起来,日志里写着:“command not found” 或者 “CUDA_VISIBLE_DEVICES 找不到”。
为什么手动执行可以,开机自启却失败?
因为开机自启时的系统环境变量(尤其是 PATH)非常干净,它不像你用 SSH 登录时会自动加载 ~/.bashrc 或 ~/.profile!
避坑解决方案:
- 使用绝对路径:在
start_cluster.sh中,尽量把python写成/usr/bin/python3,把ollama写成/usr/local/bin/ollama。 - 在脚本开头强行加载环境变量:
在你脚本的第二行加上:
source /etc/profile
source ~/.bashrc
- 显式声明需要的变量:如果你用到了 GPU,可以在脚本开头显式声明 CUDA 路径:
export PATH=/usr/local/cuda/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
结语
让脚本开机自启是服务器自动化运维的必修课。总结一下:
- 如果你是生产环境、长期运行的服务,毫无疑问选择 Systemd。
- 如果你是个人折腾、快速验证,用 Crontab 的
@reboot最省事。
希望这篇文章能帮你告别重启焦虑!你的服务器里通常跑着哪些有趣的自动化脚本呢?欢迎在评论区和我交流!
版权属于:soarli
本文链接:https://blog.soarli.top/archives/892.html
转载时须注明出处及本声明。