문제 해결: Kubernetes Pod가 Pending 또는 CrashLoopBackOff 상태에 머무는 이유는 무엇인가요?

Kubernetes Pod가 `Pending` 또는 `CrashLoopBackOff` 상태에 머무르면 배포가 중단될 수 있습니다. 이 포괄적인 가이드는 이러한 일반적인 상태를 명확히 설명하고 실용적인 단계별 문제 해결 방법을 제공합니다. `kubectl` 명령을 사용하여 리소스 제약, 이미지 풀 오류, 애플리케이션 실패 및 프로브 잘못된 구성과 같은 문제를 진단하는 방법을 알아보세요. 실행 가능한 인사이트와 모범 사례를 통해 Pod 문제를 신속하게 해결하고 강력하고 안정적인 Kubernetes 환경을 유지하여 애플리케이션이 항상 실행되도록 하세요.

23 조회수

문제 해결: Kubernetes Pod가 Pending 또는 CrashLoopBackOff 상태에 머무르는 이유는?

Kubernetes는 컨테이너화된 애플리케이션을 배포하고 관리하는 방식을 혁신하여 비할 데 없는 확장성과 복원력을 제공합니다. 그러나 잘 구성된 환경에서도 Pod는 때때로 Running 상태에 도달하지 못하게 하는 문제에 직면할 수 있습니다. Pod에서 가장 흔하고 답답한 두 가지 상태는 PendingCrashLoopBackOff입니다. Pod가 이러한 상태에 머무르는 이유와 효과적으로 진단하는 방법을 이해하는 것은 건강하고 안정적인 애플리케이션을 유지하는 데 매우 중요합니다.

이 글에서는 Pod가 Pending 또는 CrashLoopBackOff 상태에 머무르는 일반적인 원인을 살펴봅니다. 리소스 제약, 이미지 풀 실패부터 애플리케이션 수준 오류 및 잘못 구성된 프로브까지 다양한 문제를 다룰 것입니다. 더 중요한 것은, 배포로 인한 이러한 골칫거리를 신속하게 진단하고 해결하는 데 도움이 되는 실용적인 kubectl 명령어를 포함한 단계별 가이드를 제공하여 애플리케이션이 원활하게 실행되도록 보장할 것입니다.

Pod 상태 이해하기: Pending vs. CrashLoopBackOff

문제 해결에 들어가기 전에 이 두 가지 상태가 무엇을 의미하는지 이해하는 것이 중요합니다.

Pod 상태: Pending

Pending 상태의 Pod는 Kubernetes 스케줄러가 Pod를 수락했지만, 아직 워커 노드에 성공적으로 스케줄링되지 않았거나 모든 컨테이너가 생성/초기화되지 않았음을 의미합니다. 이는 일반적으로 Pod가 워커 노드에서 실행되는 것을 방해하는 문제를 나타냅니다.

Pod 상태: CrashLoopBackOff

CrashLoopBackOff 상태의 Pod는 Pod 내의 컨테이너가 반복적으로 시작, 충돌, 그리고 다시 시작되고 있음을 의미합니다. Kubernetes는 노드에 과부하가 걸리는 것을 방지하기 위해 재시작 사이에 지수 백오프 지연을 구현합니다. 이 상태는 거의 항상 컨테이너 자체 또는 즉각적인 환경 내에서 실행되는 애플리케이션의 문제점을 가리킵니다.

Pending 상태의 Pod 문제 해결

Pod가 Pending 상태일 때 가장 먼저 살펴봐야 할 곳은 스케줄러와 Pod가 할당되려는 노드입니다. 일반적인 원인 및 진단 단계는 다음과 같습니다.

1. 노드의 리소스 부족

Pod가 Pending 상태인 가장 빈번한 이유 중 하나는 클러스터의 어떤 노드도 Pod의 requests를 충족할 만큼 충분한 사용 가능한 리소스(CPU, 메모리)가 없다는 것입니다. 스케줄러는 적합한 노드를 찾을 수 없습니다.

진단 단계:

  1. Pod 설명 확인: kubectl describe pod 명령어가 가장 유용합니다. Pod가 스케줄링되지 못하는 이유를 설명하는 이벤트를 자주 보여줄 것입니다.
    bash kubectl describe pod <pod-name> -n <namespace>
    "FailedScheduling"과 같은 이벤트와 "0/3 nodes are available: 3 Insufficient cpu" 또는 "memory"와 같은 메시지를 찾으십시오.

  2. 노드 리소스 확인: 노드의 현재 리소스 사용량 및 용량을 확인합니다.
    bash kubectl get nodes kubectl top nodes # (metrics-server 필요)

