infra
Platform

모듈 맵

[Kubernetes] Kubernetes 장애 원인을 빠르게 격리하는 트러블슈팅 가이드

0 / 29 완료

펼치기
0 / 29 완료0%

Kubernetes · 29 / 29

[Kubernetes] Kubernetes 장애 원인을 빠르게 격리하는 트러블슈팅 가이드

Pending, CrashLoopBackOff, OOMKilled, Evicted 등 주요 장애 상태를 체계적으로 진단하고 복구합니다

🚨INCIDENT ALERT
HIGH

새벽 온콜 알림이 울렸고 결제 서비스 파드가 여러 Namespace에서 동시에 실패하고 있습니다. 명령어를 떠올리는 데 시간을 쓰면 원인보다 증상만 만지게 됩니다. Kubernetes 트러블슈팅은 이벤트, 로그, 리소스, 노드 상태를 정해진 순서로 좁혀가는 훈련입니다.

Kubernetes 트러블슈팅

프로덕션 Kubernetes 클러스터에서 파드 5개가 갑자기 Evicted 상태가 됐습니다. 무엇부터 해야 할까요. 무작정 파드를 삭제하고 재시작하면 같은 노드에 다시 스케줄되고 또 Evict됩니다. Kubernetes 장애는 대부분 패턴이 있습니다. Pending이면 스케줄링 실패, CrashLoopBackOff면 앱 시작 실패, OOMKilled면 메모리 초과, Evicted면 노드 자원 고갈 신호입니다. 이 패턴을 알면 kubectl describe → logs → events → exec 순서의 체계적 진단으로 대부분의 장애를 30분 안에 해결할 수 있습니다. 이 모듈은 실제 장애 시나리오별 진단 명령과 해결 절차를 담습니다. 이 모듈을 마치면 주요 파드 상태 이상을 혼자서 진단하고 복구할 수 있습니다.

이번 챕터에서 배울 것
  • 1체계적 진단 순서: kubectl describe → logs → events → exec
  • 2Pending: 리소스 부족, Affinity 미충족, PVC 미바인딩 원인 분석
  • 3CrashLoopBackOff: 앱 오류, 설정 오류, 리소스 한도 진단
  • 4OOMKilled: 메모리 limits 설정과 메모리 누수 패턴
  • 5Evicted: 노드 자원 압박, QoS 클래스, eviction 정책
  • 6TroubleCase: 운영 파드 전체 Evicted — 노드 디스크 풀 복구 절차
실습 환경 준비
전체 파드 상태 한눈에 보기
kubectl get pods -A --field-selector=status.phase!=Running
이상 상태 파드만 필터링
kubectl get pods -A | grep -v -E 'Running|Completed'
노드 자원 사용 현황
kubectl top nodes
모든 네임스페이스 이벤트 (최근 발생순)
kubectl get events -A --sort-by='.lastTimestamp' | tail -30
메트릭 서버 설치 확인 (kubectl top 사전 조건)
kubectl get deployment metrics-server -n kube-system

체계적 진단 순서

Kubernetes 장애 대응은 정보를 수집하는 순서가 있습니다. 무작정 재시작부터 하면 원인을 알 수 없게 됩니다.

장애 대응 순서:

1. kubectl get pod               → 상태 코드 확인 (Pending/CrashLoop/OOMKilled/Evicted)
2. kubectl describe pod          → Events 섹션에서 구체적 원인 확인
3. kubectl logs --previous       → 크래시 직전 로그 확인
4. kubectl get events            → 클러스터 레벨 이벤트
5. kubectl exec / kubectl debug  → 컨테이너 내부 직접 확인
6. kubectl top pod/node          → 자원 사용량 확인
💡개념

Exit Code 해석 — 숫자로 원인을 알 수 있다

새벽 장애 대응 중 파드가 계속 재시작되는데 로그에는 아무것도 없고 상태 코드만 보입니다. "Exit Code 137"이나 "Exit Code 1"을 처음 마주하는 엔지니어는 이 숫자가 무엇을 의미하는지 몰라 로그만 반복해서 확인합니다. Exit Code는 파드가 왜 종료됐는지 알려주는 첫 번째 단서이며, 숫자마다 가리키는 원인이 다릅니다. 137이면 메모리 초과, 1이면 앱 오류, 127이면 실행 파일 없음입니다. 이 CB에서는 주요 Exit Code의 의미와 각 코드에 따른 진단 방향을 다룹니다. kubectl describe podLast State 또는 State에서 확인할 수 있습니다.

