Crontab从入门到自动化运维

zheng头像
zheng 2026-07-16 11:28
楼主

Crontab 从入门到自动化运维:让服务器自己打理自己

每天凌晨备份数据库、每周清理日志、每小时检查服务状态——这些重复性操作你还在手动做?Crontab 就是 Linux 上的定时任务调度器,学会它,80% 的运维工作自动完成。这篇文章从语法基础讲到生产级运维脚本,覆盖 12 个实用场景。


Crontab 语法 30 秒速通

*    *    *    *    *    要执行的命令
分   时   日   月   周
0-59 0-23 1-31 1-12 0-7
表达式 含义
0 3 * * * 每天凌晨 3 点
*/5 * * * * 每 5 分钟
0 0 * * 1 每周一凌晨 0 点
0 0 1 * * 每月 1 号凌晨 0 点
0 9-18 * * * 每天 9 点到 18 点,每小时整点
30 2 1,15 * * 每月 1 号和 15 号凌晨 2:30

操作命令:

crontab -e      # 编辑当前用户的定时任务
crontab -l      # 列出当前用户的定时任务
crontab -r      # 删除当前用户的定时任务(危险!)


场景一:数据库自动备份 + 过期清理

最常见需求。

# 每天凌晨 2 点备份 MySQL,保留最近 7 天
0 2 * * * /opt/scripts/backup-mysql.sh >> /var/log/backup.log 2>&1

backup-mysql.sh

#!/bin/bash
BACKUP_DIR="/backup/mysql"
DB_USER="root"
DB_PASS="your_password"
RETENTION_DAYS=7

mkdir -p "$BACKUP_DIR"

# 备份所有数据库
mysqldump -u$DB_USER -p$DB_PASS --all-databases --single-transaction \
    | gzip > "$BACKUP_DIR/full_$(date +%Y%m%d).sql.gz"

# 删除 7 天前的备份
find "$BACKUP_DIR" -name "full_*.sql.gz" -mtime +$RETENTION_DAYS -delete

echo "$(date): 备份完成,已清理 ${RETENTION_DAYS} 天前的旧备份。"

备份到本地不够,再加一条同步到对象存储:

# 凌晨 3 点同步到阿里云 OSS
0 3 * * * /opt/scripts/sync-to-oss.sh


场景二:SSL 证书到期前 30 天提醒

Let's Encrypt 证书 90 天过期,acme.sh 会自动续期。但万一自动续期失败呢?

