用 systemd timer 替代 crontab:更好排查的 Linux 定时任务实践
很多人在云服务器上跑定时任务的第一反应是 crontab -e。cron 简单好用,但出了问题时常常让人摸不着头脑:任务到底跑没跑?输出去哪了?环境变量为什么和手动执行不一样?这篇文章分享一套用 systemd timer 管理定时任务的做法,适合 EulerOS、CentOS 7+、Ubuntu 16.04+ 等使用 systemd 的发行版。
一、cron 常见的三个坑
1. 日志难找:输出默认发到本地邮件,很多机器没配置邮件,结果输出直接丢失。
2. 环境不一致:cron 的 PATH 很短,脚本里调用 python3、docker 等命令时容易出现 command not found。
3. 错过就错过:机器在任务时间点关机或重启,这次执行不会补跑。
二、systemd timer 的基本结构
一个定时任务由两个文件组成:
- xxx.service:定义“做什么”
- xxx.timer:定义“什么时候做”
以每天凌晨 2 点清理 7 天前的日志为例。
/etc/systemd/system/log-clean.service:
[Unit]
Description=Clean old application logs
[Service]
Type=oneshot
ExecStart=/usr/bin/find /var/log/myapp -name "*.log" -mtime +7 -delete
/etc/systemd/system/log-clean.timer:
[Unit]
Description=Run log-clean daily
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
说明:
- Persistent=true:如果到点时机器正好关机,开机后会补跑一次,解决了 cron 错过不补的问题。
- RandomizedDelaySec=300:在 5 分钟内随机延迟,避免多台机器同一时刻打满后端或存储。
三、启用与验证
systemctl daemon-reload
systemctl enable --now log-clean.timer
systemctl list-timers --all | grep log-clean
list-timers 会直接列出下次执行时间(NEXT)和上次执行时间(LAST),这比在 cron 里猜要直观得多。
想立刻测试一次,不用等到点:
systemctl start log-clean.service
四、排查:日志统一进 journal
任务的标准输出和错误都会自动进 journald:
journalctl -u log-clean.service --since today
systemctl status log-clean.service
退出码、耗时、报错一目了然。失败时 systemctl --failed 也能看到。
五、几个实用配置
1. 环境变量:在 [Service] 中用 Environment=KEY=VALUE 或 EnvironmentFile=/etc/myapp/env 明确指定,不再依赖登录 shell。
2. 超时保护:TimeoutStartSec=30min,防止脚本卡死一直占资源。
3. 资源限制:CPUQuota=50%、MemoryMax=512M,避免定时任务影响线上业务。
4. 失败告警:OnFailure=notify@%n.service,搭配一个发送通知的模板 service,就能在任务失败时调用告警脚本。
5. 以普通用户运行:User=appuser,遵循最小权限原则。
六、OnCalendar 写法速查
- 每小时:hourly 或 *-*-* *:00:00
- 每天 2 点:*-*-* 02:00:00
- 每周一 9 点:Mon *-*-* 09:00:00
- 每 15 分钟:*:0/15
可以用 systemd-analyze calendar "Mon *-*-* 09:00:00" 校验表达式并查看下次触发时间。
七、什么时候还用 cron
如果只是个人机器上一行就能搞定的简单任务,cron 依然足够;但对生产环境中需要可观测、可补跑、可限制资源的任务,systemd timer 更省心。
总结:把定时任务从 crontab 迁到 systemd timer,主要收益是可观测(list-timers、journalctl)、可靠(Persistent 补跑)和可控(超时、资源限制、失败回调)。在云服务器上做运维时,不妨从一个日志清理任务开始试试。
- 点赞
- 收藏
- 关注作者
评论(0)