跳转到内容

定时任务 (Timer)

systemd timer 是 cron 的现代替代方案,提供了更精确的调度控制、完善的日志集成和依赖管理能力。在 CentOS/AlmaLinux/Rocky Linux 上,越来越多的系统任务已经从 cron 迁移到了 systemd timer。

特性systemd TimerCron
日志集成自动集成 journald,可用 journalctl 查看需要手动配置日志重定向
依赖管理支持 After=、Requires= 等依赖声明无依赖管理
错过执行支持 Persistent=true,错过的任务会在下次启动时补执行错过即丢失
资源控制可设置 CPU、内存、IO 限制无资源控制
精度支持微秒级精度最小粒度为 1 分钟
开机延迟支持 OnBootSec= 等相对时间触发不支持
随机延迟支持 RandomizedDelaySec= 分散负载不支持
管理方式统一使用 systemctl 管理需要编辑 crontab 文件

systemd timer 以成对的方式工作:每个 .timer 单元对应一个同名的 .service 单元。当定时器触发时,systemd 会自动启动对应的 service 单元。

例如:

  • backup.timer — 定义触发时间
  • backup.service — 定义要执行的任务

基于相对时间触发,适合”距离某个事件过了多久”的场景:

指令说明
OnBootSec=系统启动后多久触发
OnActiveSec=定时器激活后多久触发
OnStartupSec=systemd 启动后多久触发
OnUnitActiveSec=对应服务上次激活后多久再次触发
OnUnitInactiveSec=对应服务上次完成后多久再次触发

日历定时器(Realtime/Calendar Timer)

Section titled “日历定时器(Realtime/Calendar Timer)”

基于绝对时间触发,类似 cron 的定时方式:

指令说明
OnCalendar=按日历时间表达式触发

OnCalendar= 使用以下格式:

星期 年-月-日 时:分:秒
表达式说明等效 Cron
*-*-* 00:00:00每天午夜0 0 * * *
*-*-* *:00:00每小时整点0 * * * *
*-*-* *:*:00每分钟* * * * *
*-*-01 00:00:00每月 1 号午夜0 0 1 * *
Mon *-*-* 08:00:00每周一早上 8 点0 8 * * 1
Mon..Fri *-*-* 18:00:00工作日每天 18 点0 18 * * 1-5
*-*-* 06,18:00:00每天 6 点和 18 点0 6,18 * * *
*-*-* 09:00:00/2h9 点起每 2 小时

systemd 还提供了便捷的预设值:

预设值等价表达式
minutely*-*-* *:*:00
hourly*-*-* *:00:00
daily*-*-* 00:00:00
weeklyMon *-*-* 00:00:00
monthly*-*-01 00:00:00
yearly*-01-01 00:00:00

使用 systemd-analyze calendar 来验证表达式是否正确,并预览接下来的触发时间:

验证时间表达式并查看下次触发时间
systemd-analyze calendar "Mon..Fri *-*-* 09:00:00"
验证每 15 分钟触发一次的表达式
systemd-analyze calendar "*-*-* *:00/15:00"

下面以一个数据库备份任务为例,演示如何创建 timer + service 对。

创建备份脚本
sudo vim /opt/scripts/db-backup.sh

写入以下内容:

/opt/scripts/db-backup.sh
#!/bin/bash
BACKUP_DIR="/backup/database"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"
mysqldump --all-databases | gzip > "$BACKUP_DIR/all-db-$TIMESTAMP.sql.gz"
# 删除 7 天前的备份
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +7 -delete
echo "Database backup completed: all-db-$TIMESTAMP.sql.gz"
赋予脚本执行权限
sudo chmod +x /opt/scripts/db-backup.sh
创建备份服务单元文件
sudo vim /etc/systemd/system/db-backup.service
/etc/systemd/system/db-backup.service
[Unit]
Description=Database Backup Service
After=mariadb.service
Wants=mariadb.service
[Service]
Type=oneshot
ExecStart=/opt/scripts/db-backup.sh
User=root
StandardOutput=journal
StandardError=journal
# 超时设置(备份大数据库可能需要较长时间)
TimeoutStartSec=3600
# 安全加固
PrivateTmp=true
ProtectHome=true

注意:timer 触发的 service 通常不需要 [Install] 段,因为它由 timer 启动而非直接 enable。

创建备份定时器单元文件
sudo vim /etc/systemd/system/db-backup.timer
/etc/systemd/system/db-backup.timer
[Unit]
Description=Run Database Backup Daily at 2:00 AM
[Timer]
OnCalendar=*-*-* 02:00:00
RandomizedDelaySec=300
Persistent=true
[Install]
WantedBy=timers.target

配置说明:

指令说明
OnCalendar=*-*-* 02:00:00每天凌晨 2 点触发
RandomizedDelaySec=300随机延迟 0-300 秒,避免多台服务器同时执行
Persistent=true如果错过了执行时间(如服务器关机),下次启动时立即补执行
重新加载 systemd 配置
sudo systemctl daemon-reload
启用并启动定时器
sudo systemctl enable --now db-backup.timer
查看定时器状态和下次触发时间
sudo systemctl status db-backup.timer
手动触发一次备份服务进行测试
sudo systemctl start db-backup.service
查看备份服务的执行日志
sudo journalctl -u db-backup.service -n 20

除了基于日历时间的定时器,单调定时器适合”每隔一段时间执行一次”的场景:

/etc/systemd/system/health-check.timer - 开机 1 分钟后首次执行,之后每 5 分钟执行一次
[Unit]
Description=Run Health Check Every 5 Minutes
[Timer]
OnBootSec=1min
OnUnitActiveSec=5min
[Install]
WantedBy=timers.target
/etc/systemd/system/health-check.service
[Unit]
Description=System Health Check
[Service]
Type=oneshot
ExecStart=/opt/scripts/health-check.sh
列出所有活动定时器及其下次触发时间
systemctl list-timers

查看所有定时器(包括未激活的)

Section titled “查看所有定时器(包括未激活的)”
列出所有定时器(含已停用的)
systemctl list-timers --all
停止定时器(不会取消 enable)
sudo systemctl stop db-backup.timer
禁用并停止定时器
sudo systemctl disable --now db-backup.timer

如果你有一个现有的 crontab 任务:

30 3 * * * /opt/scripts/cleanup.sh

对应的 systemd timer 配置为:

/etc/systemd/system/cleanup.service
[Unit]
Description=Daily Cleanup Task
[Service]
Type=oneshot
ExecStart=/opt/scripts/cleanup.sh
/etc/systemd/system/cleanup.timer
[Unit]
Description=Run Cleanup Daily at 3:30 AM
[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
[Install]
WantedBy=timers.target
启用迁移后的定时器
sudo systemctl daemon-reload && sudo systemctl enable --now cleanup.timer

确认 timer 正常工作后,再删除对应的 crontab 条目:

编辑 crontab 移除旧任务
sudo crontab -e
查看定时器详细状态
sudo systemctl status db-backup.timer
查看定时器关联的服务最近执行结果
sudo systemctl status db-backup.service
查看定时器的所有属性
systemctl show db-backup.timer
验证 OnCalendar 表达式语法
systemd-analyze calendar "*-*-* 02:00:00"
查看定时器和服务的完整日志
sudo journalctl -u db-backup.timer -u db-backup.service --since today