我有一堆服务(比如C0
,C1
…… C9
),这些服务只能在服务S
完成初始化并完全运行并准备好其他服务后启动。我如何使用 systemd 来安排它?
在在 systemd 中使用路径激活和目标订购服务假设服务S
有一种写出某种标志文件的机制。相反,假设我可以完全控制服务S
运行的程序,并且可以在需要时向其中添加 systemd 机制。
答案1
不一定需要这个。
如果C
服务需要等待S
准备好才能打开与其的套接字连接,那么根本不需要这样做。相反,人们可以利用早期监听套接字打开由服务经理。
几个系统,包括洛朗·贝尔科特的 s6,我的小吃工具集和 systemd 有一些方法监听套接字可以尽早开放,这是设置服务的第一件事。它们都涉及除打开侦听套接字的服务程序之外的其他内容,以及服务程序在调用时接收侦听套接字作为已打开的文件描述符。
具体来说,使用 systemd 创建一个插座单元定义监听套接字。 systemd 打开套接字单元并对其进行设置,以便内核网络子系统正在侦听连接;并在生成处理套接字连接的进程时将其作为打开文件描述符传递给实际服务。 (它可以通过两种方式做到这一点,就像可以一样,但是对服务与服务inetd
细节的讨论超出了本答案的范围。)Accept=true
Accept=false
重要的一点是,人们不一定需要比这更多的订购。内核将客户端连接批量放入队列中,直到服务程序初始化并准备好接受它们并与客户端通信。
当这样做时,准备协议就是关键。
systemd 有一组准备协议它理解,通过Type=
服务单元中的设置指定服务。这里感兴趣的特定准备协议是notify
准备协议。有了它,systemd 就会被告知等待来自服务的消息,当服务准备好时,它会发送一条标记准备就绪的消息。 systemd 会延迟其他服务的激活,直到标记为就绪为止。
利用它涉及两件事:
- 修改 的代码
S
,使其调用 Pierre-Yves Ritschardnotify_systemd()
函数或 Cameron T Normannotify_socket()
函数之类的函数。 Type=notify
使用和为服务设置服务单元NotifyAccess=main
。
该NotifyAccess=main
限制(这是默认设置)是因为 systemd 需要知道忽略来自恶作剧(或只是简单的错误)程序的消息,因为系统上的任何进程都可以向 systemd 的通知套接字发送消息。
人们优先使用 Pierre-Yves Ritchard 或 Cameron T Norman 的代码,因为它不排除在 UbuntuBSD、Debian FreeBSD、实际的 FreeBSD、TrueOS、OpenBSD 等上使用此机制的可能性; systemd 作者提供的代码确实排除了这些。
要避免的一个陷阱是systemd-notify
程序。它有几个主要问题,其中最重要的是用它发送的消息最终可能会在未被 systemd 处理的情况下被丢弃。在这种情况下S
,最主要的问题是它不作为服务的“主”进程运行,因此必须使用NotifyAccess=all
.
另一个要避免的陷阱是认为forking
协议更简单。它不是。正在做正确地涉及不分叉和退出父级,直到(一方面)程序的所有工作线程都在运行。这与绝大多数分叉守护进程的实际分叉方式不符。
进一步阅读
- 乔纳森·德博因·波拉德 (2015)。Unix 守护进程的就绪协议问题。经常给出的答案。
- 伦纳特·珀特林 (2010)。
sd_notify()
。 systemd 手册页。 Freedesktop.org。 - 伦纳特·珀特林 (2010)。
systemd-notify
。 systemd 手册页。 Freedesktop.org。 - 如何编写 systemd 服务单元文件,以便它等到特定接口启动后再启动?
- 将状态信息添加到 systemd 的状态输出
答案2
像这样:
服务
[Unit]
Description=My main Service
[Service]
Type=notify
ExecStart=/usr/bin/myBinary
C0服务
[Unit]
Description=Dependent service number 0
PartOf=S.service
C1.服务
[Unit]
Description=Dependent service number 1
PartOf=S.service
C9.服务
[Unit]
Description=Dependent service number 9
PartOf=S.service
其中 /usr/bin/myBinary 使sd_notify 就绪=1初始化完成后调用。
根据您希望依赖项的行为方式,您可以使用 PartOf、Requires 或 BindsTo 或其他的。
答案3
systemd.service(5)
具体参考手册页关于类型=的部分,每种服务类型都有不同的方式让 Systemd 确定它已准备好向其他服务提供功能:
如果
Type=simple
,则应在守护程序启动之前安装其通信通道(例如,由 systemd 通过套接字激活设置的套接字)。如果
Type=forking
,则父进程预计在启动完成且所有通信通道均已建立后退出。如果
Type=dbus
,则预计守护进程会在 D-Bus 总线上获取一个名称,此时 systemd 将继续启动后续单元。如果
Type=notify
,则预计守护程序sd_notify(3)
在完成启动时通过或等效调用发送通知消息。发送此通知消息后,systemd 将继续启动后续单元。
对于最后一个选项(通过 发送消息sd_notify
),您可以使用该systemd-notify
实用程序,并记住使用 授予其访问权限NotifyAccess=all
。
鉴于您可以控制服务S
,您可以自由选择最适合您的用例的选项,或者只是最容易实现的选项。