Kubernetes CronJob 동시성 정책: 유료 API 작업용

Allow, Forbid, Replace? CronJob의 concurrencyPolicy는 Job을 다룰 뿐 Job이 시작한 API 작업은 다루지 않습니다. 유료 API 호출에는 멱등성 키를 더하세요.

읽는 시간 6분Sume
전체 글

Kubernetes CronJob의 concurrencyPolicy는 이전 Job이 아직 실행 중일 때 다음 실행 시각이 되면 어떻게 할지를 정합니다. 기본값인 Allow는 둘 다 실행하고, Forbid는 새 실행을 건너뛰며, Replace는 실행 중인 Job을 새 Job으로 교체합니다. Job이 AI 영상 생성처럼 몇 분씩 걸리는 유료 API를 호출한다면 Forbid를 쓰고 호출을 멱등하게 만드세요. 파드를 멈춰도 API가 이미 수락한 작업은 멈추지 않기 때문입니다.

Kubernetes 관련 내용은 Kubernetes의 CronJob, Jobs, kubectl create job 페이지에서, Sume 관련 내용은 Format 호출하기 (영문), 실행과 결과 (영문), 오류와 비용 (영문)에서 가져왔습니다. 모두 2026-09-27에 확인했습니다. Sume에는 Kubernetes 오퍼레이터가 없으며, 파드는 일반 HTTPS 호출을 한 번 보냅니다.

concurrencyPolicy 값마다 유료 API 호출은 어떻게 되나요?

이 정책은 같은 CronJob이 만든 Job에만 적용됩니다. Kubernetes는 또 CronJob이 예약된 시각마다 Job을 대략 한 번만 만든다고 경고합니다. 경우에 따라 두 개를 만들거나 하나도 만들지 않으므로, 정의하는 Job은 멱등해야 합니다.

Kubernetes의 CronJob 페이지와 Sume의 오류와 비용 (영문), Format 호출하기 (영문) 문서 기준, 2026-09-27 확인.
값Kubernetes 동작유료 API 호출의 경우
Allow(기본값)Job을 동시에 실행파드 두 개가 동시에 제출할 수 있음. 같은 회차라면 공유하는 멱등성 키가 두 번째 청구를 막음
Forbid이전 실행이 끝나지 않았으면 새 실행을 건너뜀. 건너뛴 실행은 놓친 것으로 집계파드가 빨리 끝나기만 하면 안전한 선택
Replace실행 중인 Job을 새 Job 실행으로 교체이전 파드가 제출한 실행은 계속 돌며 과금됨. 새 파드가 실행을 하나 더 시작할 수도 있음

Replace나 activeDeadlineSeconds로 API 지출을 멈출 수 있나요?

아닙니다. 둘 다 Job과 그 파드에 작용합니다. Job이 activeDeadlineSeconds에 도달하면 Kubernetes는 실행 중인 파드를 종료하고 Job을 DeadlineExceeded 사유로 실패 처리합니다. Replace는 Job을 바꿔 끼웁니다. 어느 쪽도 API에는 미치지 않습니다. Sume에서는 폴링 루프를 중단해도 실행과 그 지출이 멈추지 않으며, 타임아웃도 실행을 취소하지 않습니다. Format 실행은 늦어도 created_at으로부터 90분 뒤에 저절로 끝나며, 이때 Sume가 실행을 failed로 강제 종료합니다.

  • 지출을 멈추려면 실행 자체를 취소하세요. formats:write로 POST /v1/format-runs/{run_id}/cancel을 호출하면 됩니다. 취소 전에 실행이 완료한 생성은 그대로 과금되며, 취소된 실행은 웹훅을 보내지 않습니다.
  • POST /v1/videos Job 같은 생성 Job은 생성이 시작되기 전에만 취소됩니다. 그 뒤에는 취소 요청에 409 job_generation_already_started가 돌아오고, Job은 끝까지 실행됩니다.

재시도되거나 중복된 Job이 두 번 과금되지 않게 하려면 어떻게 하나요?

