强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

systemd 教程 / Timer 定时任务

Timer 定时任务

一、Timer 简介

systemd Timer 是 cron 的现代替代方案。Timer 与 Service 配合使用——Timer 负责调度,Service 负责执行。

1.1 Timer vs crontab

特性systemd Timercrontab
日志集成✅ journalctl需要 MAILTO 或重定向
依赖管理✅ 与其他 Unit 集成
错误处理✅ Restart 策略
错过执行✅ Catch-up(Persistent)
随机延迟✅ RandomizedDelaySec
资源限制✅ CGroup 集成
调试✅ systemctl status需手动排查
精度秒级分钟级

二、Timer Unit 文件

Timer 需要两个文件:.timer(调度器)和 .service(执行器),两者名称必须一致。

Timer 文件 (/etc/systemd/system/mytask.timer):

[Unit]
Description=My Scheduled Task Timer

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target

Service 文件 (/etc/systemd/system/mytask.service):

[Unit]
Description=My Scheduled Task

[Service]
Type=oneshot
ExecStart=/usr/local/bin/mytask.sh
User=myuser
# 启用并启动 Timer
systemctl enable --now mytask.timer

# 查看 Timer 状态
systemctl list-timers mytask.timer

三、[Timer] 段详解

3.1 时间触发器

指令说明
OnCalendar日历事件触发
OnBootSec系统启动后延迟
OnStartupSecsystemd 启动后延迟
OnUnitActiveSec上次激活后间隔
OnUnitInactiveSec上次停用后间隔

3.2 OnCalendar 日历事件

格式:星期 年-月-日 时:分:秒

表达式说明
*-*-* *:*:00每分钟
*-*-* *:00:00每小时
*-*-* 02:00:00每天凌晨 2 点
Mon *-*-* 09:00:00每周一上午 9 点
*-*-01 00:00:00每月 1 号
Mon..Fri *-*-* 09:00:00工作日上午 9 点
*~07 00:00:00每月最后一天
# 验证日历表达式的下次触发时间
systemd-analyze calendar "Mon *-*-* 09:00:00"
# 输出: Next elapse: Mon 2026-05-12 09:00:00 CST

# 显示多次迭代
systemd-analyze calendar --iterations=5 "*-*-* 02:00:00"

3.3 相对时间触发器

[Timer]
OnBootSec=5min           # 系统启动后 5 分钟
OnStartupSec=1min        # systemd 启动后 1 分钟
OnUnitActiveSec=1h       # 上次激活后 1 小时
OnUnitInactiveSec=30min  # 上次停用后 30 分钟
指令计时起点
OnBootSec内核启动完成
OnStartupSecsystemd 启动完成
OnUnitActiveSec同名 Service 变为 active
OnUnitInactiveSec同名 Service 变为 inactive

⚠️ 注意OnBootSecOnStartupSec 在系统运行足够长时间后不再触发,除非配合 Persistent=true 或再次重启。不要将它们与 OnCalendar 混用——两者是独立的触发器,任一满足即执行。

3.4 其他 Timer 指令

[Timer]
AccuracySec=1s              # 调度精度(默认 1min)
RandomizedDelaySec=30min    # 随机延迟上限
Persistent=true             # 错过时是否补执行
Unit=my-other.service       # 指定目标 Service(默认同名)

四、Persistent 与 Catch-up

如果系统在计划执行时间处于关机状态,开机后的行为:

Persistent 值行为
no(默认)不补执行,等待下次计划时间
yes开机后立即补执行一次

💡 提示:每日备份、日志清理等任务建议设置 Persistent=true


五、RandomizedDelaySec 随机延迟

集群中避免同时执行造成资源峰值:

[Timer]
OnCalendar=*-*-* 02:00:00
RandomizedDelaySec=30min    # 任务在 02:00 到 02:30 之间随机执行
AccuracySec=1s

六、查看与管理 Timer

# 列出所有 Timer
systemctl list-timers

# 包括未激活的
systemctl list-timers --all

