Linux 定时任务完全指南:crontab 进阶与 systemd timer 实战


阿里云特惠 - 新用户专享

Linux 定时任务完全指南:crontab 进阶与 systemd timer 实战

定时任务是运维自动化的基石。数据库备份、日志清理、定时报告、缓存预热——这些都离不开定时执行的能力。本文深度讲解 crontab 的进阶用法,以及更现代的 systemd timer,让你的定时任务更可靠、更可观测。

一、crontab 语法深度解析

# crontab 时间字段:
┌───────────── 分钟 (0-59)
│ ┌───────────── 小时 (0-23)
│ │ ┌───────────── 日期 (1-31)
│ │ │ ┌───────────── 月份 (1-12 或 JAN-DEC)
│ │ │ │ ┌───────────── 星期 (0-7, 0和7都是周日,或 SUN-SAT)
│ │ │ │ │
* * * * * /path/to/command

# 常用示例
0 2 * * *           # 每天凌晨2点
0 2 * * 1           # 每周一凌晨2点
*/5 * * * *         # 每5分钟
0 */4 * * *         # 每4小时整点
0 2 1,15 * *        # 每月1日和15日凌晨2点
0 2 * * 1-5         # 工作日(周一到周五)凌晨2点
@reboot             # 系统重启后执行一次
@daily              # 等同于 0 0 * * *
@weekly             # 等同于 0 0 * * 0

二、crontab 常用操作

crontab -l             # 列出当前用户的定时任务
crontab -e             # 编辑(会打开 vi/nano)
crontab -r             # 删除所有定时任务(谨慎!)
crontab -u nginx -l    # 查看 nginx 用户的定时任务(需要 root)

# 系统级 crontab(针对服务运行,需指定用户)
# 文件:/etc/crontab(有 username 字段)
# 目录:/etc/cron.d/(推荐:独立文件,方便管理)
# 示例:/etc/cron.d/myapp
0 2 * * * root /opt/myapp/scripts/backup.sh >> /var/log/myapp/backup.log 2>&1

三、crontab 的七个常见坑

坑1:没有加载环境变量

# crontab 运行时的 PATH 非常有限,很多命令找不到
# 解决:在脚本或 crontab 中显式设置 PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 2 * * * /opt/backup/run.sh

坑2:没有日志,出错了不知道

# 正确做法:重定向标准输出和错误
0 2 * * * /opt/backup.sh >> /var/log/backup.log 2>&1

# 或者同时发送邮件通知(需要配置 MTA)
MAILTO=ops@example.com
0 2 * * * /opt/backup.sh

坑3:时区问题

# crontab 使用系统时区,检查系统时区
timedatectl

# 临时修改 crontab 的时区(不改系统时区)
CRON_TZ=Asia/Shanghai
0 2 * * * /opt/backup.sh

坑4:并发执行

# 如果上一次任务还没跑完,下一次又开始了(典型于长时间任务)
# 使用文件锁防止并发
0 * * * * flock -n /tmp/backup.lock /opt/backup.sh || echo "上次任务未完成,跳过"

坑5:相对路径问题

# 错误:相对路径在 crontab 里通常无法工作
0 2 * * * ./scripts/backup.sh  # ❌

# 正确:始终使用绝对路径
0 2 * * * /opt/myapp/scripts/backup.sh  # ✅

四、systemd Timer:现代化定时任务

相比 crontab,systemd timer 的优势:

  • 任务输出自动写入 journald,可用 journalctl 查看历史
  • 支持 Persistent=true(机器关机期间错过的任务开机后补跑)
  • 可以设置随机延迟(RandomizedDelaySec),分散高峰压力
  • 任务之间可以定义依赖关系
# 示例:每天凌晨2点执行数据库备份
# 步骤1:创建 service 文件
# /etc/systemd/system/db-backup.service
[Unit]
Description=Database Daily Backup
After=mysql.service

[Service]
Type=oneshot              # 执行完就退出
User=backup               # 用专门的低权限用户执行
ExecStart=/opt/scripts/db_backup.sh
StandardOutput=journal
StandardError=journal

# 步骤2:创建 timer 文件
# /etc/systemd/system/db-backup.timer
[Unit]
Description=Run DB Backup Daily at 2:00

[Timer]
OnCalendar=*-*-* 02:00:00  # 每天凌晨2点
RandomizedDelaySec=300     # 在2:00-2:05之间随机启动(避免多台服务器同时备份)
Persistent=true            # 错过了开机后立即补跑

[Install]
WantedBy=timers.target

# 步骤3:启用
systemctl daemon-reload
systemctl enable --now db-backup.timer
systemctl list-timers db-backup.timer  # 查看下次执行时间
journalctl -u db-backup.service -f     # 查看备份日志

五、一键检查所有定时任务

# 查看当前用户 crontab
crontab -l

# 查看 /etc/cron.d/ 下的任务
ls -la /etc/cron.d/

# 查看系统 crontab
cat /etc/crontab

# 查看所有 systemd timer
systemctl list-timers --all

# 查看最近 timer 执行记录
journalctl -u "*.timer" --since "24 hours ago" --no-pager

总结

掌握 crontab 进阶用法(显式设置 PATH、添加日志、使用 flock 防并发)可以解决 90% 的定时任务问题。对于新项目,推荐使用 systemd timer,获得更好的可观测性和可靠性。所有定时任务都要有日志输出,没有日志就没有可观测性,出了问题无从追查。

发表评论