해결책:

  • 클러스터 용량 증설: Kubernetes 클러스터에 노드를 추가합니다.
  • Pod 리소스 요청 조정: Pod 매니페스트의 CPU 및 메모리 requests 값이 너무 높게 설정되어 있다면 줄입니다.
    yaml resources: requests: memory: "128Mi" cpu: "250m"
  • 다른 Pod 제거: 노드에서 우선순위가 낮은 Pod를 수동으로 제거하여 리소스를 확보합니다(주의해서 사용).

2. 이미지 풀 오류

Kubernetes가 Pod를 노드로 스케줄링했지만, 노드가 컨테이너 이미지를 풀하지 못하면 Pod는 Pending 상태로 유지됩니다.

일반적인 원인:

  • 잘못된 이미지 이름/태그: 이미지 이름의 오타 또는 존재하지 않는 태그 사용.
  • 비공개 레지스트리 인증: 비공개 레지스트리에 대한 ImagePullSecrets 누락 또는 잘못 설정.
  • 네트워크 문제: 노드가 이미지 레지스트리에 연결하지 못함.

진단 단계:

  1. Pod 설명 확인: 다시 말하지만, kubectl describe pod가 중요합니다. "Failed" 또는 "ErrImagePull" 또는 "ImagePullBackOff"와 같은 이벤트를 찾으십시오.
    bash kubectl describe pod <pod-name> -n <namespace>
    예제 출력 이벤트: Failed to pull image "my-private-registry/my-app:v1.0": rpc error: code = Unknown desc = Error response from daemon: pull access denied for my-private-registry/my-app, repository does not exist or may require 'docker login'

  2. ImagePullSecrets 확인: Pod 또는 ServiceAccount에 imagePullSecrets가 올바르게 구성되었는지 확인합니다.
    bash kubectl get secret <your-image-pull-secret> -o yaml -n <namespace>

해결책:

  • 올바른 이미지 이름/태그: 배포 매니페스트에서 이미지 이름과 태그를 다시 확인합니다.
  • ImagePullSecrets 구성: docker-registry 시크릿을 생성하고 이를 Pod 또는 ServiceAccount에 연결했는지 확인합니다.
    bash kubectl create secret docker-registry my-registry-secret \n --docker-server=your-registry.com \n --docker-username=your-username \n --docker-password=your-password \n --docker-email=your-email -n <namespace>
    그런 다음 Pod spec에 추가합니다:
    ```yaml
    spec:
    imagePullSecrets:
    • name: my-registry-secret
      containers:
      ...
      ```
  • 네트워크 연결: 노드에서 이미지 레지스트리까지의 네트워크 연결을 확인합니다.

3. 볼륨 관련 문제

Pod에 PersistentVolumeClaim (PVC)이 필요하고 해당 PersistentVolume (PV)을 프로비저닝하거나 바인딩할 수 없는 경우 Pod는 Pending 상태로 유지됩니다.

진단 단계:

  1. Pod 설명 확인: 볼륨 관련 이벤트를 찾습니다.
    bash kubectl describe pod <pod-name> -n <namespace>
    이벤트에 FailedAttachVolume, FailedMount 또는 유사한 메시지가 표시될 수 있습니다.

  2. PVC 및 PV 상태 확인: PVC 및 PV의 상태를 검사합니다.
    bash kubectl get pvc <pvc-name> -n <namespace> kubectl get pv
    Pending 상태에 머무르는 PVC 또는 바인딩되지 않은 PV를 찾습니다.

해결책:

  • StorageClass 보장: 특히 동적 프로비저닝을 사용하는 경우 StorageClass가 정의되어 있고 사용 가능한지 확인합니다.
  • PV 가용성 확인: 정적 프로비저닝을 사용하는 경우 PV가 존재하고 PVC 기준과 일치하는지 확인합니다.
  • Access Modes 확인: 액세스 모드(예: ReadWriteOnce, ReadWriteMany)가 호환되는지 확인합니다.

CrashLoopBackOff 상태의 Pod 문제 해결

CrashLoopBackOff 상태는 애플리케이션 수준의 문제를 나타냅니다. 컨테이너가 성공적으로 시작되었지만 오류로 인해 종료되어 Kubernetes가 반복적으로 다시 시작하게 됩니다.

1. 애플리케이션 오류

가장 흔한 원인은 애플리케이션 자체가 시작되지 않거나 시작 직후 치명적인 오류를 만나는 것입니다.

일반적인 원인:

  • 필수 종속성/구성 누락: 애플리케이션이 중요한 구성 파일, 환경 변수 또는 의존하는 외부 서비스를 찾지 못합니다.
  • 잘못된 명령/인수: 컨테이너 spec에 지정된 command 또는 args가 잘못되었거나 즉시 종료됩니다.
  • 애플리케이션 논리 오류: 시작 시 충돌을 일으키는 애플리케이션 코드의 버그.

