systemd Type=notify 正常退出,启动却报 protocol
成功退出码不能完成notify就绪协议。正常不运行用真实条件提前判断,必要服务保持真实READY及依赖等待,不用假通知隐藏失败。
服务设置了 Type=notify,还把某个退出码列入 SuccessExitStatus=,程序启动时因“不需要运行”返回这个退出码,systemd 却将服务标为 failed,Result=protocol。若进程在退出前没有完成有效的 READY=1 通知,应先检查启动协议,而不是继续增加成功退出码。
SuccessExitStatus= 定义哪些主进程退出状态可视为正常终止,不会替代 Type=notify 的就绪要求。本文针对通常由主进程发送通知、未使用 RemainAfterExit=yes 保留状态的长期服务;复杂的多进程就绪交接需要单独核对。
同时查看退出状态与启动结果
检查生效的 Type=、NotifyAccess=、SuccessExitStatus=、RestartPreventExitStatus= 和 RemainAfterExit=,再看启动日志及主进程退出状态。
如果进程以预期退出码离开、服务却以 protocol 失败,同时没有被接受的 READY 通知,就符合这里的分支。若是程序崩溃、配置解析失败、通知进程没有权限,或一直没有退出导致 timeout,应沿对应错误处理。设置成功退出码不会消除这些错误。
RestartPreventExitStatus= 控制特定退出状态是否触发自动重启,也不能把尚未完成的启动变成已就绪。是否重启和是否成功,是两个判断。
可选服务在进入启动阶段前判断条件
某些程序只有在业务配置齐备时才应该启动,缺少配置属于正常的“不运行”。可以按实际条件使用 unit 的 Condition…=,或者在支持的版本上使用 ExecCondition=。不要虚构程序并不提供的 --check-config 参数,也不要把一个文件存在当成整个业务配置有效。
ExecCondition= 从 systemd 243 起提供,执行在 ExecStartPre= 之前。通常,返回 0 或匹配 SuccessExitStatus= 的状态会继续启动;其他 1 到 254 的状态会跳过后续命令且不标为 failed。返回 255、超时或被信号终止则会导致失败。
因此,条件程序必须区分“不需要运行”和“检查发生错误”,并核对 unit 中已有的成功退出码。比如退出码已被列为成功,不能再指望它同时表示跳过。也要检查停止后处理和 SuccessAction=:ExecCondition 跳过与 unit 条件不满足的生命周期并不完全相同。
修改前保留原 unit 和 drop-in,确认依赖方接受服务没有启动的状态。需要重启或停止现有服务时,按业务维护窗口处理,不能为了观察一个绿色状态中断关键任务。
必须运行的服务应发送真实就绪通知
程序确实需要长驻时,应在初始化完成、能够履行业务职责后发送 READY=1,并确认通知被允许的进程发送、被服务管理器接收。不能为了隐藏失败,先假报 READY 再退出;依赖它的其他服务可能因此过早启动。
STOPPING=1 描述正在停止,不能用来补上从未完成的启动协议。在当前规则中,进入 STOPPING 后也不能再靠后续 READY 撤销这个状态。不要把这两条通知当成任意调整服务颜色的标记。
如果程序只需以前台生命周期管理,且依赖方不需要等待应用级就绪,可以评估与程序行为匹配的 Type=exec。它确认程序执行成功,并不保证初始化或业务就绪;依赖真实 READY 的部署不能直接替换。
验收应分别覆盖配置齐备与正常无需运行两种情况:前者必须等真实就绪后才放行依赖,后者按约定跳过且不误报配置检查错误。错误配置和启动失败仍应保留失败状态,不能全部归入成功退出。


