systemd 根据条件重新启动作业

systemd 根据条件重新启动作业

我有一个仅适用于 Windows 的守护进程,它运行在装有 wine 和 Xvfb 的 Linux 机器上。由于这个相当实验性的设置,守护进程会定期崩溃,我想实现某种机制来自动重新启动守护进程。目前我有一个带有Restart=always设置的 systemd 单元定义。

但是,我注意到有时守护进程崩溃了,但并没有退出进程。这相当于显示一个对话框,询问“守护进程崩溃了,是否要发送错误报告?”。因此,进程仍在运行,但守护进程已停止工作。

我可以在我的 Linux 机器上检查到的这种现象的唯一外部行为是两个新文件,它们出现在某个位置,但文件名可变(它们依赖于时间,并且名称中带有时间戳)。我认为它们是某种内存转储或堆栈跟踪,最初应该用于发送错误报告。

所以现在我正在寻找一个解决方案让 systemd 捕获这个解决方案,比如

  1. 在单元启动时,查看崩溃转储目标目录并制作目录内容的快照
  2. 启动守护进程
  3. 定期查看目录,如果有快照中没有的新文件,则根据某些正则表达式重新启动守护进程并刷新快照。

我考虑过用 bash 或其他语言编写的包装器,但存在两个问题:首先,我不知道如何实现这种行为;其次,这会使 systemd 的使用完全过时,因为脚本处理所有崩溃处理,而 systemd 只会执行脚本。

我也考虑过只使用 systemd 给定的功能定期重新启动守护进程,但这样做效率很低(因为 wine 包装器中的 Windows 守护进程首先并不低效),因为它有时会在没有必要的情况下重新启动守护进程,或者在守护进程崩溃后需要一些时间才能启动定期重启。

解决这个问题的最佳方案是什么?

仅供参考:我谈论的守护进程是 Google Photos 的上传程序。出于某种原因,Google 没有为 Linux 发布它。

答案1

好的,我发现了 systemd.path 的强大功能。

ExecStart=systemctl restart daemon.unit我使用和创建了第二个服务单元Type=oneshot。之后,我使用PathModified=<crashdump output directory>和创建了第三个单元,即路径单元Unit=daemon-restart.unit

现在它已经可以正常工作了。我只需要确保没有其他进程正在写入输出目录,但这可以通过多个不同的 wineprefixes 来解决。

答案2

我认为您的问题是您的程序可能崩溃了,但 wine 没有崩溃,所以 systemd 看不到任何问题(PID 仍然存在)。

首先,您可能会从以下问题的答案中找到一些帮助:有条件启动 SystemD 服务?

我认为您可能需要更详细地说明您的需求(和/或考虑调整它们以简化设置)。

基本上,我认为解决方案可以归结为巧妙使用 ConditionPathExistsGlob=,可能在辅助单元中。

一种不靠谱的解决方案可能涉及一个带有此类 PathExistsGlob 条件的计时器单元,这可能会重新启动您的主服务。我倾向于让该计时器单元也处理文件/转储的清理,而不是让主单元这样做,但这几乎肯定是一个个人喜好问题。

因此,我不会触碰您所拥有的内容,而是添加类似以下内容的内容(注意:这只是猜测,并未经过测试):

[Unit]
Description=Detect and recover issues with Uploader
After=uploader.service
Requires=uploader.service
PartOf=uploader.service
AssertPathExistsGlob=/srv/uploader/crash*.dump

[Service]
Type=oneshot
ExecStart=cleanup_script
Restart=on-success

基本逻辑是:

  • 你可以用计时器运行它,比如每 5 分钟一次(或者任何符合你需求的时间间隔)
  • 如果崩溃文件不存在,计时器单元将无法启动,主上传器服务将继续运行
  • 如果崩溃文件存在,则运行一些自定义脚本来处理它们,然后重新启动我们的计时器单元(由于 PartOf,应该还要重新启动主上传器服务)

我并不是说这是一个很好的解决方案,但它可能是一个解决方案

相关内容