진단 단계:

  1. Pod 로그 보기: 이것이 가장 중요한 단계입니다. 로그는 종종 애플리케이션이 충돌한 정확한 오류 메시지를 보여줍니다.
    bash kubectl logs <pod-name> -n <namespace>
    Pod가 반복적으로 충돌하는 경우, 로그는 가장 최근 실패한 시도의 출력을 보여줄 수 있습니다. 충돌하는 컨테이너의 이전 인스턴스에서 로그를 보려면 -p (previous) 플래그를 사용하십시오:
    bash kubectl logs <pod-name> -p -n <namespace>

  2. Pod 설명 확인: Containers 섹션에서 Restart Count를 확인하여 컨테이너가 몇 번이나 충돌했는지 나타냅니다. 또한 Last State에서 Exit Code를 확인합니다.
    bash kubectl describe pod <pod-name> -n <namespace>
    종료 코드 0은 일반적으로 정상 종료를 의미하지만, 0이 아닌 모든 종료 코드는 오류를 나타냅니다. 일반적인 0이 아닌 종료 코드는 1(일반 오류), 137(SIGKILL, 종종 OOMKilled), 139(SIGSEGV, 세그먼테이션 오류)입니다.

해결책:

  • 애플리케이션 로그 검토: 로그를 기반으로 애플리케이션 코드 또는 구성을 디버그합니다. 필요한 모든 환경 변수, ConfigMaps, Secrets가 올바르게 마운트/주입되었는지 확인합니다.
  • 로컬 테스트: 동일한 환경 변수 및 명령을 사용하여 컨테이너 이미지를 로컬에서 실행하여 문제를 재현하고 디버그해 봅니다.

2. Liveness 및 Readiness 프로브 실패

Kubernetes는 Liveness 및 Readiness 프로브를 사용하여 애플리케이션의 상태 및 가용성을 결정합니다. Liveness 프로브가 지속적으로 실패하면 Kubernetes는 컨테이너를 다시 시작하여 CrashLoopBackOff 상태가 됩니다.

진단 단계:

  1. Pod 설명 확인: Containers 섹션에서 LivenessReadiness 프로브 정의와 해당 Last State를 확인합니다.
    bash kubectl describe pod <pod-name> -n <namespace>
    "Liveness probe failed: HTTP probe failed with statuscode: 500"과 같이 프로브 실패를 나타내는 메시지를 찾으십시오.

  2. 애플리케이션 로그 검토: 때때로 애플리케이션 로그는 프로브 엔드포인트가 실패하는 이유에 대한 컨텍스트를 제공할 수 있습니다.

해결책:

  • 프로브 구성 조정: 프로브의 path, port, command, initialDelaySeconds, periodSeconds, 또는 failureThreshold를 수정합니다.
  • 프로브 엔드포인트 상태 보장: 프로브가 대상으로 하는 애플리케이션 엔드포인트가 실제로 건강하고 예상대로 응답하는지 확인합니다. 애플리케이션이 시작하는 데 시간이 너무 오래 걸릴 수 있으므로 더 큰 initialDelaySeconds가 필요할 수 있습니다.

3. 리소스 한도 초과

컨테이너가 memory.limit보다 더 많은 메모리를 지속적으로 사용하려고 하거나 cpu.limit을 초과하여 CPU 스로틀링이 발생하는 경우, 커널은 프로세스를 종료할 수 있으며, 종종 OOMKilled (Out Of Memory Killed) 이벤트가 발생합니다.

진단 단계:

  1. Pod 설명 확인: Last State 또는 Events 섹션에서 OOMKilled를 찾습니다. Exit Code: 137은 종종 OOMKilled 이벤트를 나타냅니다.
    bash kubectl describe pod <pod-name> -n <namespace>

  2. kubectl top 확인: metrics-server가 설치된 경우 kubectl top pod를 사용하여 Pod의 실제 리소스 사용량을 확인합니다.
    bash kubectl top pod <pod-name> -n <namespace>

해결책:

  • 리소스 한도 증설: 애플리케이션이 실제로 더 많은 리소스를 필요로 하는 경우, Pod 매니페스트의 memory 및/또는 cpu limits를 늘립니다. 이는 노드에 더 많은 용량이 필요할 수 있습니다.
    yaml resources: requests: memory: "256Mi" cpu: "500m" limits: memory: "512Mi" # 이것을 늘립니다. cpu: "1000m" # 이것을 늘립니다.
  • 애플리케이션 최적화: 애플리케이션을 프로파일링하여 리소스 소비를 식별하고 줄입니다.

4. 권한 문제

컨테이너가 필요한 파일, 디렉토리 또는 네트워크 리소스에 액세스할 수 있는 필요한 권한이 없는 경우 충돌할 수 있습니다.

