我有一个应用在启动时从数据库读取,该数据库每天由单独的更新程序,由以下systemd
单位描述:
application.service
:
[Unit]
Description=Application reading database
Wants=network-online.target
After=local-fs.target network-online.target nss-lookup.target
[Service]
User=application
Group=application
WorkingDirectory=/var/lib/application
ExecStart=/var/lib/application/application-exec --database /var/db/database.db
StandardOutput=file:/var/lib/application/application.stdout.log
StandardError=file:/var/lib/application/application.stderr.log
StartLimitInterval=60
StartLimitBurst=10
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
updater.service
:
[Unit]
Description=Database updater
Wants=network-online.target
After=local-fs.target network-online.target nss-lookup.target
[Service]
Type=oneshot
User=updater
Group=updater
WorkingDirectory=/var/lib/updater
ExecStart=/var/lib/updater/updater-exec --database /var/db/database.db
StandardOutput=file:/var/lib/updater/updater.stdout.log
StandardError=file:/var/lib/updater/updater.stderr.log
[Install]
WantedBy=multi-user.target
updater.timer
:
[Unit]
Description=Timer for database updater
[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=900
Persistent=true
[Install]
WantedBy=timers.target
我的问题:
我怎样才能使更新服务重新启动应用服务更新是否成功执行(以便应用程序重新读取现在更新的数据库;成功/失败由更新程序的退出代码指示)?
这个问题在服务器故障类似,但遗憾的是没有适用于我的用例的答案。
答案1
事实证明,解决方案比预期简单得多。需要添加以下行updater.service
:
ExecStartPost=+/usr/bin/systemctl restart application.service
结果updater.service
:
[Unit]
Description=Database updater
Wants=network-online.target
After=local-fs.target network-online.target nss-lookup.target
[Service]
Type=oneshot
User=updater
Group=updater
WorkingDirectory=/var/lib/updater
ExecStart=/var/lib/updater/updater-exec --database /var/db/database.db
StandardOutput=file:/var/lib/updater/updater.stdout.log
StandardError=file:/var/lib/updater/updater.stderr.log
ExecStartPost=+/usr/bin/systemctl restart application.service
[Install]
WantedBy=multi-user.target
相关摘录自man 5 systemd.service
:
├───────┼────────────────────────────────────────┤
│"+" │ If the executable path is prefixed │
│ │ with "+" then the process is executed │
│ │ with full privileges. In this mode │
│ │ privilege restrictions configured with │
│ │ User=, Group=, CapabilityBoundingSet= │
│ │ or the various file system namespacing │
│ │ options (such as PrivateDevices=, │
│ │ PrivateTmp=) are not applied to the │
│ │ invoked command line (but still affect │
│ │ any other ExecStart=, ExecStop=, ... │
│ │ lines). │
├───────┼────────────────────────────────────────┤
ExecStartPost= commands are only run after the commands specified in ExecStart= have been invoked
successfully, as determined by Type= (i.e. the process has been started for Type=simple or Type=idle,
the last ExecStart= process exited successfully for Type=oneshot, the initial process exited
successfully for Type=forking, "READY=1" is sent for Type=notify, or the BusName= has been taken for
Type=dbus).
解释:
ExecStartPost=
由于前面的原因,命令是使用 root 权限执行的+
(请参阅第一个摘录),这允许重新启动application.service
使用,systemctl
尽管指定User=
和Group=
ExecStartPost=
仅当命令成功时才执行命令ExecStart=
(请参阅第二段摘录),这允许有条件地重新启动application.service
答案2
弄清楚这一点很有趣!
我们将向混合中添加第三个服务 ,restart-application.service
然后安排restart-applicationservice
和之间的依赖关系,updater.service
以便:
- 开始
restart-application.service
开始updater.service
restart-application.service
updater.service
直到完成才真正运行。restart-application.service
updater.service
除非成功,否则不会运行。
我们restart-application.service
这样创建:
[Unit]
Description=Restart application
BindsTo=updater.service
After=updater.service
[Service]
Type=oneshot
ExecStart=/usr/bin/systemctl restart application
我们正在使用一个BindsTo
这里的依赖。文档的关键部分是:
当在同一单元上与 After= 结合使用时,BindsTo= 的行为甚至更强。在这种情况下,严格绑定的单元必须处于活动状态,该单元也处于活动状态。
现在我们将Unit
的部分修改updater.service
为如下所示:
[Unit]
Description=Database updater
Wants=network-online.target
After=local-fs.target network-online.target nss-lookup.target
StopWhenUnneeded=true
以及Service
要包括的部分:
RemainAfterExit=true
这些更改意味着 (a)在命令完成updater.service
后将保持“活动”状态;ExecStart
我们需要它,因为restart-application.service
只有在它处于活动状态时才会运行。该StopWhenUnneeded=true
选项意味着当没有其他东西依赖于该单元时(即,当restart-application.service
已停止时),该单元将变为非活动状态。
最后,我们需要修改您的计时器以启动restart-application.service
而不是updater.service
.
我创建了一个虚拟对象application.service
(仅运行一个简单的网络服务器)和一个updater.service
随机成功或失败的虚拟对象:
[Unit]
Description=Database updater
Wants=network-online.target
After=local-fs.target network-online.target nss-lookup.target
StopWhenUnneeded=true
[Service]
Type=oneshot
User=updater
Group=updater
WorkingDirectory=/home/updater
ExecStart=/bin/bash -c 'rc=$(( RANDOM % 2 )); echo "rc=$rc"; exit $rc'
RemainAfterExit=true
让我们看一下日志systemctl start restart-application.service
。以下是updater.service
失败时会发生的情况;
... systemd[1]: Starting updater.service - Database updater...
... bash[1253]: rc=1
... systemd[1]: updater.service: Main process exited, code=exited, status=1/FAILURE
... systemd[1]: updater.service: Failed with result 'exit-code'.
... systemd[1]: Failed to start updater.service - Database updater.
... systemd[1]: Dependency failed for restart-application.service - Restart application.
... systemd[1]: restart-application.service: Job restart-application.service/start failed with result 'dependency'.
updater.service
成功时会发生以下情况:
... systemd[1]: Starting updater.service - Database updater...
... systemd[1]: Finished updater.service - Database updater.
... darkhttpd[1193]: darkhttpd/1.13, copyright (c) 2003-2021 Emil Mikulic.
... darkhttpd[1193]: listening on: http://0.0.0.0:8080/
... systemd[1]: Starting restart-application.service - Restart application...
... bash[1262]: rc=0
... systemd[1]: Stopping application.service - Application reading database...
... systemd[1]: application.service: Deactivated successfully.
... systemd[1]: Stopped application.service - Application reading database.
... systemd[1]: Started application.service - Application reading database.
... systemd[1]: restart-application.service: Deactivated successfully.
... systemd[1]: Finished restart-application.service - Restart application.
... systemd[1]: updater.service: Deactivated successfully.
... systemd[1]: Stopped updater.service - Database updater.
此时一切的状态:
# systemctl status application.service updater.service restart-application.service
● application.service - Application reading database
Loaded: loaded (/etc/systemd/system/application.service; disabled; vendor preset: disabled)
Active: active (running) since Sat 2022-09-17 20:48:27 UTC; 1min 53s ago
Main PID: 1264 (darkhttpd)
Tasks: 1 (limit: 4661)
Memory: 172.0K
CPU: 1ms
CGroup: /system.slice/application.service
└─ 1264 /usr/sbin/darkhttpd /home/application/htdocs --port 8080
○ updater.service - Database updater
Loaded: loaded (/etc/systemd/system/updater.service; disabled; vendor preset: disabled)
Active: inactive (dead)
○ restart-application.service - Restart application
Loaded: loaded (/etc/systemd/system/restart-application.service; static)
Active: inactive (dead)
我认为这可以满足你想要的一切。