Exit Code 해석 — 숫자로 원인을 알 수 있다

Kubernetes
kubectl describe pod <pod-name> | grep -A5 "Last State"
# Last State:  Terminated
#   Reason:    OOMKilled
#   Exit Code: 137
#   Started:   Mon, 15 Jan 2024 02:31:22 +0000
#   Finished:  Mon, 15 Jan 2024 02:31:44 +0000
Exit Code의미주요 원인
0정상 종료의도된 종료 (Job 완료 등)
1일반 오류앱 코드 오류, 설정 오류
2쉘 에러잘못된 시작 명령어
127명령어 없음이미지에 실행 파일 없음, CMD 경로 오류
128+N시그널 N으로 종료-
137SIGKILL (128+9)OOM Killer 또는 외부 강제 종료
143SIGTERM (128+15)Graceful shutdown (정상) 또는 타임아웃
Kubernetes
# Exit Code 137 확인 방법
kubectl get pod <pod-name> -o jsonpath='{.status.containerStatuses[0].lastState.terminated.exitCode}'

Pending 상태 진단

파드가 Pending이면 컨테이너가 아직 시작되지 않은 것입니다. 스케줄러가 적합한 노드를 찾지 못한 상태입니다.

Kubernetes
# 1단계: Events에서 원인 확인
kubectl describe pod <pod-name> -n <namespace>
# Events 섹션 핵심 메시지들:

# 원인 1: 리소스 부족
# Warning  FailedScheduling  0/3 nodes are available:
#   1 Insufficient cpu, 2 Insufficient memory.

# 원인 2: Node Affinity 미충족
# Warning  FailedScheduling  0/3 nodes are available:
#   3 node(s) didn't match Pod's node affinity/selector.

# 원인 3: PVC 미바인딩
# Warning  FailedScheduling  0/3 nodes are available:
#   3 pod has unbound immediate PersistentVolumeClaims.

# 원인 4: Taint/Toleration 불일치
# Warning  FailedScheduling  0/3 nodes are available:
#   3 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }.
🔍실행 후 확인할 것
  • NAME조회 대상 리소스 이름이 예상한 대상과 일치하는지 확인합니다.
  • STATUS/READYRunning, Ready, Available처럼 정상 상태를 나타내는 필드가 있는지 봅니다.
  • RESTARTS/EVENTS재시작 횟수나 Warning 이벤트가 증가하지 않는지 확인합니다.

리소스 부족 해결

Kubernetes
# 노드별 할당 가능 자원 확인
kubectl describe nodes | grep -A5 "Allocated resources"
# 출력:
#   Allocated resources:
#     (Total limits may be over 100 percent, i.e., overcommitted.)
#     Resource           Requests    Limits
#     --------           --------    ------
#     cpu                1850m/2     3500m/2     ← 거의 꽉 찬 상태
#     memory             1.8Gi/4Gi   3.2Gi/4Gi

# 파드별 리소스 요청량 확인
kubectl get pods -n <namespace> -o json | \
  jq '.items[] | {name: .metadata.name, cpu: .spec.containers[0].resources.requests.cpu, mem: .spec.containers[0].resources.requests.memory}'

# 해결 1: 파드의 resources.requests 줄이기 (실제 사용량 기반)
kubectl top pod -n <namespace>   # 실제 사용량 확인 후 requests 조정

# 해결 2: 노드 추가 (클러스터 오토스케일러 또는 수동)
# 해결 3: 다른 네임스페이스의 불필요한 파드 정리

PVC 미바인딩 해결

위험 명령어Git이나 매니페스트와 다른 임시 상태가 생기고 잘못된 패치가 즉시 운영 트래픽에 영향을 줄 수 있습니다.

운영 리소스 직접 패치

안전한 실행 조건: 변경 내용을 코드에 반영할 계획이 있고 영향 범위를 검토했을 때만 실행하세요.

실행 전 반드시 확인

  • 현재 컨텍스트와 Namespace가 의도한 대상인지 확인했는가
  • 운영 트래픽이나 상태 저장 데이터에 미치는 영향을 확인했는가
  • 되돌릴 매니페스트, 백업, 또는 복구 절차가 준비되어 있는가
