Kubernetes

왜 나의 karpenter 노드가 삭제되지 않을까?

백곰곰 2025. 12. 26. 10:10
728x90
반응형

개요

karpenter를 사용하다 보면 파드의 전체 request가 줄어들어도 전체 노드의 allocatable 리소스가 줄어들지 않는 상황이 종종 발생합니다. 코드 기반으로 노드 통합 방식을 파악하여 어떤 이유로 노드 통합이 중지될 수 있는지  알아봅니다.

 


Karpenter 노드 통합이 가능한 조건

1. Consolidate 후보 생성

1) 노드가 Consolidatable 상태일 때

  1.  NodeClaim이 Initialized 상태여야 함(NodeClaim 생성 후 실제 노드가 완전히 준비된 상태)
  2. NodeClaim이 마지막 Pod 이벤트 시간/초기화 시간(LastPodEventTime)으로부터 consolidateAfter 기간이 경과해야 함
    참고) LastPodEventTime란?
             노드에서 다음 Pod 이벤트 중 하나가 발생하면 LastPodEventTime이 현재 시간으로 업데이트
             - 새로운 Pod가 노드에 바인딩됨 (Scheduled)
             - Pod가 Terminal 상태로 전환됨 (Succeeded/Failed)

             - Pod가 Terminating 상태로 전환됨
            LastPodEventTime은 10초 주기로 체크하여 갱신
            데몬셋 파드는 업데이트에서 제외(참고)

2) 노드가 disruption 큐에 없어야 함 (코드)
distruption 프로세스가 진행 중인 노드는 제외

 

3) 노드와 노드의 파드가 disruptable 상태여야 함 (코드)

노드 Disruptable 검증
├─ 1. Karpenter 관리 노드인가?
├─ 2. Node 객체가 존재하는가?
├─ 3. Initialized 상태인가?
│   └─ karpenter.sh/initialized: "true" 레이블 확인
├─ 4. 삭제 중이 아닌가?
│   ├─ DeletionTimestamp 없음
│   └─ InstanceTerminating 상태 아님
├─ 5. Nominated 상태가 아닌가?
│   └─ 최근 pending pod 대상 아님
├─ 6. Do-Not-Disrupt 어노테이션 없는가?
│   └─ karpenter.sh/do-not-disrupt != "true"
├─ 7. NodePool 레이블이 있는가?
│   └─ karpenter.sh/nodepool 레이블 존재
│
└─ 8. Pod 레벨 검증 (노드의 모든 Pod를 evict 할 수 있어야 함)
    ├─ 모든 Pod가 Disruptable한가?
    │   └─ karpenter.sh/do-not-disrupt != "true"
    └─ PDB가 eviction을 허용하는가?
        └─ PodDisruptionBudget 제약 통과

 

Consolidate 후보를 생성한 이후 budget을 체크하여 실제 가능 여부를 판단합니다.

2. Disruption Budget 계산

종료 가능한 노드 수 = max(설정된 Budget - 현재 disrupting 중인 노드 수, 0)

disrupting 중으로 판단하는 노드 (코드)

  1. NotReady 상태 노드
  2. 삭제 중인 노드(MarkedForDeletion 상태 노드)
    NodeClaim에 deletionTimestamp가 있을 때 InstanceTerminating=False (drain/detach 중)인 것만 해당
    참고) NodeClaim InstanceTerminating=True (인스턴스 종료 중)은 Budget 계산에서 제외
		// If the node satisfies one of the following, we subtract it from the allowed disruptions.
		// 1. Has a NotReady conditiion
		// 2. Is marked as disrupting
		if cond := nodeutils.GetCondition(node.Node, corev1.NodeReady); cond.Status != corev1.ConditionTrue || node.MarkedForDeletion() {
			disrupting[nodePool]++
		}

 

종료 가능한 노드 수가 0이면 노드 통합이 일어나지 않습니다.

3. 파드 스케줄링 시뮬레이션 & 가용 인스턴스 체크

budget에서 허용한다면, consolidation 후보에 대해 노드 파드 스케줄링 시뮬레이션 및 가용 인스턴스를 확인합니다.

 

스케줄링 시뮬레이션 (코드)

스케줄링 시뮬레이션에 실패하면, 해당 노드는 통합할 수 없습니다.

computeConsolidation()
  ↓
