Code

쿠버네티스 노드 부족과 Pending 타임아웃, 그리고 배포 롤백의 원리

파드 대량 생성 시 발생하는 FailedScheduling, 진행 기한 초과(Timeout), 그리고 선언적 GitOps 환경에서 자동 롤백을 다루는 방법 정리

대규모 트래픽이나 배치 작업을 처리하기 위해 디스패처(Dispatcher)나 오케스트레이터가 수많은 파드(Pod)를 한 번에 생성할 때가 있다. 이때 클러스터의 노드(Node) 자원이 충분하지 않으면 파드는 어떤 상태를 거치며, 타임아웃에 걸렸을 때 배포 시스템은 어떻게 반응하는지 내부 동작과 한계를 기술적인 관점에서 정리해 본다.


파드 생성과 스케줄러의 노드 탐색

디스패처나 Deployment가 쿠버네티스 API Server에 새로운 파드 생성을 요청하면, 파드 객체가 etcd에 생성되며 초기 상태는 Pending이 된다.

이후 kube-scheduler가 주기적으로 스케줄링 대기열을 확인하며 각 파드를 실행할 적합한 노드를 탐색한다. 이 과정은 크게 두 단계로 나뉜다.

  1. 필터링(Filtering): 각 노드의 남은 CPU 및 메모리 예약 자원(requests)이 파드가 요구하는 수치 이상인지 검사한다. 테인트(Taint)나 노드 어피니티(Affinity) 조건도 이 단계에서 평가된다.
  2. 스코어링(Scoring): 조건을 만족한 노드들 중 자원 분배나 네트워크 토폴로지 측면에서 가장 적합한 노드 하나를 선택하여 파드에 nodeName을 할당한다.

만약 클러스터 안의 모든 노드가 이미 꽉 차 있어서 파드의 requests를 수용할 수 있는 노드가 단 하나도 없다면, 스케줄러는 할당을 보류하고 파드를 Pending 상태로 남겨둔다.


노드 부족 시 발생하는 FailedScheduling과 OOM의 차이

노드 자원이 부족할 때 파드의 상세 정보(kubectl describe pod)를 확인하면 다음과 같은 이벤트가 기록된다.

Warning  FailedScheduling  0/4 nodes are available: 4 Insufficient cpu, 4 Insufficient memory.

여기서 명확히 구분해야 할 점은 스케줄링 실패와 실행 중 발생하는 OOM(Out of Memory)의 차이다.

  • 스케줄링 실패 (Pending / FailedScheduling): 파드가 실제로 실행되기 전, 설정된 자원 예약값(requests)이 노드의 남은 가용량보다 커서 발생한다. 컨테이너가 뜬 적이 없으므로 크래시가 난 것이 아니라 자리가 나기를 기다리는 안전한 대기 상태다.
  • OOM 발생 (OOMKilled / Exit Code 137): 파드가 노드에 정상 배치되어 실행되던 도중, 실제 메모리 사용량이 설정된 한도(limits)나 노드 자체의 물리 메모리를 초과하여 리눅스 커널의 OOM Killer에 의해 프로세스가 강제 종료된 상태다.

타임아웃 발생 시 쿠버네티스와 ArgoCD는 자동 롤백을 하지 않는다

파드가 Pending 상태로 무한정 멈춰 있으면 상위 배포 도구들에서 타임아웃이 발생한다. 이때 흔히 오해하기 쉬운 지점은 “타임아웃이 나면 알아서 이전 버전으로 롤백되겠지”라는 생각이다.

기본 쿠버네티스와 ArgoCD의 실제 동작은 다음과 같다.

  1. Deployment의 진행 기한 초과 (progressDeadlineSeconds):
    Deployment의 기본 진행 기한은 600초(10분)다. 이 시간 동안 새 파드가 노드 부족으로 준비(Ready) 상태에 도달하지 못하면 상태가 ProgressDeadlineExceeded로 변한다. 하지만 쿠버네티스는 새 파드 생성을 멈출 뿐, 스스로 이전 버전으로 롤백하는 메커니즘을 내장하고 있지 않다.

  2. ArgoCD의 선언적 한계:
    ArgoCD는 Git 저장소를 단일 진실 공급원(Single Source of Truth)으로 신뢰한다. 클러스터 배포가 지연되어 타임아웃이 발생하면 UI 상에 🔴 DegradedSync Failed 상태를 띄울 뿐, Git의 커밋을 거꾸로 되돌려(Revert) 구버전으로 다운그레이드하지 않는다.


프로덕션 장애를 방지하는 실무 전략

노드 부족으로 인한 배포 지연이 전체 서비스 중단으로 이어지지 않도록 실무에서는 다음과 같은 설계를 적용한다.

1. RollingUpdate의 maxUnavailable 제어

배포 전략을 Recreate로 설정하면 기존 파드를 먼저 모두 종료한 뒤 새 파드를 띄우려 하므로, 노드 부족 시 즉시 서비스 다운타임이 발생한다. 반면 RollingUpdate에서 maxUnavailable: 0을 설정하면, 새 파드가 완전히 준비되기 전까지 기존 정상 파드를 단 하나도 종료하지 않아 트래픽 중단을 원천 차단할 수 있다.

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 0

2. 노드 자동 프로비저닝 (Cluster Autoscaler / Karpenter)

Pending 상태의 파드가 감지되면 클라우드 인프라에 즉시 새 워커 노드 생성을 요청하는 오토스케일러를 구성한다. 특히 Karpenter는 파드가 요구하는 CPU/메모리 스펙에 맞는 인스턴스 타입을 동적으로 계산하여 수십 초 내로 노드를 프로비저닝한다.

3. 점진적 배포 및 자동 롤백을 위한 Argo Rollouts

카나리(Canary)나 블루/그린 환경에서 메트릭 이상이나 배포 지연이 감지되었을 때 자동으로 이전 버전으로 트래픽을 되돌리려면 기본 Deployment 대신 Argo Rollouts를 채택해야 한다. 분석 템플릿(AnalysisTemplate)을 통해 에러율이나 프로그레스 타임아웃을 감지하여 자동으로 배포를 중단(Abort)하고 이전 정상 레플리카로 복귀시키는 안전장치를 둘 수 있다.


정리

대량의 파드를 생성할 때 발생하는 노드 부족 문제는 단순히 서버를 늘리는 것 이상의 배포 안정성 문제를 수반한다.

쿠버네티스와 GitOps 도구는 기본적으로 선언된 상태를 유지하려 할 뿐 실패 시 자동으로 롤백을 보장하지 않으므로, maxUnavailable: 0과 같은 무중단 롤링 업데이트 전략과 Argo Rollouts 같은 전문 롤백 도구의 역할 분담을 정확히 이해하고 설계하는 것이 중요하다.

나아가 상시 가동되는 서비스(ArgoCD / Deployment)가 아닌, 정해진 시점에 대량의 파드를 띄우고 회수하는 배치 워커나 에이전트 디스패처 환경에서는 워크플로우 엔진(Argo Workflows)과 정밀한 시간 예산(Time Budget) 관리가 결합된 별도의 회복력 설계가 요구된다.