Published on

EKS Pod CrashLoopBackOff 원인 9가지와 해결

Authors

서버리스가 아닌 이상, 운영 중인 EKS에서 CrashLoopBackOff는 언젠가 반드시 만나는 장애 패턴입니다. 중요한 건 “왜 재시작하는지”를 짧은 시간 안에 좁히는 것입니다. 이 글은 EKS 환경에서 자주 나오는 원인 9가지를 증상 → 확인 명령 → 해결 흐름으로 정리합니다.

참고로, 로그가 거의 없거나 컨테이너가 너무 빨리 죽는 경우에는 아래 글의 접근이 특히 도움이 됩니다.


CrashLoopBackOff 빠른 진단 순서(3분 루틴)

아래 3가지만으로도 원인의 70%는 걸러집니다.

1) 이벤트와 종료 코드부터 본다

kubectl -n <ns> get pod <pod> -o wide
kubectl -n <ns> describe pod <pod>

describe에서 특히 볼 것:

  • State: Terminated / Reason / Exit Code
  • Last State에 이전 종료 이유
  • EventsBack-off restarting failed container, OOMKilled, Unhealthy

2) 이전 컨테이너 로그를 본다

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

3) 프로브 실패 여부를 본다

describe podEventsReadiness probe failed 또는 Liveness probe failed가 반복되면 프로브 설계 문제일 확률이 큽니다.


원인 1) 애플리케이션 즉시 크래시(예: 설정 누락, 바인딩 실패)

전형적 증상

  • Exit Code: 1 또는 Exit Code: 2
  • 로그에 Missing env, Cannot read config, BindException: Address already in use 같은 메시지

확인

kubectl -n <ns> logs <pod> -c <container> --previous
kubectl -n <ns> get deploy <deploy> -o yaml | sed -n '1,200p'

해결

  • 필수 환경변수/시크릿 누락 확인
  • 컨테이너가 바인딩하는 포트와 서비스/프로브 포트 일치 확인
  • 애플리케이션이 SIGTERM을 받았을 때 정상 종료하도록 처리(재시작 루프 완화)

예: 환경변수 누락 방지용 최소 검증(Entrypoint 스크립트)

#!/usr/bin/env sh
set -e
: "${DATABASE_URL:?DATABASE_URL is required}"
exec node server.js

원인 2) OOMKilled(메모리 부족) 또는 메모리 누수

EKS에서 가장 흔한 CrashLoopBackOff 원인 중 하나가 메모리입니다.

전형적 증상

  • describe podReason: OOMKilled
  • Exit Code: 137

확인

kubectl -n <ns> describe pod <pod> | sed -n '/Last State:/,/Events:/p'
kubectl -n <ns> top pod <pod>

노드 레벨에서도 OOM 신호가 보일 수 있습니다. 더 깊게는 아래 글의 추적 방법이 유용합니다.

해결

  • resources.limits.memory를 현실적으로 상향
  • JVM/Node/Python 런타임 메모리 옵션을 컨테이너 제한에 맞춤
  • 메모리 누수 의심 시: 트래픽/배치 작업 구간별 메모리 증가 패턴 확인

예: Node.js는 컨테이너 제한을 고려해 힙 상한을 명시하는 편이 안전합니다.

resources:
  requests:
    memory: "256Mi"
  limits:
    memory: "512Mi"
env:
  - name: NODE_OPTIONS
    value: "--max-old-space-size=384"

원인 3) CPU 제한으로 인한 타임아웃, 스타트업 지연(간접적 크래시)

CPU가 빡빡하면 앱이 “죽지는 않지만” 프로브 타임아웃으로 재시작되며 CrashLoopBackOff처럼 보일 수 있습니다.

전형적 증상

  • 이벤트에 Liveness probe failed: context deadline exceeded
  • 배포 직후에만 재시작이 잦고, 워밍업 시간이 길수록 심해짐

확인

