개요
karpenter를 사용하다 보면 파드의 전체 request가 줄어들어도 전체 노드의 allocatable 리소스가 줄어들지 않는 상황이 종종 발생합니다. 코드 기반으로 노드 통합 방식을 파악하여 어떤 이유로 노드 통합이 중지될 수 있는지 알아봅니다.
Karpenter 노드 통합이 가능한 조건
1. Consolidate 후보 생성
1) 노드가 Consolidatable 상태일 때
- NodeClaim이 Initialized 상태여야 함(NodeClaim 생성 후 실제 노드가 완전히 준비된 상태)
- 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 중으로 판단하는 노드 (코드)
- NotReady 상태 노드
- 삭제 중인 노드(MarkedForDeletion 상태 노드)
NodeClaim에 deletionTimestamp가 있을 때InstanceTerminating=False(drain/detach 중)인 것만 해당
참고) NodeClaimInstanceTerminating=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
'Kubernetes' 카테고리의 다른 글
| EKS Node의 최대 Pod 수 - Security Groups per Pod 사용 시 (0) | 2024.11.18 |
|---|---|
| 노드의 memory에 대하여 - cgroup과 OOM killer에 대해 알아보기 (4) | 2024.07.31 |
| 여러 노드와 존에 파드를 분산하여 배포하는 방법 (0) | 2024.04.06 |
| 파드의 컨테이너 pid 및 cgroup에 할당된 cpu, mem 확인 방법(containerd) (1) | 2024.03.12 |
| 컨테이너 root로 접속하는 방법(containerd) (3) | 2023.11.07 |