用 systemd timer 替代 cron:把定时任务的日志、重试和依赖一次管起来

举报
yd_252769024 发表于 2026/11/09 17:04:57 2026/11/09
【摘要】 用 systemd timer 替代 cron 的实操笔记:一对 .service/.timer 单元把日志、失败重试、依赖顺序和错过补跑一次管起来,附排障命令与迁移时的注意事项。

在服务器上实现「每天凌晨跑一次脚本」,多数人的第一反应是写一条 crontab。真到排障的时候才会发现麻烦:脚本输出散落在邮件或某个自建日志里,跑失败了没有重试,依赖的服务是否就绪只能靠 sleep 30 硬等,想临时停掉还得去改注释,改完还容易忘记恢复。其实 systemd 自带的 timer 就能把这几件事一次性收口,而且不需要额外装任何软件。

最小可用的一对单元

timer 不是一个文件,而是两个:.service 描述「做什么」,.timer 描述「什么时候做」,都放在 /etc/systemd/system/ 下,两个文件同名不同后缀即可自动关联。

先写服务单元 /etc/systemd/system/backup-db.service:

[Unit]
Description=Daily database backup
After=network-online.target postgresql.service
Wants=network-online.target

[Service]
Type=oneshot
User=backup
ExecStart=/usr/local/bin/backup-db.sh
Restart=on-failure
RestartSec=60

Type=oneshot 表示跑完就退出,正是定时任务该用的类型。After 和 Wants 解决的是「数据库还没起来就开跑」这个经典坑:它声明了顺序和弱依赖,比在脚本里 sleep 猜时间靠谱得多。Restart=on-failure 配合 RestartSec 让偶发失败能自动重试一次——cron 本身没有这个能力,只能自己在脚本里写循环。

再写定时器 /etc/systemd/system/backup-db.timer:

[Unit]
Description=Run backup-db daily

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=300
Unit=backup-db.service

[Install]
WantedBy=timers.target

几个容易忽略但很值的参数:

  • Persistent=true:如果机器在 03:00 关机或休眠,开机后会立刻补跑一次。cron 对「错过的时间点」是直接跳过的,备份类任务最怕这个。
  • RandomizedDelaySec=300:在 0~5 分钟内随机延迟。几十台机器都配同一个 03:00 时,能避免同时打爆存储或 API。
  • OnCalendar 支持 Mon..Fri *-*-* 02:30:00、*-*-01 00:00:00 这类写法,比 crontab 的五列更可读。

启用的时候注意:要启动的是 timer,不是 service。

sudo systemctl daemon-reload
sudo systemctl enable --now backup-db.timer
systemctl list-timers --all | grep backup

systemctl list-timers 会直接列出「下次触发时间」和「上次触发时间」,不用再猜配置有没有生效。如果手工想跑一次验证脚本本身,用 systemctl start backup-db.service 即可,这不会打乱 timer 的节奏。

排障体验的差别

这是我最推荐迁移的一点。所有 stdout/stderr 自动进 journald,不用在命令行末尾追加 >> /var/log/xxx.log 2>&1:

journalctl -u backup-db.service --since "today"
journalctl -u backup-db.service -n 50 -f
systemctl status backup-db.timer

想按时间范围筛、想看某次执行的退出码、想和别的单元日志对齐时间线,都是一条命令的事。而 cron 环境下,日志去哪了往往取决于写脚本那个人当天的心情。

什么时候仍该用 cron

不是所有场景都值得迁移。容器里通常没有 systemd,Alpine 镜像更是默认不带;这种环境用 cron 或者把调度交给外部的 K8s CronJob 更合适。另外,如果只是一台临时机器上的一次性任务,systemd-run --on-calendar 可以不落文件地建个临时 timer,比改 crontab 还轻。

还有个细节:.service 里没有显式配置 Environment= 时,systemd 不会继承你当前 shell 的任何环境变量——PATH、HOME、代理设置都和手工执行时不同。脚本里尽量写绝对路径,或者用 EnvironmentFile=/etc/backup.env 明确注入,能省掉「手动跑正常、定时跑就报 command not found」这类半天排查。

从 cron 迁到 systemd timer 的成本大概就是两个 ini 文件,换来的是可观测、可重试、可声明依赖、错过能补跑。对于长期跑在生产环境上的定时任务,这笔交换很划算。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。