kubectl patch pvc payment-data -n <namespace>

위 항목을 모두 확인한 후 복사할 수 있습니다

Kubernetes
# PVC 상태 확인
kubectl get pvc -n <namespace>
# NAME           STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS
# payment-data   Pending   -        -          -              fast-ssd    ← 문제

# PVC 상세 확인
kubectl describe pvc payment-data -n <namespace>
# Events:
#   Warning  ProvisioningFailed  storageclass.storage.k8s.io "fast-ssd" not found
#   → StorageClass 이름 오류

# 사용 가능한 StorageClass 확인
kubectl get storageclass

# PVC YAML 수정 (올바른 StorageClass로)
kubectl patch pvc payment-data -n <namespace> \
  --type=json \
  -p='[{"op":"replace","path":"/spec/storageClassName","value":"standard"}]'

CrashLoopBackOff 진단

컨테이너가 시작됐다가 즉시 종료되는 것을 반복합니다. Kubernetes가 재시작 간격을 점점 늘리면서 BackOff 상태가 됩니다.

Kubernetes
# 현재 크래시 중인 컨테이너 로그
kubectl logs <pod-name> -n <namespace>

# 핵심: 이전 크래시 컨테이너 로그 (실제 오류 메시지는 여기에)
kubectl logs <pod-name> -n <namespace> --previous

# 재시작 횟수 확인
kubectl get pod <pod-name> -n <namespace> \
  -o jsonpath='{.status.containerStatuses[0].restartCount}'
# 10 이상이면 심각한 반복 크래시

주요 CrashLoopBackOff 원인별 해결

Kubernetes
# 원인 1: 설정 오류 (환경변수, ConfigMap, Secret)
kubectl logs <pod-name> --previous | grep -i "error\|fatal\|panic"
# 예: "Error: DB_HOST environment variable is required"

# 환경변수 확인
kubectl describe pod <pod-name> | grep -A20 "Environment:"

# Secret/ConfigMap 존재 확인
kubectl get secret db-credentials -n <namespace>
kubectl get configmap app-config -n <namespace>

# 원인 2: 이미지 실행 파일 없음 (CMD/ENTRYPOINT 오류)
# Exit Code: 127
kubectl describe pod <pod-name> | grep -E "Exit Code|Command|Args"

# 이미지 확인 (임시 컨테이너로)
kubectl run debug-img --image=payment:2.0.0 --restart=Never \
  --command -- /bin/sh -c "ls /app && cat /app/start.sh"

# 원인 3: 포트 충돌 또는 바인딩 실패
kubectl logs <pod-name> --previous | grep "bind\|address already in use"

# 원인 4: 리소스 한도 초과로 즉시 OOMKilled
kubectl describe pod <pod-name> | grep -E "OOMKilled|Exit Code: 137"

컨테이너 내부 직접 진단

Kubernetes
# 크래시 직전 컨테이너를 잡기 위한 debug 방법
# 방법 1: command 오버라이드로 sleep으로 시작 (YAML 수정 필요)
# command: ["sleep", "3600"]  # 앱 대신 sleep으로 시작해서 exec로 내부 확인

# 방법 2: kubectl debug (비침습적)
kubectl debug -n <namespace> <pod-name> \
  -it --image=busybox --copy-to=debug-pod \
  --container=app -- sh

# 방법 3: 에러 재현을 위한 임시 파드
kubectl run test-pod -n <namespace> \
  --image=payment:2.0.0 \
  --restart=Never \
  --env="DB_HOST=postgres.production.svc.cluster.local" \
  --env="DB_PASSWORD=$(kubectl get secret db-creds -o jsonpath='{.data.password}' | base64 -d)" \
  -- /bin/sh -c "/app/start.sh; echo exit_code=$?"
kubectl logs test-pod

OOMKilled 진단과 메모리 최적화

Kubernetes
# OOMKilled 확인
kubectl describe pod <pod-name> | grep -A3 "OOM\|137"

# 실제 메모리 사용량 측정 (최소 24시간 관찰 권장)
kubectl top pod <pod-name> -n <namespace> --containers

# 과거 메모리 사용 추이 (Prometheus 쿼리)
# container_memory_working_set_bytes{pod="payment-xxx", namespace="production"}

