[태그:] Node Affinity

  • GKE Pod이 Pending에서 반응없을 때 확인해야 할 설정 5가지

    GKE(Google Kubernetes Engine) 환경에서 Pod을 배포했을 때 상태가 Pending에 오랫동안 머무르며 Running으로 전환되지 않는 현상은, Kubernetes 스케줄러가 해당 Pod을 실행할 수 있는 적합한 노드(Node)를 전혀 찾지 못했거나, 일단 노드를 할당했다고 하더라도 그 노드에서 컨테이너 이미지를 정상적으로 다운로드하지 못하거나, 요청한 리소스(CPU, 메모리, 임시 스토리지 등)를 확보하지 못했거나, PersistentVolumeClaim(PVC) 바인딩에 실패하는 등 실행 전제 조건이 충족되지 않았음을 의미합니다.

    이 상태는 단순히 “대기 중”이라는 메시지로 표시되지만, 실제로는 클러스터 리소스 관리, 노드 상태, 이미지 레지스트리 접근성, 네트워크 연결성, IAM 및 보안 정책, 스케줄링 제약 조건(nodeSelector, affinity, taints/tolerations)다층적이고 복합적인 시스템 구성 요소 간의 불일치나 장애를 내포하고 있습니다.

    특히 GKE는 Google Cloud의 관리형 Kubernetes 서비스로서 높은 안정성과 자동화 기능을 제공하지만, 사용자가 정의한 리소스 요청/제한, 노드 풀 구성, IAM 역할 부여, VPC 네트워크 정책, Workload Identity 설정, StorageClass 및 CSI 드라이버, Cluster Autoscaler 동작 여부 등이 단 하나라도 어긋나면 Pod이 영구적으로 Pending 상태에 갇히는 문제가 빈번히 발생합니다.

    예를 들어, Pod이 requests.cpu: 2를 요구하는데 노드 풀의 모든 VM이 이미 90% 이상 CPU를 사용 중이라면 스케줄링 실패(FailedScheduling: 0/10 nodes are available: 10 Insufficient cpu)가 발생하고, 프라이빗 GCR(Google Container Registry)에 저장된 이미지를 사용하려는데 노드의 서비스 계정에 storage.objectViewer 권한이 없으면 ImagePullBackOff 상태로 전환되며, Private 클러스터에서 Cloud NAT이 설정되지 않아 외부 레지스트리에 접근하지 못하면 ErrImagePull 오류가 나타납니다.

    이처럼 Pending 상태는 단일 원인으로 끝나지 않고 연쇄적·복합적 장애로 나타날 수 있으며, 특히 멀티테넌트 환경, 프라이빗 클러스터, Autopilot 모드, VPC-native 클러스터에서는 설정 복잡도가 높아져 진단 난이도가 더욱 증가합니다.

    따라서 이 문제를 해결하려면 kubectl describe pod를 통해 Events 섹션의 상세 메시지를 정밀 분석하고, 리소스 → 스케줄링 → 이미지 → 볼륨 → 네트워크 → 보안의 순서로 체계적으로 점검해야 하며, GKE의 관리형 기능을 적극 활용해 사전 예방자동 복구 체계를 구축하는 것이 장기적인 운영 안정성의 핵심입니다.

    오늘은 이를 확인하기 위한 설정 5가지에 대해 자세히 살펴보겠습니다.


    1. 노드 스케줄링 실패 (Node Scheduling Failure)

    Pod이 적절한 노드를 찾지 못해 Pending 상태에 머무는 가장 흔한 경우입니다.

    확인 사항진단 방법해결 방법
    Node Selector 및 Affinity 불일치Pod 정의(spec.nodeSelector 또는 affinity)에 지정된 라벨을 가진 노드가 클러스터 내에 존재하는지 확인합니다.노드 셀렉터를 제거하거나, 클러스터에 필요한 라벨을 가진 노드를 추가합니다. Pod의 요구 사항을 충족하도록 라벨을 수정합니다.
    테인트(Taints) 및 톨러레이션(Tolerations) 불일치노드에 설정된 테인트(Taint)를 Pod이 톨러레이션(Toleration)으로 정확히 명시하지 않았을 경우, Pod은 해당 노드에 스케줄링되지 않습니다.Pod 정의에 노드의 테인트와 일치하는 톨러레이션을 추가합니다. 또는 불필요한 테인트를 노드에서 제거합니다.

    2. 리소스 부족 (Resource Exhaustion)

    노드가 Pod을 받을 준비는 되었으나, Pod의 리소스 요청(Request)을 충족시킬 여유가 없을 때 스케줄링이 중단됩니다.

    • 진단: kubectl describe pod <pod-name> 명령을 실행하여 이벤트(Events) 섹션을 확인했을 때, FailedScheduling 오류와 함께 Insufficient CPU 또는 Insufficient memory 메시지가 있는지 확인합니다.
    • 해결 방법:
      1. Pod 리소스 요청 줄이기: Pod 정의의 resources.requests 값을 실제로 필요한 최소한의 값으로 줄입니다.
      2. 클러스터 크기 조정: GKE 클러스터에 노드를 수동으로 추가하거나, 클러스터 자동 스케일러(Cluster Autoscaler)가 활성화되어 있는지 확인하고 필요한 경우 활성화합니다.
      3. 리소스 쿼터 확인: 네임스페이스 또는 프로젝트에 설정된 리소스 쿼터(Resource Quota)가 Pod이 요청한 리소스를 초과하지 않는지 확인합니다.

    3. 이미지 풀 실패 (Image Pull Failure)

    Pod이 노드에 스케줄링된 이후에도 Pending 상태를 벗어나지 못하고 ImagePullBackOff 오류로 전환된다면 컨테이너 이미지 접근 문제일 가능성이 높습니다.

    • 진단: kubectl describe pod <pod-name>의 이벤트 섹션에서 Failed to pull image 또는 ImagePullBackOff 메시지를 확인합니다.
    • 해결 방법:
      1. 이미지 이름 확인: Pod 정의의 이미지 이름(spec.containers[*].image)에 오타가 없는지 또는 태그(Tag)가 올바른지 확인합니다.
      2. 권한 문제: 이미지가 비공개 저장소(Private Registry)(예: Artifact Registry, Docker Hub)에 저장되어 있다면, Pod 정의에 해당 저장소 접근을 위한 imagePullSecrets가 올바르게 설정되어 있는지 확인합니다.
      3. 네트워크 방화벽: 노드에서 컨테이너 레지스트리(Registry)로의 아웃바운드(Outbound) 통신이 방화벽 규칙에 의해 차단되지 않았는지 확인합니다.

    4. PersistentVolumeClaim (PVC) 바인딩 문제

    Pod이 영구 저장소(Persistent Storage)를 요청했을 때, 해당 요청을 충족시킬 수 있는 볼륨이 없으면 Pod은 Pending 상태를 벗어나지 못합니다.

    • 진단: kubectl describe pod <pod-name>의 이벤트 섹션에서 FailedScheduling 오류와 함께 waiting for a volume to be created, either manually or via a storage provisioner 메시지를 확인합니다.
    • 해결 방법:
      1. PVC 상태 확인: kubectl describe pvc <pvc-name>을 실행하여 PVC 상태가 Pending인지 Bound인지 확인합니다. Pending이라면 볼륨이 생성되지 않았다는 뜻입니다.
      2. StorageClass 확인: PVC 정의에 사용된 storageClassName이 유효하고, 클러스터에 해당 StorageClass를 기반으로 볼륨을 프로비저닝할 수 있는 프로비저너(Provisioner)가 정상 작동하는지 확인합니다.
      3. 볼륨 리소스: 요청한 볼륨 용량을 충족시키거나 해당 영역에 동적 볼륨을 생성할 수 있는 디스크 리소스가 GCP 프로젝트에 남아 있는지 확인합니다.

    5. 네트워크 구성 오류 (CNI 및 CIDR)

    드물지만, 클러스터의 CNI(Container Network Interface) 또는 IP 주소 대역(CIDR) 설정 문제로 Pod이 Pending 상태에 고착될 수 있습니다.

    • 진단: Pod이 ContainerCreating 단계로 넘어가지 못하고 Pending에 머물러 있거나, Pod이 생성되더라도 네트워크 구성 문제로 통신이 안 될 수 있습니다.
    • 해결 방법:
      1. CNI 플러그인 상태: 노드의 kubelet 로그(journalctl -u kubelet)를 확인하여 CNI 플러그인(GKE의 경우 gcp-subnet)이 정상적으로 로드되었는지 확인합니다.
      2. IP 대역 고갈: 클러스터 생성 시 할당한 Pod CIDR 블록(--cluster-ipv4-cidr)의 IP 주소가 고갈되었는지 확인합니다. 만약 IP가 모두 소진되었다면, 클러스터를 확장하거나 새로운 Pod CIDR을 가진 노드 풀을 추가해야 합니다.
      3. 노드 준비 상태: 모든 노드가 Ready 상태인지 kubectl get nodes로 확인합니다. 노드 자체가 준비되지 않았다면 해당 노드에는 Pod이 스케줄링되지 않습니다.

    Disclaimer: 본 블로그의 정보는 개인의 단순 참고 및 기록용으로 작성된 것이며, 개인적인 조사와 생각을 담은 내용이기에 오류가 있거나 편향된 내용이 있을 수 있습니다.