自定义systemd服务手动启动正常,EC2实例重启后启动失败(状态码255)原因排查咨询
这种情况我之前在EC2上部署定时任务服务时也遇到过——手动跑完全没问题,一重启就掉链子。status=255只是个通用退出码,结合你的场景(要下载数据集、EventBridge触发实例启动),背后大概率是启动时机、环境/路径、权限这几类问题,给你梳理几个核心排查方向:
1. 服务启动太早,依赖的系统资源还没就绪
EC2实例启动时,网络、cloud-init初始化、甚至SSM Agent这些组件都需要一点时间才能完全就绪。你手动启动时,这些资源都已经跑起来了,但开机自启时systemd可能在这些依赖项就绪前就启动了你的服务,导致脚本拿不到密钥、连不上网络。
排查方法:
用journalctl看本次启动的服务日志,找具体错误:
sudo journalctl -u service-name.service -b
如果日志里出现“网络不可达”“无法获取密钥”这类提示,基本就是时机问题。
解决办法:
修改你的systemd服务文件(通常在/etc/systemd/system/下),添加依赖项,让服务在必要的系统组件就绪后再启动:
[Unit] Description=Your Python Dataset Download Service After=network-online.target cloud-init.target ssm-agent.target Wants=network-online.target
network-online.target:确保网络完全就绪(不是仅仅启动网络服务)cloud-init.target:等待EC2的初始化脚本执行完毕(比如挂载EBS、设置环境变量)ssm-agent.target:如果你的密钥存在SSM Parameter Store里,确保Agent就绪
修改后记得重载systemd配置并重启服务:
sudo systemctl daemon-reload sudo systemctl enable --now service-name.service
2. 环境变量或路径不匹配
手动启动时,你当前shell的环境变量(比如密钥所在的变量)、工作目录都是正确的,但systemd服务默认运行在干净的系统环境里,没有用户shell的环境变量,工作目录默认是/,这会导致脚本找不到依赖文件或者密钥。
排查方法:
检查日志里有没有“找不到文件”“未定义变量”的错误;或者临时修改ExecStart打印环境变量,对比手动启动时的环境:
ExecStart=/bin/bash -c "env > /tmp/service-env.log && /usr/bin/python3 /path/to/script.py --key $KEY"
重启实例后查看/tmp/service-env.log就能发现差异。
解决办法:
- 绝对路径优先:把脚本里的所有相对路径改成绝对路径(比如数据集下载路径、依赖模块路径),
ExecStart里也要用Python和脚本的绝对路径,比如/usr/bin/python3 /home/ec2-user/scripts/download.py - 显式设置环境变量:如果密钥是通过环境变量传递的,在服务文件里用
Environment字段定义,或者引用外部环境文件:[Service] Environment="DATASET_KEY=your-secret-key" # 或者引用外部环境文件 EnvironmentFile=/etc/dataset-service.env - 设置工作目录:在服务文件里添加
WorkingDirectory,指定脚本所在的目录:[Service] WorkingDirectory=/home/ec2-user/scripts
3. 权限不足
手动启动时你用sudo是以root身份运行,但systemd服务默认可能用nobody或者其他低权限用户,导致脚本无法读取密钥文件、写入下载目录,或者无法执行某些系统操作。另外,如果你的脚本需要访问挂载的EBS卷,开机时卷的权限可能还没正确设置。
排查方法:
看journalctl日志里有没有Permission denied的关键词;检查服务文件的User/Group配置,确认该用户有脚本执行权限、密钥文件读取权限、下载目录写入权限。
解决办法:
- 指定运行用户:如果不需要低权限运行,直接在服务文件里设置
User=root:[Service] User=root - 调整文件权限:给脚本和密钥文件设置严格的权限(避免泄露):
sudo chmod 700 /path/to/script.py sudo chmod 600 /path/to/key-file - 依赖存储挂载:如果用了EBS卷,在服务文件里添加
After=remote-fs.target,确保卷挂载完成后再启动服务。
4. 添加自动重试机制
有时候启动失败只是临时的(比如网络波动、SSM Agent还在初始化),给服务加上自动重试配置,能大大提高开机自启的成功率:
[Service] Restart=on-failure RestartSec=5 StartLimitInterval=300 StartLimitBurst=5
Restart=on-failure:服务失败时自动重启RestartSec=5:失败后5秒重试StartLimitInterval/StartLimitBurst:限制5分钟内最多重试5次,避免无限循环
最后提醒:每次修改服务文件后,一定要执行sudo systemctl daemon-reload重载配置,然后用sudo systemctl enable service-name.service确保服务被设置为开机自启。
内容的提问来源于stack exchange,提问作者Danny




