0. 概述

因为前后多次遇到关闭 systemd 服务遇到被取消的问题,所以我决定认真地去分析一下这个问题,这样下次我想就可以从容地应对了。

1. 问题详解

之前在测试一个应用的时候发现有时对 systemd service 的操作被取消了,情况类似于:

[[email protected]]# systemctl stop bbbb
Unable to stop service bbbb: Job for bbbb.service canceled.

一时没有思路,然后就忽略了。但是,后面我又遇到了这个问题,而且是另外一个服务:

[[email protected]]# systemctl stop aaa
Unable to stop service aaaa: Job for aaaa.service canceled.

这就有点郁闷了,所以我决定深入地了解一下为什么会这样。首先要先说一个前提,这里服务 aaaa 和 bbbb 有依赖关系:在服务 bbbb 的 service 配置里面是绑定了 aaaa 的:

[[email protected]]# cat /usr/lib/systemd/system/bbbb.service
[Unit]
BindsTo=aaaa.service
After=network.target aaaa.service

这里的意思就是如果要运行 bbbb,那么 aaaa 必须保持 running 的状态。同时 aaaa 和 bbbb 的业务关系是,当 aaaa 运行时,会检查 bbbb 是否在运行(Systemd 的状态是 runnig),如果 bbbb 不是运行状态,那么会主动拉起 bbbb。

这里稍微解释一下:

其实,这是一个简单的 sidecar 模式,只不过加了一个限制。

2. 问题解答

在 systemd 中,为每个 unit 都维护着内部的一个任务队列,当执行一个操作(可能是一个事务)的时候,如果有一个对于同类资源进行操作,那么当前操作就会被取消,然后被新的操作所替代。

3. 测试验证

[[email protected]]# cat /usr/lib/systemd/system/liqiang.io.service
[Unit]
After=network.target

[Service]
ExecStartPre=/bin/sh /root/test.sh
ExecStart=/bin/sh -c '/root/prometheus --config.file /etc/prometheus/prometheus.yml --web.listen-address="0.0.0.0:9091"'
ExecStopPre=/bin/sh /root/test.sh
ExecReload=/bin/kill -HUP $MAINPID
[[email protected]]# cat /root/test.sh
sleep 10

然后打开一个窗口执行:

[[email protected]]# systemctl start prometheus
[[email protected]]# systemctl stop prometheus

在另外一个窗口执行:

[[email protected]]# systemctl start prometheus

可以发现,第一个窗口的 prometheus 的关闭操作被第二个窗口的 start canceld 掉了,并且不会回滚,也就是说此时 prometheus 的状态是关闭的。直到第二个任务执行完毕,prometheus 才被重新拉起,和前面提到的说明是一致的。

4. Ref