定时任务架构实战:crontab自动化运维、清理脚本与企业级任务调度

一、crontab基础

1.1 crontab语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
crontab时间格式:
分钟 小时 日期 月份 星期 命令

0-59 0-23 1-31 1-12 0-7

特殊字符:
* : 任意值
, : 列表值(例如: 1,3,5)
- : 范围值(例如: 1-5
/ : 步长值(例如: */10)
@yearly : 每年
@monthly : 每月
@weekly : 每周
@daily : 每天
@hourly : 每小时

1.2 crontab命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 编辑任务
crontab -e

# 查看任务
crontab -l

# 删除所有任务
crontab -r

# 编辑指定用户任务
crontab -u username -e

# 查看指定用户任务
crontab -u username -l

# 任务列表
cat /var/spool/cron/crontabs/*

# 查看cron日志
tail -f /var/log/cron
journalctl -u cron

二、定时清理脚本

2.1 日志清理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/bin/bash
# log_cleanup.sh - 日志清理脚本

LOG_DIRS=(
"/var/log"
"/opt/app/logs"
"/tmp"
)

DAYS_TO_KEEP=7

log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $@" >> /var/log/log_cleanup.log
}

cleanup_logs() {
for dir in "${LOG_DIRS[@]}"; do
if [ -d "$dir" ]; then
log "清理目录: $dir"

# 清理超过N天的日志
find "$dir" -type f -name "*.log" -mtime +$DAYS_TO_KEEP -delete

# 清理空日志文件
find "$dir" -type f -name "*.log" -size 0 -delete

# 压缩大日志文件
find "$dir" -type f -name "*.log" -size +100M -exec gzip {} \;

log "完成清理: $dir"
fi
done
}

main() {
log "开始日志清理"
cleanup_logs
log "日志清理完成"

# 显示清理统计
echo "日志文件数: $(find ${LOG_DIRS[@]} -name "*.log" 2>/dev/null | wc -l)"
echo "磁盘使用: $(df -h /var/log | tail -1)"
}

main "$@"

2.2 备份清理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/bin/bash
# backup_cleanup.sh - 备份清理脚本

BACKUP_ROOT="/opt/backups"
DAYS_TO_KEEP=30

cleanup_backups() {
log "清理过期备份..."

# 清理目录备份
find "$BACKUP_ROOT" -type f -name "*.tar.gz" -mtime +$DAYS_TO_KEEP -delete
find "$BACKUP_ROOT" -type f -name "*.sql.gz" -mtime +$DAYS_TO_KEEP -delete

# 清理数据库备份
find "$BACKUP_ROOT/db" -name "*.sql" -mtime +$DAYS_TO_KEEP -delete

# 只保留最近N份
find "$BACKUP_ROOT" -name "*.tar.gz" -type f | \
sort -t_ -k4 | \
head -n -30 | \
xargs -r rm

log "备份清理完成"
}

# 备份空间统计
check_disk_usage() {
local usage=$(df -h "$BACKUP_ROOT" | tail -1 | awk '{print $5}' | sed 's/%//')

if [ "$usage" -gt 80 ]; then
log "警告: 备份磁盘使用率 ${usage}%"
# 可发送告警
fi
}

main() {
cleanup_backups
check_disk_usage
}

main "$@"

2.3 缓存清理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/bin/bash
# cache_cleanup.sh - 缓存清理脚本

cleanup_system_cache() {
log "清理系统缓存..."

# 清理包管理器缓存
if command -v apt &> /dev/null; then
apt-get clean
apt-get autoclean
fi

if command -v yum &> /dev/null; then
yum clean all
fi

# 清理系统临时文件
find /tmp -type f -mtime +7 -delete
find /var/tmp -type f -mtime +7 -delete

# 清理用户缓存
find /home -name ".cache" -type d -mtime +30 -exec rm -rf {} +
}

cleanup_application_cache() {
log "清理应用缓存..."

# 清理应用缓存目录
CACHE_DIRS=(
"/opt/app/cache"
"/var/www/html/cache"
)

for dir in "${CACHE_DIRS[@]}"; do
if [ -d "$dir" ]; then
find "$dir" -type f -mtime +3 -delete
fi
done
}

main() {
cleanup_system_cache
cleanup_application_cache
log "缓存清理完成"
}

main "$@"

三、系统维护脚本

3.1 磁盘空间监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#!/bin/bash
# disk_monitor.sh - 磁盘监控脚本

THRESHOLD=85

check_disk_usage() {
df -h | grep -vE '^Filesystem|tmpfs|cdrom' | awk '{print $5 " " $1 " " $6}' | \
while read output; do
usage=$(echo $output | awk '{print $1}' | cut -d'%' -f1)
partition=$(echo $output | awk '{print $2}')
mount_point=$(echo $output | awk '{print $3}')

if [ $usage -ge $THRESHOLD ]; then
echo "磁盘空间警告: $mount_point ($partition) 使用率 ${usage}%"

# 发送告警
send_alert "磁盘空间不足" "$mount_point (${usage}%)"

# 自动清理
auto_cleanup "$mount_point"
fi
done
}

auto_cleanup() {
local mount_point=$1

case "$mount_point" in
/var/log)
find /var/log -name "*.log" -mtime +7 -delete
journalctl --vacuum-time=7d
;;
/tmp)
find /tmp -type f -mtime +7 -delete
;;
/opt/backups)
find /opt/backups -name "*.tar.gz" -mtime +30 -delete
;;
esac

log "自动清理完成: $mount_point"
}

send_alert() {
local subject=$1
local message=$2

# 发送邮件
echo "$message" | mail -s "$subject" admin@example.com

# 发送钉钉/企业微信
curl -X POST "$WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"$subject: $message\"}}"
}

main() {
check_disk_usage
}

main "$@"

3.2 进程监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/bin/bash
# process_monitor.sh - 进程监控脚本

CRITICAL_PROCESSES=(
"nginx"
"mysql"
"redis"
)

check_process() {
local process=$1

if ! pgrep -x "$process" > /dev/null; then
log "错误: $process 进程未运行"

# 尝试重启
case "$process" in
nginx)
systemctl restart nginx
;;
mysql)
systemctl restart mysql
;;
redis)
systemctl restart redis
;;
esac

# 发送告警
send_alert "进程重启" "$process 已重启"
fi
}

monitor_all() {
for process in "${CRITICAL_PROCESSES[@]}"; do
check_process "$process"
done
}

main() {
monitor_all
}

main "$@"

3.3 数据库维护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#!/bin/bash
# db_maintenance.sh - 数据库维护脚本

DB_HOST="localhost"
DB_USER="root"
DB_PASS="password"

mysql_maintenance() {
log "MySQL维护任务..."

# 清理binlog
mysql -h$DB_HOST -u$DB_USER -p$DB_PASS -e "
PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);
" 2>/dev/null

# 清理慢查询日志
mysql -h$DB_HOST -u$DB_USER -p$DB_PASS -e "
SET GLOBAL slow_query_log = 0;
TRUNCATE TABLE mysql.slow_log;
SET GLOBAL slow_query_log = 1;
" 2>/dev/null

# 优化表
mysql -h$DB_HOST -u$DB_USER -p$DB_PASS -e "
OPTIMIZE TABLE table1, table2;
" 2>/dev/null

log "MySQL维护完成"
}

postgresql_maintenance() {
log "PostgreSQL维护任务..."

# 清理WAL日志
pg_archivecleanup /var/lib/postgresql/10/main/archive/ oldest_archived_wal

# VACUUM
psql -U postgres -c "VACUUM ANALYZE;"

log "PostgreSQL维护完成"
}

main() {
mysql_maintenance
# postgresql_maintenance
}

main "$@"

四、crontab最佳实践

4.1 任务组织

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# /etc/cron.daily/ - 每日任务
# /etc/cron.weekly/ - 每周任务
# /etc/cron.monthly/ - 每月任务

# 创建自定义cron目录
mkdir -p /etc/cron.hourly
mkdir -p /etc/cron.daily/custom
mkdir -p /etc/cron.weekly/custom
mkdir -p /etc/cron.monthly/custom

# 示例任务
cat > /etc/cron.daily/log-cleanup << 'EOF'
#!/bin/bash
/usr/local/bin/log_cleanup.sh
EOF

chmod +x /etc/cron.daily/log-cleanup

4.2 完整任务配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# crontab -e
# 编辑用户的crontab

# 每分钟执行一次
* * * * * /usr/local/bin/monitor.sh

# 每小时执行一次
0 * * * * /usr/local/bin/health_check.sh

# 每天凌晨2点执行
0 2 * * * /usr/local/bin/backup.sh

# 每天凌晨3点执行清理
0 3 * * * /usr/local/bin/cleanup.sh

# 每周日凌晨1点执行
0 1 * * 0 /usr/local/bin/weekly_maintenance.sh

# 每月1号凌晨0点执行
0 0 1 * * /usr/local/bin/monthly_report.sh

# 工作日执行
0 9 * * 1-5 /usr/local/bin/workday_task.sh

# 每10分钟执行一次
*/10 * * * * /usr/local/bin/frequent_task.sh

# 指定用户执行
0 2 * * * user1 /usr/local/bin/task.sh

4.3 系统级任务配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# /etc/crontab
# 系统级定时任务

# 定期同步时间
*/5 * * * * root /usr/sbin/ntpdate -s pool.ntp.org

# 日志轮转
0 0 * * * root logrotate /etc/logrotate.conf

# 系统更新
0 3 * * 0 root apt-get update && apt-get upgrade -y

# 磁盘监控
*/30 * * * * root /usr/local/bin/disk_monitor.sh

# 服务重启
0 2 * * * root /usr/local/bin/service_restart.sh

五、高级应用

5.1 互斥任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/bin/bash
# exclusive_task.sh - 互斥任务脚本

LOCK_FILE="/tmp/task.lock"

# 检查锁文件
if [ -f "$LOCK_FILE" ]; then
local pid=$(cat "$LOCK_FILE")
if ps -p $pid > /dev/null 2>&1; then
echo "任务正在运行中,跳过本次执行"
exit 0
else
# 清理过期锁
rm -f "$LOCK_FILE"
fi
fi

# 创建锁
echo $$ > "$LOCK_FILE"

# 设置退出时清理锁
trap "rm -f $LOCK_FILE" EXIT

# 执行任务
main_task() {
echo "执行任务..."
# 任务内容
sleep 10
echo "任务完成"
}

main_task

5.2 任务结果通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/bin/bash
# task_with_notification.sh - 带通知的任务

SEND_NOTIFICATION=true

task() {
# 任务内容
echo "执行任务..."

if [ $? -eq 0 ]; then
notify_success
else
notify_failure
fi
}

notify_success() {
if [ "$SEND_NOTIFICATION" = true ]; then
echo "任务执行成功" | mail -s "任务成功" admin@example.com
fi
}

notify_failure() {
if [ "$SEND_NOTIFICATION" = true ]; then
echo "任务执行失败,请检查" | mail -s "⚠️ 任务失败" admin@example.com

# 钉钉通知
curl -X POST "$DINGTALK_WEBHOOK" \
-H 'Content-Type: application/json' \
-d '{"msgtype":"text","text":{"content":"任务执行失败"}}'
fi
}

main() {
task
}

main "$@"

5.3 任务日志管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#!/bin/bash
# task_with_logging.sh - 带日志的任务

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOG_DIR="$SCRIPT_DIR/logs"
TASK_NAME="my_task"
TIMESTAMP=$(date +%Y%m%d%H%M%S)
LOG_FILE="$LOG_DIR/${TASK_NAME}_${TIMESTAMP}.log"
MAX_LOG_SIZE=10M
MAX_LOG_COUNT=30

# 创建日志目录
mkdir -p "$LOG_DIR"

# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $@" | tee -a "$LOG_FILE"
}

