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:000 0 1 1 *
@monthly매월 1일 0:000 0 1 * *
@weekly매주 일요일 0:000 0 * * 0
@daily매일 0:000 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

우선순위:

  1. /etc/cron.allow 존재 → allow에 있는 사용자만 가능
  2. /etc/cron.allow 없음, /etc/cron.deny 존재 → deny에 없는 사용자 가능
  3. 둘 다 없음 → 모든 사용자 가능

안전한 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

특징cronsystemd 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