쿠버네티스(Kubernetes)를 운영하다 보면 한 번쯤은 만나게 되는 에러가 있습니다. 바로 CrashLoopBackOff 에러죠. 파드 상태를 확인했는데 이 메시지가 떠있으면 마음이 급해지곤 합니다. 오늘은 CrashLoopBackOff 에러가 정확히 무엇인지, 왜 발생하는지, 그리고 어떻게 근본적으로 해결할 수 있는지 실제 사례와 함께 알아보겠습니다.
1. CrashLoopBackOff란 정확히 무엇인가?
CrashLoopBackOff는 쿠버네티스에서 컨테이너가 반복적으로 시작되고 실패하는 상태를 나타냅니다. 이름에서 알 수 있듯이 ‘Crash(충돌) → Loop(반복) → BackOff(지연)’의 순환 구조로 이루어져 있어요.
중요한 점은 CrashLoopBackOff 자체가 에러가 아니라는 것입니다. 실제로는 근본적인 문제가 있을 때 쿠버네티스가 보여주는 ‘증상’에 가깝습니다.
동작 원리
- 컨테이너가 시작됩니다
- 어떤 이유로 컨테이너가 실패하거나 종료됩니다
- 쿠버네티스가 컨테이너를 재시작합니다
- 다시 실패하면 백오프 딜레이를 적용합니다 (10초 → 20초 → 40초… 최대 5분)
- 문제가 해결될 때까지 이 과정을 반복합니다
이러한 백오프 딜레이는 시스템 리소스를 보호하고 관리자가 문제를 진단할 시간을 확보하기 위한 쿠버네티스의 똑똑한 메커니즘입니다.
2. CrashLoopBackOff 확인 방법
기본 확인 명령어
kubectl get pods
출력 예시:
NAME READY STATUS RESTARTS AGE
nginx-5796d5bc7d-xtl6q 0/1 CrashLoopBackOff 4 1m
여기서 주목해야 할 포인트들:
- READY:
0/1
은 1개 컨테이너 중 0개가 준비된 상태 - STATUS:
CrashLoopBackOff
상태 표시 - RESTARTS:
4
는 이미 4번 재시작했다는 의미
네임스페이스 지정 확인
kubectl get pods -n <네임스페이스명>
3. CrashLoopBackOff 주요 원인들
실제 운영 환경에서 자주 발생하는 원인들을 정리해보겠습니다.
3.1 리소스 부족 문제
가장 흔한 원인 중 하나입니다. 메모리나 CPU 한계를 초과하면 컨테이너가 OOMKilled(Out of Memory Killed) 상태가 되어 재시작됩니다.
증상 확인:
kubectl describe pod <파드명>
해결 방법:
resources:
limits:
memory: "512Mi" # 기존 256Mi에서 증가
cpu: "500m" # 기존 250m에서 증가
requests:
memory: "256Mi"
cpu: "250m"
3.2 애플리케이션 설정 오류
환경변수 누락, 잘못된 포트 설정, 설정 파일 오류 등이 원인이 될 수 있습니다.
예시: 데이터베이스 연결 정보 누락
env:
- name: DB_HOST
value: "mysql-service" # 누락된 환경변수 추가
- name: DB_PORT
value: "3306"
3.3 이미지 문제
잘못된 Docker 이미지나 태그, 권한 문제 등이 원인이 될 수 있습니다.
확인 방법:
# 이미지 Pull 상태 확인
kubectl describe pod <파드명> | grep -A5 "Events:"
3.4 의존성 서비스 문제
외부 서비스나 다른 파드와의 연결 문제로 발생할 수 있습니다.
4. 단계별 진단 및 해결 방법
4.1 1단계: 파드 상세 정보 확인
kubectl describe pod <파드명> -n <네임스페이스>
이 명령어로 확인해야 할 정보들:
- 컨테이너 상태 (State: Waiting, Reason: CrashLoopBackOff)
- 마지막 종료 원인 (Last State: Terminated, Reason: Error)
- 종료 코드 (Exit Code)
- 이벤트 목록 (Events 섹션)
4.2 2단계: 로그 분석
현재 로그 확인:
kubectl logs <파드명> -n <네임스페이스>
이전 컨테이너 로그 확인 (중요!):
kubectl logs <파드명> --previous -n <네임스페이스>
실시간 로그 모니터링:
kubectl logs -f <파드명> -n <네임스페이스>
4.3 3단계: 이벤트 확인
전체 이벤트 확인:
kubectl get events --sort-by=.metadata.creationTimestamp -n <네임스페이스>
특정 파드 이벤트만 확인:
kubectl get events --field-selector involvedObject.name=<파드명> -n <네임스페이스>
4.4 4단계: 디플로이먼트 확인
kubectl describe deployment <디플로이먼트명> -n <네임스페이스>
5. 실제 발생되어 해결된 몇 가지 케이스들…
사례 1: 메모리 부족으로 인한 OOMKilled
문제 상황:
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
해결 방법:
spec:
containers:
- name: app
image: myapp:latest
resources:
limits:
memory: "1Gi" # 기존 512Mi에서 증가
requests:
memory: "512Mi"
사례 2: 라이브니스 프로브 설정 오류
문제 상황:
Liveness probe failed: Get "http://10.244.0.5:8080/health": dial tcp 10.244.0.5:8080: connect: connection refused
해결 방법:
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60 # 기존 30초에서 증가
periodSeconds: 30 # 체크 간격 조정
timeoutSeconds: 10 # 타임아웃 증가
사례 3: 환경변수 누락
문제 상황: 애플리케이션 로그에서 DB_HOST environment variable not found
에러 발생
해결 방법:
env:
- name: DB_HOST
valueFrom:
secretKeyRef:
name: db-secret
key: host
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
6. 추가적으로 디버깅 하는 방법
6.1 임시 컨테이너로 디버깅
컨테이너가 계속 죽어서 접근이 어려울 때 유용합니다.
# entrypoint를 sleep으로 변경하여 컨테이너를 살려두기
kubectl run debug-pod --image=<이미지명> --restart=Never -- sleep 3600
또는 디플로이먼트 YAML에서 임시로 command를 변경:
spec:
containers:
- name: app
image: myapp:latest
command: ["sleep", "3600"] # 임시로 추가
6.2 kubectl exec으로 컨테이너 접근
kubectl exec -it <파드명> -- /bin/bash
컨테이너 내부에서 확인할 사항들:
- 환경변수:
env
- 파일 시스템:
ls -la /app
- 프로세스:
ps aux
- 네트워크:
netstat -tlnp
6.3 리소스 모니터링
# 파드별 리소스 사용량 확인
kubectl top pods
# 노드별 리소스 사용량 확인
kubectl top nodes
7. CrashLoopBackOff 에러 예방 및 모니터링 방법
7.1 프로브 설정 최적화
레디니스 프로브:
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
라이브니스 프로브:
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 30
failureThreshold: 3
7.2 리소스 설정 가이드라인
애플리케이션 유형 | Memory Request | Memory Limit | CPU Request | CPU Limit |
---|---|---|---|---|
마이크로서비스 | 128Mi | 256Mi | 100m | 200m |
웹 애플리케이션 | 256Mi | 512Mi | 250m | 500m |
데이터베이스 | 1Gi | 2Gi | 500m | 1000m |
7.3 모니터링 알람 설정
Prometheus 알람 예시:
- alert: PodCrashLooping
expr: rate(kube_pod_container_status_restarts_total[15m]) > 0
for: 5m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.pod }} is crash looping"
8. 자주 하는 실수와 주의사항
실수 1: 로그를 제대로 보지 않기
많은 분들이 kubectl logs
만 확인하고 --previous
옵션을 놓치는 경우가 많습니다. 크래시된 컨테이너의 로그는 이전 인스턴스에 있다는 점을 꼭 기억하세요.
실수 2: 리소스 제한을 너무 낮게 설정
특히 자바 애플리케이션의 경우 JVM 힙 메모리와 컨테이너 메모리 제한을 함께 고려해야 합니다.
실수 3: 헬스체크 경로 확인 누락
애플리케이션에서 실제로 /health
엔드포인트를 제공하는지 확인하지 않고 프로브를 설정하는 경우가 있습니다.
9. 유용한 kubectl 관리 명령어
# 파드 상태 실시간 모니터링
kubectl get pods --watch
# 특정 라벨의 파드들만 확인
kubectl get pods -l app=myapp
# 파드 삭제 (강제)
kubectl delete pod <파드명> --force --grace-period=0
# 디플로이먼트 재시작
kubectl rollout restart deployment/<디플로이먼트명>
# 파드 리소스 사용량 확인
kubectl describe pod <파드명> | grep -A5 "Limits\|Requests"
# 이벤트를 시간순으로 정렬
kubectl get events --sort-by='.lastTimestamp'
CrashLoopBackOff 에러는 처음 만났을 때는 당황스럽지만, 체계적인 접근을 통해 충분히 해결할 수 있습니다. 가장 중요한 것은 증상이 아닌 근본 원인을 찾는 것입니다.
기억해야 할 핵심 포인트들:
- CrashLoopBackOff는 증상이지 원인이 아닙니다
--previous
옵션으로 이전 컨테이너 로그를 확인하세요- 리소스 제한과 요청값을 적절히 설정하세요
- 프로브 설정을 꼼꼼히 검토하세요
쿠버네티스 운영이 어렵게 느껴질 수 있지만, 이런 경험들이 쌓이다 보면 어느새 문제를 빠르게 진단하고 해결할 수 있는 실력이 늘어있을 거라 생각 합니다. 이 포스트가 도움이 되었으면 합니다.