# 每天上午 10 点检查证书到期时间
0 10 * * * /opt/scripts/check-ssl.sh
#!/bin/bash
DOMAIN="yourdomain.com"
DAYS=$(echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN:443" 2>/dev/null \
    | openssl x509 -noout -dates | grep notAfter | cut -d= -f2)
EXPIRY=$(date -d "$DAYS" +%s)
NOW=$(date +%s)
DIFF=$(( ($EXPIRY - $NOW) / 86400 ))

if [ $DIFF -lt 30 ]; then
    # 发通知(替换成你的通知方式)
    curl -s "https://your-webhook-url" -d "SSL 证书将在 ${DIFF} 天后过期!"
fi


场景三:服务健康检查 + 自动重启

# 每 5 分钟检查关键服务
*/5 * * * * /opt/scripts/health-check.sh
#!/bin/bash
SERVICES=("nginx" "mysql" "docker" "sshd")

for svc in "${SERVICES[@]}"; do
    if ! systemctl is-active --quiet "$svc"; then
        echo "$(date): $svc 已停止,尝试重启..."
        systemctl restart "$svc"

        # 重启后再次检查
        sleep 5
        if systemctl is-active --quiet "$svc"; then
            echo "$(date): $svc 重启成功。"
        else
            echo "$(date): $svc 重启失败,需要人工介入!" | tee -a /var/log/health-critical.log
        fi
    fi
done


场景四:日志自动清理

日志不清理,3 个月后 /var 撑爆。

# 每天凌晨 4 点清理日志
0 4 * * * find /var/log -name "*.log" -mtime +30 -delete
0 4 * * * find /var/log -name "*.gz" -mtime +7 -delete
0 4 * * * journalctl --vacuum-time=7d

Docker 日志清理(更容易爆):

# 每周日凌晨清理 Docker 日志
0 5 * * 0 docker system prune -af --filter "until=168h"


场景五:磁盘空间告警

# 每小时检查磁盘使用率
0 * * * * /opt/scripts/disk-alert.sh
#!/bin/bash
THRESHOLD=85
USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')

if [ "$USAGE" -gt "$THRESHOLD" ]; then
    echo "警告:磁盘使用率 ${USAGE}%,已超过 ${THRESHOLD}% 阈值" \
        | mail -s "磁盘告警: $(hostname)" [email protected]
fi


场景六:每周更新系统包

保持系统安全但不想每次手动 apt update

# 每周日凌晨 3 点更新
0 3 * * 0 apt update && apt upgrade -y && apt autoremove -y

保险起见,可以加一个重启前自检:

0 3 * * 0 apt update && apt upgrade -y && apt autoremove -y && \
    [ -f /var/run/reboot-required ] && shutdown -r +5 "系统更新需要重启"

/var/run/reboot-required 文件存在表示更新了内核之类需要重启的包,这时才重启。


场景七:Git 仓库自动同步

如果你的博客是静态站点(Hexo/Hugo)或用 Git 管理的配置:

# 每 30 分钟自动提交配置变更
*/30 * * * * cd /opt/nginx-config && \
    git add -A && git commit -m "auto backup $(date)" && git push


场景八:定时从网站抓数据

# 每小时抓一次汇率
0 * * * * curl -s "https://api.exchangerate-api.com/v4/latest/USD" \
    -o /data/exchange_rates/$(date +%Y%m%d_%H).json

配合数据分析脚本,自动生成报表。


场景九:DDNS 动态更新

家用宽带 IP 会变,用 crontab 定时更新 DNS:

# 每 10 分钟检查 IP,变了就更新 Cloudflare DNS
*/10 * * * * /opt/scripts/ddns-update.sh
#!/bin/bash
CURRENT_IP=$(curl -s ifconfig.me)
CACHED_IP=$(cat /tmp/last_ip.txt 2>/dev/null)

if [ "$CURRENT_IP" != "$CACHED_IP" ]; then
    curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records/RECORD_ID" \
        -H "Authorization: Bearer $CF_TOKEN" \
        -H "Content-Type: application/json" \
        --data "{\"type\":\"A\",\"name\":\"home.yourdomain.com\",\"content\":\"$CURRENT_IP\",\"ttl\":120}"
    echo "$CURRENT_IP" > /tmp/last_ip.txt
    echo "$(date): DDNS 更新为 $CURRENT_IP"
fi


场景十:VPS 流量统计

阿里云轻量服务器有流量限制,超了限速。

# 每天统计 vnstat 流量并记录
0 0 * * * vnstat -i eth0 --json m >> /var/log/traffic.json

月底汇总看有没有超限风险。


场景十一:浏览器 Cookie/Token 自动刷新

有些 API 的 token 需要定期刷新:

# 每 23 小时刷新一次 GitHub Token(GitHub token 24小时过期)
0 */23 * * * /opt/scripts/refresh-token.sh


场景十二:定时发日报到微信群

# 每天下午 6 点汇总服务器状态发消息
0 18 * * * /opt/scripts/daily-report.sh
#!/bin/bash
MSG="【服务器日报】\n"
MSG+="时间: $(date)\n"
MSG+="磁盘: $(df -h / | awk 'NR==2{print $5}')\n"
MSG+="内存: $(free -h | awk 'NR==2{printf "%.0f%%", $3/$2*100}')\n"
MSG+="负载: $(uptime | awk -F'load average:' '{print $2}')\n"

# 发到企业微信机器人
curl -s "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"$MSG\"}}"


Crontab 进阶技巧

日志重定向

# 标准输出和标准错误都追加到日志
0 3 * * * /opt/backup.sh >> /var/log/backup.log 2>&1

# 不想保留任何输出
0 3 * * * /opt/backup.sh > /dev/null 2>&1

环境变量问题

Crontab 的执行环境和交互式 shell 不同,PATH 很有限。脚本开头一定要写完整路径:

#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

或者在 crontab 顶部定义:

PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
0 3 * * * /opt/backup.sh

锁机制:防止任务重叠

备份跑了 2 小时还没结束,下一次又来了。用 flock 或者自己写锁:

#!/bin/bash
LOCKFILE="/tmp/backup.lock"

if [ -f "$LOCKFILE" ]; then
    echo "上次备份还在运行,跳过本次。"
    exit 1
fi

touch "$LOCKFILE"
trap "rm -f $LOCKFILE" EXIT

# 真正的备份逻辑

trap EXIT 保证脚本无论正常结束还是异常退出,锁都会被清理。


常见问题

Q:crontab -e 和 /etc/crontab 有什么区别?

crontab -e 是当前用户的定时任务,/etc/crontab 是系统级。绝大多数场景用 crontab -e 就够了。需要指定执行用户时才用 /etc/crontab(格式多一个用户名字段)。

Q:怎么调试 crontab 任务?

  1. 先把任务改成 * * * * *(每分钟执行),确认能跑
  2. /var/log/syslog 里 crontab 的执行日志
  3. 在脚本里加详细 echo,重定向到文件

Q:时区问题?

Crontab 使用系统时区。确认时区:

timedatectl | grep "Time zone"

如果服务器在海外但你想按北京时间调度,先改时区:

timedatectl set-timezone Asia/Shanghai


12 个场景,12 个 Shell 脚本。全部扔进 crontab,你的服务器就从一个等待你下命令的工具,变成了一个自己管理自己的自治系统。

Docker Compose 搭建服务全家桶:一个文件搞定 NAS 常用服务 rsync 文件同步与增量备份教程:比 scp 快 10 倍

最新回复

暂无回复,来说两句吧

发表评论

站点总访问量:2823