요약
- DEV 환경이니 월1~2회 지연이 발생해도 개발/테스트에는 큰 지장이 없다(협의는 꼭 필요), 그 대신 매달 $200~ 이상의 비용 절감 가능
- Karpenter의 빠른 프로비저닝과 자동 통합(consolidation), 그리고 Spot 인터럽트 대응을 활용하면 개발 환경 비용을 크게 줄이면서도 안정성을 유지가능하다.
현재 새롭게 DEV 인프라를 구성하고 있는 프로젝트는 총 5개의 도메인(member, order, point 등등)은 MSA로 이루어진 플랫폼이다.
수십개의 서비스가 동시에 떠있는 DEV 환경에서 항상 켜져 있어야 하는 인프라 비용은 꽤 부담(낭비)가 된다.
이러한 문제를 해결하기 위해 Karpenter + Spot 인스턴스 조합을 선택했고, 어떻게 설계했는지 정리해본다.
--
Spot 인스턴스는 AWS의 유휴 컴퓨팅 자원을 경매 방식으로 제공하는 인스턴스 유형으로 비용 절감에 최적화되어 있다.
On-Demand에 대비해 최대 90% 저렴하다.
하지만 안정성은 AWS가 회수해갈 수 있기 때문에 불안정하며, 다행히(?)도 회수 2분전에 인터럽트 신호를 준다.
* 핵심은 2분전 통보인데, 이 신호를 받으면 pod를 안전하게 다른 노드로 이동시키면서 서비스 중단 없이 비용을 절감 할 수 있다.
--
Karpenter와 CA(Cluster Autosaler)의 큰 차이점은 시간과 인스턴스 선택이 있다.
| 특징 | Cluster Autoscaler | Karpenter |
| 스케일링 단위 | 노드그룹 | 개별 노드 |
| 인스턴스 선택 | 사전 고정 | 동적 선택 |
| 프로비저닝 시간 | 3~5분 | ~60초 |
| Spot 다변화 | 제한적 | 여러 타입 동시 시도 |
| 빈 노드 정리 | 느림 | consolidateAfter 설정에 따라 |
--
NodePoll 은 어떻게 설계했나
dev-cluster
├── infra-ng (On-Demand, m6g.large × 2) ← EKS 관리형 노드그룹
│ └── Karpenter, ESO, CoreDNS 등 인프라 컴포넌트
│
├── service-spot NodePool ← Karpenter 관리
│ └── 00개 서비스 Pod (Spot, arm64, 4코어)
│
├── batch-spot NodePool ← Karpenter 관리
│ └── Jenkins agent, Argo Workflows 배치 잡
│
└── neo4j-ondemand NodePool ← Karpenter 관리
└── Neo4j DB (On-Demand, m6g.xlarge 고정)
위 설계를 보면 On-Demand로 구분한 것도 있다.
이유는 Karpenter 자체가 Spot에서 실행되다가 회수되면 다른 Pod들을 이동시킬 주체가 없다.
Neo4j(DB)는 Spot 회수 시 데이터 정합성 문제가 생길 수 있다.
--
# dev 환경 기준 - 노드 1개에 여러 Pod 수용
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: "1"
memory: 2Gi
--
# eks.tf
resource "kubernetes_manifest" "karpenter_nodepool_spot" {
manifest = {
spec = {
weight = 100 # 최우선 사용
template.spec = {
requirements = [
{ key = "karpenter.sh/capacity-type", values = ["spot"] },
{ key = "kubernetes.io/arch", values = ["arm64"] }, # Graviton
{ key = "karpenter.k8s.aws/instance-cpu", values = ["4"] }, # 4코어 고정
]
expireAfter = "720h" # 30일 후 노드 교체 (보안 패치)
}
disruption = {
consolidationPolicy = "WhenEmptyOrUnderutilized"
consolidateAfter = "1m" # 유휴 시 1분 후 삭제
}
limits.cpu = "20" # 00서비스 × HPA 최대 0개 = 최대 ~00코어
}
}
}
서비스 pod1개의 CPU Request가 250m 이므로, 4코어 노드에 약 15개의 pod가 고밀도로 패킹된다.
(4코어 노드 실제 할당(3.8코어) , 3.8 / 0.25 = 약 15개 pod 수용 가능)
이외 8,16코어로 하면 빈 자리가 많아져 낭비가 생긴다.
--
Spot 인스턴스가 회수 될때의 흐름은 어떻게 될까?
* 2분안에 처리 가능한 이유는 Karpenter의 노드 프로비저닝이 약 60초안에 완료되기 때문이다.
신호 수신 즉시 새 노드를 생성하므로 2분의 여유 시간 안에 pod 이동까지 완료할 수 있다.
1. AWS -> Spot 회수 2분전 인터럽트 신호 전송
2.Karpenter SQS Queue에서 신호 수신
3. 해당 노드의 pod들을 Drain(새 node로 이동 시작)
4. 새 Spot 인스턴스 프로비저닝 (~60초)
5. Pod 재스케줄링 완료
--
Spot 인스턴스여도 불안하다. pod를 최소 2개를 띄어놓을텐데, 이걸 다른 인스턴스에 놓을 수 없을까?
먼저 pod는 spot 인스턴스에 배치를 할것이다.
# base-chart/values.yaml - 모든 서비스의 기본값
nodeSelector:
nodegroup: service # ← service-spot NodePool 노드와 매칭
Karpenter NodePool의 노드는 생성 시, nodegroup: service 라벨이 붙어있고, 이 라벨로 pod와 노드가 연결된다.
또한 Pod Anti-Affinity를 통해 같은 서비스의 pod 2개가 서로 다른 노드에 분산 배치 되도록 한다.
# base-chart/templates/rollout.yaml
affinity:
podAntiAffinity:
# (권장)설정이므로, 노드가 부족한 경우 같은 노드에 배치 가능
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
topologyKey: kubernetes.io/hostname # 서로 다른 노드로 분산
이를 통해서 Spot 노드 1개가 회수되더라도 같은 서비스의 나머지 Pod는 살아있어 무중단이 유지할 수 있다.
--
비용 효과로는 대략 아래와 같다.
개발 환경 기준 서비스 노드 100% Spot 인스턴스 전환 시
- On-Demand m6g.xlarge (4코어): 약 $0.154/h
- Spot m6g.xlarge 평균: 약 $0.046/h → 약 70% 절감
'DevOps' 카테고리의 다른 글
| Kubernetes Architecture (0) | 2026.05.16 |
|---|---|
| Karpenter + Spot 조합을 알아보자 (2) (0) | 2026.05.15 |
| 노드에 파드 할당하기 (0) | 2026.05.13 |
| EKS에서 파드에 AWS 권한 부여하는 3가지 방법 (0) | 2026.05.10 |
| MSK Connect + Debezium 이용하여 CDC 파이프라인 구축 (0) | 2026.05.07 |