在 systemd 中使用 OnFailure 的正确方法

在 systemd 中使用 OnFailure 的正确方法

我有一个运行软件的服务,该软件会生成一些配置文件(如果它们不存在),并读取它们(如果存在)。我一直面临的问题是这些文件有时会损坏,导致软件无法启动,从而导致服务失败。在这种情况下,我想删除这些文件并重新启动服务。

我尝试创建一个在失败时应该执行的服务,方法如下:

[Service]
ExecStart=/bin/run_program
OnFailure=software-fail.service

该服务在哪里:

[Service]
ExecStart=/bin/rm /file/to/delete
ExecStop=systemctl --user start software.service

但问题是,即使服务失败,该服务也不会启动。
我尝试做

systemctl --user enable software-fail.service

但每次系统启动时它都会启动,就像任何其他服务一样。

我的临时解决方案是使用

ExecStopPost=/bin/rm /file/to/delete

但这不是一个令人满意的解决方法,因为无论是否由于故障导致,它在停止服务时总会删除该文件。

失败时输出:

● software.service - Software
   Loaded: loaded (/home/trippelganger/.config/systemd/user/software.service; enabled;  vendor preset: enabled)
   Active: failed (Result: exit-code) since Fri 2018-05-04 09:05:26 CEST; 5s ago
  Process: 1839 ExecStart=/bin/run_program (code=exited, status=1/FAILURE)
 Main PID: 1839 (code=exited, status=1/FAILURE)



May 04 09:05:26 trippelganger systemd[595]: software.service: Main process exited, code=exited, status=1/FAILURE
May 04 09:05:26 trippelganger systemd[595]: software.service: Unit entered failed state.
May 04 09:05:26 trippelganger systemd[595]: software.service: Failed with result 'exit-code'.

systemctl --user status software-fail.service 的输出是:

● software-fail.service - Delete corrupt files
   Loaded: loaded (/home/trippelganger/.config/systemd/user/software-fail.service; disabled; vendor preset: enabled)
   Active: inactive (dead)

答案1

笔记:您可能想使用ExecStopPost=而不是OnFailure=在这里(请参阅我的其他答案),但这试图解决您的OnFailure=设置不起作用的原因。

无法启动设备的问题OnFailure=可能是因为它位于错误的部分,它需要位于该[Unit]部分而不是[Service]

你可以尝试这个:

# software.service
[Unit]
Description=Software
OnFailure=software-fail.service

[Service]
ExecStart=/bin/run_program

和:

# software-fail.service
[Unit]
Description=Delete corrupt files

[Service]
ExecStart=/bin/rm /file/to/delete
ExecStop=/bin/systemctl --user start software.service

我可以通过这个设置让它工作。

但请注意,OnFailure=这里使用并不理想,因为您无法真正说出程序失败的原因,并且ExecStop=通过直接调用来链接它的另一个启动是相当老套的......使用并查看退出状态的/bin/systemctl start解决方案绝对是优越的。ExecStopPost=

如果你定义OnFailure=inside [Service],systemd(至少是 Fedora 27 的 234 版本)会抱怨:

software.service:6: Unknown lvalue 'OnFailure' in section 'Service'

不确定您是否在日志中看到了这一点...(也许这是在最近的 systemd 中添加的?)这应该暗示那里发生了什么。

答案2

为了在服务失败时执行一些清理,您可以使用ExecStopPost=,无论服务成功与否都会执行。

在运行的代码中,您可以使用、或ExecStopPost=之一来确定失败条件并采取相应的操作。请参阅$SERVICE_RESULT$EXIT_CODE$EXIT_STATUS文档查看这些环境变量以检查哪一个适合您。

然后您可以使用Restart=on-failuresystemd 在发生故障时尝试重新启动您的设备。

把它们放在一起,这就是它的样子。假设run_program每当文件损坏时都会以状态 2 退出(希望您可以将其适应上述文档中的其他故障场景),这应该可以工作:

[Service]
ExecStart=/bin/run_program
ExecStopPost=/bin/sh -c 'if [ "$$EXIT_STATUS" = 2 ]; then rm /file/to/delete; fi'
Restart=on-failure

笔记:双美元符号$$是将其转义到 systemd,以便 shell 看到$EXIT_STATUS并访问该变量。使用单个美元符号也可以,但是 systemd 会进行替换,并且 shell 会看到[ "2" = 2 ],这可以说也可以工作......无论如何,您可以通过将所有这些逻辑放入 shell 脚本并调用来绕过大部分内容通过其完整路径ExecStopPost=,这可能会更好,您还可以轻松地向脚本添加更多命令,例如记录从错误情况中恢复所采取的操作。)

希望这将为您提供足够的指导,以了解如何根据您的特定情况正确配置!

答案3

需要警惕的是systemd版本。$EXIT_STATUS2017 年 5 月发布的 systemd 版本 232 引入了优点和相关值

一些发行版(在打字时)如 RHEL 7.x 仍在使用 systemd 版本 219,它不会设置像$EXIT_STATUS.因此你不能使用上面的优秀建议:( ...

相关内容