如何让一个systemd服务重新启动其他服务?

如何让一个systemd服务重新启动其他服务?

我有一个应用在启动时从数据库读取,该数据库每天由单独的更新程序,由以下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.serviceupdater.service直到完成才真正运行。
  • restart-application.serviceupdater.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)

我认为这可以满足你想要的一切。

相关内容