Published on

K8s CrashLoopBackOff 원인 10분내 찾는 법

Authors

서버가 죽는 것보다 더 짜증나는 건 계속 살아났다 죽었다를 반복하며 로그도 제대로 못 남기는 상황입니다. Kubernetes의 CrashLoopBackOff는 정확히 그 상태를 의미합니다.

핵심은 단순합니다. 컨테이너가 종료(exit)했고, kubelet이 재시작을 반복하니 back-off(지수적 재시작 지연)가 붙은 것입니다. 따라서 “왜 종료했는지”만 빠르게 찾으면 됩니다.

이 글은 “원인 후보를 늘어놓는” 대신, 10분 안에 원인을 좁히는 실행 순서를 제공합니다. (원인 유형을 더 폭넓게 보고 싶다면 Kubernetes CrashLoopBackOff 원인 12가지와 진단도 함께 참고하세요.)


0~1분: 대상 Pod/컨테이너 확정 (가장 먼저 할 일)

먼저 어떤 Pod가 문제인지, 그리고 어느 컨테이너가 죽는지 확정합니다. 멀티 컨테이너(Pod 내 sidecar 포함)에서 다른 컨테이너는 정상일 수 있습니다.

# 네임스페이스 포함 전체에서 CrashLoopBackOff 찾기
kubectl get pod -A | egrep 'CrashLoopBackOff|Error'

# 특정 네임스페이스에서만
NS=prod
kubectl get pod -n $NS

문제 Pod 이름을 잡았다면, 컨테이너 목록을 확인합니다.

POD=myapp-7c9d6b9d9f-abcde
kubectl get pod -n $NS $POD -o jsonpath='{.spec.containers[*].name}'

1~3분: describe로 “왜 재시작되는지” 이벤트에서 힌트 뽑기

kubectl describe pod는 CrashLoopBackOff 디버깅의 출발점입니다. **마지막 종료 사유(Reason), Exit Code, 이벤트(Event)**가 한 번에 나옵니다.

kubectl describe pod -n $NS $POD

여기서 특히 아래를 집중해서 봅니다.

  • Last State: Terminated / Reason / Exit Code
  • State: Waiting / Reason: CrashLoopBackOff
  • Events: 섹션의 Back-off restarting failed container, Failed, Unhealthy

Exit Code 빠른 해석 (시간 절약용)

  • 1: 앱이 일반 오류로 종료(환경변수 누락, 설정 오류, 예외 등)
  • 2: 잘못된 CLI 인자/사용법 오류(엔트리포인트/커맨드 문제)
  • 126: 실행 권한 문제(permission denied)
  • 127: 실행 파일/명령 없음(not found)
  • 137: OOMKilled(메모리 부족으로 SIGKILL)
  • 139: Segfault

describe에서 OOMKilled가 보이면 사실상 게임 끝입니다. 아래 “리소스/메모리” 섹션으로 바로 가면 됩니다.


3~5분: 로그는 “현재”가 아니라 “이전(previous)”을 본다

CrashLoopBackOff에서는 컨테이너가 이미 죽었기 때문에, 직전 실행의 로그를 봐야 합니다.

# 단일 컨테이너면
kubectl logs -n $NS $POD --previous

# 멀티 컨테이너면 컨테이너 지정
CTR=app
kubectl logs -n $NS $POD -c $CTR --previous

로그가 아예 없다면(출력 전에 즉시 죽는다면) 다음을 의심합니다.

  • 엔트리포인트/커맨드 자체가 실패(127/126)
  • 이미지가 잘못되었거나 아키텍처 불일치
  • 애플리케이션 시작 전에 크래시(네이티브 라이브러리 로딩 실패 등)

로그가 너무 짧을 때: 종료 시그널/스택트레이스 확보 팁

애플리케이션이 stdout에 아무것도 안 남기면, 다음도 같이 확인합니다.

# 컨테이너 종료 사유/코드만 빠르게 보기
kubectl get pod -n $NS $POD -o jsonpath='{.status.containerStatuses[*].lastState.terminated.reason}{"\n"}{.status.containerStatuses[*].lastState.terminated.exitCode}{"\n"}'

5~7분: “가장 흔한 6가지”를 2분 안에 스캔하는 체크리스트

여기부터가 10분 내 원인 찾기의 핵심입니다. 아래 6가지는 CrashLoopBackOff에서 빈도가 매우 높고, 확인도 빠릅니다.

1) Command/Args/Entrypoint 오타 (Exit 127/126)

kubectl get pod -n $NS $POD -o jsonpath='{.spec.containers[0].command}{"\n"}{.spec.containers[0].args}{"\n"}'
  • sh -c 사용 시 따옴표/공백 문제
  • 이미지에 없는 바이너리 실행
  • 실행 권한 없는 스크립트

대응: 이미지 내부에 파일 존재/권한 확인이 필요하면, 같은 이미지로 임시 Pod를 띄워 확인합니다.

IMG=repo/myapp:tag
kubectl run -n $NS debug --rm -it --image=$IMG --command -- sh
# inside
ls -al
which my-binary

2) ConfigMap/Secret 키 누락 → 앱 즉시 종료(Exit 1)

환경변수/볼륨 마운트가 실제로 들어왔는지 확인합니다.

kubectl get pod -n $NS $POD -o yaml | sed -n '1,200p'

특히 다음을 봅니다.

  • envFrom, envsecretKeyRef, configMapKeyRef
  • volumeMountsvolumes의 이름 매칭

3) Readiness/Liveness Probe가 과격해서 “살해”되는 경우

앱은 살아있지만, liveness가 실패해서 kubelet이 죽이고 재시작시키면 CrashLoopBackOff처럼 보입니다.

