Linux crontab 定时任务实战:计划任务配置、日志排查与自动化运维指南


前言:为什么每个站长都该学会 crontab

VPS 跑起来之后,真正考验运维水平的不是"装环境",而是"自动化"。备份数据库、清理日志、推送监控告警、定时抓取数据、凌晨低峰期执行重任务……这些操作如果每次都靠人工登录执行,迟早会出漏子:忘了备份、磁盘被日志塞满、证书到期没续。Linux 自带的 crontab(cron table)就是解决这类问题的标准工具:按时间规则在后台周期性执行命令或脚本,无需人工干预。

本篇文章从字段格式讲起,覆盖常用命令、备份实战、环境变量坑、日志排查与进阶技巧,看完就能在自己的 VPS 上搭一套靠谱的定时任务体系。

一、认识 crontab:五字段格式详解

一条 cron 任务由"时间表达式 + 要执行的命令"组成,时间表达式共五个字段,顺序固定:分、时、日、月、星期。

分 时 日 月 星期  命令
*  *  *  *  *    /usr/local/bin/backup.sh

字段取值范围:

字段      范围        特殊字符
分        0-59        * , - /
时        0-23        * , - /
日        1-31        * , - /
月        1-12        * , - /
星期      0-7(0 和 7 都代表周日)  * , - /

常见写法和含义:

# 每天凌晨 2:30 执行备份
30 2 * * * /opt/scripts/backup.sh

# 每 5 分钟执行一次健康检查
*/5 * * * * /opt/scripts/health.sh

# 每天 8 点到 18 点之间,每 2 小时执行一次
0 8-18/2 * * * /opt/scripts/sync.sh

# 每月 1 号和 15 号的 3 点执行
0 3 1,15 * * /opt/scripts/bill.sh

# 每周一到周五的早上 9 点执行
0 9 * * 1-5 /opt/scripts/workday.sh

几个易错点:日与星期同时设置时,两者是"或"关系,即任一匹配就会执行;比如 0 0 1 * 1 表示"每月 1 号"或"每周一"都会触发,而不是两者同时满足。

二、crontab 常用命令

crontab 命令负责管理当前用户的计划任务,最常用的就三条:

# 编辑当前用户的 crontab(默认用 vi/vim/nano)
crontab -e

# 查看当前用户的全部任务
crontab -l

# 清空当前用户的所有任务
crontab -r

管理其他用户的任务需要 root 权限,用 -u 指定用户:

crontab -u www -e    # 编辑 www 用户的任务
crontab -u www -l    # 查看 www 用户的任务

技巧:备份现有任务,防止误删后无法恢复。

crontab -l > /root/crontab-backup-$(date +%F).txt

三、实战:定时备份网站文件与 MySQL 数据库

这是最典型也最有价值的应用场景。先写备份脚本,再把它挂进 crontab。

3.1 编写备份脚本

#!/bin/bash
# /opt/scripts/backup.sh  网站文件 + 数据库全量备份

BACKUP_DIR=/data/backups
DATE=$(date +%Y%m%d-%H%M%S)
KEEP_DAYS=7

# 1. 备份网站目录(排除缓存、日志等无用文件)
tar czf "$BACKUP_DIR/web-$DATE.tar.gz" -C /var/www \
    --exclude=wp-content/cache --exclude=*.log /var/www/html

# 2. 备份 MySQL 数据库(mysqldump 全量)
mysqldump -uroot -p'你的密码' --single-transaction \
    --routines --triggers --databases myblog > "$BACKUP_DIR/db-$DATE.sql"
gzip "$BACKUP_DIR/db-$DATE.sql"

# 3. 只保留最近 7 天的备份,自动清理旧文件
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +$KEEP_DAYS -delete
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +$KEEP_DAYS -delete

echo "[$(date +%F' '%T)] backup done: $BACKUP_DIR"

脚本要可执行,并且最好先手动跑一遍确认无误:

chmod +x /opt/scripts/backup.sh
/opt/scripts/backup.sh

3.2 挂进 crontab

crontab -e

# 每天凌晨 3:00 执行备份,输出写入独立日志
0 3 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1

这里把标准输出和错误输出都追加到 /var/log/backup.log,方便日后排查。如果脚本里有 echo,别忘记 >> 追加重定向,否则每次执行都会覆盖上一次日志。

记住:crontab 里尽量写脚本的绝对路径,并且脚本内部也全部用绝对路径,避免环境变量导致的"找不到命令"问题(下文第四节详述)。

四、环境变量与 PATH:cron 里最常见的坑

很多人发现"脚本手动执行正常,挂到 cron 里却报错",八成是环境变量问题。cron 执行任务时的环境极其精简,PATH 默认通常是 /usr/bin:/bin,你用 nvm 装的 node、装在 /usr/local/bin 下的工具,都可能找不到。

解决方案:在脚本头部显式声明环境变量,或者直接用命令的绝对路径。

#!/bin/bash
# 显式指定 PATH,覆盖 cron 的默认值
export PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/sbin:$PATH"
export LANG=en_US.UTF-8

也可以查一下命令到底装在哪:

which mysqldump
# 输出类似 /usr/bin/mysqldump,直接写成绝对路径即可

另外一个经典坑:cron 里写 % 会被当作换行符处理,如果要传带百分号的参数(比如 date +%F),必须转义成 \%。稳妥的做法是把复杂逻辑全部写进脚本,crontab 只保留一行调用。

五、系统级任务:/etc/crontab 与 /etc/cron.d

除了用户级 crontab,Linux 还提供系统级定时任务配置文件。Debian/Ubuntu 系默认就有 /etc/crontab,它与用户 crontab 的格式不同:多了"执行用户"一列。

