새 서버 셋업하다가 "매일 새벽 3시에 백업 스크립트 한 번 돌려놔 주세요" 같은 요청을 받으면, 대부분 반사적으로 crontab -e를 칩니다. 빠르고 익숙하니까요. 그런데 막상 돌려보면 stdout 로그가 어디로 갔는지도 모르겠고, 실패하면 알림도 못 받고, 환경 변수가 안 먹어서 또 한참 헤맵니다. 요즘 대부분의 리눅스 배포판에는 systemd timer라는 대안이 같이 들어 있어요. 오늘은 두 방식의 차이를 정리하고, 같은 작업을 양쪽으로 어떻게 짜는지, 그리고 언제 어느 쪽을 쓰는 게 합리적인지 풀어 볼게요.
개념 / 원리
cron은 유닉스 초창기부터 있던 전통적인 스케줄러입니다. cron 데몬이 1분마다 깨어나서 /etc/crontab, /etc/cron.d/*, 각 유저의 crontab -e 항목을 읽고 시간에 맞는 명령을 실행합니다. 구조가 단순하고 문법(분 시 일 월 요일 명령)도 익숙해요.
systemd timer는 systemd 생태계의 스케줄러입니다. .timer 파일과 .service 파일이 한 쌍으로 동작해요. .timer가 "언제" 트리거할지 정의하고, 그 시점에 .service를 띄웁니다.
| 항목 | cron | systemd timer |
|---|---|---|
| 문법 | 0 3 * * * |
OnCalendar=*-*-* 03:00:00 |
| 로그 | /var/log/cron, 메일 |
journalctl -u <name> |
| 환경 변수 | 거의 비어 있음 | .service에 명시적으로 지정 |
| 종속성·의존성 | 없음 | After=, Requires=로 표현 |
| 실패 시 재시도 | 없음 (직접 wrapper 필요) | Restart=로 처리 |
| 누락된 실행 따라잡기 | 안 됨 | Persistent=true 지원 |
| 도입 난이도 | 매우 낮음 (한 줄) | 두 파일 작성 필요 |
cron은 단순한 작업에 무난하고, systemd timer는 운영 안정성을 챙기고 싶을 때 강합니다.
실전 예제
매일 새벽 3시에 /opt/scripts/backup.sh를 돌리는 같은 작업을 양쪽으로 짜 보겠습니다.
cron 버전:
# crontab -e (현재 유저)
# 또는 /etc/cron.d/backup 파일 생성 (시스템 전체)
# m h dom mon dow command
0 3 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
저장하고 끝. 1분 안에 cron이 새 항목을 읽어 들입니다. 위에서 >> /var/log/backup.log 2>&1로 표준출력·에러를 직접 파일로 리다이렉트했는데, 안 하면 cron이 메일로 출력을 보내려고 시도하다가 실패하고 로그도 안 남습니다.
systemd timer 버전:
먼저 실제 작업을 정의하는 .service 파일.
# /etc/systemd/system/backup.service
[Unit]
Description=Daily backup
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
User=backupuser
그리고 트리거 시점을 정의하는 .timer 파일.
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily at 03:00
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
활성화는 timer 쪽을 enable·start 합니다(service가 아니라).
sudo systemctl daemon-reload
sudo systemctl enable backup.timer
sudo systemctl start backup.timer
# 등록된 타이머 목록과 다음 실행 시각
systemctl list-timers --all
# 로그
journalctl -u backup.service -f
Persistent=true 한 줄이 중요합니다. 서버가 새벽 3시에 꺼져 있다가 5시에 켜졌다면, cron은 그냥 그 회차를 건너뛰지만 systemd timer는 "놓친 실행"을 부팅 직후에 한 번 따라 실행합니다.
OnCalendar 문법은 좀 더 풍부합니다. 몇 가지 예를 들면:
OnCalendar=hourly # 매시간 정각
OnCalendar=daily # 매일 자정
OnCalendar=Mon,Wed 09:00 # 월·수 9시
OnCalendar=*-*-* 03:00:00 # 매일 새벽 3시 (cron의 0 3 * * *)
OnCalendar=*-*-01 04:00:00 # 매월 1일 4시
주의할 점
1) cron의 환경 변수는 정말 빈약합니다. PATH도 /usr/bin:/bin 정도만 잡혀 있어서 python3 같은 명령이 못 찾고 실패할 수 있어요. 스크립트 안에서 /usr/local/bin/python3처럼 절대경로를 쓰거나, crontab 상단에 PATH=...를 명시합니다. systemd timer는 .service의 Environment=로 깔끔하게 잡을 수 있습니다.
2) cron 로그는 한 군데에 모이지 않습니다. stdout/stderr를 직접 파일로 리다이렉트하지 않으면 일부 배포판에서는 메일로 가거나, 그냥 사라집니다. systemd timer는 모든 출력이 journalctl로 모여서 journalctl -u backup.service로 한 번에 봅니다.
3) 실패 시 알림은 cron이 약합니다. cron은 실패 자체를 모릅니다. 종료 코드가 0이 아니어도 그냥 끝나요. systemd timer는 OnFailure=로 실패 시 다른 서비스를 띄울 수 있어서, 알림 서비스를 한 번 연결해 두면 운영이 훨씬 편합니다.
4) cron에 % 쓸 때 이스케이프 필요. crontab 안에서 %는 줄바꿈으로 해석돼서 그 뒤가 표준 입력으로 전달됩니다. date +%Y-%m-%d 같은 명령을 그냥 박으면 Y-%m-%d 부분이 입력으로 들어가서 이상하게 동작합니다. \% 로 이스케이프해야 해요. 작은 함정이지만 자주 만납니다.
5) systemd timer는 단순 작업에는 과합니다. 두 파일 만들어야 하고, daemon-reload 하고, enable 하고… 매일 1분짜리 cron job 추가에 굳이 이렇게 갈 필요는 없어요. 운영 중요도가 높아진다 싶을 때(실패 알림 필요, 로그 추적 필요, 종속성 있음) 전환을 고려하면 됩니다.
6) 둘 다 정확히 같은 시각에 안 도는 경우가 있습니다. cron은 분 단위, systemd timer는 기본 정확도가 1분 정도예요. 더 정밀하게 보장하고 싶으면 AccuracySec=1s 같이 조정할 수 있지만, 분 단위 작업에는 그럴 일이 거의 없습니다.
마무리
정리하면 세 줄입니다.
- 빠르게 한 줄로 등록할 거면 cron, 운영 안정성(로그·알림·종속성)이 중요하면 systemd timer.
- systemd timer의 결정적 장점은
journalctl통합 로그와Persistent=true로 놓친 실행 따라잡기. - cron 쓸 거면
PATH명시 + stdout/stderr 리다이렉트 + 종료 코드 모니터링은 직접 챙기자.
'IT·리눅스 > 리눅스' 카테고리의 다른 글
| SSH 키 인증과 ssh-agent - 비밀번호 없이 안전하게 접속하기 (1) | 2026.06.10 |
|---|---|
| top, htop, ps - 리눅스 모니터링 세 가지 도구 제대로 쓰기 (0) | 2026.06.08 |
| 파일 디스크립터와 ulimit - \"Too many open files\" 정체 (0) | 2026.05.29 |
| systemd 서비스 만들기 - .service 파일부터 enable까지 (0) | 2026.05.28 |
| [Linux] 시그널 - SIGTERM, SIGKILL, SIGHUP 제대로 구분하기 (0) | 2026.05.13 |