cron 시간대 사건 — 한 번 만나면 잊지 않는다
UTC/KST 혼선으로 배치 잡이 9시간 늦게 돌았던 날의 기록
사건
정산 배치가 매일 오전 9시에 돌아야 했다. 그런데 어느 날 슬랙에 알림이 없었다. 처음엔 알림 설정 문제인 줄 알았다. 로그를 열었더니 배치는 돌았다. 오후 6시에.
0 9 * * * — 작성자는 KST 9시를 적었다. 서버는 UTC였다. 결과는 KST 18시 실행. 정산 데이터가 반나절 밀렸고, 그날 오전에 수동으로 쿼리를 두 번 돌렸다. 다운타임은 없었지만 운영팀이 이미 CS를 받은 뒤였다.
당시 cron 표현식에 주석이 없었다. 파일 어디에도 “이 서버는 UTC” 라는 문장이 없었다. 그냥 적혀 있었다. 0 9 * * *. 보는 사람은 자기 머릿속 시간대로 읽는다.
왜 반복되나
이 실수가 무서운 이유는 로컬에서 테스트할 때 안 잡힌다는 거다. 내 맥북은 KST다. 개발 서버도 KST로 맞춰 놓은 팀이 많다. 스테이징은 UTC. 프로덕션은 UTC. 그 간극에서 산다.
예전에 한 번은 반대 방향으로 당했다. 야간 캐시 워밍 잡을 0 2 * * * 로 걸었다. UTC 기준 새벽 2시니까 KST 오전 11시. 트래픽 피크 한가운데서 캐시를 밀고 다시 쌓기 시작했다. DB 커넥션이 잠깐 튀었다. APM에서 이상하게 찍혔고, 한동안 이유를 못 찾았다. 발견한 건 2주 뒤였다.
팀마다 “우리 서버 UTC야” 를 알고 있다. 근데 cron 적을 때는 잊는다. 손이 먼저 나간다. 익숙한 숫자를 적는다.
그 뒤로 룰 하나를 박았다.
# [UTC] 매일 00:00 UTC = KST 09:00
0 0 * * * /opt/scripts/daily_settle.sh
주석에 UTC 명시, 그리고 KST 환산을 같이 적는다. 주석이 코드보다 길어도 상관없다. 이걸 읽는 다음 사람이 나일 수도 있고, 나보다 더 바쁜 새벽 당직자일 수도 있다.
Kubernetes CronJob이면 spec.timeZone 필드를 명시한다. 2026년 기준 stable이다. 예전엔 이게 없어서 다들 UTC로 역산해서 적었는데, 이제는 그냥:
spec:
schedule: "0 9 * * *"
timeZone: "Asia/Seoul"
이렇게 적고, 옆에 # KST 09:00 주석을 또 단다. 필드가 있어도 주석은 남긴다. 필드를 모르는 사람이 읽을 때도 있으니까.
다음 한 가지
기존 cron 파일 전수 조사 — 주석 없는 표현식에 UTC/KST 환산 한 줄씩 붙이기. 다음 글에 “진짜 했나” 한 줄 적는다.
🛒 이 글과 어울리는 추천 상품
위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.