systemd 服务起不来:Unit 找不到、依赖顺序和 Type 选错的三类坑
systemctl start 返回成功,服务却没在跑;或者刚启动就退出,systemctl status 里只剩一行 failed。这类问题八成不在应用代码,而在 unit 文件本身。下面按排查顺序讲三类最常见的坑,每类都给可直接执行的命令。
一、Unit 找不到:先确认 systemd 看见的是哪个文件
自己写的 unit 放在 /etc/systemd/system/ 下,改完必须执行 systemctl daemon-reload,否则 systemd 仍在用内存里的旧版本。若报 Unit xxx.service not found,先用 systemctl cat 和 systemctl show -p FragmentPath 确认它实际解析到的路径:
systemctl cat myapp.service
systemctl show -p FragmentPath myapp.service
FragmentPath 为空,说明这个 unit 根本没被加载。常见原因是文件名后缀漏了 .service、放错目录,或者 unit 名里的 @ 被当成模板实例。模板 unit(myapp@.service)只有在 systemctl start myapp@foo 时才会实例化,直接 start myapp@.service 是找不到的。写完先跑一遍静态检查,比启动后再猜快得多:
systemd-analyze verify /etc/systemd/system/myapp.service
二、依赖顺序:After 只排序,不会把依赖拉起来
After= 和 Before= 只决定谁先谁后,不会让 systemd 去启动对方。真正拉起依赖的是 Wants=、Requires= 和 BindsTo=。只写 After=network.target 却漏了 Wants=network-online.target,开机时服务可能比网络先起,表现为手动 start 正常、开机必失败。反过来,Wants 写太多也会拖慢启动。用 critical-chain 看真正的启动链:
systemd-analyze critical-chain myapp.service
systemctl list-dependencies myapp.service
还要区分 network.target 和 network-online.target:前者只表示网络栈初始化完成,不保证拿到地址;需要联网的服务应依赖后者,并确认 NetworkManager-wait-online 已启用。
三、Type 选错:进程退出了,systemd 还以为活着
Type=simple 下,systemd 执行 ExecStart 后立刻认为服务已启动,之后进程退出就进入 failed,并触发 Restart 策略,日志里表现为反复重启。前台常驻程序用 simple 没问题;会自己 daemonize 的老程序必须用 Type=forking 并给出 PIDFile=,否则 systemd 跟踪的是那个已经退出的父进程。使用 sd_notify 的程序要写 Type=notify,一次性任务用 Type=oneshot,需要保持 active 状态则再加 RemainAfterExit=yes。
判断服务到底怎么退的,看这三条:
systemctl status myapp.service --no-pager
journalctl -u myapp.service -b --no-pager -n 100
systemctl show -p ExecMainStatus -p Result myapp.service
ExecMainStatus 是应用自己返回的退出码,Result 是 systemd 的判定结果,两者分开看能快速区分程序自己崩了和 systemd 认为启动超时或失败。
四、别忘了环境:EnvironmentFile、WorkingDirectory 与权限
手动能跑、systemd 跑不了,多数是环境差异。EnvironmentFile 指向的文件不存在会让 unit 直接失败,可以在前面加一个减号忽略;WorkingDirectory 不存在的报错很隐晦;User= 切换后,家目录和相对路径都变了。先用 systemd-run 在同样的 unit 环境下跑一条命令验证:
systemd-run --unit=probe --property=User=app --property=WorkingDirectory=/opt/app /bin/sh -c 'env; pwd; id'
排查顺序小结
systemctl cat 确认 unit 内容,systemd-analyze verify 做静态检查,systemctl status 看 Result 和退出码,journalctl -u 看应用日志,systemd-analyze critical-chain 看依赖链,最后用 systemd-run 复现环境。按这个顺序走,绝大多数起不来的问题都能在十分钟内定位到具体是哪一行配置。
- 点赞
- 收藏
- 关注作者
评论(0)