log_info() {
log "[INFO] $@"
}

log_error() {
log "[ERROR] $@" >&2
}

# 日志轮转
rotate_logs() {
# 删除旧日志(保留最近N个)
find "$LOG_DIR" -name "${TASK_NAME}_*.log" -type f | sort -r | tail -n +$((MAX_LOG_COUNT + 1)) | xargs -r rm

# 压缩旧日志
find "$LOG_DIR" -name "${TASK_NAME}_*.log" -mtime +7 -exec gzip {} \;
}

# 主任务
main_task() {
log_info "任务开始"

# 任务内容
log_info "执行任务..."

# 检查结果
if [ $? -eq 0 ]; then
log_info "任务成功"
return 0
else
log_error "任务失败"
return 1
fi
}

# 执行
main() {
# 日志轮转
rotate_logs

# 执行任务
main_task

local exit_code=$?

# 发送通知
if [ $exit_code -ne 0 ]; then
log_error "任务执行失败,退出码: $exit_code"
# 发送告警通知
fi

exit $exit_code
}

main "$@"

六、企业级任务调度

6.1 任务调度系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
cron任务调度最佳实践:
1. 任务分类:
- 关键任务: 数据库备份
- 维护任务: 日志清理
- 监控任务: 健康检查

2. 时间选择:
- 避开高峰期
- 分散执行时间
- 考虑跨时区