진단 단계:

  1. 로그 검토: 애플리케이션 로그에 권한 거부 오류(EACCES)가 표시될 수 있습니다.
  2. Pod 설명 확인: 사용 중인 ServiceAccount와 마운트된 securityContext 설정을 확인합니다.

해결책:

  • securityContext 조정: 필요한 경우 runAsUser, fsGroup, 또는 allowPrivilegeEscalation을 설정합니다.
  • ServiceAccount 권한: Pod와 연결된 ServiceAccountRoleBindingsClusterRoleBindings를 통해 바인딩된 필요한 RolesClusterRoles를 가지고 있는지 확인합니다.
  • 볼륨 권한: 마운트된 볼륨(예: emptyDir, hostPath, ConfigMap, Secret)에 컨테이너 사용자에게 올바른 권한이 있는지 확인합니다.

일반적인 진단 단계 및 도구

Pod 문제에 직면했을 때 실행할 수 있는 빠른 명령 목록입니다.

  • 빠른 개요 확인: Pod 상태를 확인합니다.
    bash kubectl get pods -n <namespace> kubectl get pods -n <namespace> -o wide
  • 상세 Pod 정보: Pod 이벤트, 상태 및 조건을 이해하는 데 가장 중요한 명령어입니다.
    bash kubectl describe pod <pod-name> -n <namespace>
  • 컨테이너 로그: 애플리케이션이 보고하는 내용을 확인합니다.
    bash kubectl logs <pod-name> -n <namespace> kubectl logs <pod-name> -p -n <namespace> # 이전 인스턴스 kubectl logs <pod-name> -f -n <namespace> # 로그 따라가기
  • 클러스터 전체 이벤트: 때때로 문제는 특정 Pod가 아니라 클러스터 전체 이벤트(예: 노드 압력)일 수 있습니다.
    bash kubectl get events -n <namespace>
  • 대화형 디버깅: 컨테이너가 시작되었지만 빠르게 충돌하는 경우, 잠시 동안 exec하여 들어가거나 구성된 경우 별도의 디버그 컨테이너에 들어갈 수 있습니다.
    bash kubectl exec -it <pod-name> -n <namespace> -- bash
    (참고: 이 기능은 컨테이너가 연결될 만큼 충분히 오래 살아있는 경우에만 작동합니다.)

Pod 문제 방지를 위한 모범 사례

예방은 치료보다 항상 낫습니다. 이러한 모범 사례를 따르면 PendingCrashLoopBackOff 발생을 크게 줄일 수 있습니다.

  • 현실적인 리소스 요청 및 한도 설정: 합리적인 requestslimits로 시작한 다음, 애플리케이션 프로파일링 및 모니터링을 기반으로 조정합니다.
  • 특정 이미지 태그 사용: 프로덕션에서는 latest 태그를 피합니다. 재현성을 위해 불변 태그(예: v1.2.3, commit-sha)를 사용합니다.
  • 견고한 프로브 구현: 애플리케이션 상태를 정확하게 반영하는 livenessreadiness 프로브를 구성합니다. initialDelaySeconds로 시작 시간을 고려합니다.
  • 중앙 집중식 로깅 및 모니터링: Prometheus, Grafana, ELK 스택 또는 클라우드 네이티브 로깅 서비스와 같은 도구를 사용하여 Pod 로그 및 메트릭을 수집하고 분석합니다.
  • 매니페스트 버전 관리: Kubernetes 매니페스트를 버전 관리 시스템(예: Git)에 저장하여 변경 사항을 추적하고 롤백을 용이하게 합니다.
  • 철저한 테스트: 프로덕션에 배포하기 전에 개발 및 스테이징 환경에서 컨테이너 이미지 및 Kubernetes 배포를 테스트합니다.
  • 정상 종료: 애플리케이션이 종료 전에 리소스를 해제할 수 있도록 SIGTERM 신호를 처리하여 정상 종료를 보장합니다.

결론

Kubernetes 환경에서 Pending 또는 CrashLoopBackOff 상태에 머무르는 Pod를 만나는 것은 흔한 시나리오입니다. 처음에는 부담스러울 수 있지만, 이러한 상태는 귀중한 단서를 제공합니다. Pod 설명, 로그 및 클러스터 이벤트를 체계적으로 검토함으로써 리소스 제약, 이미지 풀 실패 또는 애플리케이션 수준의 버그 등 근본 원인을 정확히 파악할 수 있습니다. 이 가이드에서 설명한 진단 단계와 모범 사례를 통해 Kubernetes 배포를 건강하게 유지하고 애플리케이션을 안정적으로 실행할 수 있습니다. 즐거운 디버깅 되세요!