CronJob

CronJob

CronJob은 반복 일정으로 일회성 Job을 시작합니다.

기능 상태: Kubernetes v1.21 [stable]

CronJob은 반복 일정으로 [Job]을 만들 수 있게 해줘요.

CronJob은 백업, 보고서 생성 같은 정기적인 예약 작업을 수행하기 위한 것입니다. 하나의 CronJob 객체는 Unix 시스템의 crontab(cron table) 파일 한 줄과 같아요. [Cron] 형식으로 작성된 주어진 일정에 따라 주기적으로 Job을 실행합니다.

CronJob에는 제한 사항과 특이한 점이 있어요. 예를 들어 특정 상황에서 하나의 CronJob이 여러 개의 동시 Job을 만들 수 있습니다. 아래 제한 사항을 참고하세요.

컨트롤 플레인이 CronJob에 대한 새 Job과 (간접적으로) Pod를 만들 때, CronJob의 .metadata.name은 그 Pod들의 이름을 정하는 기준의 일부가 됩니다. CronJob의 이름은 유효한 [DNS 하위 도메인] 값이어야 하지만, 이것은 Pod 호스트 이름에 예상치 못한 결과를 만들 수 있어요. 최고의 호환성을 위해 이름은 더 제한적인 [DNS 레이블] 규칙을 따라야 합니다. 이름이 DNS 하위 도메인이라도 52자 이하여야 해요. CronJob 컨트롤러가 제공한 이름에 자동으로 11자를 추가하고, Job 이름의 길이가 63자를 넘지 않아야 한다는 제약이 있기 때문입니다.

예시 (Example)

이 예시 CronJob 매니페스트는 매분 현재 시간과 hello 메시지를 출력합니다.

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox:1.28
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

(Running Automated Tasks with a CronJob 문서가 이 예시를 더 자세히 설명합니다.)

CronJob 스펙 작성하기 (Writing a CronJob spec)

일정 구문 (Schedule syntax)

.spec.schedule 필드는 필수입니다. 그 값은 [Cron] 구문을 따릅니다.

# ┌───────────── 분 (0 - 59)
# │ ┌───────────── 시 (0 - 23)
# │ │ ┌───────────── 일 (1 - 31)
# │ │ │ ┌───────────── 월 (1 - 12)
# │ │ │ │ ┌───────────── 요일 (0 - 6) (일요일부터 토요일까지)
# │ │ │ │ │             또는 sun, mon, tue, wed, thu, fri, sat
# │ │ │ │ │
# │ │ │ │ │
# * * * * *

예를 들어 0 3 * * 1은 이 작업이 매주 월요일 오전 3시에 실행되도록 예약된다는 뜻이에요.

이 형식은 확장된 "Vixie cron" 단계 값도 포함합니다. [FreeBSD 매뉴얼]에서 설명하듯:

단계 값은 범위와 함께 사용할 수 있어요. 범위 뒤에 /<number>를 붙이면 범위를 따라 해당 숫자만큼 건너뛰는 것을 지정합니다. 예를 들어 시(hours) 필드에서 0-23/2를 사용하면 매 두 시간마다 명령이 실행되도록 지정할 수 있어요 (V7 표준에서의 대안은 0,2,4,6,8,10,12,14,16,18,20,22입니다). 단계는 별표 뒤에도 허용되므로 "매 두 시간"이라고 말하고 싶다면 */2를 사용하면 됩니다.

참고: 일정의 물음표(?)는 별표 *와 같은 의미를 가져요. 즉 주어진 필드의 사용 가능한 값 중 어떤 것이든 나타냅니다.

표준 구문 외에도 @monthly 같은 일부 매크로를 사용할 수 있어요.

항목 설명 동일 표현
@yearly (또는 @annually) 1월 1일 자정에 1년에 한 번 실행 0 0 1 1 *
@monthly 매달 1일 자정에 한 번 실행 0 0 1 * *
@weekly 일요일 아침 자정에 1주일에 한 번 실행 0 0 * * 0
@daily (또는 @midnight) 매일 자정에 한 번 실행 0 0 * * *
@hourly 매시간 시작에 한 번 실행 0 * * * *

CronJob 일정 표현식을 생성하려면 crontab.guru 같은 웹 도구를 사용할 수도 있어요.

Job 템플릿 (Job template)

.spec.jobTemplate은 CronJob이 만드는 Job들의 템플릿을 정의하며 필수입니다. 중첩되어 있고 apiVersion이나 kind가 없다는 점을 제외하면 [Job]과 정확히 같은 스키마를 가져요. 템플릿화된 Job에 [레이블]이나 [주석] 같은 공통 메타데이터를 지정할 수 있습니다. Job .spec 작성에 대한 정보는 [Writing a Job Spec]을 참고하세요.

지연된 Job 시작의 데드라인 (Deadline for delayed Job start)

.spec.startingDeadlineSeconds 필드는 선택 사항입니다. 이 필드는 어떤 이유로 Job이 예약 시간을 놓친 경우 Job을 시작하기 위한 데드라인(정수 초)을 정의합니다.

