适用于 Linux 上由 systemd 托管的服务反复进入 activating (auto-restart)、failed,或出现 Start request repeated too quickly 的场景。命令基线为 systemd 245+;示例单元为 app.service,执行前应替换为实际单元名。先保留退出现场,不要一开始就反复执行 restart 或提高启动频率。
1. 固定时间线与最近一次退出结果
date -u
systemctl status app.service --no-pager -l
systemctl show app.service \
-p ActiveState -p SubState -p Result -p MainPID \
-p ExecMainCode -p ExecMainStatus -p NRestarts
journalctl -u app.service --since '-15 min' --no-pager -o short-iso
ExecMainCode=exited 时重点看退出码,killed 或 dumped 时重点看信号、内核日志和是否生成了 core dump。NRestarts 是累计值,应结合 UTC 时间和日志判断它是否仍在增长;单次状态快照不能证明循环已经停止。
systemd 自身的特殊退出码也有定位价值。例如 200/CHDIR 常指向 WorkingDirectory=,203/EXEC 常见于可执行文件不存在、不可执行、解释器路径错误或被访问控制策略拒绝。应用自定义退出码仍应以该程序文档为准。
2. 核对实际生效的单元与重启策略
systemctl cat app.service
systemctl show app.service \
-p FragmentPath -p DropInPaths -p ExecStart \
-p User -p Group -p WorkingDirectory -p EnvironmentFiles \
-p Restart -p RestartUSec \
-p StartLimitIntervalUSec -p StartLimitBurst
systemctl cat 会同时显示主文件和 drop-in,应以这里的合并来源为准,不要只看某个手头副本。逐项确认 ExecStart= 使用的绝对路径、服务用户、工作目录和环境文件都存在且权限匹配。systemd 的 ExecStart= 默认不经过交互式 shell,不能假定 shell profile、当前目录、PATH 展开或管道语法会自动生效。
Restart=always 会让正常退出也触发重启;Restart=on-failure 则应继续确认哪些退出码或信号被视为失败。StartLimitBurst 触发后,单元进入 failed 是保护结果,不是根因。仅增加限速阈值可能把高频崩溃变成持续资源消耗。
3. 排除资源限制与 OOM
systemctl show app.service \
-p MemoryCurrent -p MemoryMax -p TasksCurrent -p TasksMax \
-p LimitNOFILE -p OOMPolicy
journalctl -k --since '-15 min' --no-pager | \
grep -iE 'out of memory|oom-kill|killed process'
若单元已退出,部分当前值可能显示为未设置,需与故障时的监控或日志结合。出现 OOM 记录时,应确认被终止的 PID 是否属于该单元,并区分单元级 MemoryMax=、系统整体内存压力和应用自身内存上限。文件描述符或任务数达到上限时,应用日志常伴随 EMFILE、Too many open files 或无法创建线程;不要只放大限制,还要查连接、文件或线程为何持续增长。
4. 区分配置错误与应用启动失败
如果最近修改过单元文件,可对准备部署的文件做静态检查:
systemd-analyze verify /etc/systemd/system/app.service
路径必须替换为实际待检查文件。verify 能发现部分指令和依赖问题,但不能证明外部依赖、权限、端口或业务初始化一定成功。不要在服务仍可能运行时直接复制 ExecStart 到 root shell 执行:用户、环境、工作目录和隔离策略不同,而且可能启动第二个写入同一资源的实例。
若日志显示配置文件、证书、socket 或数据目录访问失败,应沿实际服务用户逐级检查路径权限,并同时核对 SELinux 或 AppArmor 的拒绝记录。遇到端口占用时,用 ss -lntp 或 ss -lxnp 确认监听者,不要先终止未知进程。
5. 修复后的受控恢复与闭环
只有在根因已确认、配置已准备好并处于允许变更的窗口时,才执行:
sudo systemctl daemon-reload
sudo systemctl reset-failed app.service
sudo systemctl start app.service
reset-failed 会清除 failed 状态和启动限速计数,因此应放在修复之后,而不是用来隐藏循环。随后至少验证:
date -u
systemctl is-active app.service
systemctl show app.service \
-p ActiveState -p SubState -p Result -p MainPID -p NRestarts
journalctl -u app.service --since '-5 min' --no-pager -o short-iso
还应从真实调用路径执行一个无副作用的健康检查,并在超过原 RestartSec= 和典型崩溃周期后再次查看 NRestarts。只有服务持续为 active、主 PID 稳定、重启计数不再增长、日志无新增启动错误且业务检查成功,才能认为重启循环已经闭环。