# 메모리 limits 확인
kubectl get pod <pod-name> -n <namespace> \
  -o jsonpath='{.spec.containers[0].resources.limits.memory}'

메모리 Limits 조정

YAML
# 측정값 기반으로 limits 상향 조정
# 실제 최대 사용량의 130% 정도를 limits로 설정
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment
spec:
  template:
    spec:
      containers:
        - name: payment
          resources:
            requests:
              memory: "256Mi"    # 평균 사용량
              cpu: "100m"
            limits:
              memory: "512Mi"    # 최대 사용량 × 1.3
              cpu: "500m"
Kubernetes
# 메모리 누수 패턴 감지 (시간에 따라 계속 증가하는지)
# 정상: 사용량이 일정 수준 유지 또는 GC 후 감소
# 누수: 사용량이 계속 우상향, GC 후에도 감소하지 않음

# Java 애플리케이션 메모리 덤프 (OOM 전에)
kubectl exec -n production <pod-name> -- \
  jmap -dump:format=b,file=/tmp/heap.hprof $(pgrep java)
kubectl cp <pod-name>:/tmp/heap.hprof ./heap.hprof -n production

# Node.js 힙 스냅샷
kubectl exec -n production <pod-name> -- \
  kill -SIGUSR2 $(pgrep node)
# inspector에서 heapsnapshot 파일 확인

# Python 메모리 프로파일링
kubectl exec -n production <pod-name> -- \
  python -c "
import tracemalloc
tracemalloc.start()
# ... 앱 실행 ...
snapshot = tracemalloc.take_snapshot()
for stat in snapshot.statistics('lineno')[:10]:
    print(stat)
"

Evicted 상태 진단

kubelet이 노드 자원 압박(메모리, 디스크, PID) 시 파드를 강제 종료합니다.

Kubernetes
# Evicted 파드 전체 확인
kubectl get pods -A | grep Evicted
kubectl get pods -A --field-selector=status.phase=Failed | grep Evicted

# Eviction 이유 확인
kubectl describe pod <evicted-pod-name> | grep -A5 "Message"
# Message: The node was low on resource: ephemeral-storage.
#          Threshold quantity: 10%, available: 1152Ki.
# 또는:
# Message: The node had condition: [MemoryPressure].
# 또는:
# Message: The node had condition: [DiskPressure].

# 노드 컨디션 확인
kubectl describe nodes | grep -A5 "Conditions:"
# MemoryPressure    True  → 메모리 압박
# DiskPressure      True  → 디스크 압박
# PIDPressure       True  → 프로세스 수 압박

Evicted 파드 정리

Kubernetes
# Evicted 파드는 Running이 아니므로 수동 정리 필요
# (새 파드는 자동 생성되지만 Evicted 파드 객체는 남음)

# Evicted 파드 일괄 삭제
kubectl get pods -A --field-selector=status.phase=Failed \
  -o json | \
  jq -r '.items[] | select(.status.reason=="Evicted") | "\(.metadata.namespace) \(.metadata.name)"' | \
  while read ns pod; do kubectl delete pod "$pod" -n "$ns"; done

# 한 줄 명령 버전
kubectl get pods --all-namespaces -o json | \
  jq '.items[] | select(.status.phase=="Failed" and .status.reason=="Evicted") | .metadata | "\(.namespace) \(.name)"' -r | \
  xargs -L1 bash -c 'kubectl delete pod $2 -n $1' _

트러블슈팅

월요일 아침, 모든 프로덕션 파드가 Evicted 상태가 됩니다. 새로 시작되는 파드도 곧 Evicted됩니다.

1단계: 상황 파악

Kubernetes
# 전체 파드 상태 확인
kubectl get pods -n production
# NAME                     READY   STATUS    RESTARTS
# payment-7d9b4c8f6-xxx    0/1     Evicted   0
# order-6c8b9d7f5-yyy      0/1     Evicted   0
# catalog-5b7a8c6e4-zzz    0/1     Evicted   0

# Eviction 이유 확인
kubectl describe pod payment-7d9b4c8f6-xxx -n production | grep Message
# Message: The node was low on resource: ephemeral-storage.

# 노드 상태 확인
kubectl describe nodes worker-1 | grep -E "DiskPressure|ephemeral"
# DiskPressure    True   → 디스크 압박 활성화

2단계: 디스크 사용량 원인 파악

