- Published on
EKS CrashLoopBackOff인데 로그가 0줄? 원인 8가지
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스처럼 보이던 EKS가 갑자기 CrashLoopBackOff에 빠졌는데, 정작 kubectl logs는 0줄(빈 출력) 이라면 디버깅이 급격히 어려워집니다. 하지만 대부분의 케이스는 “애플리케이션이 로그를 안 남겼다”가 아니라, 로그가 생성되기 전에 컨테이너가 죽었거나, 내가 보고 있는 대상이 잘못됐거나, 로그 파이프라인/런타임 이슈로 로그를 못 읽는 상황입니다.
이 글에서는 EKS에서 자주 만나는 “CrashLoopBackOff + 로그 0줄” 조합의 원인 8가지를 우선순위대로 정리하고, 각 원인별로 증상, 확인 방법, 해결 팁을 제공합니다.
먼저: ‘0줄’이 진짜 0인지부터 확인
아래 3가지는 모든 케이스에서 공통으로 먼저 수행하세요.
1) 종료된 이전 컨테이너 로그 확인 (--previous)
CrashLoopBackOff는 “재시작 중” 상태이므로 현재 컨테이너가 막 떠 있거나 이미 죽었을 수 있습니다.
kubectl logs -n <ns> <pod> -c <container> --previous
--previous에서 로그가 나오면: 원인은 애플리케이션/프로세스 레벨일 가능성이 큼--previous도 0줄이면: 컨테이너가 로그를 남기기 전에 죽었거나, 로그 접근 자체가 안 되는 상황일 수 있음
2) 이벤트로 “왜 죽는지”를 먼저 본다
로그가 없을 때 가장 강력한 단서는 이벤트입니다.
kubectl describe pod -n <ns> <pod>
kubectl get events -n <ns> --sort-by=.lastTimestamp | tail -n 50
3) 컨테이너 상태/종료코드 확인
kubectl get pod -n <ns> <pod> -o jsonpath='{.status.containerStatuses[*].state.terminated.exitCode}{"\n"}'
kubectl get pod -n <ns> <pod> -o jsonpath='{.status.containerStatuses[*].lastState.terminated.reason}{"\n"}'
exitCode=137: OOMKill/강제 종료 계열 가능성reason=Error,ContainerCannotRun,StartError: 런타임/권한/엔트리포인트 문제 가능성
원인 1) 컨테이너가 너무 빨리 죽어서 stdout/stderr에 아무것도 못 씀
전형적 증상
kubectl logs와--previous모두 빈 출력- 이벤트에는
Back-off restarting failed container정도만 보임 - 컨테이너 시작 직후 즉시 종료 (exitCode 1 등)
확인 방법
describe pod에서Started/Finished시간이 거의 동일- 엔트리포인트/커맨드 오타, 필수 env 누락 등
해결 팁
- 임시로 sleep를 넣어 컨테이너를 붙잡고, 내부에서 수동 실행해 원인을 확인합니다.
# (디버그용) command를 잠시 바꿔서 컨테이너가 바로 죽지 않게 함
spec:
containers:
- name: app
image: <image>
command: ["/bin/sh", "-c"]
args: ["echo 'debug mode'; sleep 3600"]
- 그 다음 exec로 들어가 원래 커맨드를 실행해 봅니다.
kubectl exec -n <ns> -it <pod> -c app -- /bin/sh
# 내부에서 원래 엔트리포인트/바이너리 실행
원인 2) kubectl logs 대상이 틀림 (컨테이너/파드/레이블)
멀티 컨테이너 파드(사이드카 포함)에서 가장 흔합니다.
전형적 증상
kubectl logs <pod>는 0줄인데, 실제로는 다른 컨테이너가 죽고 있음- 혹은 내가 보고 있는 파드가 이미 교체되어 새 파드에는 로그가 없음
확인 방법
kubectl get pod -n <ns> <pod> -o wide
kubectl get pod -n <ns> <pod> -o jsonpath='{.spec.containers[*].name}{"\n"}'
해결 팁
- 컨테이너 지정해서 확인
kubectl logs -n <ns> <pod> -c <container>
kubectl logs -n <ns> <pod> -c <container> --previous
- 레플리카셋 교체가 잦다면, 레이블로 “최근 파드들”을 한 번에 확인
kubectl get pod -n <ns> -l app=<label> --sort-by=.status.startTime
원인 3) 이미지가 사실상 실행 불가 (ENTRYPOINT/CMD/아키텍처 불일치)
이미지가 pull은 되었지만 실행이 안 되는 케이스입니다. 이 경우 애플리케이션 로그가 생성될 기회 자체가 없습니다.
전형적 증상
ContainerCannotRun,StartError,exec format error등- ARM/AMD64 혼용(Graviton 노드에서 x86 이미지 등)
확인 방법
kubectl describe pod -n <ns> <pod> | sed -n '/State:/,/Events:/p'
해결 팁
- 멀티 아키텍처 이미지로 빌드하거나 노드 아키텍처에 맞는 이미지를 사용
- ENTRYPOINT/CMD가 존재하는지, 실행 권한이 있는지 확인
원인 4) OOMKilled(메모리 부족)로 죽어서 로그가 남지 않음
메모리 부족은 “로그 0줄”과 자주 엮입니다. 특히 앱이 초기화 중 대량 메모리를 먹고 바로 죽으면 stdout에 찍을 틈이 없습니다.
전형적 증상
exitCode=137,reason=OOMKilled- 노드/파드 메모리 압박 이벤트
확인 방법
kubectl describe pod -n <ns> <pod> | egrep -i 'oom|killed|memory'
해결 팁
- requests/limits 재조정
- 초기화 단계의 메모리 피크(빌드/압축/캐시) 제거
- Node 사이징/오토스케일 조정
빌드/런타임에서 메모리 피크가 나는 워크로드라면 캐시/메모리 튜닝 관점도 함께 보세요: Next.js 14 빌드 OOM·느려짐 해결 - SWC 캐시·메모리 튜닝
원인 5) liveness/readiness probe가 너무 공격적이라 “정상 프로세스”도 죽임
프로세스는 살아있는데 liveness probe 실패로 kubelet이 계속 죽여서 CrashLoopBackOff가 됩니다. 이때 앱 로그가 거의 없거나(혹은 readiness 로그만) 0줄처럼 보일 수 있습니다.
전형적 증상
- 이벤트에
Liveness probe failed반복 - 앱은 부팅 시간이 긴데 initialDelay가 짧음
확인 방법
kubectl describe pod -n <ns> <pod> | egrep -i 'liveness|readiness|probe|failed'
해결 팁
startupProbe를 추가해 “부팅 완료 전”에는 liveness를 유예initialDelaySeconds,timeoutSeconds,failureThreshold완화
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 60
periodSeconds: 2
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
원인 6) 권한/파일시스템 문제로 앱이 시작 전에 죽음 (읽기 전용, 권한, Secret/Config)
EKS에서 보안 강화를 위해 readOnlyRootFilesystem: true, non-root 실행, 엄격한 fsGroup 등을 적용하면, 앱이 /tmp, 로그 파일 경로, 캐시 디렉터리 등에 쓰려다 즉시 죽을 수 있습니다.
전형적 증상
- 로그 0줄 또는 아주 짧은 에러만 남고 종료
- 이벤트/종료 사유는 단순
Error
확인 방법
describe에서 컨테이너 securityContext 확인- Secret/ConfigMap 마운트 경로 존재 여부 확인
해결 팁
- 쓰기 경로를
emptyDir로 제공 - 앱의 로그를 파일이 아니라 stdout/stderr로 내보내기
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
원인 7) 로그 드라이버/런타임/노드 문제로 kubectl logs가 비어 보임
kubectl logs는 노드의 컨테이너 런타임 로그 파일(또는 CRI 로그)을 읽어옵니다. 노드의 디스크 압박, containerd 문제, 로그 파일 손상/권한 문제 등이 있으면 컨테이너가 뭔가를 찍었어도 API로 못 가져와 0줄처럼 보일 수 있습니다.
전형적 증상
- 같은 노드에 뜬 다른 파드도 로그가 이상함
- 이벤트에
NodeHasDiskPressure,containerd관련 메시지
확인 방법
- 파드가 어느 노드에 있는지 확인
kubectl get pod -n <ns> <pod> -o wide
kubectl describe node <node>
해결 팁
- 노드 디스크 정리(특히
/var/log, 컨테이너 이미지/레이어) - 문제 노드 cordon/drain 후 교체
kubectl cordon <node>
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
원인 8) 애플리케이션이 stdout/stderr로 로그를 안 남김 (파일로만 로깅)
의외로 많습니다. 로컬에서는 파일 로그가 잘 쌓이는데, 컨테이너에서는 파일이 ephemeral이거나 경로가 없어서 조용히 실패하고, stdout에는 아무 것도 안 찍는 구성입니다.
전형적 증상
kubectl logs0줄- exec로 들어가 보면
/var/log/app.log같은 파일이 존재하거나(혹은 생성 실패)
확인 방법
디버그 모드로 컨테이너를 잠깐 살려서 파일을 확인합니다.
kubectl exec -n <ns> -it <pod> -c <container> -- /bin/sh
ls -al /var/log
find / -maxdepth 3 -type f -name "*.log" 2>/dev/null | head
해결 팁
- 기본은 stdout/stderr 로깅으로 전환
- 파일 로깅이 필요하면 사이드카(Fluent Bit 등) 또는 볼륨 마운트로 수집
예: Python이라면 기본 로거를 stdout으로
import logging
import sys
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
handlers=[logging.StreamHandler(sys.stdout)],
)
logging.info("app started")
실전 체크리스트: 3분 안에 결론 내는 순서
kubectl describe pod로 Events 확인kubectl logs --previous로 이전 컨테이너 로그 확인exitCode/reason으로 OOM/StartError 분기- probe 실패 여부 확인
- 권한/FS/Secret 마운트 점검
- 노드 디스크/런타임 이상 점검
부록: 자주 쓰는 원샷 커맨드 모음
파드 상태/이벤트/컨테이너 상태 한번에
NS=<ns>
POD=<pod>
kubectl get pod -n $NS $POD -o wide
kubectl describe pod -n $NS $POD
kubectl get pod -n $NS $POD -o jsonpath='{range .status.containerStatuses[*]}{.name}{"\t"}{.restartCount}{"\t"}{.lastState.terminated.reason}{"\t"}{.lastState.terminated.exitCode}{"\n"}{end}'
레이블 기준으로 최근 파드 로그 훑기
NS=<ns>
LABEL='app=myapp'
for p in $(kubectl get pod -n $NS -l $LABEL --sort-by=.status.startTime -o jsonpath='{.items[*].metadata.name}'); do
echo "==== $p ===="
kubectl logs -n $NS $p --all-containers --previous --tail=50 2>/dev/null || true
done
함께 보면 좋은 글
- 이미지 pull 단계에서 인증서(x509) 문제로 시작조차 못 하는 경우도 종종 CrashLoop로 오해됩니다: Kubernetes ErrImagePull x509 인증서 오류 해결
- EKS에서 L7 장애(502/504)가 애플리케이션 크래시처럼 보이는 경우, 인그레스/ALB 측 체크도 병행하세요: AWS ALB 502·504 난사 - 원인별 해결 체크리스트
- gRPC 서비스라면 메시지 크기/프록시 설정 문제로 장애가 증폭될 수 있습니다: EKS에서 413 없이 502? gRPC 최대 메시지 해결
CrashLoopBackOff에서 로그가 0줄일 때 핵심은 “로그가 없어서 못 고친다”가 아니라, 로그가 생기기 전에 죽는지, 내가 로그를 잘못 보고 있는지, 런타임/노드가 로그를 못 주는지를 빠르게 분기하는 것입니다. 위 8가지를 순서대로 체크하면 대부분의 케이스는 10분 안에 원인을 좁힐 수 있습니다.