Karpenter consolidation은 무엇을 하는가
10초마다 무슨 일이 일어나나
Karpenter가 프로비저닝했던 노드들은 다음과 같은 루프를 돌며 “잘 쓰고 있었는가”를 평가합니다.
① 삭제 대상 검사 전체 노드를 훑어 손대도 되는 것만 남긴다
② 삭제 분류 Empty · Drifted · Underutilized 중 어디에
③ 시뮬레이션 지우면 새 노드가 몇 대 필요한가
④ 수행 삭제는 바로 · 교체는 대체 노드 기동 후 · 아니면 그대로
이 네 단계들을 매번 반복합니다.
Emptiness → Drift → MultiNode → SingleNode어떤 노드가 검사 대상인가
검사는 클러스터의 모든 Karpenter 노드들이 대상입니다. 매 바퀴 모든 노드들을 검사하면서, 결격 사유 있는 노드들을 배제하는 형태로 동작합니다.
검사 대상에 예외가 있는데
중간에 노드 삭제 대상으로 분류하지 않는 케이스는 이렇습니다.
| 삭제 방지 조건 | 상세 설명 |
|---|---|
| PDB에 걸림 | 파드를 노드에서 축출 불가능 |
파드에 do-not-disrupt | 사용자가 명시적으로 제한 |
노드에 do-not-disrupt | 사용자가 명시적으로 제한 |
Initialized 상태가 아님 | 기동 중인 노드는 대상에서 배제 |
외에도 새로운 파드들을 스케줄링 중이거나, NodePool 자체의 상태가 변경되는 엣지 케이스들이 있지만 여기서는 다루지 않겠습니다.
검사에서 떨어지면 DisruptionBlocked 이벤트가 남습니다.
여기서는 “죽일까?” 정도의 분류만 진행한 것이고 이제 “왜?”를 찾게 되는 단계로 넘어갑니다.
삭제 후보는 어떤 것들을 볼까
검사를 통과하면 그 노드는 Candidate가 되고, 이때 다음 값들이 함께 계산됩니다.
| 키워드 | 설명 | 비고 |
|---|---|---|
Price | 그 노드의 시간당 가격 | 교체 시 “더 싼가” 비교 |
reschedulablePods | 실제로 옮겨야 할 파드 목록 | DaemonSet은 제외 |
RescheduleDisruptionCost | 노드 몫 1.0 + 위 파드들의 비용 합 | 노드 자체를 교체하는데 가중치 부여. Balanced 스코어 계산에 사용 |
instanceTypecapacityTypezone | Price를 조회하는 복합 키 | 셋 중 하나라도 없으면 통합 후보에서 탈락 |
RescheduleDisruptionCost는 v1.14에서 Balanced 모드 계산을 위해 추가된 필드입니다.
같은 m8g.xlarge라도 AZ, spot 혹은 on-demand 상태의 노드가 또 다르기 때문에, 하나라도 다르다면 비교를 진행하지 않습니다.
Price / RescheduleDisruptionCost = $/시간 ÷ 파드 수 = 파드 하나 옮기는 대가로 시간당 아끼는 돈파드 하나 옮기는 비용이 얼마나 되는가를 Karpenter 내부적으로 계산합니다. 이 비율이 큰 노드부터 정렬하니, 비싸면서 한산한 노드가 먼저 정리됩니다.
RescheduleDisruptionCost는 DaemonSet은 모두 제외하고
노드 자체를 삭제하는 비용을 base로 1 설정해 cordon·drain·대체 노드 기동 지연 자체의 비용으로 계산합니다.
파드별 비용은 기본 1에 pod-deletion-cost로 같이 합산되어 계산합니다.
어떤 이유로 노드를 삭제할까
앞에서 검사 대상으로 확인된 노드는 아직 어떻게 할지를 결정하지 않았는데요 노드는 세 가지 중 해당되는것을 Reason으로 기록 합니다.
| Reason의 종류 | 세부 설명 |
|---|---|
Empty | 파드가 없는 노드 |
Drifted | NodePool 스펙과 어긋난 노드 |
Underutilized | 합쳐서 줄일 수 있는 노드 |
이 셋을 위에서부터 차례로 시도하고, 해당사항이 있는쪽으로 분류됩니다.
Underutilized는 두 가지로 나뉘는데요, SingleNode와 MultiNode로 나눠집니다.
SingleNode — 맨 앞 한 대의 파드를 새 노드로 옮깁니다.
실패하면 다음 후보로 내려가고, 삭제가 가능한 노드를 찾을 때까지 수행합니다.
SingleNode — 맨 앞 한 대를 집어 그 파드를 새 노드로 옮긴다. cost 는 집는 순간 확정되고 옮기면서 치러진다
MultiNode — 앞에서부터 여러 대를 한꺼번에 집습니다.
여러 대를 동시에 묶은 만큼 비용도 합쳐지지만, 한 대씩 정리하는 것만으로는 나오지 않을 조합이 여기서 나옵니다.
MultiNode — 앞 세 대를 한꺼번에 집어 한 대로 접는다. 교체 노드는 파드 요구에 맞는 것 중 싼 것이면 되고, 같은 패밀리일 필요가 없다
두 도식 모두 집는 순간 비용 합계가 확정되고(옅은 칸), 파드를 옮기면서 그 값을 치릅니다(진한 칸). 옮기면서 계산하는 게 아니라 후보를 만들 때 이미 정해진 값입니다.
실행 순서는 MultiNode → SingleNode 순서대로 진행됩니다.
MultiNode가 먼저 돌고, 수행 가능한 시나리오를 찾으면 그 라운드는 거기서 끝납니다.
SingleNode는 MultiNode가 실패했을 때, 후보를 다 훑기 전에 3분이 지나면 중단합니다 — 이때 못 본 NodePool이 있었다면, 다음 라운드에 먼저 봅니다.
정책 셋은 무엇이 다른가
앞에서 본 Underutilized의 실행이 실제로는 세 가지 정책에 맞게 갈라집니다.
표에 나오는 “삭제 또는 교체”가 무엇에서 갈리는지는 뒤에서 따로 봅니다.
| 정책 | 빈 노드 | 그 외 후보 | 통과해야 할 게이트 |
|---|---|---|---|
WhenEmpty | 삭제 | 그냥 삭제 대상임 | 없음 |
WhenEmptyOrUnderutilized | 삭제 (같음) | 시뮬레이션 후 삭제 또는 교체 | 파드 재배치 + (교체면) 가격 |
Balanced | 삭제 (같음) | 시뮬레이션 후 스코어 심사 | WhenEmptyOrUnderutilized 방식 + score ≥ 0.5 |
적게 지움 ←────────────────────────────────────→ 많이 지움
WhenEmpty Balanced WhenEmptyOrUnderutilized세 정책은 “얼마나 지우느냐” 정도로 보시면 될 것 같습니다.
Empty 노드가 아닌 WhenEmptyOrUnderutilized 개념이 Karpenter의 강점이었다고 생각합니다.
오른쪽으로 갈수록 삭제 강도가 커지는 개념이지만, 너무 공격적인 축출을 방지하기 위해 1.14 버전에서 생긴 Balanced가 새로운 기준들을 보강했습니다.
이어서 좀 더 똑똑하게 consolidation하기 — Balanced 모드를 봅니다.