主题
定时任务
定时任务是运维自动化的基础——备份、清理、监控检查,都离不开它。Linux 提供 cron 和 systemd timer 两套方案,本篇重点讲 cron,因为它在生产中仍然广泛使用。
crontab 语法
bash
# 查看当前用户的定时任务
crontab -l
# 编辑(默认用 vim/nano)
crontab -e
# 删除所有定时任务
crontab -r
# 以指定用户身份操作
sudo crontab -u www-data -l时间表达式
# * * * * * command
# │ │ │ │ │
# │ │ │ │ └── 星期 (0-7, 0和7都表示周日)
# │ │ │ └──── 月份 (1-12)
# │ │ └────── 日期 (1-31)
# │ └──────── 小时 (0-23)
# └────────── 分钟 (0-59)bash
# 经典示例
0 3 * * * /opt/scripts/backup.sh # 每天凌晨3点
*/5 * * * * /opt/scripts/health_check.sh # 每5分钟
0 9-18 * * 1-5 /opt/scripts/work_hours.sh # 工作日9-18点,每小时整点
0 1 1 * * /opt/scripts/monthly_report.sh # 每月1号凌晨1点
0 0 * * 0 /opt/scripts/weekly_cleanup.sh # 每周日凌晨
# 特殊字符串
@reboot /opt/scripts/on_boot.sh # 开机时执行
# 注意:@reboot 在 cron 启动时立即执行,此时网络可能尚未就绪。
# 需要网络的任务请改用 systemd timer 或脚本内加 sleep + 重试逻辑。
@yearly = 0 0 1 1 * # 每年1月1日
@monthly = 0 0 1 * * # 每月1日
@weekly = 0 0 * * 0 # 每周日凌晨
@daily = 0 0 * * * # 每天凌晨
@hourly = 0 * * * * # 每小时整点目录结构
除了 crontab -e 的个人任务,系统还有很多定时任务的位置:
bash
# 系统级定时任务文件
cat /etc/crontab
# 和用户 crontab 的区别:多了 user 字段
# 格式:min hour day month weekday user command
# 包管理器安装的定时任务放在这里
ls /etc/cron.d/
# 例如:certbot、logrotate 等会在这里创建文件
# 定时执行的目录(用 run-parts 执行)
ls /etc/cron.hourly/
ls /etc/cron.daily/
ls /etc/cron.weekly/
ls /etc/cron.monthly/
# 查看执行时间配置
cat /etc/crontab | grep run-parts
# 可以看到 cron.daily 默认在 6:25 执行实战脚本
1. 数据库自动备份
bash
# 编辑定时任务
crontab -e
# 每天凌晨 2 点备份 MySQL 数据库
0 2 * * * /opt/scripts/mysql_backup.sh >> /var/log/backup.log 2>&1mysql_backup.sh 参考Shell 脚本编程中的备份脚本。
2. SSL 证书自动续期(Let's Encrypt)
bash
# certbot 安装后自动在 /etc/cron.d/certbot 创建定时任务
cat /etc/cron.d/certbot
# 0 */12 * * * root certbot renew -q
# 手动续期测试(建议先 dry-run)
sudo certbot renew --dry-run
# 续期后重载 Nginx
# 在 /etc/letsencrypt/renewal-hooks/deploy/ 加脚本:
cat > /etc/letsencrypt/renewal-hooks/deploy/nginx.sh << 'EOF'
#!/bin/bash
systemctl reload nginx
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/nginx.sh3. 日志自动清理
bash
# 每天凌晨 4 点删除 30 天前的日志
0 4 * * * find /var/log/myapp/ -name "*.log" -mtime +30 -delete
# 每天凌晨 3 点压缩昨天的日志
0 3 * * * find /var/log/myapp/ -name "*.log" -mtime +0 -exec gzip {} \;4. 服务健康监控
bash
# 每分钟检查一次网站是否正常
*/1 * * * * /opt/scripts/health_check.shbash
#!/bin/bash
# health_check.sh
URL="https://example.com/health"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$URL")
if [ "$HTTP_CODE" != "200" ]; then
echo "[$(date)] Health check FAILED: HTTP $HTTP_CODE" >> /var/log/health.log
# 发告警
# curl -X POST -d "Health check failed: $HTTP_CODE" https://your-alert-webhook
fi常见坑
坑 1:环境变量不同
bash
# crontab 运行的环境极为精简,PATH 可能只有 /usr/bin:/bin
# 你的脚本里用到了 /usr/local/bin/xxx → 找不到命令
# 两个解决方案:
# 方案 1:在脚本里用绝对路径
/usr/local/bin/mysqldump -u root mydb > /backup/db.sql
# 而不是 mysqldump
# 方案 2:在 crontab 顶部定义 PATH
# crontab -e 的第一行:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SHELL=/bin/bash
MAILTO=ops@example.com
0 3 * * * /opt/scripts/backup.sh坑 2:% 号要转义
bash
# 错误!cron 把 % 当特殊字符
0 3 * * * echo "Backup at $(date +%Y%m%d)" >> /var/log/backup.log
# 正确:% 前面加反斜杠转义
0 3 * * * echo "Backup at $(date +\%Y\%m\%d)" >> /var/log/backup.log
# 更好的做法:把复杂逻辑放脚本里,crontab 只负责调度
0 3 * * * /opt/scripts/backup.sh坑 3:输出重定向别忘了
bash
# 错误:cron 的输出会发邮件(如果系统有 MTA),或堆积在 /var/mail/
0 3 * * * /opt/scripts/backup.sh
# 正确:重定向到日志文件
0 3 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
# 不需要任何输出时丢到黑洞
0 3 * * * /opt/scripts/backup.sh > /dev/null 2>&1坑 4:任务重叠
bash
# 如果备份任务需要 90 分钟,但 crontab 设的是每小时执行
# 就会有两个备份同时跑,可能压垮数据库
# 解决:用 flock 加文件锁,防止重复执行
*/30 * * * * flock -n /var/lock/backup.lock /opt/scripts/backup.sh
# -n: 拿不到锁直接退出(不等待)坑 5:时区问题
bash
# cron 使用的是系统时区
timedatectl # 查看当前时区
sudo timedatectl set-timezone Asia/Shanghai
# 不同服务的 crontab 可能用不同时区
# Ubuntu: 默认用系统时区
# 容器: 通常用 UTC
# 验证:创建一个临时任务看日志时间
* * * * * echo "Cron test: $(date)" >> /tmp/cron_test.log
cat /tmp/cron_test.log调试 cron 的正确姿势
- 先手动运行脚本,确认脚本本身没问题
- 设一个 1 分钟后触发的 cron 测试(如
20 * * * *提前写好) - 看日志:
grep CRON /var/log/syslog(Ubuntu)或/var/log/cron(RHEL) - 确认脚本执行过了,再调整为生产时间
生产环境经验
- 绝对路径:脚本里所有命令、文件都用绝对路径
- 加锁防重叠:
flock或文件锁(mkdir /tmp/mylock 2>/dev/null || exit) - 集中管理:系统级任务放
/etc/cron.d/,别分散在各用户 crontab 里 - 监控告警:关键定时任务如果失败要能告警(脚本内部发 webhook/邮件)
- 记录输出:每个任务都重定向日志,出了问题能回溯
- systemd timer 是更好的选择:支持随机延迟、错过补执行、日志自动集成到 journald
🎯 本章要点
- Cron五个字段:分/时/日/月/周,缺一不可
- /N 是步长语法,/5在分钟位=每5分钟执行一次
加载练习题中...