3. 错误处理:
- 重试机制
- 告警通知
- 日志记录

4. 资源管理:
- 避免资源竞争
- 限制并发数
- 监控资源使用

6.2 任务依赖管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#!/bin/bash
# task_chain.sh - 任务链管理

TASK_STATUS_FILE="/tmp/task_status.json"

# 检查前置任务
check_dependency() {
local dependency=$1

if [ -f "$TASK_STATUS_FILE" ]; then
local status=$(jq -r ".$dependency.status" "$TASK_STATUS_FILE" 2>/dev/null)

if [ "$status" != "success" ]; then
log_error "前置任务 $dependency 未完成"
return 1
fi
fi

return 0
}

# 记录任务状态
record_status() {
local task=$1
local status=$2

# 创建或更新状态文件
if [ -f "$TASK_STATUS_FILE" ]; then
jq ".$task = {\"status\": \"$status\", \"time\": \"$(date -Iseconds)\"}" \
"$TASK_STATUS_FILE" > "$TASK_STATUS_FILE.tmp" && \
mv "$TASK_STATUS_FILE.tmp" "$TASK_STATUS_FILE"
else
echo "{\"$task\": {\"status\": \"$status\", \"time\": \"$(date -Iseconds)\"}}" > \
"$TASK_STATUS_FILE"
fi
}

