了解 systemd “Requires=” 选项

了解 systemd “Requires=” 选项

从 中systemd.unit(5),该选项的Requires=含义是:

配置对其他单元的需求依赖性。如果该单位被激活,此处列出的单位也将被激活。如果其他单位之一被停用或激活失败,则该单位将被停用。

我对此做了一个小实验。我创建了 2 个服务:a.serviceb.service​​ :

# cat a.service
[Service]
ExecStart=/bin/false

# cat b.service
[Unit]
Requires=a.service

[Service]
ExecStart=/bin/sleep 1000

我这样做之后

systemctl start b.service

我预计两者都会a.service失败,b.service因为a.service失败会失败/bin/false,失败b.service也会失败a.service

但是,b.service正在运行:

root@john-ubuntu:/etc/systemd/system# systemctl status a.service b.service
● a.service
   Loaded: loaded (/etc/systemd/system/a.service; static; vendor preset: enabled)
   Active: failed (Result: exit-code) since Thu 2017-09-07 16:38:39 CST; 2s ago
  Process: 1245 ExecStart=/bin/false (code=exited, status=1/FAILURE)
 Main PID: 1245 (code=exited, status=1/FAILURE)

Sep 07 16:38:39 john-ubuntu systemd[1]: Started a.service.
Sep 07 16:38:39 john-ubuntu systemd[1]: a.service: Main process exited, code=exited, status=1/FAILURE
Sep 07 16:38:39 john-ubuntu systemd[1]: a.service: Unit entered failed state.
Sep 07 16:38:39 john-ubuntu systemd[1]: a.service: Failed with result 'exit-code'.

● b.service
   Loaded: loaded (/etc/systemd/system/b.service; static; vendor preset: enabled)
   Active: active (running) since Thu 2017-09-07 16:38:39 CST; 2s ago
 Main PID: 1244 (sleep)
    Tasks: 1
   Memory: 88.0K
      CPU: 696us
   CGroup: /system.slice/b.service
           └─1244 /bin/sleep 1000

Sep 07 16:38:39 john-ubuntu systemd[1]: Started b.service.

我错过了什么?谢谢。

答案1

这很棘手。需要理解的主要一点是,systemd 会并行启动所有事情,除非被告知不要这样做。这实际上在手册页的“要求”部分中有说明:

需求依赖性不影响服务启动或停止的顺序

你想要实现的是“等待 a.service 启动启动 b.service”。为此,您需要“需要”b.service 文件中的“After”选项:

[Unit]
Requires=a.service
After=a.service

[Service]
ExecStart=/bin/sleep 1000

= 更新 =

好的,我明白出了什么问题:在 a.service 文件中,您放置了 ExecStart 命令,但没有指定类型。这意味着类型将默认为“简单”。您需要“分叉”类型才能使其工作。来自 systemd.service 手册页:

如果设置为 simple(如果既没有指定 Type= 也没有指定 BusName=,但指定了 ExecStart=,则为默认值),则预计使用 ExecStart= 配置的进程是服务的主进程。在此模式下,如果进程向系统上的其他进程提供功能,则应在守护进程启动之前安装其通信通道(例如,由 systemd 通过套接字激活设置的套接字),如下所示systemd 将立即开始启动后续单元

如果设置为 forking,则预计使用 ExecStart= 配置的进程将调用 fork() 作为其启动的一部分。当启动完成并且所有通信通道都建立后,父进程预计将退出。子进程继续作为主守护进程运行。这是传统 UNIX 守护程序的行为。如果使用此设置,建议同时使用 PIDFile= 选项,以便 systemd 可以识别守护进程的主进程。 一旦父进程退出,systemd 将继续启动后续单元。

因此,您应该更新 a.service 文件以包含“Type=Forking”:

[Service]
Type=forking
ExecStart=/bin/false

这会起作用。 :)

答案2

如果您将 CentOS 7 与 systemd-219(其手册页与问题中引用的部分相匹配)一起使用systemd.unit(5),则这似乎部分是由于文档错误造成的。也许这同样适用于其他一些发行版和 systemd 版本。

评论中引用了这句话:

如果其他单位之一被停用或激活失败,则该单位将被停用。

会向我建议这systemctl start b.service将导致这两项服务被激活,但在a.service从 返回失败后/bin/falseb.service将自动停用。正如问题指出这不是观察到的行为一样,我也没有在 CentOS 7 上观察到这种行为。

引用的句子已替换为这些句子https://www.freedesktop.org/software/systemd/man/systemd.unit.html

如果其他单元之一无法启动,并且设置了失败单元的 After= 顺序依赖项,则此单元将不会启动。此外,无论是否指定 After=,如果其他单元之一明确停止,此单元也将停止。

更新后的文档同意 @gerard 的声明,即您需要After=在 中进行设置b.service,并且与我在 CentOS 7 上观察到的行为相匹配。

然后,正如 @gerard 所说,当 systemd 启动Type=simple服务时,它“将立即开始启动后续单元”。请注意,这Type=forking并不是可用于解决此问题的唯一设置,您还可以设置Type=notify或手册页中描述的其他类型之一systemd.service(5)(除了Type=idle)。当您完成测试故障处理阶段后,请确保服务实际上按照给定的要求运行Type,例如它调用fork()sd_notify()等。

另请注意,systemd-219 中的故障处理存在很多边缘情况,例如https://github.com/systemd/systemd/issues/8398

相关内容