cron 표현식 해설기

모든 cron 표현식을 해설 — Vixie, Quartz, EventBridge, Jenkins. 원하는 시간대의 다음 실행을 나열하고 함정을 짚어 줍니다.

입력

Vixie cron 방언으로 해석했습니다.

이 표현식의 의미

00:00에 실행합니다. 실행일은 13일 또는 금요일입니다.

필드별 분석

필드표기일치하는 값
00
00
1313
*모든 값
요일5금요일

주의할 점

  • 날짜와 요일 필드가 둘 다 제한되어 있으므로 cron은 어느 한쪽이 맞는 시점에 실행합니다. 둘이 동시에 성립할 필요는 없습니다. 13과 금요일의 조합이 "13일의 금요일"이 아니라 "매달 13일과 매주 금요일"을 뜻하는 이유가 이것입니다.

다음 실행

    이 도구가 하는 일

    cron 표현식을 말로 되읽어 주고, 그 작업이 실제로 쓰는 어느 시간대에서든 다음에 발화할 시각을 나열하며, 표현식이 시사하지만 말하지 않는 것을 이름 붙입니다. 그 마지막 부분이 요점입니다: cron 표현식은 결코 오류를 내는 방식으로 틀리지 않습니다. 누군가 눈치챌 때까지 조용히 잘못된 날에 돕니다.

    하나가 아니라 네 방언이 이해됩니다. crontab이 받는 다섯 필드 형식, 초 필드와 달력 규칙을 가진 Quartz, 끝에 연도가 붙은 AWS EventBridge, 그리고 해시를 가진 Jenkins입니다. 하나를 다른 것만 아는 도구에 붙여넣는 것이 대부분의 사람이 하나보다 많다는 것을 처음 알게 되는 방법입니다.

    필드, 그리고 그것이 몇 개인가

    고전적 표현식에는 공백으로 나뉜 다섯 필드가 고정된 순서로 있습니다: 분, 시, 일, 월, 요일. 각각은 "매번"을 뜻하는 별표, 숫자, 쉼표로 나뉜 목록, 붙임표로 된 범위, 또는 슬래시로 된 스텝 중 하나입니다. 월과 일은 세 글자 이름으로 쓸 수 있고, 그것이 거의 늘 숫자보다 분명합니다.

    • 별표는 필드가 취할 수 있는 모든 값을 뜻합니다.
    • 9-17 같은 범위는 9부터 17까지 양끝을 포함한 모든 값을 뜻합니다.
    • */15 같은 스텝은 필드의 아래 끝에서 시작해 15번째마다의 값을 뜻합니다. 9-17/2는 범위 안에서 스텝하고, 5/15는 5에서 위 끝까지 스텝합니다.
    • 0,30 같은 목록은 바로 그 값들을 뜻합니다.
    • 13-13 같은 퇴화한 범위는 그저 13입니다 — 일부 라이브러리가 이것을 틀려 별표로 다룹니다.

    여섯 번째 필드가 방언이 갈리는 곳입니다. Quartz는 그것을 앞에 두고 초로 읽고, EventBridge는 끝에 두고 연도로 읽습니다. 그래서 여섯 개의 맨 필드는 정말로 모호하고, 이 도구는 조용히 고르는 대신 어느 쪽으로 읽었는지 말합니다. 표현식을 cron(...) — EventBridge 자체 구문 — 으로 감싸면 그것이 결판납니다.

    매크로는 약기입니다: @hourly, @daily, @midnight, @weekly, @monthly, @yearly, @annually. 보통 표현식으로 펼쳐지고, 도구가 그것을 보입니다. @reboot은 예외이고 아예 일정이 아닙니다.

    두 날짜 필드, 그리고 "그리고"처럼 보이는 "또는"

    날짜는 두 다른 방식으로 고를 수 있고 — 일로, 요일로 — cron은 두 필드를 다 가집니다. 둘 다 제한되면 작업은 어느 한쪽이 맞을 때 돕니다. 둘 다가 아니라.

    그래서 0 0 13 * 5는 13일의 금요일이 아닙니다. 매달 13일이고 매주 금요일입니다: 하나나 둘이 아니라 연 약 64일입니다. 표현식은 논리곱처럼 보이고 논리합처럼 동작하며, 아무것도 오류를 보고하지 않고, 작업이 그저 뜻한 것보다 자주 돕니다.

    규칙에는 더 이상한 후반이 있고, 그것이 거의 아무도 모르는 것입니다. cron은 날짜 필드가 제한됐는지 시험하지 않습니다. 필드의 첫 글자가 별표인지 시험합니다. 그래서 */14 같은 스텝은 필드를 1일, 15일, 29일로 제한하는데도 별표로 셉니다 — 그리고 그 존재가 두 날짜 필드를 "또는"에서 "그리고"로 뒤집습니다. 똑같이 제한되어 보이는 두 표현식이 그러면 완전히 다르게 동작하고, 이 도구는 어느 규칙이 효력인지 보고합니다.

    Quartz와 EventBridge는 두 필드가 무언가를 말하기를 거부해 물음 전체를 피합니다: 정확히 하나가 물음표여야 하고, "특정 값 없음, 다른 필드가 정한다"를 뜻합니다.

    자기 필드를 나누어떨어뜨리지 못하는 스텝

    스텝은 간격인 것처럼 쓰이고, 필드의 한 주기 안에서는 그렇습니다. 경계를 넘으면 대개 그렇지 않습니다.

    시 필드의 */7은 0, 7, 14, 21시를 뜻합니다. 21 뒤 필드가 소진되어, 다음 실행은 이튿날 0시 — 일곱 시간 뒤가 아니라 세 시간 뒤 — 입니다. 모든 주기가 한 짧은 간격으로 끝나고, 분의 */7(56 뒤 4분 간격)과 자기 범위를 고르게 나누지 못하는 다른 어떤 스텝에서도 같은 일이 일어납니다. 분의 */15와 시의 */6은 안전하고, 사람이 손을 뻗는 수의 대부분은 아닙니다.

    이것은 스텝이 작업을 간격 두려고 골라졌을 때 중요합니다. */7 시의 작업은 일곱 시간마다 돌지 않고, 일곱에 맞춘 속도 제한은 하루에 한 번 어겨집니다.

    요일은 곳마다 다르게 번호 매겨진다

    Vixie cron은 일요일을 양끝에 두고 날을 0부터 7까지 번호 매기므로 0과 7이 같은 날이고 1이 월요일입니다. Quartz와 EventBridge는 일요일을 1에 두고 1부터 7까지 번호 매기므로 거기서 1이 일요일이고 2가 월요일입니다.

    그래서 Quartz 설정에서 crontab으로 복사한 표현식은 하루 일찍 돌고, 어디서도 아무것도 불평하지 않습니다. 두 철자 모두 두 시스템에서 유효한 숫자이기 때문입니다. 세 글자 이름 — MON, FRI — 은 어디서나 같은 날을 뜻하고 간단한 방어입니다.

    서머타임: 해마다 두 아침

    cron 데몬은 순간을 예약하지 않습니다. 매분 로컬 벽시계를 보고 표현식이 맞는지 묻습니다. 해마다 두 번 그 시계는 얌전한 나열이 아닙니다.

    앞으로 뛸 때 한 시간 치 읽기가 결코 일어나지 않습니다. 02:30으로 맞춘 작업은 그날 02:30이 없습니다. 되돌아갈 때 한 시간 치 읽기가 두 번 일어나고, 01:30으로 맞춘 작업은 그것을 둘 가집니다.

    Vixie cron은 여기서 두 종류의 작업을 가르고, 그 구별은 당신의 어느 작업이 영향받는지를 정하기에 알아 둘 만합니다. 벽시계 시각에 고정된 작업 — 고정된 시와 고정된 분 — 은 어느 쪽이든 정확히 한 번 돕니다: 앞으로 뛴 뒤 곧바로 돌고, 되돌아간 뒤 cron이 반복하지 않도록 살핍니다. 시나 분에 별표가 있는 작업은 간격 작업이고 그저 시계를 따릅니다: 봄에 한 시간 치 실행을 잃고 가을에 한 시간 치를 반복합니다.

    그래서 매시 백업은 정말로 해마다 하루 밤에 두 번 돌고, 02:30의 밤마다 작업은 정말로 다른 날에 이례적인 순간에 돕니다. 이 도구는 고른 지역의 다음 열두 달을 당신의 표현식에 대해 확인하고 둘 중 어느 것이 적용되는지 날짜와 함께 말합니다.

    믿을 만한 탈출구는 작업을 UTC로, 또는 전환이 떨어지지 않는 로컬 01:00–03:00 밖으로 예약하는 것입니다.

    작업이 실제로 어느 시계에 있는가

    crontab은 머신의 로컬 지역, 또는 파일 맨 위의 CRON_TZ나 TZ가 말하는 것에서 돕니다. 그것은 표현식을 읽는 사람의 지역인 경우가 드물고, 그래서 여기 시간대가 브라우저의 것이 아니라 설정입니다.

    Kubernetes CronJob은 또 다릅니다: 매니페스트가 timeZone 필드를 설정하지 않는 한, 노드 자체 시계가 무엇을 말하든 일정을 UTC로 읽습니다. 로컬 업무 시간을 위해 쓰여 그 필드 없이 배포된 일정은 UTC 밖 어디서나 하루의 잘못된 시각에 돌고 — 계절에 따라 바뀌지 않는데, 작업에 따라 버그이거나 안도입니다.

    Jenkins와 H

    Jenkins는 다른 어떤 방언에도 없는 토큰을 더합니다: H로, 무작위처럼 보이고 아닙니다. Jenkins는 작업 이름을 필드 범위 안의 고정값으로 해시하므로, 작업이 매일 같은 시각 — 저만의 시각 — 에 돌고, H * * * *로 쓴 백 개의 작업이 모두 정시에 시작하는 대신 한 시간에 걸쳐 고르게 흩어집니다.

    값이 작업 이름에서 오고 작업 이름이 표현식의 일부가 아니므로, 어떤 도구도 정확한 시각을 계산할 수 없습니다. 이 도구는 모든 H를 그 범위의 아래 끝으로 해석하고 그렇게 말합니다: 일정의 모양 — 얼마나 자주, 어느 날에 — 은 정확하고, 각 기간 안의 오프셋만 대역입니다.

    Jenkins는 매크로도 다시 정의합니다. 그 @daily는 @midnight이고, 그것은 자정이 아니라 하루의 첫 세 시간 어딘가에 있는 해시된 분입니다.

    cron이 하지 않는 것

    • 따라잡지 않습니다. 실행이 예정됐을 때 머신이 잠들었거나 데몬이 내려가 있었으면 그 실행은 나중에 일어나지 않습니다 — 그냥 놓칩니다. anacron이 바로 이것을 위해 있고 다른 프로그램입니다.
    • 겹치는 실행을 막지 않습니다. 작업이 간격보다 오래 걸리면 다음 것이 어쨌든 시작하고, 한참 뒤에는 여럿이 됩니다. 잠금 파일이 흔한 답입니다.
    • 고전적 형식에 초의 개념이 없습니다. 가장 고운 분해능은 1분이고, 더 빠른 것은 Quartz, systemd, 또는 작업 안의 루프가 필요합니다.
    • @reboot은 일정이 아닙니다. 데몬이 시작될 때 한 번 돌아 다음 실행이 없고, 결코 재부팅하지 않는 머신에서는 아예 돌지 않습니다.
    • EventBridge의 rate 표현식은 규칙이 만들어진 때부터 세고, 그 순간은 표현식에 없어 어떤 도구도 다음에 언제 발화할지 말할 수 없습니다.

    자주 묻는 질문

    그 달의 첫 월요일에 작업을 어떻게 돌리나요?
    맨 cron 표현식으로는 안 됩니다 — 그것은 두 날짜 필드 사이의 "그리고"가 필요하고 cron은 "또는"을 줍니다. 표준 우회는 0 0 1-7 * *를 예약하고 명령이 스스로 요일을 시험하게 하는 것입니다. Quartz와 EventBridge는 그것을 0 0 12 ? * MON#1로 곧바로 표현할 수 있습니다.
    0 0 13 * 5가 왜 그토록 자주 도나요?
    13일의 금요일이 아니라 13일 또는 매주 금요일을 뜻하기 때문입니다. 두 날짜 필드가 모두 제한되고 어느 것도 별표로 시작하지 않으면, cron은 어느 한쪽이 맞을 때 작업을 돌립니다. 고전 방언에 "그리고"를 쓸 방법이 없습니다.
    */5와 0-59/5의 차이는 무엇인가요?
    분 필드에는 아무것도 없습니다: 둘 다 0, 5, 10 등을 줍니다. 차이는 두 날짜 필드에 드러나고, 거기서 cron은 필드가 별표로 시작하는지 보고 "그리고"와 "또는"을 정합니다 — 그래서 */5와 0-59/5는 같은 날을 고르지만 다른 날짜 필드와 다르게 결합됩니다.
    시계가 되돌아갈 때 제 작업이 두 번 도나요?
    시나 분에 별표가 있으면 네: 시계를 따르고 그 시간이 두 번 일어납니다. 고정된 시와 분이 있으면 아니요 — Vixie cron은 그런 작업을 한 번 돌립니다. 소견 패널이 당신의 것이 어느 경우인지 적용되는 날짜와 함께 말합니다.
    cron 표현식은 어느 시간대를 쓰나요?
    머신의 것, crontab에 CRON_TZ나 TZ가 설정되지 않는 한. Kubernetes CronJob은 매니페스트가 timeZone을 설정하지 않는 한 UTC를 씁니다. 지역이 표현식의 일부가 아니므로 이 도구는 브라우저의 것을 가정하는 대신 그것을 청합니다.
    무언가를 90분마다 예약할 수 있나요?
    하나의 표현식으로는 안 됩니다. 각 필드가 독립으로 주기를 돌고 90분이 한 시간에 들어맞지 않기 때문입니다. 흔한 철자는 두 표현식, 0 0,3,6,9,12,15,18,21 * * *와 30 1,4,7,10,13,16,19,22 * * *로, 함께 90분마다 실행을 줍니다.
    제가 붙여넣은 것이 서버로 전송되나요?
    아니요. 파싱, 해설, 일정 계산 전체가 브라우저 안에서 돕니다. 아무것도 업로드되거나 기록되지 않고, 네트워크 연결 없이 동작합니다.