
왜 잡(Job) 장애가 발생하면 당황하게 될까요
새벽에 알람이 울려 클러스터에 접속했을 때, 배치 작업이 수행되어야 할 시간에 Failed 상태로 멈춰 있는 잡을 마주한다면 누구나 당황하기 마련이에요. 서비스 API와 달리 잡은 특정 작업을 완료하고 종료되는 것이 목적이라, 한 번 실패하면 다음 스케줄까지 기다려야 하거나 수동으로 개입해야 하는 경우가 많기 때문이에요.
단순히 애플리케이션 코드가 잘못된 것인지, 아니면 쿠버네티스 노드의 리소스가 부족해서 실행조차 안 된 것인지 판단하는 것부터가 고난도 작업이에요. 로그를 봐도 왜 죽었는지 명확하지 않을 때가 많고, backoffLimit 설정 때문에 계속해서 재시도만 반복되는 상황을 보면 머릿속이 하얘지기도 하죠.
하지만 잡 트러블슈팅은 일정한 패턴이 있어요. 문제의 범위를 인프라, 컨테이너, 애플리케이션으로 나누어 좁혀나가면 의외로 빠르게 해결책을 찾을 수 있어요. 이번 글에서는 현장에서 바로 써먹을 수 있는 진단 순서와 상황별 조치 방법을 정리해 드릴게요.
이 글을 읽고 나면 다음과 같은 능력을 갖추게 돼요.
- 잡이 실패했을 때 가장 먼저 확인해야 할 명령어를 알 수 있어요.
- 로그와 이벤트를 조합해 장애 원인을 논리적으로 좁히는 법을 배워요.
- 리소스 부족이나 설정 오류 같은 빈번한 문제의 해결책을 익혀요.
- 장애가 다시 발생하지 않도록 예방 설정을 적용할 수 있어요.
트러블슈팅 시작 전 꼭 알아야 할 핵심 개념
문제를 풀기 전에 우리가 다루는 대상이 무엇인지 정확히 정의하는 것이 중요해요. 쿠버네티스에서 잡은 단순히 포드를 실행하는 것이 아니라, 성공(Succeeded)이라는 목표를 달성하기 위해 설계된 컨트롤러예요. 따라서 포드가 죽었다고 해서 무조건 잡이 실패한 것은 아니라는 점을 명심해야 해요.
잡의 상태를 진단할 때 우리가 마주하게 될 주요 상태값들을 먼저 정리해 볼게요. 이 표를 통해 현재 내 잡이 어떤 단계에서 막혀 있는지 빠르게 판단해 보세요.
| 상태(Status) | 의미 | 트러블슈팅 포인트 |
|---|---|---|
| Running | 작업이 정상적으로 진행 중임 | 로그를 통해 작업 진행률 확인 |
| Succeeded | 설정된 완료 횟수만큼 성공함 | 정상 종료 여부 검증 |
| Failed | 재시도 횟수를 초과하여 실패함 | exit code 및 이벤트 확인 |
| Pending | 스케줄링을 기다리거나 준비 중임 | 리소스 부족 또는 노드 제약 확인 |
또한, 잡 장애를 분석할 때는 backoffLimit이라는 개념을 반드시 이해해야 해요. 이는 잡이 실패했을 때 쿠버네티스가 자동으로 몇 번까지 재시도할지를 결정하는 설정이에요. 만약 이 값이 너무 높으면 잘못된 설정 때문에 무한 루프에 빠진 것처럼 보일 수 있고, 너무 낮으면 일시적인 네트워크 오류에도 작업이 바로 실패할 수 있어요.
잡 트러블슈팅의 핵심은 ‘포드가 왜 죽었는가’와 ‘잡 컨트롤러가 왜 작업을 중단했는가’를 분리해서 생각하는 것이에요. 포드는 애플리케이션의 결과물이고, 잡은 그 결과물을 관리하는 감독관이기 때문이에요.
마지막으로 준비해야 할 것은 적절한 권한이에요. 잡이 사용하는 ServiceAccount가 데이터베이스나 외부 API에 접근할 수 있는 권한을 가지고 있는지, 그리고 여러분의 계정이 포드의 로그를 읽을 수 있는 권한이 있는지 미리 체크해 두는 것이 시간을 아끼는 방법이에요.
잡 장애 원인 파악과 단계별 진단 프로세스
이제 본격적으로 실전 진단에 들어가 볼게요. 잡이 실패했다는 신호를 받으면, 감에 의존하지 말고 아래의 5단계 프로세스를 따라가 보세요. 범위를 좁혀나가는 것이 핵심이에요.
STEP 1. 전체적인 상태와 이벤트 확인하기
가장 먼저 해야 할 일은 잡의 전반적인 상태를 훑어보는 것이에요. 포드가 생성이 안 된 것인지, 실행 중에 죽은 것인지 알아야 하거든요. 우선 kubectl describe job [잡-이름] 명령어를 사용하세요. 이 명령어는 잡 컨트롤러가 어떤 동작을 했는지, 그리고 현재 어떤 상태인지를 보여주는 핵심적인 지표예요.
여기서 눈여겨봐야 할 곳은 맨 아래에 있는 Events 섹션이에요. 만약 FailedCreate라는 메시지가 보인다면, 이건 애플리케이션 문제가 아니라 쿠버네티스가 포드를 만들지 못하고 있다는 뜻이에요. 노드에 자원이 없거나, 특정 노드에 배치하라는 조건(NodeSelector)을 충족하지 못했을 가능성이 매우 높아요.
STEP 2. 생성된 포드의 상세 상태 분석
잡이 포드를 생성했다면, 이제 포드 단위로 들어가야 해요. 잡에 속한 포드들의 목록을 확인하기 위해 kubectl get pods --selector=job-name=[잡-이름]을 입력하세요. 여기서 포드의 상태가 CrashLoopBackOff인지, Error인지, 아니면 OOMKilled인지 확인하는 것이 급선무예요.
각 상태는 아주 다른 의미를 가져요. CrashLoopBackOff는 포드는 떴지만 애플리케이션이 즉시 종료되고 다시 뜨는 과정을 반복한다는 뜻이고, OOMKilled는 메모리 제한을 초과해서 시스템이 강제로 프로세스를 죽였다는 명확한 신호예요. 이 단계에서 문제의 성격이 인프라인지 애플리케이션인지 80% 이상 결정돼요.
STEP 3. 애플리케이션 로그를 통한 내부 로직 진단
포드의 상태가 Error라면 이제 내부를 들여다봐야 해요. kubectl logs [포드-이름] 명령어를 사용하세요. 만약 포드가 이미 종료되어 로그를 볼 수 없다면, kubectl logs [포드-이름] --previous 옵션을 써서 이전 컨테이너의 로그를 찾아내야 해요. 이것이 종료된 포드의 마지막 유언을 듣는 유일한 방법이에요.
로그를 볼 때는 다음 세 가지를 중점적으로 보세요.
- 데이터베이스 연결 실패 (Connection Timeout, Access Denied)
- 설정 파일 로드 실패 (File Not Found, Invalid Config)
- 비즈니스 로직 상의 예외 (NullPointerException, Out of Memory 등)
애플리케이션 로그에 에러가 찍히지 않았는데도 포드가 종료된다면, 쉘 스크립트의 문법 오류나 실행 권한 문제를 의심해 봐야 해요.
STEP 4. 리소스 및 시스템 제약 사항 검토
애플리케이션 코드에 문제가 없다면 시스템 환경을 의심해야 해요. 특히 잡은 대량의 데이터를 처리하는 경우가 많아서 리소스 사용량이 급증하곤 하죠. OOMKilled가 발생했다면, 잡의 resources.limits.memory 값을 늘려주거나, 애플리케이션의 메모리 사용 효율을 높이는 작업을 해야 해요.
메모리 제한을 너무 낮게 설정하면 작업 중간에 프로세스가 죽어버려 데이터 정합성이 깨질 수 있어요. 반대로 너무 높게 잡으면 다른 서비스의 자원을 뺏어올 수 있으니 적절한 수치를 찾는 테스트가 필요해요.
또한, 디스크 공간(Ephemeral Storage)이 부족한지도 확인해야 해요. 로그 파일이 너무 크게 쌓이거나 임시 파일을 대량으로 생성하는 잡이라면 노드의 디스크 압박(Disk Pressure) 때문에 포드가 쫓겨날 수 있어요.
STEP 5. 네트워크 및 외부 의존성 확인
마지막으로 잡이 통신해야 하는 외부 서비스와의 연결을 확인하세요. 쿠버네티스 내부의 Service나 Endpoint가 올바른지, 혹은 외부 API로 나가는 방화벽 정책이 잡이 실행되는 노드에서 허용되어 있는지 체크해야 해요. 가끔 DNS 문제로 인해 외부 도메인을 찾지 못해 잡이 무한 대기에 빠지는 경우도 빈번해요.
[실전 시나리오: DB 마이그레이션 잡 실패 사례]
새로운 스키마를 적용하는 잡이 실행되자마자 1분 만에 종료되었습니다. 진단 과정은 다음과 같았어요.
kubectl describe job확인 → Events에 ‘Failed’ 기록 확인.kubectl get pods확인 → 포드 상태가 Error임 확인.kubectl logs --previous확인 → ‘Access denied for user root@10.244.0.1’ 메시지 발견.- 결론: 잡이 사용하는 ServiceAccount에 DB 접근 권한이 없거나, DB 쪽에서 해당 IP의 접속을 차단하고 있었음. 권한 수정 후 성공.
자주 하는 실수와 해결법 및 자주 묻는 질문
자주 하는 실수와 해결법
현장에서 자주 발생하는 실수들을 모아봤어요. 비슷한 상황을 겪고 있다면 바로 적용해 보세요.
- ❌ 실수: 잡이 계속 실패하는데 원인을 모르고 재시작만 반복함 → 왜 발생하는가: backoffLimit 설정만 믿고 로그를 보지 않음 → ✅ 해결법: 반드시
kubectl logs로 에러 메시지를 먼저 확인하세요. - ❌ 실수: 리소스 제한을 설정하지 않음 → 왜 발생하는가: 개발 환경에서는 잘 돌아갔기 때문 → ✅ 해결법: 잡의 특성에 맞춰 requests와 limits를 반드시 명시하여 노드 전체의 안정성을 확보하세요.
- ❌ 실수: 완료된 포드를 찾지 못함 → 왜 발생하는가: 성공한 포드는 기본적으로 숨겨지거나 리스트에서 찾기 어려움 → ✅ 해결법:
kubectl get pods -a(또는 설정에 따라 모든 상태 보기)를 통해 완료된 포드의 로그를 확인하세요. - ❌ 실수: 컨테이너 이미지 태그를 ‘latest’로 사용함 → 왜 발생하는가: 편리하기 때문 → ✅ 해결법: 잡은 실행 시점의 버전이 중요하므로 반드시 v1.2.3처럼 고정된 태그를 사용하세요.
- ❌ 실수: 환경 변수 설정을 누락함 → 왜 발생하는가: 로컬 환경과 클러스터 환경의 차이를 간과함 → ✅ 해결법: ConfigMap과 Secret이 잡에 올바르게 마운트되었는지 확인하세요.
자주 묻는 질문
Q. 잡이 성공했는데 포드가 왜 사라지지 않나요?
잡이 성공하면 포드는 Succeeded 상태로 남게 돼요. 이는 관리자가 로그를 확인하거나 사후 분석을 할 수 있도록 쿠버네티스가 의도적으로 남겨두는 것이에요. 자원을 아끼고 싶다면 ttlSecondsAfterFinished 설정을 사용해 보세요.
Q. 특정 포드가 계속 Pending 상태로 있어요. 어떻게 하죠?
대부분 리소스 부족 문제입니다. kubectl describe pod을 실행해 보세요. ‘Insufficient cpu’나 ‘Insufficient memory’라는 메시지가 있다면 노드에 여유 공간이 없는 것이니, 노드를 증설하거나 잡의 리소스 요청량을 줄여야 해요.
Q. 잡을 강제로 다시 실행하고 싶을 때는 어떻게 하나요?
이미 완료되었거나 실패한 잡은 그대로 둔 채, 새로운 잡 오브젝트를 생성하는 것이 가장 깔끔해요. 기존 잡을 삭제하고 다시 만드는 방법도 있지만, 히스토리 관리를 위해 새로운 이름을 가진 잡을 생성하는 것을 추천해요.
Q. OOMKilled 에러가 떴는데 메모리를 늘려도 또 죽어요. 이유가 뭘까요?
두 가지 가능성이 있어요. 첫째, 메모리 누수(Memory Leak)가 있는 코드 자체의 문제일 수 있고, 둘째, 정말로 처리해야 할 데이터 양에 비해 할당된 메모리가 턱없이 부족한 경우예요. 코드 리뷰와 함께 점진적으로 메모리 제한을 높여가며 테스트해 보세요.
Q. 잡이 실행되자마자 Error 상태로 바뀌며 죽어요. 로그도 안 보여요.
애플리케이션이 뜨기도 전에 프로세스가 종료되는 경우예요. 주로 컨테이너 실행 명령(command/args)이 잘못되었거나, 필수적인 설정 파일이 없는 경우죠. 이럴 때는 kubectl describe pod의 이벤트 섹션을 반드시 확인해야 해요.
장애 대응을 위한 최종 점검 리스트
트러블슈팅은 막막한 싸움이 아니라, 논리적인 추적 과정이에요. 갑작스러운 장애 상황에서도 당황하지 않도록 아래의 핵심 요약을 머릿속에 저장해 두세요.
- 1단계:
describe job으로 전체 이벤트 흐름 파악하기 - 2단계: 포드 상태(Error, OOMKilled, Pending) 확인하기
- 3단계:
logs --previous로 종료 직전의 로그 확보하기 - 4단계: 리소스(CPU, Memory) 제한 값과 실제 사용량 비교하기
- 5단계: 네트워크 및 권한(ServiceAccount) 설정 검증하기
장애를 해결했다면 거기서 멈추지 말고, 왜 이런 일이 일어났는지 기록하세요. 그리고 다음 단계를 준비해야 합니다.
- 오늘 할 일: 이번 장애의 근본 원인을 문서화하고 팀원들과 공유하기
- 이번 주 할 일: 실패한 잡의 리소스 사용량을 모니터링하여 적정 임계치 재설정하기
- 실행 직전 할 일: ttlSecondsAfterFinished 설정을 추가해 클러스터 자원 관리 자동화하기
실무에서 겪는 트러블슈팅은 이론보다 훨씬 까다롭지만, 그만큼 성장의 밑거름이 됩니다. 오늘 정리한 내용이 여러분의 밤잠을 지켜주는 데 도움이 되길 바라요. 실습 환경에서 직접 적용해 보고, 해결되지 않는 막히는 부분이 있다면 언제든 댓글로 질문을 남겨 주세요!
관련해서 더 깊이 있는 학습을 원하신다면 다음 글들도 참고해 보세요.