kubectl get pod -n $NS $POD -o jsonpath='{.spec.containers[0].livenessProbe}{"\n"}{.spec.containers[0].readinessProbe}{"\n"}'

징후: describe 이벤트에 Liveness probe failed가 반복.

대응:

  • initialDelaySeconds 늘리기
  • timeoutSeconds/failureThreshold 조정
  • startup이 긴 앱이면 startupProbe 도입

4) OOMKilled (Exit 137) — 메모리 한 방에 확인

kubectl describe pod -n $NS $POD | egrep -n 'OOMKilled|Limits|Requests|Last State|Exit Code'

리소스가 너무 낮거나, JVM/Node/Python이 기본 메모리 사용량이 큰데 제한을 낮게 준 경우가 흔합니다.

대응:

  • resources.limits.memory 상향
  • 애플리케이션 힙/워커 수 조정

5) 이미지/런타임 불일치(아키텍처) 또는 동적 라이브러리 문제

ARM 노드인데 AMD64 이미지를 올렸거나 그 반대면 컨테이너가 바로 죽을 수 있습니다.

kubectl get node -o wide
kubectl describe node <node-name> | egrep -i 'Architecture|OS Image|Kernel'

이미지 매니페스트가 멀티아키인지 확인(로컬에서):

docker buildx imagetools inspect repo/myapp:tag

6) (EKS) IRSA/권한 문제로 앱이 즉시 종료

앱이 시작하자마자 AWS API 호출을 하고, 권한이 없으면 예외로 죽는 패턴이 많습니다. 이 경우 로그에 AccessDenied, 403, NoCredentialProviders 류가 뜹니다.


7~9분: 컨테이너 안으로 못 들어갈 때의 “디버그 Pod/ephemeral” 전략

CrashLoopBackOff는 kubectl exec가 실패하는 경우가 많습니다(컨테이너가 이미 죽어서). 이럴 때는 같은 이미지로 디버그용 Pod를 띄우거나, 가능하면 ephemeral container를 붙입니다.

방법 A) 같은 이미지로 디버그 Pod 실행

kubectl run -n $NS myapp-debug --rm -it \
  --image=repo/myapp:tag --command -- sh

여기서 다음을 확인합니다.

  • 설정 파일 경로 존재 여부
  • 실행 파일 권한
  • DNS/네트워크
  • 라이브러리 로딩(ldd) 등

방법 B) Ephemeral container로 네임스페이스/네트워크 공유

클러스터 정책에 따라 안 될 수 있지만, 가능하면 가장 강력합니다.

kubectl debug -n $NS -it $POD --image=nicolaka/netshoot --target=$CTR

네트워크/DNS 확인 예:

nslookup kubernetes.default.svc.cluster.local
curl -v http://127.0.0.1:8080/health
curl -v http://my-dependency:8080/

9~10분: “원인 확정”을 위한 최소 증거 3종 세트

10분 내에 결론을 내려야 한다면, 아래 3가지만 확보해도 대부분 원인이 확정됩니다.

  1. describe에서 Last State / Exit Code / Events 캡처
kubectl describe pod -n $NS $POD > /tmp/${POD}.describe.txt
  1. --previous 로그 확보
kubectl logs -n $NS $POD -c $CTR --previous > /tmp/${POD}.${CTR}.prev.log
  1. 스펙(특히 command/args/probe/resources/env) 덤프
kubectl get pod -n $NS $POD -o yaml > /tmp/${POD}.yaml

이 3개를 보면, 보통 다음 중 하나로 귀결됩니다.

  • 앱 설정/환경 변수 문제(로그에 명확)
  • 프로브 설정 문제(이벤트에 명확)
  • OOMKilled(Exit 137)
  • 엔트리포인트/권한 문제(Exit 127/126)
  • 외부 의존성(AWS/DB/HTTP) 실패로 즉시 종료(로그에 403/timeout/connection refused)

실전 예시: 10분 루틴을 그대로 적용해 원인 찾기

아래는 실제로 많이 나오는 패턴을 “명령 순서 그대로” 예시로 구성한 것입니다.

NS=prod
POD=$(kubectl get pod -n $NS | awk '/CrashLoopBackOff/{print $1; exit}')

kubectl describe pod -n $NS $POD | sed -n '1,220p'

# 컨테이너 이름 확인
kubectl get pod -n $NS $POD -o jsonpath='{.spec.containers[*].name}{"\n"}'
CTR=app

# 직전 로그
kubectl logs -n $NS $POD -c $CTR --previous | tail -n 200

# 프로브/커맨드/리소스 빠른 확인
kubectl get pod -n $NS $POD -o jsonpath='{.spec.containers[0].command}{"\n"}{.spec.containers[0].args}{"\n"}{.spec.containers[0].resources}{"\n"}{.spec.containers[0].livenessProbe}{"\n"}'
  • 여기서 로그에 AccessDeniedException이 보이면 IRSA/권한으로 바로 분기
  • Exit Code: 137이면 리소스/메모리로 바로 분기
  • Liveness probe failed면 probe 조정으로 바로 분기

마무리: CrashLoopBackOff는 “증상”, 해답은 Exit/Events/Previous logs

CrashLoopBackOff를 빨리 끝내려면, 감으로 YAML을 뜯기보다 다음 원칙을 지키는 게 가장 효율적입니다.

  • describelogs --previous → 스펙(command/probe/resources/env) 확인 순서를 고정
  • Exit Code로 큰 가지를 먼저 자른다(137/127/126은 특히 강력)
  • exec가 안 되면 디버그 Pod 또는 ephemeral container로 전환

원인 유형을 더 확장해서 보고 싶다면 위에서 언급한 Kubernetes CrashLoopBackOff 원인 12가지와 진단도 함께 읽어두면, 다음 장애에서 “10분 컷” 확률이 더 올라갑니다.