Idempotency-Key와 본문은 예약 회차만으로 만들고, 다른 것은 넣지 마세요. 매일 실행한다면 UTC 날짜 같은 값입니다. 그러면 그 회차의 모든 시도가 똑같은 요청을 보냅니다. 파드 재시도(Job의 backoffLimit 기본값은 6이고, 백오프 지연은 10초, 20초, 40초 식으로 늘어나며 상한은 육 분), 같은 시각에 생긴 두 번째 Job, 수동 재실행이 모두 그렇습니다. 키는 Format 하나 단위로 적용되며 최대 255자입니다.

  • 같은 키, 같은 본문: 원래 영수증과 idempotency_hit: true가 담긴 200이 돌아옵니다. 두 번째 실행도, 두 번째 청구도 없습니다.
  • 같은 키, 다른 본문: 409 idempotency_conflict가 돌아오고 아무것도 실행되지 않습니다. 본문에 타임스탬프나 무작위 값을 절대 넣지 마세요.
  • 같은 키로 두 요청이 같은 순간에 도착: 하나가 이기고, 다른 하나는 409 idempotency_key_in_use를 받습니다. 이 오류는 약 일 초 뒤에 재시도할 수 있습니다.
  • 실패한 생성 요청(402, 503, …)은 키를 해제하므로, 파드가 재시도하면 그 회차의 실행을 여전히 시작할 수 있습니다.
#!/bin/sh
# submit-nightly.sh: one Format run per UTC day, then exit
set -eu
SLOT=$(date -u +%Y-%m-%d)  # UTC, like the CronJob's timeZone
curl --fail-with-body -sS -X POST \
  "https://api.sume.com/v1/formats/acme/daily-recap/runs" \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: daily-recap-$SLOT" \
  -d "{
    \"input\": { \"date\": \"$SLOT\" },
    \"on_active_run\": \"skip\",
    \"communication\": { \"webhook_url\": \"https://example.com/hooks/sume\" }
  }"

파드가 영상이 끝날 때까지 기다려야 하나요?

아닙니다. 제출하고 종료하세요. 기다리는 파드는 렌더링 내내 Job을 실행 상태로 붙잡아 둡니다. 그래서 Forbid에서는 주기보다 오래 걸리는 렌더링이 다음 회차를 건너뛰게 만들고, 어떤 기한을 걸어도 끊기는 것은 기다림뿐입니다. communication.webhook_url을 지정하면 Sume는 실행이 완료되거나 실패할 때 실행 영수증을 한 번 POST하며, 받는 쪽은 일반 Deployment로 운영하는 수신기입니다. 영수증의 trigger.idempotency_key를 보면 어느 회차가 끝났는지 알 수 있습니다. --fail-with-body를 쓰면 curl은 상태가 400 이상일 때 오류를 반환하면서도 Sume의 오류 내용을 출력하므로, 거부된 제출은 파드를 실패시킵니다.

파드가 몇 초 만에 종료된다는 것은 Forbid가 더 이상 렌더링을 보지 못한다는 뜻이기도 하므로, 겹침은 Sume 쪽에서도 막으세요. Format 실행의 on_active_run 기본값은 allow입니다. skip은 skipped 실행을 기록하고 아무것도 시작하지 않으며, reject는 409 format_run_in_progress로 응답합니다. 두 방법은 on_active_run으로 Sume AI 에이전트 중복 실행 막기에서 비교합니다.

apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-recap
spec:
  schedule: "0 2 * * *"
  timeZone: "Etc/UTC"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      backoffLimit: 3
      activeDeadlineSeconds: 300  # bounds the submit, not the video
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: submit
              image: registry.example.com/submit-nightly:1  # sh, date, curl
              command: ["/bin/sh", "/app/submit-nightly.sh"]
              env:
                - name: SUME_API_KEY
                  valueFrom:
                    secretKeyRef:
                      name: sume-api
                      key: api-key

CronJob은 수동으로 어떻게 실행하고, 시간대는 어떻게 정하나요?

kubectl create job daily-recap-manual --from=cronjob/daily-recap은 CronJob의 템플릿으로 Job을 만듭니다. --from이 지원하는 리소스는 CronJob뿐입니다. 키가 회차에서 나오므로, 이미 실행된 날에 수동으로 실행하면 그날의 실행이 재전송될 뿐 아무것도 시작되지 않습니다. 새로 실행하려면 직접 올리는 버전을 키에 붙이세요. Sume 문서도 의도적인 재실행은 이렇게 요청하라고 안내합니다.

Kubernetes v1.27부터 안정 기능인 .spec.timeZone을 Etc/UTC 같은 시간대 이름으로 설정하세요. 설정하지 않으면 kube-controller-manager가 자신의 로컬 시간대로 스케줄을 해석하므로, 자정 무렵에는 파드가 계산한 회차와 스케줄이 어긋날 수 있습니다.

출처

관련 글

연동 카테고리의 다른 글

연동 글 전체 보기

작성자 Sume