# /etc/crontab 示例(注意第六列是指定运行任务的用户)
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )

/etc/cron.d/ 目录里的文件格式与 /etc/crontab 一致,适合软件包安装自己的定时任务(例如 /etc/cron.d/certbot)。/etc/cron.hourly、/etc/cron.daily 等目录则放可直接执行的脚本,系统会按规则统一调度。

写进 /etc/cron.d/ 的任务需要带用户名:

# /etc/cron.d/backup-jobs
30 2 * * * root /opt/scripts/backup.sh >> /var/log/backup.log 2>&1

注意:cron.d 下的文件对权限敏感,属主必须是 root,且不能有组/其他用户的写权限,否则 cron 会静默忽略该文件。

六、特殊字符串:@reboot、@daily 等

除了五字段表达式,cron 还支持几个便捷的"别名":

@reboot      # 开机时执行一次(适合启动定时拉取、预热等)
@daily       # 每天 0 点执行,等价于 0 0 * * *
@hourly      # 每小时执行,等价于 0 * * * *
@weekly      # 每周日 0 点
@monthly     # 每月 1 号 0 点
@yearly      # 每年 1 月 1 号 0 点

实用例子:开机后自动启动 frp 内网穿透客户端。

@reboot /usr/local/bin/frpc -c /etc/frpc.toml >> /var/log/frpc.log 2>&1

提示:如果机器会不定期重启,配合 systemd 开机自启也许更合适;但 @reboot 在"脚本型任务"场景下仍然简单高效。

七、日志与排错:cron 没执行怎么查

定时任务"该跑没跑"是最高频的故障,按下面顺序排查基本能定位。

7.1 确认 cron 服务在运行

systemctl status cron          # Debian/Ubuntu
systemctl status crond         # CentOS/RHEL
# 未运行则启动并设置开机自启
systemctl enable --now cron

7.2 查看 cron 执行日志

# Debian/Ubuntu 默认记在 syslog
grep CRON /var/log/syslog | tail -50

# CentOS/RHEL 有独立日志文件
tail -50 /var/log/cron

日志里能看到每次任务的触发时间、执行用户和命令,例如:

Oct 27 03:00:01 vps CRON[12345]: (root) CMD (/opt/scripts/backup.sh >> /var/log/backup.log 2>&1)

7.3 检查脚本输出与退出码

# 手动执行并查看退出码,非 0 即为失败
/opt/scripts/backup.sh
echo "exit code: $?"
# 查看任务输出日志最后 50 行
tail -50 /var/log/backup.log

7.4 常见问题清单

症状                            大概率原因
脚本手动执行正常、cron 不执行     PATH/环境变量缺失,或脚本不可执行(缺 +x)
任务执行了但没效果              脚本内部用了相对路径,cwd 与预期不符
日志里看不到任何记录             cron 服务未启动,或任务语法错误被静默忽略
每天执行的时间不对              服务器时区不是 Asia/Shanghai,timedatectl 查看
同一时刻任务重复执行             !!!注意这是非预期的重复执行

排查时区问题:

timedatectl set-timezone Asia/Shanghai
date

八、进阶技巧:防止重复执行、随机延迟与日志轮转

8.1 用锁文件防止脚本重叠执行

如果任务耗时可能超过执行间隔(例如每 5 分钟跑一次,但脚本要跑 10 分钟),会出现多个实例同时运行。用 flock 加锁:

# crontab 中通过 flock 保证同一时刻只有一个实例
*/5 * * * * /usr/bin/flock -xn /tmp/backup.lock /opt/scripts/backup.sh

锁被占用时该次任务直接跳过,不会排队堆积。

8.2 随机延迟,错峰执行

多个 VPS 或本机多任务都在整点执行会给上游接口造成压力,可以用 sleep 打散:

# 每次执行前随机等待 0-300 秒
30 2 * * * sleep $((RANDOM % 300)) && /opt/scripts/sync.sh

8.3 防止日志无限膨胀

重定向输出到固定日志文件后,文件会越涨越大,务必让 logrotate 接管(配置参考):

# /etc/logrotate.d/backup
/var/log/backup.log {
    daily
    rotate 14
    compress
    missingok
    notifempty
    copytruncate
}

copytruncate 用于不中断正在写的文件;配置完后执行 logrotate -f /etc/logrotate.d/backup 验证。

8.4 邮件通知(可选)

cron 默认会把任务输出通过邮件发给用户,VPS 上一般没有 MTA 会静默丢弃。如果想让失败时收到通知,在脚本里判断退出码并调用 Webhook/钉钉机器人的接口即可:

if [ $? -ne 0 ]; then
    curl -s -X POST "https://your-webhook-url" \
        -H "Content-Type: application/json" \
        -d '{"text":"backup failed at '"$(date)"'"}'
fi

九、总结

crontab 是 Linux 自动化运维的地基:五字段格式记牢(分 时 日 月 星期)、命令用绝对路径、脚本显式声明 PATH、日志重定向到独立文件、定期用 logrotate 清理。按照"先手动跑通脚本 → 再挂 crontab → 最后看日志确认"的流程,基本能避开绝大多数坑。

推荐的排错口诀:一查服务、二看日志、三查权限、四看时区。把这篇文章里的配置模板抄下来改造,就能轻松实现数据库定时备份、日志自动清理、监控告警推送等常用自动化场景,让 VPS 真正"自己会干活"。


手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

-

Linux SELinux 安全模块实战:原理、模式切换、布尔值与排错指南

评 论
更换验证码