定时任务 (Timer)
systemd timer 是 cron 的现代替代方案,提供了更精确的调度控制、完善的日志集成和依赖管理能力。在 CentOS/AlmaLinux/Rocky Linux 上,越来越多的系统任务已经从 cron 迁移到了 systemd timer。
Timer 与 Cron 的对比
Section titled “Timer 与 Cron 的对比”| 特性 | systemd Timer | Cron |
|---|---|---|
| 日志集成 | 自动集成 journald,可用 journalctl 查看 | 需要手动配置日志重定向 |
| 依赖管理 | 支持 After=、Requires= 等依赖声明 | 无依赖管理 |
| 错过执行 | 支持 Persistent=true,错过的任务会在下次启动时补执行 | 错过即丢失 |
| 资源控制 | 可设置 CPU、内存、IO 限制 | 无资源控制 |
| 精度 | 支持微秒级精度 | 最小粒度为 1 分钟 |
| 开机延迟 | 支持 OnBootSec= 等相对时间触发 | 不支持 |
| 随机延迟 | 支持 RandomizedDelaySec= 分散负载 | 不支持 |
| 管理方式 | 统一使用 systemctl 管理 | 需要编辑 crontab 文件 |
Timer 的工作原理
Section titled “Timer 的工作原理”systemd timer 以成对的方式工作:每个 .timer 单元对应一个同名的 .service 单元。当定时器触发时,systemd 会自动启动对应的 service 单元。
例如:
backup.timer— 定义触发时间backup.service— 定义要执行的任务
两种定时器类型
Section titled “两种定时器类型”单调定时器(Monotonic Timer)
Section titled “单调定时器(Monotonic Timer)”基于相对时间触发,适合”距离某个事件过了多久”的场景:
| 指令 | 说明 |
|---|---|
OnBootSec= | 系统启动后多久触发 |
OnActiveSec= | 定时器激活后多久触发 |
OnStartupSec= | systemd 启动后多久触发 |
OnUnitActiveSec= | 对应服务上次激活后多久再次触发 |
OnUnitInactiveSec= | 对应服务上次完成后多久再次触发 |
日历定时器(Realtime/Calendar Timer)
Section titled “日历定时器(Realtime/Calendar Timer)”基于绝对时间触发,类似 cron 的定时方式:
| 指令 | 说明 |
|---|---|
OnCalendar= | 按日历时间表达式触发 |
OnCalendar 时间语法
Section titled “OnCalendar 时间语法”OnCalendar= 使用以下格式:
星期 年-月-日 时:分:秒常用时间表达式
Section titled “常用时间表达式”| 表达式 | 说明 | 等效 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/2h | 9 点起每 2 小时 | — |
systemd 还提供了便捷的预设值:
| 预设值 | 等价表达式 |
|---|---|
minutely | *-*-* *:*:00 |
hourly | *-*-* *:00:00 |
daily | *-*-* 00:00:00 |
weekly | Mon *-*-* 00:00:00 |
monthly | *-*-01 00:00:00 |
yearly | *-01-01 00:00:00 |
验证时间表达式
Section titled “验证时间表达式”使用 systemd-analyze calendar 来验证表达式是否正确,并预览接下来的触发时间:
systemd-analyze calendar "Mon..Fri *-*-* 09:00:00"systemd-analyze calendar "*-*-* *:00/15:00"创建定时任务:完整示例
Section titled “创建定时任务:完整示例”下面以一个数据库备份任务为例,演示如何创建 timer + service 对。
第一步:创建备份脚本
Section titled “第一步:创建备份脚本”sudo vim /opt/scripts/db-backup.sh写入以下内容:
#!/bin/bashBACKUP_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 -deleteecho "Database backup completed: all-db-$TIMESTAMP.sql.gz"sudo chmod +x /opt/scripts/db-backup.sh第二步:创建 service 单元文件
Section titled “第二步:创建 service 单元文件”sudo vim /etc/systemd/system/db-backup.service[Unit]Description=Database Backup ServiceAfter=mariadb.serviceWants=mariadb.service
[Service]Type=oneshotExecStart=/opt/scripts/db-backup.shUser=rootStandardOutput=journalStandardError=journal
# 超时设置(备份大数据库可能需要较长时间)TimeoutStartSec=3600
# 安全加固PrivateTmp=trueProtectHome=true注意:timer 触发的 service 通常不需要 [Install] 段,因为它由 timer 启动而非直接 enable。
第三步:创建 timer 单元文件
Section titled “第三步:创建 timer 单元文件”sudo vim /etc/systemd/system/db-backup.timer[Unit]Description=Run Database Backup Daily at 2:00 AM
[Timer]OnCalendar=*-*-* 02:00:00RandomizedDelaySec=300Persistent=true
[Install]WantedBy=timers.target配置说明:
| 指令 | 说明 |
|---|---|
OnCalendar=*-*-* 02:00:00 | 每天凌晨 2 点触发 |
RandomizedDelaySec=300 | 随机延迟 0-300 秒,避免多台服务器同时执行 |
Persistent=true | 如果错过了执行时间(如服务器关机),下次启动时立即补执行 |
第四步:启用定时器
Section titled “第四步:启用定时器”sudo systemctl daemon-reloadsudo systemctl enable --now db-backup.timer第五步:验证定时器
Section titled “第五步:验证定时器”sudo systemctl status db-backup.timersudo systemctl start db-backup.servicesudo journalctl -u db-backup.service -n 20使用单调定时器
Section titled “使用单调定时器”除了基于日历时间的定时器,单调定时器适合”每隔一段时间执行一次”的场景:
[Unit]Description=Run Health Check Every 5 Minutes
[Timer]OnBootSec=1minOnUnitActiveSec=5min
[Install]WantedBy=timers.target[Unit]Description=System Health Check
[Service]Type=oneshotExecStart=/opt/scripts/health-check.sh列出和管理定时器
Section titled “列出和管理定时器”查看所有活动的定时器
Section titled “查看所有活动的定时器”systemctl list-timers查看所有定时器(包括未激活的)
Section titled “查看所有定时器(包括未激活的)”systemctl list-timers --all临时停止定时器
Section titled “临时停止定时器”sudo systemctl stop db-backup.timer永久禁用定时器
Section titled “永久禁用定时器”sudo systemctl disable --now db-backup.timer从 Cron 迁移到 Timer
Section titled “从 Cron 迁移到 Timer”如果你有一个现有的 crontab 任务:
30 3 * * * /opt/scripts/cleanup.sh对应的 systemd timer 配置为:
[Unit]Description=Daily Cleanup Task
[Service]Type=oneshotExecStart=/opt/scripts/cleanup.sh[Unit]Description=Run Cleanup Daily at 3:30 AM
[Timer]OnCalendar=*-*-* 03:30:00Persistent=true
[Install]WantedBy=timers.targetsudo systemctl daemon-reload && sudo systemctl enable --now cleanup.timer确认 timer 正常工作后,再删除对应的 crontab 条目:
sudo crontab -e排查定时器问题
Section titled “排查定时器问题”sudo systemctl status db-backup.timersudo systemctl status db-backup.servicesystemctl show db-backup.timersystemd-analyze calendar "*-*-* 02:00:00"sudo journalctl -u db-backup.timer -u db-backup.service --since today