从 中systemd.unit(5)
,该选项的Requires=
含义是:
配置对其他单元的需求依赖性。如果该单位被激活,此处列出的单位也将被激活。如果其他单位之一被停用或激活失败,则该单位将被停用。
我对此做了一个小实验。我创建了 2 个服务:a.service
和b.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/false
,b.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