Cron 스케줄링
Linux에서 주기적인 작업을 자동화하는 cron, at, systemd timer 완벽 가이드
Linux에서 주기적인 작업을 자동화하는 cron, at, systemd timer 완벽 가이드
목표: 복잡한 스케줄링 요구사항을 구현하고 관리할 수 있는 수준
🕐 cron 기초
cron이란?
시간 기반 작업 스케줄러 (time-based job scheduler)
- 백그라운드에서 데몬으로 실행
- 지정된 시간에 명령이나 스크립트 자동 실행
- 시스템 유지보수, 백업, 로그 로테이션 등에 사용
cron 데몬
# 상태 확인
systemctl status cron # Debian/Ubuntu
systemctl status crond # RHEL/CentOS
# 시작/재시작
sudo systemctl restart cron
# 프로세스 확인
ps aux | grep cron
📝 crontab 파일
crontab 형식
분 시 일 월 요일 명령
│ │ │ │ │ │
│ │ │ │ │ └─ 실행할 명령
│ │ │ │ └────── 요일 (0-7, 0과 7은 일요일)
│ │ │ └───────── 월 (1-12)
│ │ └──────────── 일 (1-31)
│ └─────────────── 시 (0-23)
└────────────────── 분 (0-59)
시간 필드 값
| 필드 | 허용 값 | 특수 문자 |
|---|---|---|
| 분 | 0-59 | * , - / |
| 시 | 0-23 | * , - / |
| 일 | 1-31 | * , - / |
| 월 | 1-12 | * , - / |
| 요일 | 0-7 (0,7=일) | * , - / |
특수 문자
| 문자 | 의미 | 예시 |
|---|---|---|
* | 모든 값 | * * * * * = 매분 |
, | 목록 | 0,30 * * * * = 매시 0, 30분 |
- | 범위 | 9-17 * * * 1-5 = 평일 9시~17시 |
/ | 간격 | */5 * * * * = 5분마다 |
# | 주석 | # 이 줄은 실행되지 않음 |
자주 사용하는 예시
# 매분
* * * * * /path/to/script.sh
# 매 5분
*/5 * * * * /path/to/script.sh
# 매시 0분
0 * * * * /path/to/script.sh
# 매일 오전 3시
0 3 * * * /path/to/script.sh
# 매주 일요일 오전 2시
0 2 * * 0 /path/to/script.sh
# 매월 1일 오전 4시
0 4 1 * * /path/to/script.sh
# 평일 매시 30분
30 * * * 1-5 /path/to/script.sh
# 업무 시간 (9-18시) 매 30분
*/30 9-18 * * 1-5 /path/to/script.sh
# 10분마다, 업무 시간 동안
*/10 9-17 * * 1-5 /path/to/script.sh
# 매일 0시, 12시
0 0,12 * * * /path/to/script.sh
# 월요일부터 금요일까지 매 15분
*/15 * * * 1-5 /path/to/script.sh
# 매월 15일, 30일 오전 2시
0 2 15,30 * * /path/to/script.sh
특수 문자열 (Special Strings)
| 문자열 | 의미 | crontab equivalent |
|---|---|---|
@reboot | 부팅 시 한 번 | - |
@yearly | 매년 1월 1일 0:00 | 0 0 1 1 * |
@monthly | 매월 1일 0:00 | 0 0 1 * * |
@weekly | 매주 일요일 0:00 | 0 0 * * 0 |
@daily | 매일 0:00 | 0 0 * * * |
@hourly | 매시 0분 | 0 * * * * |
# 사용 예시
@reboot /path/to/startup.sh
@daily /path/to/daily-backup.sh
@hourly /path/to/hourly-task.sh
🛠️ crontab 관리
사용자 crontab
# 현재 사용자의 crontab 편집
crontab -e
# 현재 사용자의 crontab 목록
crontab -l
# 현재 사용자의 crontab 삭제
crontab -r
# 특정 사용자의 crontab (root만)
sudo crontab -u username -e
sudo crontab -u username -l
시스템 crontab
# 시스템 전체 crontab
sudo nano /etc/crontab
# crontab 디렉토리
ls /etc/cron.d/
# cron 시간별 디렉토리
/etc/cron.hourly/ # 매시 실행
/etc/cron.daily/ # 매일 실행
/etc/cron.weekly/ # 매주 실행
/etc/cron.monthly/ # 매월 실행
crontab 파일 형식
# 사용자 crontab (crontab -e)
# 환경변수 설정 가능
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO="admin@example.com"
HOME=/home/username
# 작업 정의
*/5 * * * * /path/to/script.sh
0 2 * * * /path/to/backup.sh > /var/log/backup.log 2>&1
# 시스템 crontab (/etc/crontab, /etc/cron.d/*)
# 사용자 필드 추가
*/5 * * * * root /path/to/script.sh
0 2 * * * backup /path/to/backup.sh
📧 출력 및 로깅
기본 동작
- cron 작업의 표준 출력/에러는 메일로 발송
- MAILTO 변수로 수신자 지정
- 로컬 메일 시스템 필요 (postfix, nullmailer 등)
로그 파일로 리다이렉션
# 표준출력과 에러를 모두 로그로
* * * * * /path/to/script.sh >> /var/log/script.log 2>&1
# 표준출력은 버리고 에러만 로그
* * * * * /path/to/script.sh > /dev/null 2>> /var/log/script-error.log
# 날짜별 로그 파일
0 0 * * * /path/to/script.sh >> /var/log/script-$(date +\%Y\%m\%d).log 2>&1
주의: crontab에서 %는 개행 문자로 해석됩니다. >> 대신 >> 사용 또는 >> 대신 >> 사용
syslog에 로깅
# logger 명령어 사용
* * * * * /path/to/script.sh 2>&1 | logger -t myscript
# 로그 확인
grep myscript /var/log/syslog
🔒 보안
cron 접근 제어
# cron.allow (존재하면 이 파일에 있는 사용자만 사용 가능)
sudo nano /etc/cron.allow
# cron.deny (cron.allow가 없으면 이 파일에 있는 사용자는 사용 불가)
sudo nano /etc/cron.deny
우선순위:
/etc/cron.allow존재 → allow에 있는 사용자만 가능/etc/cron.allow없음,/etc/cron.deny존재 → deny에 없는 사용자 가능- 둘 다 없음 → 모든 사용자 가능
안전한 cron 작성
#!/bin/bash
# safe-cron-script.sh
# 1. 오류 발생 시 즉시 종료
set -euo pipefail
# 2. 로그 함수
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a /var/log/myscript.log
}
# 3. 임시 파일 정리
trap 'rm -f "$TMPFILE"' EXIT
TMPFILE=$(mktemp)
# 4. 작업 수행
log "Starting backup..."
if /path/to/backup.sh; then
log "Backup completed successfully"
else
log "ERROR: Backup failed"
exit 1
fi
⏰ at 명령어 (일회성 예약)
at 기본 사용법
# 1회성 작업 예약
echo "/path/to/script.sh" | at 14:00
echo "/path/to/script.sh" | at now + 1 hour
echo "/path/to/script.sh" | at tomorrow
echo "/path/to/script.sh" | at midnight
echo "/path/to/script.sh" | at teatime # 오후 4시
# 대화형 입력
at 14:00
> /path/to/script.sh
> /path/to/another.sh
> Ctrl+D
# 파일에서 입력
at 14:00 < script-list.txt
at 시간 형식
at 14:00 # 오늘 14:00
at 2:00 PM # 오늘 14:00
at now + 1 hour # 1시간 후
at now + 30 minutes # 30분 후
at today + 2 hours # 2시간 후
at tomorrow # 내일 같은 시간
at tomorrow + 1 hour # 내일 1시간 후
at noon # 정오
at midnight # 자정
at teatime # 오후 4시
at 14:00 2026-06-15 # 특정 날짜
at Jun 15 14:00 # 특정 날짜
at 작업 관리
# 예약된 작업 목록
atq
at -l
# 작업 상세 내용
at -c JOB_NUMBER
# 작업 삭제
atrm JOB_NUMBER
at -d JOB_NUMBER
# at 데몬 상태
systemctl status atd
🔄 systemd Timer (현대적 대안)
cron vs systemd timer
| 특징 | cron | systemd timer |
|---|---|---|
| 정밀도 | 분 단위 | 초 단위 |
| 의존성 | 없음 | 다른 서비스와 연계 가능 |
| 로깅 | 메일 또는 파일 | journalctl 통합 |
| 실행 환경 | 최소한의 환경 | 완전한 systemd 환경 |
| 영속성 | 없음 | Persistent timer |
| Wake from suspend | 불가 | 가능 |
Timer 유닛 파일
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily
[Timer]
# 매일 2시
OnCalendar=*-*-* 02:00:00
# 부팅 후 10분 후 첫 실행, 이후 매일
OnBootSec=10min
OnUnitActiveSec=1d
# 누락된 실행 보정
Persistent=true
[Install]
WantedBy=timers.target
# /etc/systemd/system/backup.service
[Unit]
Description=Database Backup
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
User=backup
Timer 관리
# 활성화 및 시작
systemctl enable backup.timer
systemctl start backup.timer
# 상태 확인
systemctl status backup.timer
# 예약된 타이머 목록
systemctl list-timers
# 모든 타이머
systemctl list-timers --all
# 로그 확인
journalctl -u backup.service
OnCalendar 형식
# 매일 2시
OnCalendar=*-*-* 02:00:00
# 매주 월요일 3시
OnCalendar=Mon *-*-* 03:00:00
# 매월 1일 4시
OnCalendar=*-*-01 04:00:00
# 매년 1월 1일 0시
OnCalendar=*-01-01 00:00:00
# 업무 시간 매시
OnCalendar=Mon..Fri *-*-* 09..17:00:00
# 10분마다
OnCalendar=*-*-* *:00/10:00
# 복잡한 예시
OnCalendar=Mon..Fri 08..17:00/30:00 # 평일 업무 시간 30분마다
📝 실전 예시
데이터베이스 백업
# /etc/cron.d/database-backup
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO="dba@example.com"
# 매일 2시 백업
0 2 * * * postgres /opt/scripts/backup-db.sh >> /var/log/db-backup.log 2>&1
# 매주 일요일 전체 백업
0 3 * * 0 postgres /opt/scripts/backup-db.sh full >> /var/log/db-backup.log 2>&1
#!/bin/bash
# /opt/scripts/backup-db.sh
BACKUP_TYPE=${1:-incremental}
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backup/postgres"
RETENTION_DAYS=30
# 백업 디렉토리 생성
mkdir -p "$BACKUP_DIR"
# 백업 실행
if [ "$BACKUP_TYPE" == "full" ]; then
pg_dumpall > "$BACKUP_DIR/full_$DATE.sql"
else
pg_dump mydb > "$BACKUP_DIR/mydb_$DATE.sql"
fi
# 오래된 백업 삭제
find "$BACKUP_DIR" -name "*.sql" -mtime +$RETENTION_DAYS -delete
echo "Backup completed: $BACKUP_TYPE at $DATE"
로그 로테이션
# /etc/cron.d/log-cleanup
0 0 * * * root /opt/scripts/cleanup-logs.sh
#!/bin/bash
# /opt/scripts/cleanup-logs.sh
# 애플리케이션 로그 압축
find /var/log/app -name "*.log" -mtime +1 -exec gzip {} \;
# 30일 이상 된 압축 로그 삭제
find /var/log/app -name "*.log.gz" -mtime +30 -delete
# tmp 파일 정리
find /tmp -type f -atime +7 -delete
find /var/tmp -type f -atime +7 -delete
시스템 모니터링
# /etc/cron.d/system-monitor
*/5 * * * * root /opt/scripts/monitor.sh
#!/bin/bash
# /opt/scripts/monitor.sh
# 디스크 사용량 체크
DISK_USAGE=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
if [ "$DISK_USAGE" -gt 80 ]; then
echo "WARNING: Disk usage is ${DISK_USAGE}%" | \
mail -s "Disk Alert" admin@example.com
fi
# 메모리 사용량 체크
MEMORY_USAGE=$(free | awk 'NR==2{printf "%.0f", $3*100/$2}')
if [ "$MEMORY_USAGE" -gt 90 ]; then
echo "WARNING: Memory usage is ${MEMORY_USAGE}%" | \
mail -s "Memory Alert" admin@example.com
fi
# 로드 어버리지 체크
LOAD=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | tr -d ',')
if (( $(echo "$LOAD > 4.0" | bc -l) )); then
echo "WARNING: Load average is $LOAD" | \
mail -s "Load Alert" admin@example.com
fi
🐛 디버깅
cron 작업이 실행되지 않을 때
# 1. cron 데몬 상태 확인
systemctl status cron
# 2. cron 로그 확인
grep CRON /var/log/syslog
grep CRON /var/log/cron
# 3. 환경변수 확인
crontab -l | grep -E "^(SHELL|PATH|MAILTO)"
# 4. 명령어 직접 실행 테스트
/path/to/script.sh
# 5. 권한 확인
ls -la /path/to/script.sh
# 6. 쉘 지정 확인
crontab -l | grep SHELL
cron 환경 문제
# cron 환경과 사용자 환경의 차이
# cron은 최소한의 환경변수만 가짐
# 해결: crontab에 환경변수 명시
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin
HOME=/home/username
LANG=en_US.UTF-8
# 또는 스크립트 시작 부분에서 환경 설정
#!/bin/bash
source /home/username/.bashrc
source /home/username/.profile
🔗 관련 노트
- 02-02-서비스-관리-systemd — systemd timer 상세
- 01-01-리눅스-기본-명령어-모음 — 기본 명령어
- 02-04-모니터링-스택 — 로그 로테이션
- 백업-복구-전략 — 백업 전략
- 03-02-성능-모니터링 — 시스템 모니터링
마지막 업데이트: 2026-06-01