SimulateScheduling()
  ↓
provisioner.NewScheduler() - Scheduler 생성
  ↓
scheduler.Solve() - 스케줄링 시뮬레이션 실행
  ↓
  ├─ trySchedule(pod) - 각 파드 스케줄링 시도
  │   ↓
  │   └─ add(pod) - 파드를 노드에 추가 시도
  │       ↓
  │       ├─ addToExistingNode() - 기존 노드에 배치 시도
  │       ├─ addToInflightNode() - 생성 예정 노드에 배치 시도
  │       └─ addToNewNodeClaim() - 새 노드 생성
  │           ↓
  │           └─ NodeClaim.CanAdd() ← 여기서 모든 제약 조건 체크!
  │               ↓
  │               ├─ 1. Taints/Tolerations
  │               ├─ 2. HostPort 충돌
  │               ├─ 3. NodeAffinity (Requirements)
  │               ├─ 4. Topology (PodAffinity/AntiAffinity/TopologySpread)
  │               └─ 5. 리소스 & 인스턴스 타입 (filterInstanceTypesByRequirements 호출)
  └─ preferences.Relax() - 실패 시 제약 완화 후 재시도

 

가용 인스턴스 체크 (코드)

현재 원하는 조건으로 사용할 수 있는 인스턴스 타입이 있는지 확인합니다.

가용 인스턴스가 없으면(spot이나 특정 zone에서의 타입 부족 등) 실패합니다.


노드 병합이 불가능한 시나리오

consolidateAfter 시간이 너무 길 때

해당 시간이 너무 길게 설정되어 있으면 LastPodEventTime 타임이 계속 업데이트되기 때문에, 파드 생성/삭제가 빈번한 경우 빈 노드도 삭제가 되지 않을 수 있습니다.

 

NotReady 노드가 있을 때

distruption budget에서 NotReady 노드도 차감되기 때문에, NotReady 노드가 다수 있다면 budget에 의해 노드 삭제가 되지 않을 수 있습니다.

 

PDB에 허용된 중단 가능 파드 수가 너무 적을 때

PDB에 허용된 중단 가능 파드 수가 너무 적으면 파드 eviction이 차단되어 노드 drain에 시간이 오래 걸립니다. 이때 distrupting 중인 노드는 distruption budget에서 차감되기 때문에 budget에 의해 노드 삭제가 되지 않을 수 있습니다.

 

topologySpreadConstraints, Affinity 등 파드 스케줄링에 제약이 많을 때

해당 설정이 엄격하면 노드가 underutilized인 경우에 파드 재배치가 어려워 노드 병합이 되지 않을 수 있습니다.


노드 병합이 불가능할 때 참고 가능한 metric

karpenter_nodepool_allowed_disruptions

-> nodepool budget에 설정한 값으로 0이 아닌지 확인합니다.

 

karpenter_voluntary_disruption_eligible_nodes

-> 해당 메트릭은 consolidation 후보를 카운팅한 값입니다. 실제 사용량이 낮아 병합 대상은 맞지만 budget, 파드 스케줄링 시뮬레이션 등은 검증되지 않은 상태입니다. karpenter_voluntary_disruption_eligible_nodes{reason="empty"} 가 계속 높다면 budget에 걸린 상황이고, karpenter_voluntary_disruption_eligible_nodes{reason="underutilized"} 가 계속 높다면 budget 또는 파드 스케줄링 시뮬레이션에 실패한 경우일 수 있습니다.

 

karpenter_voluntary_disruption_decisions_total

-> 모든 검증에 통과하여 수행된 distruption의 수입니다. 해당 값이 증가하지 않는데 karpenter_voluntary_disruption_eligible_nodes가 높다면, 어떠한 이유로 노드 consolidation이 불가한지 확인해야 합니다.

 


마무리

이상으로 karpenter가 어떤 방식으로 노드 consolidation을 결정하는지 알아보았습니다. 이 글이 비용 효율적인 리소스 사용에 도움이 되었으면 합니다.

 

 

GitHub - kubernetes-sigs/karpenter: Karpenter is a Kubernetes Node Autoscaler built for flexibility, performance, and simplicity

Karpenter is a Kubernetes Node Autoscaler built for flexibility, performance, and simplicity. - kubernetes-sigs/karpenter

github.com

 

728x90