Kubernetes
# 노드에 직접 접근해서 디스크 사용량 확인
kubectl debug node/worker-1 -it --image=busybox -- df -h
# /dev/sda1   100G   98G   0   100%  /   ← 가득 참!

# 가장 많이 차지하는 디렉토리 찾기
kubectl debug node/worker-1 -it --image=busybox -- \
  du -sh /host/var/lib/docker/*
# 48G  /host/var/lib/docker/overlay2   ← 컨테이너 레이어

# 또는 nsenter로 노드 파일시스템 직접 조회
kubectl debug node/worker-1 -it --image=busybox -- \
  du -sh /host/var/log/pods/
# 32G  /host/var/log/pods/    ← 로그 파일이 문제!

3단계: 로그 파일 정리

Kubernetes
# 어느 파드 로그가 가장 큰지 확인
kubectl debug node/worker-1 -it --image=busybox -- \
  find /host/var/log/pods -name "*.log" \
  -exec ls -lh {} \; | sort -k5 -rh | head -10

# 출력 예시:
# -rw-r--r-- 1 root root 28G /host/var/log/pods/production_payment-.../payment/0.log
# 로그 로테이션 없이 한 파드가 28GB 사용 중

# 즉시 해당 로그 파일 비우기 (truncate)
kubectl debug node/worker-1 -it --image=busybox -- \
  truncate -s 0 /host/var/log/pods/production_payment-.../payment/0.log

# 오래된 종료 파드의 로그 삭제
kubectl debug node/worker-1 -it --image=busybox -- \
  find /host/var/log/pods -name "*.log" -mtime +7 -delete

# Docker/containerd 이미지 캐시 정리
# (SSH 접근 가능한 경우)
ssh worker-1 "docker system prune -f && docker image prune -a -f"
# 또는 crictl (containerd)
ssh worker-1 "crictl rmi --prune"

4단계: DiskPressure 해제 확인

위험 명령어대상 리소스가 즉시 변경되거나 삭제되어 서비스 중단, 데이터 손실, 스케줄링 영향이 발생할 수 있습니다.

위험한 kubectl 작업

안전한 실행 조건: 실습 환경이거나 영향 범위와 롤백 방법을 확인한 뒤에만 실행하세요.

실행 전 반드시 확인

  • 현재 컨텍스트와 Namespace가 의도한 대상인지 확인했는가
  • 운영 트래픽이나 상태 저장 데이터에 미치는 영향을 확인했는가
  • 되돌릴 매니페스트, 백업, 또는 복구 절차가 준비되어 있는가
kubectl delete pods -n production --field-selector=status.phase=Failed

위 항목을 모두 확인한 후 복사할 수 있습니다

로컬 터미널
# 노드 컨디션 회복 확인 (수 분 소요)
watch kubectl describe node worker-1 | grep DiskPressure
# DiskPressure    False   ← 해제되면 파드 재스케줄 시작

# 새 파드 시작 확인
kubectl get pods -n production -w

# 여전히 Evicted 파드 객체 정리
kubectl delete pods -n production --field-selector=status.phase=Failed

5단계: 재발 방지

YAML
# 컨테이너 로그 크기 제한 (kubelet 설정 또는 Docker daemon.json)
# /etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",    # 컨테이너당 로그 최대 100MB
    "max-file": "3"        # 최대 3개 파일 보관
  }
}

# ephemeral-storage limits 설정 (파드 스펙)
resources:
  requests:
    ephemeral-storage: "1Gi"
  limits:
    ephemeral-storage: "5Gi"   # 이 이상 사용하면 Evict
로컬 터미널
# Eviction 임계값 조정 (kubelet 설정)
# /var/lib/kubelet/config.yaml
evictionHard:
  memory.available: "200Mi"
  nodefs.available: "10%"
  nodefs.inodesFree: "5%"
evictionSoft:
  memory.available: "300Mi"
  nodefs.available: "15%"
evictionSoftGracePeriod:
  memory.available: "1m30s"
  nodefs.available: "2m"

고급 진단 도구

kubectl debug로 프로덕션 파드 비침습 진단

Kubernetes
# 실행 중인 파드 옆에 디버그 컨테이너 붙이기 (파드 재시작 없음)
kubectl debug -n production <pod-name> \
  -it --image=nicolaka/netshoot \
  --target=payment              # 같은 PID 네임스페이스 공유

# 디버그 컨테이너에서:
# - 같은 네트워크 네임스페이스 (네트워크 연결 테스트 가능)
# - 같은 PID 네임스페이스 (ps aux로 앱 프로세스 확인)
# - 파드 재시작 없이 진단 가능

# 네트워크 연결 테스트
kubectl debug -n production <pod-name> \
  -it --image=nicolaka/netshoot -- \
  curl -v http://postgres.production.svc.cluster.local:5432

# DNS 해석 확인
kubectl debug -n production <pod-name> \
  -it --image=nicolaka/netshoot -- \
  nslookup postgres.production.svc.cluster.local

kubectl events로 실시간 이벤트 모니터링

Kubernetes
# 특정 네임스페이스 이벤트 실시간 확인
kubectl get events -n production --watch

# 경고 이벤트만 필터링
kubectl get events -n production \
  --field-selector type=Warning \
  --sort-by='.lastTimestamp'

# 특정 파드 관련 이벤트
kubectl get events -n production \
  --field-selector involvedObject.name=<pod-name>

# 자주 발생하는 경고 이벤트 집계
kubectl get events -A --sort-by='.count' | \
  grep Warning | tail -20

노드 수준 진단

위험 명령어노드의 기존 Pod가 Eviction되어 PDB가 없거나 복제본이 부족한 서비스는 중단될 수 있습니다.

노드 드레인

안전한 실행 조건: PDB, 복제본 수, 다른 노드의 여유 리소스를 확인한 유지보수 상황에서만 실행하세요.

실행 전 반드시 확인

  • 현재 컨텍스트와 Namespace가 의도한 대상인지 확인했는가
  • 운영 트래픽이나 상태 저장 데이터에 미치는 영향을 확인했는가
  • 되돌릴 매니페스트, 백업, 또는 복구 절차가 준비되어 있는가
kubectl drain worker-1

위 항목을 모두 확인한 후 복사할 수 있습니다

Kubernetes
# 노드에 스케줄된 파드 현황
kubectl get pods -A --field-selector=spec.nodeName=worker-1

# 노드 자원 할당 현황 상세
kubectl describe node worker-1 | grep -A20 "Allocated resources"

# 노드 조건 전체 확인
kubectl get node worker-1 -o json | \
  jq '.status.conditions[] | {type: .type, status: .status, message: .message}'

# 문제 노드 격리 (새 파드 스케줄 방지 + 기존 파드 evict)
kubectl drain worker-1 \
  --ignore-daemonsets \
  --delete-emptydir-data \
  --grace-period=30
# 노드 수리 후 복구:
kubectl uncordon worker-1

# 일시적으로 새 파드만 스케줄 방지 (기존 파드 유지)
kubectl cordon worker-1
💼
실무 맥락
현업 패턴

시나리오: 새벽 장애 대응 체크리스트 — 단계별 5분 안에 원인 특정

새벽 3시, PagerDuty 알림. "결제 서비스 Health Check 실패". SSH 접속 후 체계적으로 진단합니다.

위험 명령어모든 Pod가 순차 재시작되어 readinessProbe나 복제본 설정이 부족하면 일시 장애가 발생할 수 있습니다.

운영 Deployment 재시작

안전한 실행 조건: 무중단 배포 설정과 모니터링을 확인한 뒤 변경 창구에서 실행하세요.

실행 전 반드시 확인

  • 현재 컨텍스트와 Namespace가 의도한 대상인지 확인했는가
  • 운영 트래픽이나 상태 저장 데이터에 미치는 영향을 확인했는가
  • 되돌릴 매니페스트, 백업, 또는 복구 절차가 준비되어 있는가
kubectl rollout restart deployment/payment -n production

위 항목을 모두 확인한 후 복사할 수 있습니다

Kubernetes
# T+00:00 — 전체 현황 파악 (30초)
kubectl get pods -n production
# payment-7d9b4c8f6-abc   0/1   CrashLoopBackOff   5   12m

# T+00:30 — 재시작 횟수와 이전 Exit Code 확인
kubectl describe pod payment-7d9b4c8f6-abc -n production | \
  grep -E "Restart Count|Exit Code|Reason"
# Restart Count: 5
# Exit Code:     1   ← 앱 오류 (127=파일없음, 137=OOM)

# T+01:00 — 이전 크래시 로그 확인 (핵심)
kubectl logs payment-7d9b4c8f6-abc -n production --previous | tail -30
# 출력: "Error: DATABASE_URL is required but not set"
# → 환경변수 누락 발견!

# T+01:30 — 환경변수 확인
kubectl describe pod payment-7d9b4c8f6-abc -n production | \
  grep -A30 "Environment:"
# DATABASE_URL: <set to the key 'url' in secret 'db-secret'>  Optional: false
# Secret 참조는 있는데...

# T+02:00 — Secret 존재 확인
kubectl get secret db-secret -n production
# Error from server (NotFound): secrets "db-secret" not found
# → Secret 없음! 배포 시 Secret 생성 누락

# T+02:30 — 긴급 조치: Secret 복구
kubectl create secret generic db-secret -n production \
  --from-literal=url="postgresql://user:password@postgres:5432/payment"

# T+03:00 — 파드 재시작 확인
kubectl rollout restart deployment/payment -n production
kubectl rollout status deployment/payment -n production

# T+04:00 — 복구 확인
kubectl get pods -n production | grep payment
# payment-6c9d5e8f7-xyz   1/1   Running   0   45s  ← 정상!

curl -s http://payment.production.svc.cluster.local/health
# {"status": "ok"}

# T+05:00 — 사후 조치
# 1. Secret 생성 누락된 배포 파이프라인 수정
# 2. Health check alert 임계값 검토
# 3. Secret 존재 여부 확인하는 pre-deploy 검증 스크립트 추가

교훈: kubectl logs --previous가 핵심입니다. CrashLoopBackOff에서 현재 로그는 짧거나 없지만, 이전 크래시 로그에 실제 에러 메시지가 있습니다. 5가지 상태(Pending/CrashLoop/OOMKilled/Evicted/Unknown) 각각에 대한 진단 패턴을 외워두면 새벽 장애 대응 시간이 30분에서 5분으로 줄어듭니다.

상태별 진단 요약표

상태첫 번째 확인주요 원인해결
Pendingdescribe pod → Events리소스 부족, Affinity, PVC리소스 확보, 노드 추가, PVC 수정
CrashLoopBackOfflogs --previous앱 오류, 설정 누락, CMD 오류로그로 원인 파악, 설정/이미지 수정
OOMKilleddescribe → Exit Code 137memory limits 초과limits 상향, 메모리 누수 수정
Evicteddescribe → Message노드 자원 압박디스크/메모리 정리, 자원 제한 설정
ImagePullBackOffdescribe → Events이미지 없음, 레지스트리 인증이미지 태그 확인, ImagePullSecret
Terminatingdescribe → FinalizersFinalizer 미제거, 강제 삭제 필요Finalizer 제거 또는 force delete

다음 단계

  • Kubernetes Dashboard와 Lens로 시각적 클러스터 모니터링
  • Prometheus AlertManager로 장애 상태 자동 알림
  • Velero로 클러스터 상태 백업 및 재해 복구
  • Chaos Engineering (Chaos Monkey, LitmusChaos)으로 장애 내성 검증

지식 확인

퀴즈 — 4문제

Q1

파드가 Pending 상태에서 벗어나지 않을 때 가장 먼저 확인해야 할 명령어는?

Q2

CrashLoopBackOff 상태인 파드에서 로그를 확인하는 올바른 방법은?

Q3

파드가 OOMKilled 상태로 종료된 직접적인 원인과 해결 방법은?

Q4

노드 디스크 압박으로 파드 Eviction이 발생할 때 eviction 순서를 결정하는 주요 기준은?

0 / 4 답변

🧪 실습으로 확인하기

K8s 기초 — Pod/Deployment/Service 생성

초급

kubectl로 nginx Pod를 생성하고 Deployment와 Service를 차례로 만들어 클러스터 외부에서 접근 가능한 상태까지 구성한다. K8s 3대 리소스의 역할과 관계를 직접 손으로 익힌다.

40📋 5단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

docker입문 · 30
[Docker] 백엔드 개발자에게 Docker와 컨테이너 가상화가 필수인 이유
Docker 트랙 시작점
linux입문 · 30
[Linux] 개발자가 왜 리눅스 서버와 커맨드라인을 반드시 배워야 하는가
Linux 트랙 시작점