我一直在将 crontab 迁移到 systemd 的计时器单元。它们看起来都类似于:
.计时器文件:
[Unit]
Description=timer that uses myjob.service
[Timer]
OnCalendar=*-*-* *:00:00
Unit=myjob.service
[Install]
WantedBy=timers.target
.服务文件:
[Unit]
Description=Script that runs myjob.sh
[Service]
ExecStart=/home/user/myjob.sh
我的计时器可以工作,但它们也会在系统重新启动时执行。我希望我的 OnCalendar 事件仅在指定时间运行,而不是在我重新启动电脑的任意时间运行。有任何想法吗?
更新:我通过将“用户”计时器转换为根/系统计时器解决了这个问题。
我禁用了所有 .service 和 .timer 文件,并将它们从我的主目录移到 /etc/systemd/system 中。
我将“User=”部分添加到每个服务文件中,以便我的脚本由普通用户而不是 root 运行。
现在,我的计时器在系统启动时没有被触发,并且当我通过 ssh 登录时,我也遇到了偶尔触发的问题。现在这个问题也已经解决了,因为它们受到 root 帐户的控制,但运行我的脚本仍然以普通用户的 PID 运行,这保留了我的文件的所有权属性。问题解决了。
答案1
我也遇到了同样的问题,无法弄清楚为什么单位总是在启动时运行,无论Persistent
.timer 文件中的设置如何,我花了一段时间,但我终于找到了原因(@alexander-指出了正确的方向)托尔卡切夫的评论)。
问题是我总是WantedBy=basic.target
在 .service 文件的 [Install] 部分中包含类似的内容(因为它是标准 systemd 服务复制意大利面的一部分)。事实证明,这实际上会导致该单元在 basic.target 启动时启动(也称为系统启动)。
https://www.freedesktop.org/software/systemd/man/systemd.unit.html#WantedBy= https://www.freedesktop.org/software/systemd/man/systemd.special.html#basic.target
我怀疑OP无意中通过禁用旧的 .service (您必须这样做才能删除由 .service 创建的符号链接WantedBy
)并在重写时忽略 [Install] 部分或从不运行 来解决了他们的问题systemctl enable
。
太长了;您不希望 .service 文件中的 [Install] 部分由 .timer 文件触发。
答案2
其他答案中没有涵盖更多可能性......
可能性A
重新启动时,可能不是计时器启动服务,而是服务本身systemctl enable <service-unit>
在之前的某个时刻安装/启用(即通过)。
例如,服务启动是因为您[Install]
的服务单元中有一个部分并在过去的某个时刻启用了该服务。
可能性B
该服务可以运行,因为它被声明为某个其他单元的依赖项。
有一篇关于 cron 到 systemd 计时器的热门搜索结果博客文章,其中包含计时器执行Requires=myUnit.service
.当计时器在启动时启动(应该如此)...因为计时器需要服务(依赖项),因此服务在计时器之前启动...即每次启动时。
去检查...
- 重新启动,
- 该服务刚刚在重新启动时运行吗?
systemctl status <service-unit>
- 如果没有,就没有问题,完成。
- 是启动时运行服务的计时器吗?:
systemctl list-timers
- 检查您的服务的计时器。它刚刚运行/触发吗?
- 如果是:则计时器触发服务运行。
- 检查
Persistent
计时器单元中的设置 - 可能运行是因为计时器认为它错过了一次运行。
- 检查
- 如果不:
- 确保计时器单元不依赖于您的服务(
Requires=<service-unit>
或类似的愚蠢的东西) - 可以
grep -R <service-unit> /etc/systemd/system/
查看其他单位是否正在引用或取决于您的服务。 - 如果您的计时器(和其他单元)不依赖于您的服务......那么该服务可能由于之前安装/启用而运行。
- 确保计时器单元不依赖于您的服务(
- 跑步
systemctl is-enabled <service-unit>
。如果它说static
这意味着它未启用并且没有[Install]
部分(好)。- 白痴通过运行
find /etc/systemd/system/*.wants -name '<service-name>*'
并确保没有从先前安装/启用来启动服务的剩余符号链接来检查这一点。
- 白痴通过运行
- 如果由于某种原因启用了该服务...
- 跑步
systemctl disable <service-unit>
[Install]
从服务单元中删除该部分- 运行
systemctl --system daemon-reload
以加载您的更改。 - 重新启动并确认该服务(希望)不再在启动时运行。
- 跑步
使用计时器作为 cron 替代品的技巧...
- 您不希望计时器为
Requires=
您服务。 - 您不希望您的服务
Wants=
或Requires=
(等等)您的计时器。 - 您应该
[Install]
在计时器单元中包含一个部分,以便它在启动时与其余计时器一起启动。 - 您想要启用/启动计时器,以便其运行并在重新启动后再次开始运行。 (注意计时器运行并不意味着服务运行)。
- 您不想要
enable
您的服务,也不应该[Install]
在您的服务单元中包含一个部分。
我认为这里的许多“解决方案”都有效,因为人们在新位置/名称中重新创建了单元文件,而没有部分[Install]
和/或没有运行systemctl enable <service-unit>
.这有效但不是必需的。您只需systemctl disable <service-unit>
在原始服务单元上运行即可,无需重新创建。
答案3
根据文档您应该将配置更改为Persistent=false
或Persistent
完全删除,因为默认情况下它是错误的。
答案4
在我自己的服务器上调查此问题时,我发现了以下内容:
$ systemctl status man-db.timer
Apr 09 08:10:00 anemone systemd[1]: man-db.timer: Not using persistent file timestamp Mon 2018-04-16 19:40:02 EDT as it is in the future.
Apr 09 08:10:00 anemone systemd[1]: Started Daily man-db cache update.
事实证明,板载 RTC 的电池没电了,因此系统以过去的日期启动(可能从文件系统中获取)。通过运行确认
journalctl --boot
并看到当前启动的日志有错误的时间戳。添加这个以防其他人的问题恰好与我的问题相符。