Job과 CronJob 컨트롤러 아키텍처: 배치 프로세싱의 정석
쿠버네티스 생태계에서 다루는 워크로드는 크게 두 가지 패러다임으로 나뉩니다. 24시간 중단 없이 외부 트래픽을 처리하며 무한히 실행되어야 하는 데몬(Daemon) 성격의 워크로드, 그리고 주어진 특정 작업을 완수하면 스스로 종료되어야 하는 배치(Batch) 성격의 워크로드입니다.
웹 서버나 API 서버를 배포할 때 사용하는 Deployment나 StatefulSet이 전자를 위한 것이라면, 후자를 위해 탄생한 완벽한 오케스트레이션 도구가 바로 Job과 CronJob 컨트롤러입니다. 대규모 데이터 마이그레이션, 야간 데이터베이스 백업, 복잡한 AI 모델 학습 등 '끝이 있는 작업'을 클라우드 네이티브 환경에서 어떻게 안정적이고 분산된 형태로 처리하는지 그 내부 아키텍처를 심층 분석합니다.

1. Job 컨트롤러의 핵심 철학: 완벽한 종료(Completion) 보장
Job 컨트롤러의 단 하나의 존재 목적은 파드(Pod) 내부의 컨테이너가 정상적으로 실행을 마치고 종료 코드 '0'을 반환하며 죽는 것(Terminated)을 보장하는 데 있습니다.
만약 물리적인 노드 장애로 파드가 중간에 비정상 종료되거나, 애플리케이션 내부 로직의 에러로 인해 '1' 등 0이 아닌 에러 코드를 반환한다면, Job 컨트롤러는 이를 '실패'로 간주합니다. 그리고 관리자가 명세서에 설정한 정책에 따라 파드를 다른 노드에 다시 스케줄링하여 작업이 성공할 때까지 끈질기게 재시도합니다.
따라서 끝없이 실행되어야 하는 Deployment의 파드와는 근본적인 태생이 다르며, Job의 파드는 명세서의 restartPolicy를 무조건 Never 또는 OnFailure로만 설정해야 작동합니다.
2. 병렬 처리와 완수 조건의 정밀한 통제
Job 컨트롤러는 단순히 파드 하나를 실행하고 끝내는 것을 넘어, 거대한 작업 큐(Work Queue)를 처리하기 위한 고도의 병렬 처리 파라미터들을 제공합니다.
- completions (목표 완료 횟수): 이 Job이 최종적으로 '성공' 판정을 받기 위해 정상적으로 종료되어야 하는 파드의 총개수입니다. 예를 들어 100만 건의 데이터를 10만 건씩 나누어 처리해야 한다면 이 값을 10으로 설정합니다.
- parallelism (동시 실행 개수): 목표 완료 횟수를 채우기 위해 '동시에 병렬로' 띄울 수 있는 파드의 최대 개수입니다. 이 값을 조절함으로써 노드의 리소스가 한 번에 고갈되는 것을 막고 워크로드의 처리 속도를 유연하게 스로틀링(Throttling) 할 수 있습니다.
- backoffLimit (최대 재시도 횟수): 파드 실행이 실패했을 때 다시 시도하는 최대 횟수입니다. (기본값 6) 재시도 간격은 무작정 연속해서 이루어지지 않고, 지수 백오프(Exponential Backoff - 10초, 20초, 40초...) 알고리즘을 따라 점진적으로 늘어나 클러스터의 부하를 방지합니다.
- activeDeadlineSeconds (실행 제한 시간): 무한 루프나 교착 상태(Deadlock)에 빠져 파드가 종료되지 않는 상황을 막기 위한 강력한 타임아웃 밸브입니다. 설정된 시간이 지나면 Job 컨트롤러가 진행 중인 모든 파드를 강제 종료(
SIGKILL) 처리합니다.
3. CronJob 아키텍처: 시간 기반의 계층적 스케줄링
Job이 수동 트리거 기반의 일회성 실행을 보장한다면, CronJob은 리눅스의 cron 유틸리티처럼 특정한 시간 주기에 맞춰 정기적으로 Job을 자동으로 생성해 주는 상위 오케스트레이터입니다. 즉, CronJob 컨트롤러가 주기에 맞춰 Job을 낳고, Job 컨트롤러가 파드를 낳아 관리하는 견고한 계층적 구조를 가집니다.
CronJob 아키텍처에서 가장 중요한 제어 메커니즘은 동시성 정책(concurrencyPolicy)입니다. 만약 이전 주기에 실행된 거대한 백업 작업이 아직 끝나지 않았는데 다음 백업 주기가 도래했을 때, 컨트롤러가 어떻게 행동할지 결정합니다.
- Allow (기본값): 이전 작업 완료 여부와 철저히 무관하게, 시간이 되면 무조건 새로운 Job을 병렬로 중첩하여 실행합니다. 데이터베이스 부하나 리소스 고갈의 위험을 안고 있습니다.
- Forbid: 이전 주기의 작업이 끝나지 않았다면, 시스템 보호를 위해 이번 주기의 실행 스케줄은 과감히 건너뜁니다(Skip).
- Replace: 끝나지 않고 엉켜있는 이전 주기의 Job을 가차 없이 강제로 취소(삭제)해버리고, 신선한 이번 주기의 Job으로 새롭게 교체하여 실행합니다.
4. 스케줄링 지연 방어: startingDeadlineSeconds
쿠버네티스의 컨트롤 플레인에 과부하가 걸렸거나 마스터 노드에 일시적인 장애가 발생하면, CronJob이 정해진 시간에 정확히 Job을 생성하지 못하고 밀리는(Delay) 현상이 발생할 수 있습니다. 이때 startingDeadlineSeconds가 매우 핵심적인 방어 기제로 작용합니다.
예를 들어 자정에 반드시 실행되어야 하는 정산 작업에 이 파라미터를 200(초)으로 설정했다고 가정해 보겠습니다.
API 서버가 지연을 겪다가 자정 12시 3분 10초에 정신을 차렸다면, 200초 이내이므로 늦게라도 정산 Job을 실행합니다. 하지만 시스템 복구가 늦어져 자정 12시 5분이 넘어버렸다면, 컨트롤러는 "이미 골든타임이 지난 작업은 실행할 가치가 없거나 위험하다"고 수학적으로 판단하여 해당 주기를 아예 포기(Miss)하고 다음 날 자정을 기약합니다.
5. 프로덕션 배포 시 필수 아키텍처 고려사항
클라우드 네이티브 환경에서 배치 워크로드를 다룰 때 인프라 엔지니어와 개발자가 반드시 명심해야 할 아키텍처 원칙이 있습니다.
5.1. 애플리케이션의 멱등성 (Idempotency) 보장
파드가 실행되던 중 워커 노드의 플러그가 뽑혀 다운되면, Job 컨트롤러는 작업을 완수하기 위해 파드를 다른 건강한 노드에 다시 띄웁니다. 이는 동일한 데이터 처리 로직이 두 번 이상 실행될 수 있음을 의미합니다. 따라서 배치 애플리케이션 코드 레벨에서 '동일한 작업이 여러 번 중복 실행되더라도 최종 결과 데이터가 꼬이거나 오염되지 않는 멱등성'을 완벽하게 구현해 두는 것이 생명입니다.
5.2. 좀비 객체의 가비지 컬렉션 (Clean-up)
정상적으로 완료된 Job과 파드 객체는 관리자가 에러 로그를 확인할 수 있도록 기본적으로 클러스터에 계속 남아있게 됩니다. 하지만 하루에 수천 번 실행되는 CronJob이라면, 순식간에 etcd 데이터베이스를 비대하게 만들어 컨트롤 플레인의 붕괴를 초래합니다.
이를 방지하기 위해 Job 명세서에 ttlSecondsAfterFinished 파라미터를 명시하여 작업 종료 후 즉시 자폭하도록 만들거나, CronJob의 successfulJobsHistoryLimit 및 failedJobsHistoryLimit 값을 적절한 숫자(예: 3)로 튜닝하여 오래된 이력부터 스스로 깨끗하게 소멸(가비지 컬렉션)되도록 인프라 파이프라인을 구성해야 합니다.
결론적으로 Job과 CronJob 컨트롤러는 쿠버네티스가 단순한 무상태 웹 서버 호스팅 도구를 넘어, 엔터프라이즈의 무겁고 복잡한 백그라운드 데이터 파이프라인까지 완벽하게 삼켜낼 수 있음을 증명하는 가장 강력한 증거입니다. 이 컨트롤러들의 상태 머신과 동시성 제어 원리를 완벽하게 통제하는 것이 흔들림 없는 데이터 아키텍처 구축의 첫걸음입니다.
'1. K8s Core & Architecture > 1.3. 파드(Pod)와 워크로드 아키텍처' 카테고리의 다른 글
| PreStop 훅(Hook)을 활용한 무중단 배포(Zero Downtime Deployment) 아키텍처 (0) | 2026.04.10 |
|---|---|
| 파드의 Graceful Termination: SIGTERM과 SIGKILL 사이의 30초 (1) | 2026.04.10 |
| DaemonSet 동작 원리: 모든 노드에 파드를 보장하는 스케줄링 메커니즘 (0) | 2026.04.09 |
| StatefulSet 아키텍처: 상태를 가지는 애플리케이션의 식별자 유지 비결 (0) | 2026.04.09 |
| 롤링 업데이트(Rolling Update) 전략과 MaxSurge, MaxUnavailable 이해 (0) | 2026.04.09 |