Linux 定时任务 crontab 实战:自动备份、日志清理与健康巡检
服务器跑久了,总有些事需要按固定节奏反复做:每天备份数据库、每周清理日志、每小时检查磁盘水位。这些事靠人记着,早晚会忘;写进脚本却没定时跑,工资一样白发。Linux 的 crontab 就是为这类场景准备的:写一行规则,剩下的交给系统。
这篇文章把 crontab 按“能直接用”的顺序拆开:先讲清楚五个时间字段怎么填,再给三个真上生产的例子(MySQL 备份、日志清理、磁盘巡检、告警落盘),最后列出新手最容易踩的四个坑。
一、crontab 的基本结构
一条定时任务由六个字段组成,前五个描述时间,第六个是要执行的命令:
minute hour day_of_month month day_of_week command
取值范围分别是:分钟 0-59,小时 0-23,日 1-31,月 1-12,星期 0-7(0 和 7 都代表周日)。除了具体数字,还可以用三种特殊写法:星号表示“每一个”,逗号表示“列举”,斜杠表示“每隔”。
举个例子:0 3 * * * /opt/backup/mysql.sh 的意思是“每天 3 点整跑一次备份脚本”。而 0 3 * * 1 则是“每周一 3 点”。
二、三个最容易混淆的时间写法
很多人写错任务,不是逻辑错,是时间字段理解错。
第一,*/5 * * * * 是“每 5 分钟一次”,而不是“每天第 5 分钟”。只在第一个字段加斜杠,其他字段仍是星号。
第二,“日”和“星期”同时指定时,cron 用的是“或”关系。比如 0 3 1 * 1 实际含义是“每月 1 号或者每周一”,两个条件满足任意一个就触发,并不需要同时满足。想表达“每月 1 号且恰好是周一”,得在脚本里自己判断。
第三,cron 跑到点就在整分算起,没有“延时 30 秒”这种写法。需要秒级控制,得靠 sleep 或换成 systemd timer。
三、实战一:每天凌晨备份 MySQL
先写备份脚本,放在 /opt/backup/mysql.sh,并赋予执行权限:
#!/bin/bash
DATE=$(date +%F_%H%M)
DIR=/opt/backup/mysql
mkdir -p $DIR
mysqldump -uroot -p"$DB_PASS" --single-transaction --routines --events app_db | gzip > $DIR/app_db_$DATE.sql.gz
find $DIR -name "*.sql.gz" -mtime +7 -delete
这里有两个细节值得说。--single-transaction 让 InnoDB 备份在一个一致性快照里进行,避免边备份边写入造成逻辑不一致;--routines --events 则把存储过程和事件调度器一并导出,不然恢复时才发现少了东西。最后一行顺手清理七天前的旧备份,避免磁盘被自己的备份撑满。
对应的 crontab 规则:
0 3 * * * /opt/backup/mysql.sh >> /var/log/mysql-backup.log 2>&1
注意末尾的重定向。cron 默认把任务输出邮件发给 root,本机没装 MTA 就静默丢弃,出错你也看不到。把 stdout 和 stderr 一起写进日志,是排查问题的第一道保险。
四、实战二:日志按天切割并自动清理
应用日志默认都是一直往一个文件里追加,跑一个月就是几个 G。比较稳的做法是按天切分,再按保留天数删首。
0 0 * * * cd /var/log/app && mv app.log app.log.$(date -d yesterday +%F) 2>/dev/null; kill -USR1 $(cat /run/app.pid)
这里用了两个小技巧:mv 把旧日志改名后,日志文件描述符还指向旧文件,需要给进程发 USR1 信号让它重新打开一个新文件;很多服务(Nginx、部分 Java 应用)都支持这个约定。如果你的应用不响应信号,就改成重启服务,但重启会有短暂中断,深夜执行更安全。
清理部分单独一条任务,只留最近七天:
30 4 * * * find /var/log/app -name "app.log.*" -mtime +7 -delete
五、实战三:磁盘水位巡检与告警落盘
磁盘满是最典型的事故源。与其等报警平台告警,不如先在服务器上做一道自检:超过阀值就往一个日志文件里写记录,后面再接入你的通知渠道。
#!/bin/bash
THRESHOLD=85
USED=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%')
if [ "$USED" -ge "$THRESHOLD" ]; then
echo "$(date '+%F %T') WARN root usage ${USED}%" >> /var/log/disk-check.log
du -sh /var/* 2>/dev/null | sort -rh | head -5 >> /var/log/disk-check.log
fi
把它挂到每半小时一次的规则上:
*/30 * * * * /opt/check/disk.sh
之所以顺便把 du 排名前五的目录写进日志,是因为告警本身不解决问题,定位才解决。等真出事那一刻,你直接看日志就能知道是哪个目录在膨胀,不用再登上去一遍遍排查。
六、新手必踩的四个坑
第一,环境变量不全。cron 执行任务时用的是精简 PATH,你在终端里能跑的 node、python、docker,在 cron 里经常报“command not found”。解决办法是在脚本开头显式设置:export PATH=/usr/local/bin:/usr/bin:/bin,或者写绝对路径。
第二,百分号转义。crontab 里命令部分的 % 有特殊含义,会被当成换行符。要用 date +%F 这类格式,必须写成 \%F。所以上面的例子都把 date 写进了脚本,而不是直接塞进 crontab,省得跟转义打交道。
第三,脚本权限。cron 以当前用户身份执行,脚本没有执行位就会静默失败。chmod +x 是必要动作;另外脚本里第一行的 shebang 要写对,bash 脚本就别用 sh 去解释。
第四,改完不验证。改 crontab 之后,最好像检查配置文件一样先看一遍:crontab -l 确认内容入库,再去 /var/log/syslog 或 /var/log/cron 里搜 CRON 关键字,看看任务有没有被调度、什么时候跑的。如果是手写测试,可以先把时间设成下一分钟,拿一个 echo 命令试水,确认链路通了再把真实任务挂上去。
七、小结
crontab 的价值不在于多复杂,而在于把“应该做的事”变成“不需要记忆的事”。一份好的定时任务清单,通常包含备份、清理、巡检三类,每一条都配合日志和失败重定向。它不解决所有问题,但能让你在半夜两点不被“磁盘满了”的电话叫醒。
最后补一句运维习惯:定时任务也应该进版本库。把 crontab 内容导出来放到 git 里,换机器、重装修、恢复环境的时候,一份文件就能把自动化能力重建起来。
- 点赞
- 收藏
- 关注作者
评论(0)