kubectl -n <ns> describe pod <pod>
kubectl -n <ns> top pod <pod>

해결

  • 초기 구동이 느린 앱은 startupProbe를 추가
  • cpu request/limit을 적절히 상향

예: 스타트업 프로브로 “초기 부팅 시간”을 분리

startupProbe:
  httpGet:
    path: /health
    port: 8080
  failureThreshold: 30
  periodSeconds: 2
livenessProbe:
  httpGet:
    path: /health
    port: 8080
  failureThreshold: 3
  periodSeconds: 10
readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  periodSeconds: 5

원인 4) Liveness/Readiness 프로브 설계 오류

프로브는 “장애를 감지”하는 장치지만, 잘못 설계하면 “장애를 만든다”가 됩니다.

전형적 증상

  • 애플리케이션 로그는 정상인데, 이벤트에 프로브 실패가 반복
  • livenessProbe가 DB 같은 외부 의존성까지 확인하여 일시 장애에도 재시작

확인

kubectl -n <ns> describe pod <pod> | sed -n '/Liveness:/,/Events:/p'

해결

  • liveness는 프로세스 생존/데드락 정도만 확인
  • 외부 의존성(DB, Redis, 외부 API)은 readiness에서만 반영
  • 타임아웃/초기 지연(initialDelaySeconds)을 실제 부팅 시간에 맞춤

원인 5) 이미지 문제(잘못된 엔트리포인트, 아키텍처 불일치, 이미지 풀 실패)

EKS 노드가 amd64인데 이미지가 arm64만 있거나, 엔트리포인트가 잘못되면 즉시 종료합니다.

전형적 증상

  • exec format error
  • no such file or directory
  • 이벤트에 ImagePullBackOff 또는 ErrImagePull (엄밀히는 CrashLoopBackOff 이전 단계지만 현장에서 함께 묶어 대응)

확인

kubectl -n <ns> describe pod <pod>

해결

  • 멀티 아키텍처 이미지(linux/amd64, linux/arm64)로 빌드
  • 엔트리포인트 파일에 실행 권한 부여
  • 프라이빗 레지스트리면 imagePullSecrets 설정

예: 멀티 아키텍처 빌드

docker buildx build --platform linux/amd64,linux/arm64 -t <repo>/<image>:<tag> --push .

원인 6) Secret/ConfigMap 마운트 또는 환경변수 주입 실패

키 이름이 틀리거나, 존재하지 않는 Secret을 참조하면 컨테이너가 뜨지 않거나 즉시 종료합니다.

전형적 증상

  • 이벤트에 secret "..." not found, configmap "..." not found
  • 앱 로그에 설정 파싱 실패

확인

kubectl -n <ns> get secret
kubectl -n <ns> get configmap
kubectl -n <ns> describe pod <pod>

해결

  • 배포 전 kubectl apply 순서 보장(Secret/ConfigMap 먼저)
  • 키 이름을 명확히 하고, 애플리케이션에서 기본값/검증 로직 추가

예: 키가 없으면 바로 실패시키기(운영에서 조기 감지)

env:
  - name: APP_KEY
    valueFrom:
      secretKeyRef:
        name: app-secret
        key: APP_KEY

원인 7) IRSA(IAM Roles for Service Accounts) 권한/설정 오류

EKS에서 S3, SQS, DynamoDB, Secrets Manager 등을 쓰는 앱이 IRSA 설정이 틀리면 부팅 중 AWS SDK 호출에서 죽고 재시작합니다.

전형적 증상

  • 로그에 AccessDeniedException, InvalidIdentityToken, NoCredentialProviders
  • 특정 API 호출 직후 종료

확인

kubectl -n <ns> get sa <sa> -o yaml
kubectl -n <ns> describe pod <pod>

ServiceAccount에 아래 같은 어노테이션이 있는지 확인합니다.

  • eks.amazonaws.com/role-arn

