Linux 定时任务自动化实战:crontab 与 systemd timer 从入门到进阶

重复性的运维工作,交给定时任务

数据库备份、日志清理、磁盘空间监控、SSL 证书续期……这些事如果你一直手动做,总会有忘记的一天。定时任务就是把这些重复劳动交给系统自动执行:到点就跑、跑了就完、日志留痕。Linux 下有两套主流方案:传统的 crontab 和现代的 systemd timer。本文从 crontab 基础讲起,再带你用 systemd timer 搭一个可靠的自动备份任务,最后给出三个站长必配的实战场景。

一、crontab 五分钟入门

crontab 的配置格式是五个时间字段加一条命令:

分 时 日 月 周  命令

常用命令:crontab -e 编辑当前用户的定时任务,crontab -l 查看,crontab -r 删除。下面这几个是出现频率最高的写法:

0 2 * * * /usr/local/bin/backup.sh            # 每天凌晨 2 点
*/5 * * * * /usr/local/bin/check.sh            # 每 5 分钟一次
0 3 * * 0 /usr/local/bin/weekly.sh             # 每周日凌晨 3 点
0 0 1 * * /usr/local/bin/monthly.sh            # 每月 1 号零点
30 4 1,15 * * /usr/local/bin/twice.sh          # 每月 1 号和 15 号 4:30

注意:星期字段里 0 和 7 都代表周日;*/n 表示每隔 n 个单位执行一次;多个值用逗号分隔。编辑保存后 crontab 立即生效,不需要重启任何服务。如果记不住这些写法,收藏下面这张速查表就够了:

写法含义
*/10 * * * *每 10 分钟执行一次
0 * * * *每小时整点执行
30 1 * * *每天凌晨 1 点 30 分
0 4 * * 6每周六凌晨 4 点
0 0 1 * *每月 1 号零点
@reboot开机时执行一次

另外还有 @daily、@weekly、@monthly 这类简写,分别代表每天、每周、每月执行一次,适合不想记五段格式的场景,可读性也更好。

二、crontab 的三大坑

坑一:环境变量。 cron 执行脚本时的 PATH 非常精简,很多命令(比如 mysqldump)不在默认 PATH 里,脚本里裸写命令名会直接失败。解决办法:脚本里写命令全路径,或者在脚本开头 source /etc/profile 把环境加载进来。

坑二:输出丢失。 cron 执行的任务默认把标准输出通过邮件发走,很多服务器没配邮件服务,输出就悄悄丢了,出问题都不知道。规范做法是重定向到日志文件:

0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

坑三:百分号转义。 命令里出现 % 需要写成 \%(例如 date +\%F),否则 cron 会把它当成换行处理,命令被截断,执行结果完全不是你想的那样。

除了上面三个坑,还要养成一个习惯:新建定时任务后,先手动执行一遍脚本确认无误,再把它写进 crontab。手动执行正常、cron 里却失败的情况,几乎都是环境变量或路径问题,按上面的方法排查即可。

三、为什么推荐 systemd timer

systemd timer 是 systemd 原生的定时任务方案,相比 crontab 有几个实打实的优势:

  • 依赖管理:可以声明 After=network.target,网络就绪后才执行,不用担心脚本里 wget 因为网络没起来而失败;
  • 失败重试:service 单元可以配置 Restart=on-failure,任务失败自动重试;
  • 精确调度:OnCalendar 支持秒级精度,还支持夏令时等复杂规则;
  • 日志统一:任务输出全部进 journald,一条 journalctl 命令就能查,不用自己维护日志文件;
  • 错过补跑:Persistent=true 时,如果停机期间错过了执行点,开机后会自动补跑一次,这个特性对备份类任务特别重要。

另外,systemd timer 的配置本身就带校验:执行 systemd-analyze verify /etc/systemd/system/backup.timer 可以检查单元文件语法,写错的字段会直接报错,比 crontab「写错也不吭声」的作风友好得多。

四、实战:用 systemd timer 做自动备份

第一步,写服务单元 /etc/systemd/system/backup.service:

[Unit]
Description=Daily backup
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
StandardOutput=journal

第二步,写定时器单元 /etc/systemd/system/backup.timer:

[Unit]
Description=Run backup daily at 02:30

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

[Install]
WantedBy=timers.target

第三步,加载并启用:

systemctl daemon-reload
systemctl enable --now backup.timer
systemctl list-timers backup.timer   # 查看下次执行时间
journalctl -u backup -n 30           # 查看执行日志

Type=oneshot 表示这个服务只跑一次就结束,适合备份、清理这类一次性任务;OnCalendar 的格式和 crontab 不同,上面的写法表示每天 2 点 30 分,也支持星期,例如 Mon..Fri 03:00:00 表示工作日凌晨 3 点。

如果只想手动触发一次服务验证脚本是否正确,不需要等定时器:执行 systemctl start backup.service 会立即运行一次并留下日志。这个习惯特别重要——新写的定时任务先手动触发验证,确认没问题再交给定时器,能省掉大量排查时间。

