我有一些服务(A、B、C)需要另一个服务(activemq)处于“活动”状态,并且在启动它们之前让所有必需的端口侦听连接。服务B和C依赖于A,而A又依赖于activemq及其监听的端口(特别是61616端口)。
我遇到的问题是,在 activemq 完成打开所有端口之前,systemd 将 activemq 服务标记为“活动”。所以我的服务 (A) 启动然后终止。
我尝试过的:
set: Restart=on-success (或始终)它有效,但我需要为所有服务( ABC )执行此操作。我不想应用这种规则。
它的工作原理是在服务 A 中添加:
ExecStartPre=/bin/sleep 30
然后服务 A 正确启动,因此 B 和 C 无需任何进一步配置(除了为所有服务设置的相应“After=”和“Requires=”之外:A、B 和 C)。但我认为这不是一个正确/干净的解决方案。
我还尝试了 [Service] 中的其他选项,但没有任何效果,例如 Type=fork 等
我想要的是:
- 告诉 systemd 仅在所有端口(其中有四个)或至少最后一个端口(这是导致问题的端口)侦听时才将 activemq 服务标记为“活动”:61616 或
- 仅在端口 61616 监听后启动服务 A(某种 Require=tcp/61616 或类似的东西)
activemq.service 文件是:
[Unit]
Description=Activemq Servoce
After=local-fs.target
After=network.target
[Service]
Type=simple
SuccessExitStatus=0 143
ExecStart=/usr/bin/activemq console
User=activemq
Group=activemq
Restart=always
PrivateTmp=true
[Install]
WantedBy=multi-user.target
答案1
基于:
在 activemq 完成打开所有端口之前,systemd 将 activemq 服务标记为“活动”。
和:
[服务 A] 依赖于 activemq 及其端口进行监听(特别是端口 61616)。
...您可以将 systemd 对服务状态的理解从“进程正在运行”修改为“进程正在运行”和该端口已打开”。对我来说,这似乎比修改所有后续单元文件来检查打开的端口更简单。一种方法是使用ExecStartPost 选项该繁忙循环等待操作系统指示该端口正在被监听。
一个例子:
ExecStartPost=/usr/bin/timeout 30 sh -c 'while ! ss -H -t -l -n sport = :61616 | grep -q "^LISTEN.*:61616"; do sleep 1; done'
这为 systemd 提供了一个在 ExecStart 之后执行的命令。该命令是timeout
,它为主应用程序在 30 秒内未成功启动并侦听端口的情况提供故障停止。该timeout
命令包装了一个简单的sh -c ...
shell 循环,用于测试正在侦听的端口。 shell 循环本身会运行,直到条件为真为止,每次测试之间间隔ss ... | grep ...
一秒钟。sleep
这ss
命令有以下选项:
-H
-- 抑制标题(这个选项不是必需的,但我喜欢消除噪音)-t
-- 仅显示 TCP 套接字-l
-- 仅显示监听套接字-n
-- 不要将服务号码解析为名称 -- 将它们保留为号码,以便我们grep
稍后使用sport = :61616
-- 一个过滤器,将输出限制为源端口为 61616 的条目
然后,该grep
命令查找以“LISTEN”开头且后跟字符串“:61616”的行,表明主应用程序已开始侦听该端口。该-q
标志告诉 grep 仅报告成功或失败,并且不发出任何输出,因为我们只关心该行是否存在。
在主应用程序启动期间,systemd 显示“正在激活(start-post)”状态,直到 ExecStartPost 命令退出。如果应用程序成功启动并在 30 秒内打开端口,systemd 会将状态更新为“活动(正在运行)”。如果发生故障,systemd 将(在您的示例中)Restart=always
重新启动整个过程,但否则会报告“失败(结果:退出代码)”并指向 ExecStartPost 命令及其退出代码“(code=exited, status =124)”,表示命令超时,将终止主进程。