해결

  • OIDC Provider 연결 여부 확인
  • IAM Role 신뢰 정책에서 sub가 정확히 매칭되는지 확인
  • Pod가 사용하는 ServiceAccount가 의도한 것인지 확인

예: ServiceAccount 어노테이션

apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-sa
  namespace: <ns>
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::<account>:role/<role>

원인 8) 볼륨/스토리지(EBS, EFS) 마운트 실패 또는 권한 문제

특히 EBS PVC는 AZ 제약, 노드 스케줄링, CSI 드라이버 이슈가 섞이면 컨테이너가 올라왔다가 앱이 스토리지 접근 시 죽는 형태로도 나타납니다.

전형적 증상

  • 이벤트에 FailedMount, MountVolume.SetUp failed
  • 앱 로그에 Permission denied, Read-only file system

확인

kubectl -n <ns> describe pod <pod>
kubectl -n <ns> get pvc
kubectl -n <ns> describe pvc <pvc>

해결

  • EBS 사용 시 Pod가 스케줄되는 노드의 AZ와 볼륨 AZ 정합성 확인
  • 컨테이너 runAsUser, fsGroup 등 보안 컨텍스트로 권한 정리

예: fsGroup로 마운트 디렉터리 권한 정리

securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  fsGroup: 1000

원인 9) 네트워크/DNS 문제로 외부 의존성 연결 실패(부팅 중 종료)

애플리케이션이 시작 단계에서 DB/Redis/외부 API 연결을 “필수”로 잡아두고, 연결 실패 시 프로세스가 종료되면 CrashLoopBackOff가 됩니다. EKS에서는 CoreDNS 장애, 보안그룹, 네트워크폴리시, VPC 라우팅 등도 원인이 됩니다.

전형적 증상

  • 로그에 Name or service not known, getaddrinfo ENOTFOUND, i/o timeout
  • 특정 도메인만 간헐적으로 실패

확인

임시 디버그 Pod로 DNS와 연결을 확인합니다.

kubectl -n <ns> run net-debug --rm -it --image=busybox:1.36 --restart=Never -- sh

# inside pod
nslookup <host>
wget -S -O- http://<service>:<port>/health

해결

  • CoreDNS 상태 확인
kubectl -n kube-system get pods -l k8s-app=kube-dns
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=200
  • 외부 의존성 연결 실패 시 앱이 즉시 종료하지 않도록 재시도/백오프 적용
  • 보안그룹/네트워크 ACL/네트워크 폴리시에서 egress 허용 확인

재발 방지 체크리스트(운영에서 효과 큰 것들)

  • startupProbe로 “초기 부팅”과 “운영 중 생존”을 분리
  • 리소스는 requests를 현실적으로 잡고, limits는 OOM 방지 관점에서 관리
  • 종료 코드와 Reason을 표준화해서 알림에 포함(예: Exit Code: 137은 즉시 OOM 의심)
  • 로그가 짧게 끊기면 --previous를 습관화
  • AWS 의존성이 있으면 IRSA를 기본값으로 하고, 배포 템플릿에 ServiceAccount를 포함

추가로, “로그가 없어서 막막한” 상황에서의 우회 진단(이벤트 중심, ephemeral 컨테이너, 프로브/엔트리포인트 점검)은 아래 글에 더 체계적으로 정리했습니다.


마무리

EKS의 CrashLoopBackOff는 결국 “컨테이너가 반복 종료된다”는 결과일 뿐이고, 원인은 **종료 코드, 이벤트, 프로브, 리소스, 의존성(AWS/네트워크/스토리지)**로 빠르게 분류할 수 있습니다. 위 9가지를 순서대로 체크하면, 대부분은 10분 내로 1차 원인에 도달합니다.

원하시면 사용 중인 describe pod 출력(민감정보 제거)과 Deployment 스펙 일부를 기반으로, 어떤 원인에 가장 가까운지와 우선 조치 순서를 함께 잡아드릴게요.