systemd timer 没跑起来,先查这四件事
cron 换成 systemd timer 之后,最容易出的问题不是命令写错,而是以为它跑了。第二天翻日志什么都没发生,其实是 timer 压根没触发,或者触发了但服务立刻失败退出。这两种情况的排查方向完全不同,先分清再动手,一般十分钟内能定位。
一、先看 timer 有没有排进队列
systemctl list-timers --all 会列出每个 timer 的上次触发和下次触发时间。NEXT 显示 n/a,说明这个 timer 当前没被排期,先 systemctl is-enabled 你的 timer 看开机是否启用,再 systemctl status xxx.timer 看状态是不是 failed。另一个高频错误是改完 .timer 文件只重启了业务服务,systemd 根本没有重新读时间表,必须 systemctl daemon-reload 之后 restart xxx.timer。只 restart 同名 service 是没用的,那只是手动跑一次。
二、确认 OnCalendar 的时区
OnCalendar 用的永远是服务器本机时区。机器跑 UTC 而你按北京时间写 08:00,实际触发会是北京时间十六点,看起来像是「跑晚了八小时」。用 systemd-analyze calendar 'Mon..Fri 08:00' 让 systemd 自己把下次触发时间翻译成人能读的格式,对着日历看一遍就不会错。如果业务要求严格按北京时间,可以在 [Timer] 里用 OnCalendar=... Asia/Shanghai 指定时区,比手动算偏移可靠。Persistent=true 也值得加上:机器关机期间错过的任务会在开机后补跑一次,不加就只能等下一个周期,账单、日报这类累计型任务漏一次很难补。
三、服务本身有没有跑完就失败
timer 触发后拉起的是同名 service,去 journalctl -u xxx.service --since today 看那一次的完整输出。最常见的失败原因是 PATH 不同:你在交互式 shell 里敲个命令能跑,是因为 shell 加载了 .bashrc;service 的环境变量只有最小集合。脚本里该用绝对路径,或者在 unit 里写 Environment=PATH=/usr/local/bin:/usr/bin:/bin。第二个坑是工作目录,service 默认在 / 下执行,脚本里的相对路径全部失效,加了 WorkingDirectory=/opt/myapp 才和手敲时一致。第三个是权限和用户,默认按 root 跑或者按 User= 指定的用户跑,读写文件的权限要在那个身份下重新确认。
四、确认真跑完了,而不是启动了
systemctl show xxx.service -p Result,ExecMainStatus 看最近一次的执行结果,Result=success 且 ExecMainStatus=0 才算成功。再把 list-timers 里 timer 的 LAST 时间和 journalctl 里那条日志的时间戳对一遍,两者能对上,才说明你看到的是这次触发产生的日志,而不是上一次留下的。
五、别把日志当成结果
有一种情况最迷惑:journalctl 每天都有输出,但业务数据完全没变。这时候要区分「进程启动了」和「任务完成了」。在脚本开头和结尾各打一行带时间戳的标记,中间的关键步骤也各留一行,日志里就能看出它是走完了全程还是半路静默退出。配合 set -e 让脚本在第一条失败命令处停下,比带着错误继续往下跑好查得多,静默跳过往往比直接报错更难定位。
定时任务出问题,绝大多数不是命令本身写错,而是它根本没被执行,或者执行环境和你手敲时不是同一个。先把触发时间确认清楚,再去看脚本逻辑,能省掉一大半绕路的时间。
- 点赞
- 收藏
- 关注作者
评论(0)