我有两个服务(一个视频播放器和一个图像识别守护程序),我希望它们每天早上 9 点开始运行,晚上 23 点结束运行(以及在启动时运行,尽管系统将持续运行)。对于每个服务,我创建了一个-startup.service
和-shutdown.service
。服务组合在 和 中daemon-on.target
,然后分别由和daemon-off.target
触发。daemon-on.timer
daemon-off.timer
经过几次尝试和测试,我发现计时器最初会按预期触发目标,但进入的状态下下次设置为 n/a。
为了让两个应用程序使用计时器运行,这需要做大量的工作。我确信它忽略了一些显而易见的东西,如果能提供任何提示,我将不胜感激!
mpv-startup.service启动视频播放器服务:
[Unit]
Description=MPV Video Player Startup
After=xorg.target
Requires=xorg.target
[Service]
Environment=DISPLAY=:0
ExecStart=/usr/bin/python3 /opt/videoplayer/app.py
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=10
[Install]
Also=daemon-on.timer
recog-startup.service启动图像识别服务:
[Unit]
Description=Recog Startup Service
[Service]
Type=simple
WorkingDirectory=/opt/recog
ExecStart=/opt/recog/recog run
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=30
[Install]
Also=daemon-on.timer
mpv-shutdown.service 以一次性命令停止视频播放器服务
[Unit]
Description=MPV Video Player Shutdown
[Service]
Type=oneshot
ExecStart=/bin/systemctl --user stop mpv-startup.service
[Install]
Also=daemon-off.timer
recog-shutdown.service 以一次性命令停止图像识别服务:
[Unit]
Description=Recog Shutdown Service
[Service]
Type=oneshot
ExecStart=/bin/systemctl --user stop recog-startup.service
[Install]
Also=daemon-off.timer
daemon-on.target 结合了上述两个启动服务,并且也与 default.target 一起在启动时启用:
[Unit]
Description=Daemon Startup Target
Wants=recog-startup.service mpv-startup.service
After=recog-startup.service mpv-startup.service
[Service]
Type=oneshot
[Install]
WantedBy=default.target
Also=daemon-on.timer
daemon-off.target 结合了上述两个关闭服务:
[Unit]
Description=Daemon Shutdown Target
Wants=recog-shutdown.service mpv-shutdown.service
After=recog-shutdown.service mpv-shutdown.service
[Service]
Type=oneshot
[Install]
Also=daemon-off.timer
daemon-on.timer 设置为在上午 9 点触发 daemon-on.target:
[Unit]
Description=Daemon Startup Schedule
[Timer]
OnCalendar=9:00
Unit=recog-on.target
Persistent=true
[Install]
WantedBy=timers.target
daemon-off.timer 设置为在晚上 23 点触发 daemon-off.target:
[Unit]
Description=Daemon Shutdown Schedule
[Timer]
OnCalendar=23:00
Unit=daemon-off.target
Persistent=true
[Install]
WantedBy=timers.target
将所有这些放在下面后,~/.config/systemd/user/
我以以下方式启用它们:
systemctl --user enable --now daemon-on.target
systemctl --user enable --now daemon-on.timer
systemctl --user enable --now daemon-off.timer
这将在每次启动时自动启动守护进程,在这里我希望计时器根据选项启动/停止守护进程OnCalendar=
。此时systemctl --user list-timers
显示我的两个计时器将在正确的时间触发(请注意,我将计时器的间隔设置为仅 3 分钟,以节省等待时间):
NEXT LEFT LAST PASSED UNIT ACTIVATES
Wed 2019-06-26 12:12:00 JST 1min 4s left Wed 2019-06-26 11:35:02 JST 35min ago daemon-off.timer daemon-off.target
Wed 2019-06-26 12:15:00 JST 4min 4s left Wed 2019-06-26 11:40:48 JST 30min ago daemon-on.timer daemon-on.target
然后关闭定时器被触发,守护进程停止,几分钟后开启定时器被触发,守护进程按预期启动。然而,再次查看定时器时,我发现它们被重置,并且 NEXT/LEFT 字段设置为 n/a,并且永远不会再次触发守护进程。这里的问题是什么?
NEXT LEFT LAST PASSED UNIT ACTIVATES
n/a n/a Wed 2019-06-26 12:12:06 JST 4min 18s ago daemon-off.timer daemon-off.target
n/a n/a Wed 2019-06-26 12:15:33 JST 50s ago daemon-on.timer daemon-on.target
答案1
经过进一步搜索和阅读 systemd 手册页后,我设法简化了一些事情并获得了我服务所需的行为。
首先,我在文件中放入 [Service] 部分时犯了错误.target
,这没有意义。其次,我发现了一个PartOf=
语句,它允许我制作一个以视频播放器和识别服务为组件的顶级应用程序。最后,通过使用Conflicts=
选项,我可以使用两个相互冲突的目标来启动/停止我的应用程序。.timer
附加到这些目标的 s 将在彼此之间切换,禁用或启用我的应用程序链和各自冲突的.targets
。结果仍然包含在 7 个文件中,这比我想要维护的要多,但它可以按要求工作。
app.service 是顶级应用程序,尽管它是一个虚拟的,但它可以运行其他东西:
[Unit]
Description=App Service
[Service]
Type=oneshot
ExecStart=/bin/true
RemainAfterExit=yes
[Install]
WantedBy=default.target
Also=app-on.timer app-off.timer
app-mpv.service 是应用程序的视频播放器组件,因此也依赖于 xorg:
[Unit]
Description=App Video Player Service
PartOf=app.service
After=app.service
Requires=xorg.target
After=xorg.target
[Service]
Environment=DISPLAY=:0
ExecStart=/usr/bin/python3 /opt/videoplayer/app.py
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=10
[Install]
WantedBy=app.service
app-recog.service 是应用程序的图像识别组件,不依赖于视频播放器:
[Unit]
Description=App Recognition Service
PartOf=app.service
After=app.service
[Service]
WorkingDirectory=/opt/recog
ExecStart=/opt/recog/recog run
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=30
[Install]
WantedBy=app.service
app-on.target 是一个虚拟目标,一旦 app-on.timer 被触发,它就会变为活动状态,进而启用 app.service:
[Unit]
Description=App Startup Target
Conflicts=app-off.target
Wants=app.service
After=app.service
RefuseManualStart=yes
[Install]
Also=app-on.timer
app-off.target 是一个虚拟目标,一旦 app-off.timer 被触发,它就会变为活动状态,进而禁用 app.service:
[Unit]
Description=App Shutdown Target
Conflicts=app.service app-on.target
RefuseManualStart=yes
[Install]
Also=app-off.timer
app-on.timer 只是触发 app-on.target:
[Unit]
Description=App Startup Schedule
[Timer]
OnCalendar=9:00
Unit=app-on.target
Persistent=true
[Install]
WantedBy=timers.target
app-off.timer 只是触发 app-off.target:
[Unit]
Description=App Shutdown Schedule
[Timer]
OnCalendar=23:00
Unit=app-off.target
Persistent=true
[Install]
WantedBy=timers.target
这一切混乱都是由本文):
systemctl --user enable app app-mpv app-recog
systemctl --user enable --now app-on.timer app-off.timer
systemctl --user start app
应用程序开始运行,如果我检查计时器,它们都处于活动状态,并且先到的计时器将首先执行:
NEXT LEFT LAST PASSED UNIT ACTIVATES
Wed 2019-06-26 14:00:00 JST 1min 25s left n/a n/a app-off.timer app-off.target
Wed 2019-06-26 14:01:00 JST 2min 25s left n/a n/a app-on.timer app-on.target
在触发关闭定时器后,它会停止应用程序,但是app-off.target
开启目标仍然保持计划:
NEXT LEFT LAST PASSED UNIT ACTIVATES
Wed 2019-06-26 14:01:00 JST 55s left n/a n/a app-on.timer app-on.target
n/a n/a Wed 2019-06-26 14:00:04 JST 3ms ago app-off.timer app-off.target
当开启定时器被触发时,它会启动应用程序并禁用app-on.target
,但会再次重新激活关闭定时器:
NEXT LEFT LAST PASSED UNIT ACTIVATES
Thu 2019-06-27 14:00:00 JST 23h left Wed 2019-06-26 14:00:04 JST 57s ago app-off.timer app-off.target
n/a n/a Wed 2019-06-26 14:01:01 JST 5ms ago app-on.timer app-on.target
循环还在继续。我仍然怀疑这是否是配置的最佳方式,并希望听到其他建议!