# 启用/禁用
systemctl enable --now mytask.timer
systemctl disable mytask.timer

# 查看 Timer 日志
journalctl -u mytask.timer

# 查看 Service 执行日志
journalctl -u mytask.service -n 50

# 手动触发测试
systemctl start mytask.service

输出示例:

NEXT                         LEFT       LAST                         PASSED     UNIT               ACTIVATES
Mon 2026-05-12 02:00:00 CST  15h left   Sun 2026-05-11 02:00:00 CST  8h ago     backup.timer       backup.service
Mon 2026-05-12 03:00:00 CST  16h left   Sun 2026-05-11 03:00:00 CST  7h ago     logrotate.timer    logrotate.service

七、实际案例

7.1 日志清理

# /etc/systemd/system/log-cleanup.timer
[Unit]
Description=Log Cleanup Timer
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=15min
[Install]
WantedBy=timers.target
# /etc/systemd/system/log-cleanup.service
[Unit]
Description=Log Cleanup
[Service]
Type=oneshot
ExecStart=/bin/bash -c 'find /var/log/myapp -name "*.log" -mtime +30 -delete; journalctl --vacuum-time=30d'
StandardOutput=journal

7.2 数据库备份

# /etc/systemd/system/db-backup.timer
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=10min
# /etc/systemd/system/db-backup.service
[Unit]
Description=Database Backup
After=postgresql.service
[Service]
Type=oneshot
User=postgres
ExecStart=/usr/local/bin/backup-db.sh
TimeoutStartSec=7200
MemoryMax=1G
CPUQuota=50%
StandardOutput=journal
SyslogIdentifier=db-backup

7.3 每周报告

# /etc/systemd/system/weekly-report.timer
[Timer]
OnCalendar=Mon *-*-* 08:00:00
Persistent=true

7.4 证书续期

# /etc/systemd/system/cert-renew.timer
[Timer]
OnCalendar=*-*-* 04:00:00
Persistent=true
RandomizedDelaySec=1h
# /etc/systemd/system/cert-renew.service
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"

八、从 crontab 迁移

crontabsystemd Timer
0 2 * * *OnCalendar=*-*-* 02:00:00
*/5 * * * *OnUnitActiveSec=5min
0 9 * * 1OnCalendar=Mon *-*-* 09:00:00
0 0 1 * *OnCalendar=*-*-01 00:00:00
@rebootOnBootSec=0OnStartupSec=0
@dailyOnCalendar=*-*-* 00:00:00
@weeklyOnCalendar=Mon *-*-* 00:00:00

迁移步骤:

# 1. 查看现有 crontab
crontab -l

# 2. 为每个任务创建 Timer + Service
# 3. 启用并验证
systemctl enable --now mytask.timer
journalctl -u mytask.service -f

# 4. 确认无误后删除 crontab 条目

九、调试 Timer

# 1. 检查 Timer 状态
systemctl status mytask.timer

# 2. 检查下次执行时间
systemctl list-timers mytask.timer

# 3. 验证日历表达式
systemd-analyze calendar "*-*-* 02:00:00"

# 4. 查看 Service 执行日志
journalctl -u mytask.service -n 50

# 5. 手动执行测试
systemctl start mytask.service
问题原因解决方案
Timer 不触发未 enablesystemctl enable mytask.timer
Service 不执行名称不匹配Timer 和 Service 文件名一致
日历格式错误语法不对systemd-analyze calendar "..." 验证
错过执行Persistent 未设添加 Persistent=true
同时执行无随机延迟添加 RandomizedDelaySec

十、生产场景

场景 1:分布式备份

[Timer]
OnCalendar=*-*-* 01:00:00
Persistent=true
RandomizedDelaySec=2h   # 集群中随机延迟 0-2 小时

场景 2:健康检查(高频)

[Timer]
OnUnitActiveSec=30s     # 每 30 秒
AccuracySec=1s

场景 3:月度清理

[Timer]
OnCalendar=*-*-01 04:00:00
Persistent=true
RandomizedDelaySec=30min

十一、扩展阅读