3개월 전에 설정해둔 DB 백업 cron이 어느 시점부터 조용히 실패하고 있었습니다. 장애가 나서 복구하려는데 백업 파일이 없습니다. 로그를 보니 cron은 실행을 시도했지만 스크립트가 에러를 냈고, 아무도 몰랐습니다. 자동화는 실행만 되면 끝이 아닙니다 — 실패했을 때 알 수 있어야 하고, 로그가 남아야 하고, 다음에 재시도할 수 있어야 진짜 자동화입니다.
Cron과 systemd timer 자동화
백업 스크립트를 매일 돌리고, 인증서를 자동 갱신하고, 오래된 로그를 정리하는 것 — 전부 사람이 기억해서 하면 언젠가 빠뜨립니다. 자동화의 첫걸음은 작업 스케줄러를 제대로 쓰는 것입니다.
crontab은 간단하고 빠릅니다. systemd timer는 로그와 실패 감지가 필요할 때 씁니다. 둘 다 알아두면 상황에 맞게 선택할 수 있습니다.
- 1crontab 문법과 자주 쓰는 패턴
- 2cron 환경 트러블슈팅 — PATH, 환경변수, 로그 확인
- 3systemd timer — .timer + .service 유닛 구성
- 4OnCalendar, OnBootSec, Persistent 옵션
- 5실무 자동화 예제 — 백업, 인증서 갱신, 정리 작업
crontab 문법 — 핵심 패턴
분 시 일 월 요일 명령
* * * * * /path/to/command
특수 문자:
* — 모든 값
, — 여러 값 (1,3,5)
- — 범위 (1-5)
/ — 간격 (*/10 = 10마다)
# crontab 편집
crontab -e
# 자주 쓰는 패턴
0 2 * * * # 매일 새벽 2시
0 2 * * 0 # 매주 일요일 새벽 2시
0 2 1 * * # 매월 1일 새벽 2시
*/10 * * * * # 10분마다
0 9-18 * * 1-5 # 월~금, 9~18시 매 정시
0 2 * * * /usr/local/bin/daily-backup.sh >> /var/log/backup.log 2>&1
# 특수 문자열 (단축어)
@reboot # 부팅 시 1회
@daily # = 0 0 * * *
@weekly # = 0 0 * * 0
@monthly # = 0 0 1 * *
@reboot /usr/local/bin/startup-init.sh
# cron 실행 로그 확인
grep CRON /var/log/syslog | tail -20
# 또는
journalctl -u cron --since today
systemd timer — .timer + .service 구성
systemd timer는 두 파일로 구성됩니다:
.service— 실제 실행할 명령.timer— 스케줄 정의
# /etc/systemd/system/daily-backup.service
[Unit]
Description=Daily Backup Job
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/daily-backup.sh
User=root
StandardOutput=journal
StandardError=journal
# /etc/systemd/system/daily-backup.timer
[Unit]
Description=Run daily-backup.service every day at 02:00
[Timer]
OnCalendar=*-*-* 02:00:00 # 매일 02:00
Persistent=true # 실행 누락 시 부팅 후 즉시 실행
AccuracySec=1min # 1분 오차 허용 (기본 1분)
[Install]
WantedBy=timers.target
# 활성화 및 시작
sudo systemctl daemon-reload
sudo systemctl enable --now daily-backup.timer
# 상태 확인
systemctl status daily-backup.timer
systemctl list-timers # 전체 timer 목록과 다음 실행 시각
# 로그 확인
journalctl -u daily-backup.service # 실행 이력
journalctl -u daily-backup.service -n 20 --no-pager
OnCalendar 시간 표현식
# 자주 쓰는 OnCalendar 표현
OnCalendar=daily # 매일 00:00:00
OnCalendar=weekly # 매주 월요일 00:00:00
OnCalendar=monthly # 매월 1일 00:00:00
OnCalendar=hourly # 매 정시
OnCalendar=*-*-* 02:00:00 # 매일 02:00
OnCalendar=Mon *-*-* 03:00:00 # 매주 월요일 03:00
OnCalendar=*-*-01 00:00:00 # 매월 1일 자정
OnCalendar=Sat,Sun 10:00:00 # 주말 10시
# 표현식 검증 (실행 전 확인 필수)
systemd-analyze calendar '*-*-* 02:00:00'
# Original form: *-*-* 02:00:00
# Normalized form: *-*-* 02:00:00
# Next elapse: Wed 2026-04-15 02:00:00 KST
# (in UTC): Tue 2026-04-14 17:00:00 UTC
# From now: 11h left
# 특수 옵션
OnBootSec=5min # 부팅 후 5분 뒤 1회 실행
OnUnitActiveSec=1h # 마지막 실행 후 1시간마다
Persistent=true # 시스템이 꺼져있어 놓친 실행을 부팅 후 즉시 실행
# 수동 실행: OK
$ /usr/local/bin/daily-backup.sh
백업 완료
# cron 실행 후 로그 확인
$ grep CRON /var/log/syslog
Apr 14 02:00:01 server CRON[12345]: (root) CMD (/usr/local/bin/daily-backup.sh)
# 하지만 백업 파일이 없음...
원인 1: PATH 차이
# 스크립트 상단에 PATH 명시
#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# 또는 crontab 상단에 추가
$ crontab -e
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 2 * * * /usr/local/bin/daily-backup.sh
원인 2: 환경변수 누락 (AWS CLI, DB 패스워드 등)
# 스크립트에서 직접 환경변수 파일 로드
#!/bin/bash
source /etc/environment
source /home/ubuntu/.env.backup
# 이제 $AWS_ACCESS_KEY_ID 등 사용 가능
원인 3: 출력 리다이렉션 없어서 에러를 못 봄
# 표준출력 + 에러 모두 로그로
0 2 * * * /usr/local/bin/daily-backup.sh >> /var/log/backup.log 2>&1
실무 자동화 패턴
패턴 1: Let's Encrypt 자동 갱신 (certbot timer 확인)
# Ubuntu에서 certbot 설치 시 자동 생성됨
systemctl status certbot.timer
systemctl list-timers | grep certbot
# 없으면 직접 생성
# /etc/systemd/system/certbot-renew.service
[Unit]
Description=Certbot Renewal
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"
# /etc/systemd/system/certbot-renew.timer
[Unit]
Description=Certbot Renewal Timer
[Timer]
OnCalendar=*-*-* 03:00:00
OnCalendar=*-*-* 15:00:00 # 하루 2회 (Let's Encrypt 권장)
Persistent=true
RandomizedDelaySec=1h # 서버 몰림 방지
[Install]
WantedBy=timers.target
패턴 2: 오래된 파일 정리 (임시파일, 로그)
# /etc/systemd/system/cleanup-tmp.service
[Service]
Type=oneshot
ExecStart=/bin/bash -c 'find /tmp -mtime +7 -delete && find /var/log/app -name "*.log.*" -mtime +30 -delete'
패턴 3: DB 덤프 자동화
# /etc/systemd/system/mysql-backup.service
[Service]
Type=oneshot
User=backup
EnvironmentFile=/etc/backup.env # MYSQL_PASS 등 환경변수
ExecStart=/usr/local/bin/mysql-backup.sh
# timer: 매일 새벽 1시
OnCalendar=*-*-* 01:00:00
언제 crontab, 언제 systemd timer?
| 상황 | 추천 |
|---|---|
| 간단한 1줄 명령, 빠르게 추가 | crontab |
| 실행 실패 감지가 중요 | systemd timer |
| journalctl로 이력 조회 필요 | systemd timer |
| 부팅 후 누락 실행 보장 필요 | systemd timer (Persistent=true) |
| 기존 systemd 서비스와 연동 | systemd timer |