CronJob

CronJob

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

(CronJob으로 자동 태스크 실행하기가 이 예시를 더 자세히 안내합니다.)

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

일정 구문 (Schedule syntax)

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

# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday)
# │ │ │ │ │                                   OR sun, mon, tue, wed, thu, fri, sat
# │ │ │ │ │
# │ │ │ │ │
# * * * * *

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

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

단계 값은 범위와 함께 사용할 수 있습니다. 범위 다음에 /를 붙이면 범위를 number 값만큼 건너뛰도록 지정합니다. 예를 들어 0-23/2는 시간 필드에서 격시간으로 명령이 실행되도록 지정하는 데 사용할 수 있습니다(V7 표준의 대안은 0,2,4,6,8,10,12,14,16,18,20,22). 별표 다음에도 단계가 허용되므로 "매 2시간"이라고 하려면 */2만 사용하면 됩니다.

참고: 일정의 물음표(?)는 별표(*)와 같은 의미입니다. 즉 주어진 필드에 대해 사용 가능한 값 중 하나를 나타냅니다.

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

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

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

Job 템플릿 (Job template)

.spec.jobTemplate은 CronJob이 만드는 Job에 대한 템플릿을 정의하며 필수입니다. Job과 정확히 같은 스키마를 가지지만, 중첩되어 있고 apiVersion이나 kind가 없습니다. 템플릿된 Job에 라벨이나 어노테이션 같은 공통 메타데이터를 지정할 수 있어요. Job .spec 작성에 대한 정보는 Writing a Job Spec을 참고하세요.

지연된 Job 시작 마감 (Deadline for delayed Job start)

.spec.startingDeadlineSeconds 필드는 선택적입니다. 이 필드는 어떤 이유로든 Job이 예정된 시간을 놓친 경우 Job 시작을 위한 마감(정수 초)을 정의합니다.

마감을 놓치면 CronJob은 그 Job 인스턴스를 건너뜁니다(향후 발생은 여전히 예약됩니다). 예를 들어 하루에 두 번 실행되는 백업 Job이 있다면, 최대 8시간 늦게 시작하도록 허용할 수 있지만 그보다 늦으면 안 돼요. 더 늦은 백업은 쓸모가 없으니까요. 다음 예약 실행을 기다리는 편이 낫습니다.

구성된 마감을 놓친 Job에 대해 Kubernetes는 이를 실패한 Job으로 취급합니다. CronJob에 startingDeadlineSeconds를 지정하지 않으면 Job 발생에는 마감이 없습니다.

.spec.startingDeadlineSeconds 필드가 설정되면(비어 있지 않으면), CronJob 컨트롤러는 Job이 생성될 것으로 예상되는 시간과 지금 사이의 시간을 측정합니다. 그 차이가 그 한도보다 높으면 이 실행을 건너뜁니다.

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

동시성 정책 (Concurrency policy)

.spec.concurrencyPolicy 필드도 선택적입니다. 이 CronJob이 만드는 Job의 동시 실행을 어떻게 취급할지 지정합니다. 스펙은 다음 동시성 정책 중 하나만 지정할 수 있습니다.

  • Allow (기본값): CronJob은 동시에 실행되는 Job을 허용합니다.
  • Forbid: CronJob은 동시 실행을 허용하지 않습니다. 새 Job 실행 시간이 되었는데 이전 Job 실행이 아직 끝나지 않았다면 ChronJob은 새 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으로 간주됩니다. 시작 마감(starting deadline)이 없는 기존 CronJob에서 .spec.suspendtrue에서 false로 바뀌면 놓친 Job이 즉시 예약됩니다.

Job 기록 한도 (Jobs history limits)

.spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimit 필드는 유지할 완료 및 실패한 Job 수를 지정합니다. 두 필드 모두 선택적입니다.

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

Job을 자동으로 정리하는 또 다른 방법은 완료된 Job 자동 정리를 참고하세요.

시간대 (Time zones)

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

.spec.timeZone을 유효한 시간대의 이름으로 설정해 CronJob에 시간대를 지정할 수 있어요. 예를 들어 .spec.timeZone: "Etc/UTC"로 설정하면 Kubernetes가 Coordinated Universal Time 기준으로 일정을 해석하도록 지시합니다.

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

CronJob 제한 사항 (CronJob limitations)

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

.spec.schedule 안에서 CRON_TZ 또는 TZ 변수로 시간대를 지정하는 것은 공식적으로 지원되지 않습니다(그리고 결코 지원된 적이 없습니다). TZ 또는 CRON_TZ 시간대 지정을 포함한 일정을 설정하려고 하면 Kubernetes는 검증 오류로 리소스를 생성하거나 업데이트하지 못합니다. 대신 time zone 필드를 사용해 시간대를 지정해야 합니다.

CronJob 수정 (Modifying a CronJob)

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

Job 생성 (Job creation)

CronJob은 일정의 각 실행 시간마다 대략 한 번씩 Job 객체를 만듭니다. 스케줄링이 대략적인 이유는 두 개의 Job이 만들어질 수도 있고, 어떤 Job도 만들어지지 않을 수도 있는 특정 상황이 있기 때문입니다. Kubernetes는 그런 상황을 피하려고 하지만 완전히 방지하지는 못합니다. 따라서 정의하는 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 컨트롤러는 마지막 예약 시간부터 지금까지의 기간 동안 얼마나 많은 일정을 놓쳤는지 확인합니다. 놓친 일정이 100개보다 많으면 Job을 시작하지 않고 오류를 기록합니다.

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

이 동작은 따라잡기(catch-up) 스케줄링에 적용되며, CronJob이 실행을 멈춘다는 뜻은 아닙니다.

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

startingDeadlineSeconds 필드가 설정되면(nil이 아니면), 컨트롤러는 마지막 예약 시간부터 현재까지가 아니라 startingDeadlineSeconds 값부터 현재까지 발생한 놓친 Job 수를 계산한다는 점에 유의하는 것이 중요해요. 예를 들어 startingDeadlineSeconds가 200이면 컨트롤러는 지난 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)

더 알아보기 (Learn more)