# 任务1: 备份
task_backup() {
log_info "执行备份..."

if check_dependency "monitor"; then
# 执行备份
backup_database

if [ $? -eq 0 ]; then
record_status "backup" "success"
else
record_status "backup" "failed"
fi
fi
}

# 任务2: 清理
task_cleanup() {
log_info "执行清理..."

if check_dependency "backup"; then
# 执行清理
cleanup_old_files

record_status "cleanup" "success"
fi
}

main() {
task_backup
task_cleanup
}

main "$@"

七、监控和维护

7.1 任务监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/bin/bash
# monitor_cron.sh - cron任务监控

check_cron_status() {
# 检查cron服务
if ! systemctl is-active --quiet cron; then
echo "❌ Cron服务未运行"
systemctl start cron
else
echo "✓ Cron服务运行正常"
fi
}

check_recent_executions() {
# 查看最近的执行日志
echo "最近的cron执行:"
tail -20 /var/log/cron 2>/dev/null || journalctl -u cron -n 20
}

check_failed_tasks() {
# 检查失败的任务
echo "检查失败的任务..."

# 分析cron日志查找失败
grep -i "error\|fail" /var/log/cron 2>/dev/null | tail -10
}

main() {
check_cron_status
check_recent_executions
check_failed_tasks
}

main "$@"

7.2 任务审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/bin/bash
# audit_cron.sh - cron任务审计

generate_report() {
local report_file="/tmp/cron_report_$(date +%Y%m%d).txt"

cat > "$report_file" << EOF
Cron任务审计报告
生成时间: $(date)
========================================

1. 所有用户的任务:
EOF

# 列出所有用户的任务
for user in $(cut -f1 -d: /etc/passwd); do
tasks=$(crontab -u $user -l 2>/dev/null | grep -v '^#' | wc -l)
if [ $tasks -gt 0 ]; then
echo "$user: $tasks 个任务" >> "$report_file"
fi
done

echo "
2. 系统级任务:
" >> "$report_file"

ls -la /etc/cron.* >> "$report_file"

# 发送报告
mail -s "Cron任务审计报告" admin@example.com < "$report_file"
}

main() {
generate_report
}

main "$@"

八、最佳实践

8.1 cron配置规范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
cron最佳实践:
1. 任务命名:
- 使用有意义的名称
- 包含用途说明
- 时间戳记录

2. 日志管理:
- 统一日志目录
- 定期清理旧日志
- 记录执行结果

3. 错误处理:
- 所有命令添加错误处理
- 使用set -e
- 发送失败告警

4. 资源控制:
- 避免在高峰期执行
- 限制任务并发
- 监控资源使用

5. 安全考虑:
- 最小权限原则
- 敏感信息加密
- 定期审计任务

8.2 任务模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/bin/bash
# task_template.sh - cron任务模板

set -e # 遇到错误立即退出
set -u # 使用未定义变量时报错

# 配置
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOG_DIR="$SCRIPT_DIR/logs"
LOG_FILE="$LOG_DIR/task_$(date +%Y%m%d).log"

# 创建日志目录
mkdir -p "$LOG_DIR"

# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $@" | tee -a "$LOG_FILE"
}

error() {
log "ERROR: $@" >&2
}

success() {
log "SUCCESS: $@"
}

# 清理函数
cleanup() {
log "清理临时文件..."
# 清理逻辑
}

# 主任务
main_task() {
log "任务开始"

# 任务逻辑
# ...

if [ $? -eq 0 ]; then
success "任务完成"
return 0
else
error "任务失败"
return 1
fi
}

# 错误处理
trap 'error "任务失败于: $LINENO, 错误码: $?"; cleanup; exit 1' ERR
trap 'cleanup' EXIT

# 执行
main() {
main_task
}

main "$@"

九、总结

crontab是企业自动化运维的核心工具。本文涵盖:

核心要点

  1. 基础配置:crontab语法、常用命令
  2. 清理脚本:日志、备份、缓存
  3. 维护脚本:磁盘监控、进程监控、数据库维护
  4. 任务管理:互斥任务、通知、日志

技术要点

  • 定时调度:crontab配置、任务分类
  • 脚本编写:模板、错误处理、日志
  • 任务监控:执行状态、审计
  • 最佳实践:规范、安全、可维护

实践建议

  1. 任务分类:按优先级与频率分类
  2. 日志管理:统一目录与轮转
  3. 错误处理:捕获错误并告警
  4. 安全审计:定期审查任务
  5. 性能监控:避免资源竞争

通过合理的crontab配置,可提高运维效率与可靠性。