Kubernetes5분 읽기· 연습 문제 3개

CrashLoopBackOff가 뜰 때 가장 먼저 볼 것

Pod가 반복 재시작하면서 이전 로그와 event에 실제 원인이 남는 상황에서 먼저 볼 신호, CLI 확인 순서, 흔한 오진, 안전한 복구 방향을 정리한 InfraTree 가이드입니다.

목차
  1. CrashLoopBackOff는 “에러”가 아니라 kubelet이 재시작을 포기하기 직전이라는 신호다
  2. 1단계 — describe로 Last State와 Exit Code부터 읽는다
  3. 2단계 — 죽은 컨테이너의 로그를 본다 (현재 로그가 아니라)
  4. 가장 흔한 착시: 앱은 멀쩡한데 probe가 죽인다
  5. readiness와 liveness를 헷갈리지 말 것
  6. 구성 누락 계열은 CrashLoop이 아니라 다른 상태로 뜬다
  7. 복구는 되돌리기 쉬운 순서로
  8. 재발 방지 한 가지
  9. 빠른 진단 체크리스트

CrashLoopBackOff는 “에러”가 아니라 kubelet이 재시작을 포기하기 직전이라는 신호다

CrashLoopBackOff 자체는 원인을 알려주지 않는다. 컨테이너가 시작과 종료를 반복하자 kubelet이 재시작 간격을 10초, 20초, 40초로 두 배씩, 최대 5분까지 늘리며 백오프 상태로 표시한 것뿐이다. 그래서 마지막 로그 한 줄부터 읽으면 대개 헛짚는다. 먼저 볼 것은 컨테이너가 어떤 종료 코드로 죽었는가다.

1단계 — describe로 Last State와 Exit Code부터 읽는다

kubectl describe pod <pod> -n <ns>

Containers 섹션의 Last State: Terminated 아래 Exit Code와 Reason이 진단의 8할이다.

  • Exit Code 137 · Reason: OOMKilled — 메모리 한도를 넘겨 SIGKILL. 앱 버그가 아니라 resources.limits.memory가 낮거나 실제 누수다. limit을 올리기 전에 정상 시점 RSS를 먼저 확인한다.
  • Exit Code 1 / 2 — 앱이 스스로 죽음(예외, 설정 파싱 실패). 이때는 --previous 로그가 정답에 가장 가깝다.
  • Exit Code 0인데 재시작 — 프로세스가 정상 종료했는데 restartPolicy: Always라 다시 뜬다. 배치성 작업을 Deployment로 돌린 전형적 오배치다.
  • Reason: Error, Exit Code 143 — SIGTERM 후 유예 안에 못 끝나 정리됨. 종료 처리(graceful shutdown)나 terminationGracePeriodSeconds를 본다.

2단계 — 죽은 컨테이너의 로그를 본다 (현재 로그가 아니라)

kubectl logs <pod> -n <ns> --previous

--previous가 없으면 방금 재시작한 빈 컨테이너를 보게 되어 “로그가 없다”고 오판한다. 컨테이너가 여럿이면 -c <container>로 지정한다. initContainer가 원인이면 Pod가 Init:CrashLoopBackOff로 뜨므로 init 컨테이너 로그를 따로 본다.

가장 흔한 착시: 앱은 멀쩡한데 probe가 죽인다

events에 Liveness probe failed가 반복되면 앱 코드가 아니라 probe 타이밍 문제일 때가 많다. 콜드 스타트가 느린 앱(JVM 기동, 시작 시 마이그레이션)은 initialDelaySeconds가 짧으면 준비되기 전에 liveness가 죽여 무한 재시작에 빠진다. 해법은 limit을 늘리는 게 아니라 startupProbe로 기동 구간을 liveness에서 분리하는 것이다.

startupProbe:
  httpGet: { path: /healthz, port: 8080 }
  failureThreshold: 30
  periodSeconds: 5   # 최대 150초 기동 허용, 그 뒤에만 liveness 시작

readiness와 liveness를 헷갈리지 말 것

readiness 실패는 트래픽만 끊고 재시작시키지 않는다. 그래서 CrashLoopBackOff의 직접 원인은 언제나 liveness 실패 또는 프로세스 종료이지 readiness가 아니다. “재시작이 도는가(liveness/exit)”와 “엔드포인트에서 빠졌는가(readiness)”를 먼저 갈라야 엉뚱한 probe를 만지지 않는다.

구성 누락 계열은 CrashLoop이 아니라 다른 상태로 뜬다

ConfigMap/Secret 참조가 틀리면 보통 CreateContainerConfigError, 이미지를 못 받으면 ImagePullBackOff로 뜬다 — 둘 다 CrashLoopBackOff가 아니다. 상태 문자열을 정확히 먼저 읽으면 이 셋을 섞지 않는다.

복구는 되돌리기 쉬운 순서로

  1. describe/logs로 읽기 전용 상태 고정 — 종료 코드·probe·이벤트를 먼저 기록한다.
  2. 원인이 최근 배포면 kubectl rollout undo로 한 리비전만 되돌려 재현 여부를 확인한다.
  3. OOM이면 실측 RSS 기반으로 limit을 조정하고, probe면 startupProbe/initialDelay를 조정한다.
  4. 전체 재시작·리소스 삭제 같은 비가역 조치는 원인 후보가 하나로 좁혀진 뒤에만 선택한다.

재발 방지 한 가지

사후에는 “왜 오래 못 잡았는가”를 남긴다. 대개 --previous를 안 봤거나, probe 실패를 앱 장애로 오해했거나, 종료 코드를 읽지 않은 것이다. 이 세 줄을 런북 맨 앞에 두면 다음 CrashLoopBackOff는 몇 분 안에 좁혀진다.

빠른 진단 체크리스트

  • describe의 Last State에서 Exit Code·Reason부터 읽는다
  • 137·OOMKilled면 메모리 limit/누수, 143이면 SIGTERM 종료 처리를 본다
  • Exit 1·2면 kubectl logs --previous로 앱 예외를 본다
  • Exit 0인데 재시작이면 restartPolicy·배치 오배치를 의심한다
  • events의 Liveness probe failed 반복이면 probe 타이밍이다
  • 느린 콜드 스타트는 startupProbe로 기동 구간을 분리한다
  • readiness(트래픽만)와 liveness(재시작)를 갈라서 본다
  • CreateContainerConfigError·ImagePullBackOff와 상태 문자열을 구분한다

이 가이드로 연습하기

같은 장애를 다룬 검수된 문제입니다. 원인·복구·재발 방지를 직접 적어 보고 모범 풀이와 비교해 보세요.

다음에 읽을 가이드