- Published on
EKS 노드 비용 40%↓ - Karpenter Spot+중단대응
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
EKS에서 노드 비용은 대개 전체 인프라 비용의 상당 부분을 차지합니다. 특히 워커 노드를 온디맨드(OD)로만 운영하면, 트래픽 변동이 큰 서비스에서 유휴 용량이 바로 비용 낭비로 이어집니다. 반대로 Spot을 쓰면 비용은 크게 내려가지만, 언제든 회수(interruption) 될 수 있어 가용성/지연/배포 안정성에 대한 불안이 생깁니다.
이 글은 Karpenter로 Spot 중심 오토스케일링을 구성해 노드 비용을 40% 이상 절감하면서도, Spot 중단 이벤트에 예측 가능하게 대응하는 실전 운영 패턴을 다룹니다. (중요: 본문에 >/< 같은 부등호는 MDX 빌드 이슈가 있어 코드 블록/인라인 코드로만 표기합니다.)
왜 Cluster Autoscaler 대신 Karpenter인가
Cluster Autoscaler(CA)도 노드 오토스케일링이 가능하지만, 다음 제약이 자주 발목을 잡습니다.
- 노드그룹(Managed Node Group, ASG) 단위로 스케일링: 인스턴스 타입/용량을 세밀하게 바꾸기 어렵습니다.
- Spot 다변화(여러 타입, 여러 AZ, 여러 용량 전략) 최적화가 제한적입니다.
- 파드 스케줄 요구사항(아키텍처, 용량, 디스크, 네트워크)을 만족하는 “최적” 노드를 즉시 만들기보다, 미리 정의한 그룹에서 증감하는 방식입니다.
Karpenter는 미리 정해진 노드그룹에 덜 의존하고, 파드 요구사항을 보고 그때그때 최적 인스턴스를 선택해 띄웁니다.
- 다양한 인스턴스 타입을 한 번에 후보로 두고, 가격/가용성에 따라 유연하게 선택
- 필요하면 더 작은 노드 여러 개로 쪼개거나, 반대로 큰 노드로 합쳐서(binpacking) 비용 최적화
- TTL/Consolidation로 유휴 노드를 적극적으로 정리
비용 40% 절감이 나오는 전제 조건
비용 절감 폭은 워크로드 성격에 따라 달라지지만, 아래 조건을 만족하면 40%는 현실적인 수치입니다.
- 워크로드의 상당 부분이 무상태(stateless) 이거나, 상태가 있어도 재시작/재스케줄에 강함
- HPA/큐 기반 소비자/배치처럼 스케일 아웃이 자연스러운 구조
requests/limits를 대충 잡지 않고, 최소한requests는 현실적으로 설정- Spot 중단을 “장애”가 아니라 “정상 이벤트”로 보고, 중단 대응(Drain, PDB, graceful shutdown) 을 갖춤
참고로 파드가 자주 재시작되거나 노드 교체가 잦아지면, CrashLoopBackOff 같은 증상으로 비용 최적화 이전에 안정성 문제가 먼저 터집니다. 관련 진단은 Kubernetes CrashLoopBackOff 10가지 원인과 15분 진단도 함께 보시면 좋습니다.
아키텍처 개요: Spot은 기본, On-Demand는 안전망
권장하는 기본 구조는 이렇습니다.
- 기본 노드풀: Spot (대부분의 워크로드)
- 안전망 노드풀: On-Demand (핵심 시스템 파드, 스케줄러/컨트롤플레인 주변, 혹은 Spot 회수 폭주 시)
- 중요한 파드: PDB + 우선순위(PriorityClass) + graceful termination
- Spot 중단 이벤트: Node Termination Handler(NTH) 또는 이벤트 기반 드레이닝
Karpenter만으로도 노드 생성/정리는 되지만, Spot 회수 이벤트를 감지하고 빠르게 드레인하는 컴포넌트가 있으면 “중단이 와도 서비스 영향이 작아지는” 운영이 됩니다.
Karpenter 설치 전 체크리스트
1) EKS 버전/권한/IRSA
- Karpenter Controller는 AWS API(EC2, Pricing 등) 호출이 필요합니다.
- IRSA(IAM Roles for Service Accounts)로 최소 권한을 부여하세요.
EKS에서 IAM/SDK 인증이 꼬이면 403 계열로 고생합니다. 특히 노드/파드 역할 분리가 불명확하면 디버깅이 길어집니다. 필요하면 EKS에서 AWS SDK 403 MissingAuthenticationToken 해결도 참고하세요.
2) 서브넷/보안그룹 태그
Karpenter가 노드를 띄울 서브넷과 보안그룹을 찾을 수 있도록 태그가 필요합니다. (태그 키는 Karpenter 버전/가이드에 따라 달라질 수 있으니 공식 문서를 기준으로 하되, 원리는 동일합니다.)
- 클러스터 식별 태그
- 노드 디스커버리 태그
3) 인스턴스 타입 후보군 전략
Spot 안정성을 높이는 핵심은 “한 타입만 고집하지 않는 것”입니다.
m/c/r계열을 섞고- 세대도 2~3개로 넓히고
- AZ도 2개 이상
후보군을 넓히면 단가 최저는 조금 올라가도, 스케일 실패/용량 부족으로 인한 장애 확률이 내려갑니다.
Karpenter NodePool 설계: Spot 중심 구성 예시
아래 예시는 개념을 보여주는 예시입니다. 실제 필드명은 사용 중인 Karpenter API 버전에 맞춰 조정하세요.
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: spot-general
spec:
template:
spec:
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: node.kubernetes.io/instance-type
operator: In
values:
- m6i.large
- m6i.xlarge
- c6i.large
- c6i.xlarge
- r6i.large
taints:
- key: spot
value: "true"
effect: NoSchedule
limits:
cpu: "500"
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
expireAfter: 720h
핵심 포인트:
karpenter.sh/capacity-type를spot으로 제한- 후보 인스턴스 타입을 여러 개로 넓힘
taints로 Spot 노드에 기본적으로 NoSchedule을 걸고, Spot 허용 워크로드만 toleration으로 올라오게 설계- Consolidation/expire로 유휴/비효율 노드를 정리
On-Demand 안전망 NodePool
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: ondemand-core
spec:
template:
spec:
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
taints:
- key: core
value: "true"
effect: NoSchedule
limits:
cpu: "100"
- 핵심 파드만
tolerations로core노드에 올라가게 하면, Spot 변동이 커도 최소 기능은 지킬 수 있습니다.
워크로드 쪽 “Spot 중단 내성” 체크리스트
Spot을 쓰는 순간, 노드는 “언젠가 사라지는 리소스”가 됩니다. 따라서 파드가 다음을 만족해야 합니다.
1) PDB로 동시 축출 한도 제한
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: api
- 노드 드레인/축출 시 한 번에 너무 많이 빠지지 않도록 제한
- 단,
minAvailable을 과도하게 높이면 드레인이 막혀서 오히려 복구가 늦어질 수 있습니다.
2) graceful shutdown: preStop + terminationGracePeriodSeconds
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 6
template:
spec:
terminationGracePeriodSeconds: 40
containers:
- name: api
image: your-api:latest
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]
- 로드밸런서/서비스 디스커버리에서 트래픽이 빠질 시간을 확보
- 앱이 SIGTERM을 제대로 처리하도록 구현(커넥션 드레인, 작업 중단/재시도)
3) PriorityClass로 “살아남아야 하는 파드” 우선 배치
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: critical
value: 100000
preemptionPolicy: PreemptLowerPriority
globalDefault: false
description: "Critical workloads"
- Spot 대규모 회수로 용량이 급감할 때, 중요 파드가 우선 스케줄되도록 유도
4) Spot 노드에만 올라갈 워크로드를 명시
spec:
tolerations:
- key: spot
operator: Equal
value: "true"
effect: NoSchedule
- “기본은 OD, 일부만 Spot”도 가능하지만, 비용 최적화 목표가 크다면 “기본은 Spot, 핵심만 OD”가 더 효과적입니다.
Spot 중단 대응: 이벤트 감지 후 빠른 드레인
Spot은 보통 중단 2분 전후의 사전 통지를 제공합니다. 이 시간 안에 해야 할 일은 단순합니다.
- 해당 노드에
cordon - 파드
evict(PDB 준수) - 새 노드를 빠르게 확보(Karpenter)
이를 자동화하는 대표 옵션이 AWS Node Termination Handler(NTH) 입니다. NTH는 EC2 메타데이터/이벤트를 감지해 노드를 드레인합니다.
NTH 설치(Helm) 예시
helm repo add eks https://aws.github.io/eks-charts
helm upgrade --install aws-node-termination-handler eks/aws-node-termination-handler \
--namespace kube-system \
--set enableSpotInterruptionDraining=true \
--set enableRebalanceMonitoring=true \
--set enableScheduledEventDraining=true
운영 팁:
- 드레인 속도가 느리면
PDB/terminationGracePeriodSeconds/애플리케이션 종료 로직을 먼저 점검 - 중단 이벤트가 자주 오는데 서비스 영향이 크다면, 인스턴스 타입 후보군이 너무 좁은지 확인
Karpenter Consolidation(통합)로 “유휴 비용” 더 깎기
Spot을 쓰더라도, 파드 배치가 비효율적이면 노드가 남아서 비용이 새어 나갑니다. Karpenter의 consolidation은 다음 상황에서 효과가 큽니다.
- 트래픽 피크가 지나간 뒤 노드가 많이 남는 구조
requests가 과대 설정되어 binpacking이 안 되는 구조
권장 흐름:
- 먼저
requests를 현실화 - consolidation을
WhenEmptyOrUnderutilized로 켬 - 관측 지표(노드 수, 파드 재스케줄 횟수, P95 지연)를 보며 점진적으로 튜닝
비용 절감 효과를 “숫자”로 검증하는 방법
1) Cost Explorer: On-Demand 대비 Spot 비중
- EC2 비용을 인스턴스 타입/구매 옵션(Spot/OD)로 분해
- EKS 노드가 쓰는 ASG/태그 기준으로 필터링
2) Kubernetes 레벨: 노드/파드 효율
- 노드 CPU/메모리 사용률 평균, P95
- 파드 pending 시간(스케줄 지연)
- eviction 횟수, 노드 교체 빈도
3) 장애/품질 지표
- 배포 중 오류율 증가 여부
- P95/P99 지연, 타임아웃 비율
- 워커 재시작 증가(CrashLoopBackOff)
자주 겪는 장애 패턴과 해결책
스케일 아웃이 간헐적으로 실패한다
원인 후보:
- 인스턴스 타입 후보군이 너무 좁음
- 특정 AZ에만 서브넷이 열려 있음
- 보안그룹/서브넷 태그 누락
- EBS/ENI 제한으로 파드 밀집이 안 됨
대응:
- 타입/세대/AZ 후보군 확장
requests조정으로 파드 밀도를 개선- 노드 부트스트랩/네트워크 설정 점검
드레인이 느려서 중단 시 영향이 크다
원인 후보:
terminationGracePeriodSeconds가 너무 큼preStop에서 불필요하게 오래 대기- PDB가 너무 엄격해서 eviction이 막힘
대응:
- 종료 시간을 “필요한 만큼만” 확보
- PDB를
minAvailable에서maxUnavailable로 바꾸는 것도 고려
로그 폭주로 노드 디스크가 꽉 차서 축출이 연쇄 발생한다
Spot 전환과 별개로, 노드가 자주 교체되면 로그/디스크 이슈가 더 빨리 드러납니다. 특히 journalctl 로그가 쌓이면 노드 안정성이 급격히 떨어질 수 있습니다. journalctl 로그 폭주로 디스크 찰 때 10분 해결처럼 노드 레벨 로그 정책도 함께 정비하세요.
운영에서 통하는 권장 조합(요약)
- Spot NodePool: 다양한 인스턴스 타입 + 여러 AZ +
taints로 대상 워크로드 제한 - On-Demand NodePool: 최소 안전망(핵심 파드만 toleration)
- 워크로드: PDB + graceful shutdown + PriorityClass
- 중단 대응: NTH로 interruption 감지 후 자동 드레인
- 비용 최적화: consolidation +
requests현실화
이 조합을 제대로 적용하면, “Spot이라 불안하다”가 아니라 “중단은 자주 오지만 영향은 작다”로 운영 감각이 바뀌고, 그 결과 EKS 노드 비용을 안정적으로 40% 이상 낮출 수 있습니다.
다음 단계 체크리스트
- 현재 워크로드를
core(OD)와spot-ok로 분류했는가 requests/limits가 실측 기반으로 정리되어 있는가- PDB가 드레인을 막지 않으면서도 가용성을 지키는 수준인가
- NTH 이벤트가 실제로 감지되고, 드레인 로그가 남는가
- consolidation 이후 지연/오류율이 악화되지 않는가
위 항목을 순서대로 점검하면, 비용 절감과 안정성을 동시에 잡는 Karpenter Spot 운영을 빠르게 정착시킬 수 있습니다.