개발 이야기3분 읽기

cron 표현식을 정확히 읽는 법

필드 수가 5개인 곳과 6개인 곳, `*/7`이 7분 간격이 아닌 이유, 일과 요일을 함께 쓰면 생기는 일까지 정리했습니다.

cron 표현식은 짧아서 읽기 쉬워 보이지만, 구현마다 규칙이 조금씩 다르고 직관과 어긋나는 부분이 몇 군데 있습니다. 잘못 적어도 오류가 나지 않고 그냥 다른 시각에 실행되기 때문에 한참 뒤에 발견됩니다.

필드 수부터 확인한다

같은 표현식이 환경에 따라 다르게 해석됩니다.

필드 수 구성 쓰는 곳
5 분 시 일 월 요일 리눅스 crontab, 대부분의 클라우드 스케줄러
6 초 분 시 일 월 요일 Quartz, Spring @Scheduled, node-cron
7 6개 + 연도 Quartz

0 0 * * *는 5필드에서 매일 자정이지만, 6필드 환경에서는 앞이 초로 읽혀 매일 0시 0분이 아니라 매시 0분 0초가 됩니다. 붙여 넣은 표현식이 예상과 다르게 도는 원인 중 가장 흔한 쪽입니다.

각 필드의 범위

  • 0-59, 시 0-23, 일 1-31, 월 1-12
  • 요일 0-70과 7이 모두 일요일입니다
  • 월과 요일은 JAN, MON 같은 약어도 받습니다

요일 번호는 헷갈리기 쉽습니다. 1이 월요일입니다.

*/n은 간격이 아니다

*/5처럼 쓰는 것은 "5분마다"로 읽히지만, 정확히는 해당 필드 범위를 0부터 n씩 건너뛴 값의 목록입니다.

분 필드에서 */5는 0, 5, 10, …, 55입니다. 60이 5로 나누어떨어지므로 결과적으로 5분 간격이 맞습니다.

*/7은 다릅니다. 0, 7, 14, 21, 28, 35, 42, 49, 56이 되고, 그다음은 다음 시간의 0분입니다. 56분과 0분 사이는 4분입니다. 매시 경계에서 목록이 다시 시작하기 때문입니다.

60의 약수(1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30)가 아니면 간격이 일정하지 않습니다. 시 필드도 마찬가지로 24의 약수가 아니면 하루 경계에서 어긋납니다.

정확한 간격이 필요하다면 값을 직접 나열하거나, cron 대신 실행이 끝난 뒤 다음 실행을 예약하는 방식을 씁니다.

일과 요일을 함께 쓰면 OR이다

가장 자주 틀리는 부분입니다.

0 0 1 * MON

"매월 1일이면서 월요일"로 읽기 쉽지만 실제로는 매월 1일 또는 매주 월요일에 실행됩니다. 일과 요일이 둘 다 *가 아니면 두 조건은 OR로 결합됩니다. POSIX에 그렇게 규정되어 있고 대부분의 구현이 따릅니다.

둘 다 만족하는 날에만 돌리려면 cron으로는 표현할 수 없습니다. 매주 월요일에 실행되게 두고, 스크립트 안에서 날짜가 1일인지 확인하는 편이 확실합니다.

타임존과 서머타임

cron은 시스템의 로컬 시각을 따릅니다. 서머타임이 있는 지역에서는 시계가 건너뛰거나 되돌아가는 날이 있습니다.

  • 시계가 앞으로 갈 때 — 존재하지 않는 시각에 걸린 작업은 건너뜁니다
  • 시계가 뒤로 갈 때 — 같은 시각이 두 번 오므로 작업이 두 번 실행될 수 있습니다

새벽 1시에서 3시 사이에 걸린 정산이나 백업 작업이 이 구간에 들어갑니다. 서버 타임존을 UTC로 두면 이 문제가 사라집니다. 한국처럼 서머타임이 없는 지역만 대상이라도, 로그와 다른 시스템을 맞추기에는 UTC 쪽이 편합니다.

관리형 스케줄러는 대개 타임존을 지정할 수 있습니다. 지정할 수 있다면 IANA 식별자(Asia/Seoul)를 씁니다.

겹쳐 실행되는 것

cron은 이전 실행이 끝났는지 보지 않습니다. 5분마다 도는 작업이 7분 걸리기 시작하면 두 개가 동시에 돌고, 그 상태가 이어지면 계속 쌓입니다.

  • 작업 안에서 잠금(파일 락, DB 락)을 잡습니다
  • flock 같은 명령으로 감쌉니다
  • 실행 시간을 기록해 두고 평소 소요와 주기를 비교합니다

별칭

읽기 쉬운 별칭이 있습니다. @yearly, @monthly, @weekly, @daily, @hourly, 그리고 부팅 시 한 번 도는 @reboot입니다. 대부분 자정 정각에 걸리므로, 여러 작업에 @daily를 쓰면 같은 시각에 몰립니다. 분산이 필요하면 분을 다르게 지정합니다.

tools.onuel.devcron 파서에 표현식을 넣으면 각 필드가 어떻게 해석되는지와 다음 실행 시각을 확인할 수 있습니다. 위의 */7이나 일·요일 조합도 실행 목록으로 보면 바로 드러납니다.

  • #cron
  • #스케줄링
  • #서버