Certified Kubernetes Application Developer (CKAD) #7 Workloads 3: Job, CronJob (백오프, 동시성)
#5에서 Deployment로 무중단 배포를 익히고 #6에서 DaemonSet과 StatefulSet으로 노드 단위,상태 보존 워크로드를 다뤘다면, 이번에는 성격이 전혀 다른 워크로드를 봅니다. Deployment가 계속 떠 있어야 하는 서비스를 위한 것이라면, Job은 한 번 실행하고 끝나는 작업을 위한 것입니다. 데이터 마이그레이션, 백업, 배치 연산처럼 완료되면 그만인 일이 여기에 해당합니다.
서비스용 Pod가 죽으면 다시 살려야 하지만, 배치 작업 Pod는 성공적으로 끝나면 더 이상 재시작하면 안 됩니다. 이 “끝남"을 다루는 것이 Job의 핵심입니다. 그리고 그 Job을 cron 표기로 주기 실행하는 것이 CronJob 입니다. 시험에서는 backoffLimit과 concurrencyPolicy가 특히 단골이므로, 이번 글에서 YAML과 kubectl 양쪽으로 손에 익히겠습니다.
Job: 한 번 실행하고 끝나는 작업 #
Job은 하나 이상의 Pod를 만들어 지정한 횟수만큼 성공적으로 완료될 때까지 보장하는 워크로드입니다. Deployment가 항상 N 개의 Pod를 살려 두는 것과 달리, Job은 정해진 작업이 끝나면 Pod를 더 만들지 않고 멈춥니다.
가장 단순한 Job을 generator로 뽑아 보겠습니다.
k create job pi --image=perl:5.34 \
$do -- perl -Mbignum=bpi -wle 'print bpi(2000)' > job.yaml$do는 #1에서 정의한 --dry-run=client -o yaml입니다. 위 명령이 만든 뼈대는 다음과 같습니다.
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: perl:5.34
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never여기서 Job은 apiVersion: batch/v1이라는 점, 그리고 Pod 템플릿의 restartPolicy가 Never라는 점이 Deployment와 다릅니다.
restartPolicy: Job에서는 Always가 금지된다 #
Job의 Pod 템플릿에는 restartPolicy로 OnFailure 또는 Never만 쓸 수 있습니다. Deployment와 달리 Always는 허용되지 않습니다. 작업이 끝나면 다시 돌리지 않아야 하기 때문입니다.
| 값 | 동작 |
|---|---|
Never | 실패한 Pod를 재시작하지 않고, Job이 새 Pod 를 만들어 재시도 |
OnFailure | 같은 Pod 안에서 컨테이너를 재시작해 재시도 |
Never는 실패할 때마다 Pod가 새로 쌓여 디버깅 로그가 남는 장점이 있고, OnFailure는 Pod 수를 늘리지 않는 장점이 있습니다. 시험에서는 문제 요구를 그대로 따르면 됩니다.
completions와 parallelism #
Job의 동작은 두 필드로 결정됩니다.
| 필드 | 의미 | 기본값 |
|---|---|---|
completions | 성공으로 간주하려면 몇 번 완료되어야 하는가 | 1 |
parallelism | 동시에 몇 개의 Pod를 실행할 것인가 | 1 |
예를 들어 completions: 6, parallelism: 2이면, Job은 한 번에 두 개씩 Pod를 돌려 총 여섯 번의 성공을 채울 때까지 진행합니다.
backoffLimit: 재시도 한도 #
backoffLimit은 Job이 실패를 몇 번까지 재시도할지를 정하는 한도입니다. 이 횟수를 넘기면 Job은 Failed로 표시되고 더 이상 새 Pod를 만들지 않습니다. 기본값은 6 입니다.
spec:
backoffLimit: 4재시도 사이에는 점점 길어지는 지연(exponential backoff)이 적용됩니다. 무한히 실패하는 작업이 클러스터 자원을 갉아먹지 않도록 막는 안전장치입니다. 시험에서 “최대 N 번까지만 재시도하라"는 요구가 나오면 이 필드를 잡으면 됩니다.
activeDeadlineSeconds: 시간 제한 #
activeDeadlineSeconds는 Job이 시작된 뒤 이 초를 넘기면 강제로 종료시키는 시간 제한입니다. backoffLimit이 횟수 기준이라면 이쪽은 시간 기준입니다. 둘 중 먼저 도달하는 조건이 적용됩니다.
spec:
activeDeadlineSeconds: 100시간을 넘겨 종료되면 Job은 DeadlineExceeded 사유로 실패 처리됩니다.
ttlSecondsAfterFinished: 자동 정리 #
완료되거나 실패한 Job은 기본적으로 클러스터에 그대로 남습니다. ttlSecondsAfterFinished를 설정하면 종료된 뒤 지정한 초가 지났을 때 Job과 그 Pod가 자동으로 삭제됩니다.
spec:
ttlSecondsAfterFinished: 60배치 작업이 끝난 뒤 오래된 Job이 쌓이는 것을 막는 용도이며, 0으로 두면 완료 즉시 삭제됩니다.
병렬 실행 패턴 #
Job은 completions와 parallelism의 조합으로 세 가지 대표 패턴을 만듭니다.
| 패턴 | 설정 | 쓰임 |
|---|---|---|
| 단일 작업 | completions 미지정, parallelism 미지정 | 한 번 돌고 끝나는 단발 작업 |
| 고정 완료 수 | completions: N (병렬 선택) | N 개의 독립 항목을 처리 |
| 작업 큐 | parallelism: M, completions 미지정 | 워커가 큐에서 항목을 꺼내 처리하고 큐가 비면 종료 |
작업 큐 패턴에서는 각 Pod가 외부 큐를 보고 스스로 일을 가져가므로 completions를 비워 두고 parallelism으로 워커 수만 정합니다.
고정 완료 수 + 병렬 Job 예제 #
여섯 번의 완료를 한 번에 세 개씩 처리하고, 재시도는 두 번까지만 허용하는 Job 입니다.
apiVersion: batch/v1
kind: Job
metadata:
name: batch-import
spec:
completions: 6
parallelism: 3
backoffLimit: 2
activeDeadlineSeconds: 120
ttlSecondsAfterFinished: 120
template:
spec:
restartPolicy: Never
containers:
- name: importer
image: busybox:1.36
command: ["sh", "-c", "echo importing && sleep 5"]이 Job은 동시에 세 개의 Pod를 돌려 총 여섯 번의 성공을 채우고, 두 번을 초과해 실패하면 멈추며, 120 초를 넘기면 강제 종료되고, 끝난 뒤 120 초가 지나면 자동으로 정리됩니다.
CronJob: Job을 주기적으로 돌린다 #
CronJob은 정해진 일정에 따라 Job을 만들어 내는 워크로드입니다. Job이 일회성이라면 CronJob은 그것을 반복합니다. 백업을 매일 새벽에 돌리거나, 리포트를 5 분마다 생성하는 작업이 여기에 해당합니다.
generator로 뼈대를 만들어 보겠습니다.
k create cronjob report \
--image=busybox:1.36 \
--schedule="*/5 * * * *" \
$do -- /bin/sh -c 'date; echo report' > cronjob.yamlapiVersion: batch/v1
kind: CronJob
metadata:
name: report
spec:
schedule: "*/5 * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: report
image: busybox:1.36
command: ["/bin/sh", "-c", "date; echo report"]CronJob은 spec.jobTemplate 아래에 Job의 spec을 그대로 품고, 그 안에 다시 Pod 템플릿이 들어가는 3 단 중첩 구조입니다. 시험에서 들여쓰기를 가장 틀리기 쉬운 리소스이므로 generator로 뽑는 편이 안전합니다.
schedule: cron 표기 #
schedule은 표준 cron 다섯 자리 표기를 씁니다.
┌── 분 (0〜59)
│ ┌── 시 (0〜23)
│ │ ┌── 일 (1〜31)
│ │ │ ┌── 월 (1〜12)
│ │ │ │ ┌── 요일 (0〜6, 0=일요일)
│ │ │ │ │
* * * * *| 표기 | 의미 |
|---|---|
*/5 * * * * | 5 분마다 |
0 * * * * | 매시 정각 |
0 2 * * * | 매일 새벽 2 시 |
0 0 * * 0 | 매주 일요일 자정 |
concurrencyPolicy: 동시성 정책 #
이전 실행이 아직 끝나지 않았는데 다음 일정이 도래하면 어떻게 할지를 정하는 것이 concurrencyPolicy입니다. 시험 단골이므로 세 값의 차이를 정확히 외워 두겠습니다.
| 값 | 동작 |
|---|---|
Allow (기본) | 동시 실행을 허용. 이전 Job이 돌고 있어도 새 Job을 생성 |
Forbid | 이전 Job이 끝나지 않았으면 새 일정을 건너뜀 |
Replace | 이전 Job을 취소하고 새 Job으로 교체 |
겹치면 안 되는 백업 작업은 Forbid, 최신 실행만 의미가 있는 작업은 Replace가 적절합니다.
startingDeadlineSeconds: 시작 마감 시한 #
컨트롤러가 멈춰 있었거나 노드 문제로 예정된 시각에 Job을 시작하지 못했을 때, 이 초 안에라면 늦게라도 시작하고 그 시한을 넘기면 해당 실행을 건너뛰게 하는 필드입니다.
spec:
startingDeadlineSeconds: 30suspend: 일시 중지 #
suspend: true로 두면 CronJob이 새 Job을 만들지 않고 멈춥니다. 점검 중에 주기 작업을 잠깐 꺼 둘 때 씁니다. 이미 돌고 있는 Job에는 영향을 주지 않습니다.
# 일시 중지
k patch cronjob report -p '{"spec":{"suspend":true}}'
# 재개
k patch cronjob report -p '{"spec":{"suspend":false}}'히스토리 한도 #
CronJob은 끝난 Job을 일정 개수만큼 보관합니다.
| 필드 | 의미 | 기본값 |
|---|---|---|
successfulJobsHistoryLimit | 성공한 Job을 몇 개까지 남길지 | 3 |
failedJobsHistoryLimit | 실패한 Job을 몇 개까지 남길지 | 1 |
오래된 Job이 무한히 쌓이지 않도록 막는 설정이며, 디버깅을 위해 더 많은 이력을 남기려면 값을 올립니다.
concurrencyPolicy를 적용한 CronJob 예제 #
겹치는 실행을 막고, 늦은 시작을 30 초까지만 허용하며, 이력을 줄여 둔 백업 CronJob 입니다.
apiVersion: batch/v1
kind: CronJob
metadata:
name: db-backup
spec:
schedule: "0 2 * * *"
concurrencyPolicy: Forbid
startingDeadlineSeconds: 30
successfulJobsHistoryLimit: 5
failedJobsHistoryLimit: 2
jobTemplate:
spec:
backoffLimit: 3
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: busybox:1.36
command: ["/bin/sh", "-c", "echo backing up && sleep 10"]매일 새벽 2 시에 돌되 이전 백업이 끝나지 않았으면 이번 실행은 건너뛰고, jobTemplate 안의 backoffLimit으로 실패 시 세 번까지 재시도합니다.
실습: Job과 CronJob 다루기 #
Job만들고 로그 확인 #
# Job 생성
k apply -f job.yaml
# 상태 확인 (COMPLETIONS 열이 6/6이 되면 완료)
k get job batch-import
# Job이 만든 Pod 보기
k get pods -l job-name=batch-import
# 로그 확인 (Job 이름으로 한 Pod의 로그를 본다)
k logs job/batch-import
# 자세한 진행 상황과 이벤트
k describe job batch-importk get job의 COMPLETIONS 열은 성공/목표 형식으로 진행도를 보여 줍니다. DURATION과 AGE로 소요 시간도 확인할 수 있습니다.
CronJob 다루기와 수동 트리거 #
CronJob은 일정이 도래해야 Job을 만들기 때문에, 시험에서 동작을 즉시 확인하려면 수동으로 트리거합니다.
# CronJob 생성
k apply -f cronjob.yaml
# 등록 확인 (SCHEDULE,LAST SCHEDULE,ACTIVE 열)
k get cronjob db-backup
# 일정을 기다리지 않고 지금 한 번 실행 (CronJob으로부터 Job 생성)
k create job manual-run --from=cronjob/db-backup
# 수동 실행한 Job의 로그
k logs job/manual-run
# CronJob이 만든 Job 목록
k get jobsk create job <이름> --from=cronjob/<CronJob 이름>은 CronJob의 jobTemplate을 그대로 복제해 Job을 즉시 생성합니다. 채점 전에 작업이 제대로 도는지 확인하는 데 가장 빠른 방법입니다.
시험 포인트 #
- Job은
batch/v1. apiVersion을 틀리면 리소스가 만들어지지 않습니다. generator로 뽑으면 자동으로 맞습니다. - restartPolicy는
Never또는OnFailure만. Job과 CronJob의 Pod 템플릿에Always를 쓰면 거부됩니다. - backoffLimit은 단골. “최대 N 번 재시도” 요구가 나오면 이 필드입니다. 기본값 6을 기억하면 빠릅니다.
- concurrencyPolicy 세 값.
Allow(기본),Forbid(건너뜀),Replace(교체)의 차이를 정확히 구분하는 문제가 자주 나옵니다. - CronJob은 3 단 중첩.
jobTemplate.spec.template.spec까지 들어가는 구조라 들여쓰기 사고가 잦습니다. generator로 뼈대를 만든 뒤 필드만 고치는 편이 안전합니다. - 수동 트리거. CronJob을 즉시 검증하려면
k create job --from=cronjob/<이름>을 씁니다. - completions와 parallelism의 조합으로 단일,고정,큐 패턴이 나뉩니다. 둘 다 기본값은 1 입니다.
정리 #
이번 글에서 잡은 것:
- Job은 한 번 실행하고 끝나는 배치 워크로드.
completions로 목표 완료 수를,parallelism으로 동시 실행 수를 정합니다. - 재시도와 한도.
backoffLimit(횟수),activeDeadlineSeconds(시간)으로 실패를 제어하고,ttlSecondsAfterFinished로 끝난 Job을 자동 정리합니다. - restartPolicy는
Never또는OnFailure. Job에서Always는 쓸 수 없습니다. - CronJob은 Job을 cron 일정으로 반복.
schedule,concurrencyPolicy,startingDeadlineSeconds,suspend,히스토리 한도로 동작을 다듬습니다. - 실습. Job은
k logs job/<이름>으로, CronJob은k create job --from=cronjob/<이름>으로 수동 트리거해 검증합니다.
배치 워크로드의 개념을 더 넓게 잡고 싶다면 쿠버네티스 중급 시리즈도 함께 보면 좋습니다.
다음: Deployment 전략 #
워크로드 3 종(Job,CronJob)까지 정리하며 #5〜#7의 워크로드 묶음을 마쳤습니다. 다음은 그 워크로드를 어떻게 교체하느냐의 문제로 돌아갑니다.
#8 Deployment 전략: Blue-green, canary에서는 한 버전을 통째로 옮기는 blue-green 배포와, 일부 트래픽만 새 버전으로 흘려 보내는 canary 배포를 다루겠습니다. Deployment와 Service, 그리고 레이블 셀렉터를 조합해 시험에서 직접 두 전략을 구현해 보며 정리하겠습니다.