我必须将一个服务和一个套接字设置为 Systemd 单元。期望的行为是,当我启动套接字时,服务将首先启动,然后是套接字,当其中一个被关闭时,它们都应该关闭。服务在套接字单元启动之前绑定到套接字,这就是为什么我将其作为通知服务,这样它就不会报告为已启动,直到它在套接字上绑定。
这是两个单位的文件。
#rustyvxcan.service
[Unit]
Description=Docker VXCAN plugin Service
#PartOf=rustyvxcan.socket
Before=docker.service
After=network.target
[Service]
Type=notify
ExecStartPre=/usr/bin/mkdir -p /run/docker/plugins
ExecStart=/home/braedon/.cargo/bin/rustycan4docker
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
#rustyvxcan.socket
[Unit]
Description=A network plugin for vxcan
Before=docker.service
AssertPathExists=/run/docker/plugins
Requires=rustyvxcan.service
After=rustyvxcan.service
[Socket]
ListenStream=/run/docker/plugins/rustyvxcan.sock
RemoveOnStop=True
[Install]
WantedBy=sockets.target
错误信息如下。
Sep 19 16:50:38 localhost systemd[1]: rustyvxcan.service: Found ordering cycle on rustyvxcan.socket/start
Sep 19 16:50:38 localhost systemd[1]: rustyvxcan.service: Found dependency on rustyvxcan.service/start
Sep 19 16:50:38 localhost systemd[1]: rustyvxcan.service: Unable to break cycle starting with rustyvxcan.service/start
Sep 19 16:55:09 localhost systemd[1]: rustyvxcan.socket: Found ordering cycle on rustyvxcan.service/start
Sep 19 16:55:09 localhost systemd[1]: rustyvxcan.socket: Found dependency on rustyvxcan.socket/start
Sep 19 16:55:09 localhost systemd[1]: rustyvxcan.socket: Unable to break cycle starting with rustyvxcan.socket/start
从资源来看,这意味着循环只发生在这两个文件中,我可以通过从 .socket 文件中删除 After 行来清除错误,但它们同时启动,服务的可执行文件失败。我可能只需在套接字中添加一个睡眠即可解决这个问题,但我在这里没有看到足够的依赖信息来导致循环。
我尝试清除 /{lib,etc}/systemd/system 中的服务和套接字文件,然后重新安装粘贴在此处的文件,以确保旧版本未运行,当然,在修改它时定期运行 systemctl daemon-reload。但我似乎无法弄清楚为什么即使在从服务文件中删除依赖项后,指定 after 也会破坏依赖关系图。
我遗漏了发生循环的地方在哪里?
这听起来像是你试图调用
bind()
自己。systemd 套接字实际上会为你执行此操作。这就是为什么当你强制先启动服务时,套接字无法绑定的原因。对于
ListenStream=
( TCP连接),systemd 将为您调用socket()
、bind()
和。listen()
如果您有
Accept=no
(默认),当套接字上收到数据时,systemd
将启动您的进程并在环境变量中将该套接字的 FD 传递给您$LISTEN_FDS
(有关详细信息,请参阅sd_listen_fds(3) )。此 FD 是侦听套接字的文件描述符。您需要调用accept()
以在每个传入连接上生成一个连接套接字。使用
Accept=yes
,您的服务应该是一个实例(例如foo.socket
和[email protected]
)。在这种情况下,systemd 会accept()
为您调用并启动您的每个连接一次,传递连接ExecStart=
套接字的 FD 。您可以立即从或向该 FD执行、 、 。read()
select()
poll()
write()
我倾向于使用
Accept=yes
、StandardInput=socket
和StandardOutput=socket
。这样我就不必担心$LISTEN_FDS
、 TCP 还是 UDP 了。我只需从 stdin 读取并写入 stdout 即可。在这种情况下,我想要记录的任何内容都会写入到 stdoutstderr
并进入日志。如果您想自己调用、和
socket()
,bind()
则不需要 systemd.socket 单元。listen()
accept()
就您的使用情况而言...
通常,启用/启动套接字才有意义。套接字仅在收到数据时负责启动服务。您与
After=
、Requires=
、Assert*=
和 的斗争ExecStartPre=mkdir...
实际上只会让您的生活更加艰难,因为您违背了预期用途。我会这样做:ExecStartPre=mkdir
我会使用部署配置来tmpfiles.d
确保 systemd 生成该路径。systemd-tmpfiles-setup.service
将使用此文件确保在启动时创建目录。如果您希望现在创建该文件(可能在安装时),则将systemd-tmpfiles --create
为您执行此操作。