데드라인을 놓치면 CronJob은 그 Job 인스턴스를 건너뜁니다 (이후 발생은 여전히 예약됩니다). 예를 들어 하루에 두 번 실행되는 백업 Job이 있다면, 최대 8시간까지는 늦게 시작해도 되도록 허용할 수 있어요. 그보다 늦으면 백업이 유용하지 않기 때문입니다. 그보다는 다음 예약 실행을 기다리는 것이 나아요.

구성된 데드라인을 놓친 Job은 쿠버네티스가 실패한 Job으로 취급합니다. CronJob에 startingDeadlineSeconds를 지정하지 않으면 Job 발생에 데드라인이 없습니다.

.spec.startingDeadlineSeconds 필드가 설정되면(null이 아니면), CronJob 컨트롤러는 Job이 생성되기로 예상된 시각과 현재의 차이를 측정합니다. 그 차이가 한도보다 크면 이 실행을 건너뜁니다.

예를 들어 200으로 설정하면 실제 일정 후 최대 200초까지 Job 생성을 허용합니다.

동시성 정책 (Concurrency policy)

.spec.concurrencyPolicy 필드도 선택 사항입니다. 이 CronJob이 만든 Job의 동시 실행을 어떻게 처리할지 지정합니다. 스펙은 다음 동시성 정책 중 하나만 지정할 수 있어요.

  • Allow (기본값): CronJob이 동시에 실행되는 Job을 허용합니다
  • Forbid: CronJob이 동시 실행을 허용하지 않습니다. 새 Job 실행 시간이 되었는데 이전 Job 실행이 아직 끝나지 않았다면, CronJob은 새 Job 실행을 건너뜁니다. 또한 이전 Job 실행이 끝나면 .spec.startingDeadlineSeconds가 여전히 고려되어 새 Job 실행이 발생할 수 있다는 점에 주의하세요.
  • Replace: 새 Job 실행 시간이 되었는데 이전 Job 실행이 아직 끝나지 않았다면, CronJob은 현재 실행 중인 Job을 새 Job 실행으로 교체합니다

동시성 정책은 같은 CronJob이 만든 Job에만 적용된다는 점에 유의하세요. 여러 CronJob이 있다면 각각의 Job은 항상 동시에 실행되도록 허용됩니다.

일정 일시 중지 (Schedule suspension)

선택적 .spec.suspend 필드를 true로 설정하면 CronJob의 Job 실행을 일시 중지할 수 있어요. 이 필드는 기본적으로 false입니다.

이 설정은 CronJob이 이미 시작한 Job에는 영향을 주지 않습니다.

이 필드를 true로 설정하면 이후의 모든 실행이 일시 중지됩니다 (예약된 상태는 유지되지만 CronJob 컨트롤러는 작업을 실행할 Job을 시작하지 않습니다). CronJob을 일시 중지 해제할 때까지 그렇죠.

주의: 예약 시간에 일시 중지된 실행은 놓친 Job으로 간주됩니다. 시작 데드라인이 없는 기존 CronJob에서 .spec.suspendtrue에서 false로 바뀌면, 놓친 Job들은 즉시 예약됩니다.

Job 기록 한도 (Jobs history limits)

.spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimit 필드는 완료된 Job과 실패한 Job을 각각 몇 개 보관할지 지정합니다. 두 필드 모두 선택 사항입니다.

  • .spec.successfulJobsHistoryLimit: 보관할 성공적으로 완료된 Job 수를 지정합니다. 기본값은 3입니다. 이 필드를 0으로 설정하면 성공한 Job을 보관하지 않습니다.
  • .spec.failedJobsHistoryLimit: 보관할 실패한 Job 수를 지정합니다. 기본값은 1입니다. 이 필드를 0으로 설정하면 실패한 Job을 보관하지 않습니다.

Job을 자동으로 정리하는 또 다른 방법은 [Clean up finished Jobs automatically]을 참고하세요.

시간대 (Time zones)

기능 상태: Kubernetes v1.27 [stable]

시간대가 지정되지 않은 CronJob의 경우, [kube-controller-manager]는 일정을 자신의 로컬 시간대 기준으로 해석합니다.

CronJob에 시간대를 지정하려면 .spec.timeZone을 유효한 [시간대] 이름으로 설정하면 돼요. 예를 들어 .spec.timeZone: "Etc/UTC"로 설정하면 쿠버네티스가 Coordinated Universal Time 기준으로 일정을 해석하도록 지시합니다.

Go 표준 라이브러리의 시간대 데이터베이스가 바이너리에 포함되어 있으며, 시스템에 외부 데이터베이스가 없을 때 대체로 사용됩니다.

CronJob 제한 사항 (CronJob limitations)

지원되지 않는 TimeZone 지정 (Unsupported TimeZone specification)