五、三个站长必备的定时任务场景

场景一:MySQL 自动备份并保留最近 7 天。 脚本核心就两行:

mysqldump --single-transaction -u备份用户 -p密码 数据库名 | gzip > /backup/db_$(date +\%F).sql.gz
find /backup -name "*.sql.gz" -mtime +7 -delete

--single-transaction 用于 InnoDB 表,备份过程中不锁表,不会影响线上访问;find 加 -mtime +7 -delete 自动清理 7 天前的旧备份,防止磁盘被备份文件占满。备份完顺手用 gzip -t 校验一下压缩包完整性,比什么都不检查强得多。

场景二:日志自动轮转。 直接用 logrotate,在 /etc/logrotate.d/ 下新建 nginx 配置:

/var/log/nginx/*.log {
    weekly
    rotate 4
    compress
    missingok
    notifempty
}

每周轮转一次、保留 4 份、旧日志压缩,nginx 日志文件不会无限膨胀。logrotate 本身由系统的 cron 驱动,你不需要自己写清理脚本。

场景三:SSL 证书自动续期。 certbot 自带续期命令,每天跑一次即可:

0 3 * * * certbot renew --quiet --deploy-hook "systemctl reload nginx"

renew 只在证书临近过期时才会真正续期,平时执行几乎不耗资源;续期成功后通过 --deploy-hook 重载 nginx,让新证书立即生效。这是全站 HTTPS 最省心的维护方式。

场景四:磁盘空间监控与告警。 网站跑着跑着磁盘满了,是个人站长最常遇到的高频事故,写个脚本每天检查一次:

#!/bin/bash
USE=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
if [ "$USE" -gt 90 ]; then
    echo "磁盘使用率已达 ${USE}%,请及时清理" | mail -s "磁盘告警" admin@example.com
fi

超过 90% 就发邮件提醒,配合前面保留 7 天备份、logrotate 轮转日志的做法,基本能杜绝「磁盘满导致网站打不开」这类事故。

六、任务没执行怎么排查

  • crontab 任务:先手动执行一遍脚本,确认脚本本身没问题;再看系统日志 journalctl -u cron 或 /var/log/syslog,搜索你配置的时间点附近有没有执行记录;
  • systemd 任务:systemctl status backup.service 看最近一次执行状态,journalctl -u backup -n 50 看具体报错;
  • 检查时区:timedatectl 确认服务器时区和预期一致,crontab 还可以在文件开头写 CRON_TZ=Asia/Shanghai 指定时区;
  • 检查脚本权限:脚本要有执行权限(chmod +x),且第一行写清楚 #!/bin/bash 这类解释器声明,否则 cron 可能无法直接执行它。

最后提醒一句:排查定时任务问题时,先把脚本里的命令一条条手动执行,排除脚本自身的错误,再怀疑定时器配置。定时器本身很少出错,出问题的往往是脚本细节、环境变量和权限这几样东西。按「脚本 → 环境 → 调度」的顺序排查,效率最高,也最不容易绕弯路。

七、crontab 还是 systemd timer?

选择其实很简单:简单脚本、习惯传统、不想多学,crontab 完全够用,它几十年如一日地稳定;需要依赖管理、失败重试、精确到秒、统一日志的场景,用 systemd timer。两个方案可以共存,不存在冲突。关键不是选哪个,而是先把它配起来——备份和证书续期这种事,晚一天都可能出事。

常见问题(FAQ)

Q:为什么我的脚本手动执行正常,cron 里就是失败? A:十有八九是环境变量问题。cron 的 PATH 默认只有 /usr/bin:/bin,脚本里用全路径调用命令,或者直接在脚本开头写死 PATH,问题立刻消失。

Q:OnCalendar 有哪些常用写法? A:*-*-* 02:30:00 表示每天 2 点 30 分;Mon..Fri 03:00:00 表示工作日凌晨 3 点;*-*-1,15 表示每月 1 号和 15 号。格式拿不准时可以用 systemd-analyze calendar "表达式" 来验证,它会告诉你下次执行时间。

Q:定时任务和时区有关系吗? A:有。cron 和 systemd 默认用系统时区,服务器租在海外而时区没改的话,你以为是凌晨 2 点执行,实际可能是当地时间。先执行 timedatectl set-timezone Asia/Shanghai 把时区改对,再配置定时任务。

Q:服务器关机期间错过的任务会执行吗? A:crontab 错过的任务不会补跑;systemd timer 设置了 Persistent=true 后,开机时会自动补跑错过的执行点,这是它比 crontab 更适合备份任务的原因。

Q:定时任务执行时间不准怎么办? A:先确认系统时间本身准确,用 systemd-timesyncd 或 chrony 保持 NTP 同步,再检查时区。系统时间不对,所有定时任务都会跟着错。

Last modification:August 5th, 2026 at 08:08 am

Leave a Comment