如何让一个 systemd 服务在另一个服务“完成”后启动?

如何让一个 systemd 服务在另一个服务“完成”后启动?

我总是遇到需要与服务 A 关联的程序在服务 B 之前运行完成的情况。“之后”并不能解决这个问题。之后说

最重要的是,对于服务单元,当所有已配置的启动命令都已被调用并且它们失败或报告启动成功时,出于 Before=/After= 的目的,启动被视为已完成。",但这并不意味着底层流程已成功完成。

在我看来,功能上有一个明显的漏洞。如果服务B依赖于服务A的成功完成,我该如何启动服务B?

答案1

这个答案几乎清楚地回答了我的问题,但遗漏了 的相关描述Type=oneshot。以下是相关文字systemd.service 文档

oneshot 的行为与 simple 类似;但是,服务管理器将在主进程退出后考虑该单元。然后它将启动后续单位。

因此,我们可以使用一次性服务来进行确定性排序。

答案2

这是Requires=您在使用时通常使用的固有解决方案After=;引用man systemd.unit

需要=

与 Wants= 类似,但声明了更强的需求依赖性。也可以通过将符号链接添加到单元文件附带的 .requires/ 目录来配置这种类型的依赖项。

如果该单位被激活,列出的单位也将被激活。如果其他单元之一无法激活,并且设置了对失败单元的排序依赖性 After=,则该单元将不会启动。此外,无论是否指定 After=,如果其他单元之一显式停止(或重新启动),则该单元将停止(或重新启动)。

After=只需指定您的单元相对于您所依赖的单元启动的时间点,并Requires=定义您需要在该点成功启动其他单元。

答案3

目标的问题是它会干扰关闭。这就是为什么你很难找到答案。如果shutdown.target被提升,那么你的服务将会停止,但是这会尝试触发你的后续服务。


如果您的第一份工作是短期工作,那么请考虑将它们合并为一项服务Type=simple,并运行它ExecStartPre=。系统将一直保留activating到第一个作业完成为止,如果第一个作业成功,它只会执行第二个作业。如果第一个作业应该为第二个作业进行设置,则此样式非常有用。

[Service]
ExecStartPre=/path/to/firstjob
ExecStart=/path/to/secondjob

[Install]
WantedBy=multi-user.target

如果第二个作业也是短期运行的,则使用Type=oneshot。在这种情况下,这两项工作都可以ExecStart=

[Service]
Type=oneshot
ExecStart=/path/to/firstjob
ExecStart=/path/to/secondjob

[Install]
WantedBy=multi-user.target

如果第一个作业不是短期运行的,但第二个作业是短期运行的,那么考虑在ExecStopPost=。但请务必小心,因为此命令无论是否ExecStart=成功。您可以将第二个作业包装在脚本中,通过检查第一个作业的成功情况来有条件地运行它 $SERVICE_RESULT,$EXIT_CODE$EXIT_STATUS。如果第二项工作是清理任务,则此范例特别有用。

[Service]
ExecStart=/path/to/firstjob
ExecStopPost=/bin/bash -c "[ $SERVICE_RESULT = success ] && /path/to/secondjob"

[Install]
WantedBy=multi-user.target

如果这两项工作都需要长时间运行,那么您需要发挥更多创意。你可以做的一件事是设置一个触发器*.path单元。这将触发其姊妹服务PathChanged=被感动了。对于路径,我会使用一些独特的东西,并以前缀%t说明符将此文件放入/run

# A.service
[Service]
ExecStart=/path/to/firstjob
ExecStopPost=/bin/bash -c "[ $SERVICE_RESULT = success ] && touch %t/B.trigger"

[Install]
WantedBy=multi-user.target

# B.path
[Path]
PathChanged=%t/B.trigger
Service=B.service

[Install]
WantedBy=multi-user.target

# B.service
[Service]
ExecStart=/path/to/secondjob

这不会干扰关闭,因为在触发下一个单元之前路径将停止。

相关内容