.spec.schedule 안의 CRON_TZ 또는 TZ 변수를 사용한 시간대 지정은 공식적으로 지원되지 않습니다 (그리고 지금까지도 그랬어요). TZCRON_TZ 시간대 지정을 포함한 일정을 설정하려 하면, 쿠버네티스는 검증 오류와 함께 리소스 생성이나 업데이트에 실패합니다. 대신 시간대 필드를 사용해 시간대를 지정해야 해요.

CronJob 수정 (Modifying a CronJob)

설계상 CronJob은 새로운 Job을 위한 템플릿을 포함합니다. 기존 CronJob을 수정하면, 그 변경 사항은 수정이 완료된 후 시작되는 새 Job에 적용됩니다. 이미 시작된 Job(및 그 Pod)은 변경 없이 계속 실행됩니다. 즉 CronJob은 여전히 실행 중이더라도 기존 Job을 업데이트하지 않아요.

Job 생성 (Job creation)

CronJob은 일정의 실행 시간마다 대략 하나의 Job 객체를 만듭니다. 두 개의 Job이 생성되거나 아무 Job도 생성되지 않을 수 있는 특정 상황이 있기 때문에 스케줄링은 근사치입니다. 쿠버네티스는 이런 상황을 피하려고 하지만 완전히 막지는 못해요. 따라서 정의하는 Job은 *멱등(idempotent)*해야 합니다.

Kubernetes v1.32부터 CronJob은 생성한 Job에 batch.kubernetes.io/cronjob-scheduled-timestamp라는 주석을 적용합니다. 이 주석은 Job의 원래 예약된 생성 시간을 나타내며 RFC3339 형식입니다.

startingDeadlineSeconds가 큰 값으로 설정되어 있거나 설정되지 않았고(기본값) concurrencyPolicyAllow로 설정되어 있으면, Job은 항상 최소 한 번 실행됩니다.

주의: startingDeadlineSeconds가 10초 미만의 값으로 설정되면 CronJob이 예약되지 않을 수 있습니다. CronJob 컨트롤러가 10초마다 항목을 확인하기 때문입니다.

각 CronJob에 대해 CronJob [Controller]는 마지막 예약 시간부터 현재까지의 기간 동안 얼마나 많은 일정을 놓쳤는지 확인합니다. 놓친 일정이 100개를 초과하면 Job을 시작하지 않고 오류를 기록합니다.

too many missed start times. Set or decrease .spec.startingDeadlineSeconds or check clock skew

이 동작은 따라잡기(catch-up) 스케줄링에 적용되며, CronJob이 계속 실행되지 않는다는 뜻은 아니에요.

예를 들어 concurrencyPolicy: Forbid를 사용할 때, 오래 실행되는 Job이 예약된 시간을 건너뛰게 할 수 있지만 이전 Job이 완료되면 새 Job이 생성될 수 있습니다.

.spec.startingDeadlineSeconds 필드가 설정되어 있으면(null이 아니면) 컨트롤러는 마지막 예약 시간부터 현재까지가 아니라 startingDeadlineSeconds 값부터 현재까지 발생한 놓친 Job 수를 계산한다는 점에 유의하는 것이 중요해요. 예를 들어 startingDeadlineSeconds200이면 컨트롤러는 지난 200초 동안 발생한 놓친 Job 수를 계산합니다.

CronJob은 예약 시간에 생성에 실패하면 놓친 것으로 간주됩니다. 예를 들어 concurrencyPolicyForbid이고 이전 일정이 아직 실행 중일 때 CronJob이 예약되려고 시도했다면 그것은 놓친 것으로 간주됩니다.

예를 들어 CronJob이 08:30:00부터 매 1분마다 새 Job을 예약하도록 설정되어 있고 startingDeadlineSeconds 필드가 설정되지 않았다고 해볼게요. CronJob 컨트롤러가 08:29:00부터 10:21:00까지 다운되어 있다면, 일정을 놓친 Job 수가 100보다 크므로 Job은 시작되지 않습니다.

이 개념을 더 설명하기 위해, CronJob이 08:30:00부터 매 1분마다 새 Job을 예약하도록 설정되어 있고 startingDeadlineSeconds가 200초로 설정되어 있다고 해볼게요. CronJob 컨트롤러가 이전 예시와 같은 기간(08:29:00부터 10:21:00까지) 다운되어 있어도, Job은 여전히 10:22:00에 시작됩니다. 컨트롤러가 이제 마지막 예약 시간부터 현재까지가 아니라 지난 200초 동안 발생한 놓친 일정 수(즉 3개의 놓친 일정)를 확인하기 때문입니다.

CronJob은 일정과 일치하는 Job을 만드는 것만 담당하며, Job은 차례로 자신이 나타내는 Pod의 관리를 담당합니다.

다음 내용 (What's next)

  • CronJob이 의존하는 두 개념인 [Pods]과 [Jobs]에 대해 배우기
  • CronJob .spec.schedule 필드의 자세한 [형식]에 대해 읽기
  • CronJob 생성 및 작업 지침과 CronJob 매니페스트 예시는 [Running automated tasks with CronJobs] 참고
  • CronJob은 쿠버네티스 REST API의 일부예요. 자세한 내용은 CronJob API 참조를 읽어보세요.