들어가며: 쿠버네티스 비용의 숨겨진 진실
쿠버네티스(Kubernetes)는 이제 클라우드 네이티브 인프라의 사실상 표준이 되었습니다. 하지만 채택률이 폭발적으로 증가하는 만큼, 비용 낭비 역시 심각한 수준에 이르고 있죠. 2026년 1월 발표된 대규모 연구 결과에 따르면, 600개 이상의 기업에서 운영하는 3,042개 프로덕션 클러스터를 분석한 결과, 전체 파드의 68%가 실제 사용량보다 3~8배 많은 메모리를 요청하고 있었습니다.
수치는 더욱 충격적입니다.
요청된 CPU 중 실제 사용되는 비율은 평균 13%에 불과하며, 요청된 리소스의 20~45%만이 실제 워크로드를 처리하는 데 활용됩니다. CNCF 마이크로서베이에 따르면, 과다 프로비저닝이 쿠버네티스 비용 상승의 70%를 차지합니다. 워크로드 유형별로 살펴보면, Jobs/CronJobs는 60~80%, StatefulSets는 40~60%, Deployments는 30~50%의 리소스 낭비가 발생하고 있습니다.
즉, 대부분의 기업이 쿠버네티스를 운영하면서 필요한 것보다 훨씬 많은 비용을 지불하고 있다는 뜻입니다. BCG(보스턴 컨설팅 그룹)의 분석에 따르면, 기업 클라우드 지출의 최대 30%가 과다 프로비저닝된 리소스와 유휴 서비스로 인해 낭비되고 있으며, 이는 업계 전반에 걸쳐 기본적으로 28~35% 수준의 비효율이 존재한다는 것을 의미합니다.
그렇다면 왜 이런 낭비가 발생하는 걸까요? 근본적인 원인은 쿠버네티스의 리소스 관리 모델에 있습니다. 개발자는 컨테이너에 CPU와 메모리를 "요청(request)"하고 "제한(limit)"을 설정하는데, 대부분의 경우 장애를 두려워하여 실제 필요량보다 훨씬 큰 값을 설정합니다 (사실 이건 누구나 한 번쯤 겪어봤을 겁니다). 스케줄러는 이 요청값을 기준으로 노드에 파드를 배치하므로, 결국 실제로는 사용하지 않는 리소스를 위해 비용을 지불하게 됩니다. 2025년 블랙 프라이데이/사이버 먼데이 기간 동안 23개 기업을 추적한 결과, 적절하게 라이트사이징된 메모리 설정으로도 무장애 운영이 가능했으며, 메모리 스파이크의 94%는 과다 프로비저닝보다 스케일 아웃으로 더 효과적으로 처리되었습니다.
이 글에서는 AWS EKS, Azure AKS, Google GKE 세 가지 주요 관리형 쿠버네티스 서비스에서 비용을 체계적으로 최적화하는 방법을 실전 코드와 함께 상세히 안내합니다. 리소스 라이트사이징부터 오토스케일링, 스팟 인스턴스 활용, 네임스페이스 거버넌스, 비용 모니터링 도구, 그리고 자동화 파이프라인까지 단계별로 살펴보겠습니다.
쿠버네티스 비용 구조 이해하기
비용 최적화를 시작하기 전에, 쿠버네티스 환경에서 비용이 어디서 발생하는지 정확히 파악해야 합니다. 쿠버네티스 비용은 크게 다섯 가지 영역으로 나뉩니다.
컨트롤 플레인 비용
관리형 쿠버네티스 서비스에서 컨트롤 플레인(API 서버, etcd, 스케줄러 등)은 클라우드 제공자가 관리합니다. 각 서비스별 컨트롤 플레인 비용은 다음과 같습니다.
| 항목 | AWS EKS | Azure AKS | Google GKE |
|---|---|---|---|
| 컨트롤 플레인 비용 | $0.10/시간 (~$73/월) | 무료 (Standard 티어) | $0.10/시간 (~$73/월) |
| SLA 보장 티어 | 기본 포함 | Premium: $0.10/시간 | Enterprise: 별도 요금 |
| 클러스터당 무료 티어 | 없음 | Free 티어 제공 | Autopilot 모드 별도 |
| 리전별 가격 차이 | 동일 | 동일 | 동일 |
| 엔터프라이즈 예상 총비용 (월) | ~$3,135 | ~$3,369 | ~$3,730 |
워커 노드 컴퓨트 비용
전체 쿠버네티스 비용의 60~70%를 차지하는 가장 큰 비용 항목입니다.
EC2 인스턴스(EKS), Virtual Machine(AKS), Compute Engine(GKE)의 vCPU와 메모리에 대한 비용이며, 인스턴스 유형, 크기, 과금 모델(온디맨드, 예약, 스팟)에 따라 크게 달라집니다. 예를 들어, AWS의 m5.xlarge 인스턴스(4 vCPU, 16GB 메모리)를 서울 리전에서 온디맨드로 사용하면 월 약 $140이 발생하지만, 1년 예약 인스턴스를 활용하면 약 40%, 스팟 인스턴스를 활용하면 최대 90%까지 절감할 수 있습니다. 워커 노드 비용은 비용 최적화의 가장 큰 레버이므로, 이후 섹션에서 상세히 다루겠습니다.
네트워킹 및 데이터 전송 비용
가용 영역(AZ) 간 트래픽, 리전 간 트래픽, 인터넷 아웃바운드 트래픽, 로드밸런서 비용 등이 포함됩니다. 마이크로서비스 아키텍처에서 서비스 간 통신이 많을수록 이 비용이 급격히 증가할 수 있습니다. 특히 AWS에서는 AZ 간 데이터 전송에 GB당 $0.01이 양방향으로 부과되는데, 트래픽이 많은 서비스에서는 이 비용이 월 수백 달러에 달할 수 있습니다 (처음엔 별 거 아닌 것 같지만 쌓이면 무섭습니다). topologySpreadConstraints와 토폴로지 인식 라우팅(Topology Aware Routing)을 활용하면 크로스 AZ 트래픽을 크게 줄일 수 있습니다.
스토리지(PV/PVC) 비용
PersistentVolume과 PersistentVolumeClaim으로 프로비저닝되는 블록 스토리지(EBS, Azure Disk, Persistent Disk) 비용입니다. 사용하지 않는 PV가 방치되는 경우가 많으므로 정기적인 정리가 필요합니다. StatefulSet이 삭제되어도 PVC는 자동으로 삭제되지 않기 때문에, 고아 PVC(orphaned PVC)가 누적되어 불필요한 비용을 발생시킵니다. 주기적으로 바인딩되지 않은 PVC를 확인하고 정리하는 자동화 스크립트를 운영하는 것을 권장합니다.
부가 비용
모니터링(Prometheus, Datadog), 로깅(CloudWatch, Elasticsearch), 서비스 메시(Istio, Linkerd), 보안 도구 등의 비용도 간과해서는 안 됩니다. 특히 로그 수집량에 비례하는 비용은 예상보다 클 수 있습니다. Datadog과 같은 SaaS 모니터링 도구는 호스트당 월 $15~$23을 청구하며, 노드 수가 많아지면 모니터링 비용만으로도 상당한 금액이 됩니다. 또한 Istio와 같은 서비스 메시의 사이드카 프록시(Envoy)는 파드당 약 50~100m CPU와 64~128Mi 메모리를 추가로 소비하므로, 파드 수가 많은 클러스터에서는 무시할 수 없는 오버헤드입니다.
리소스 요청과 제한: 올바르게 설정하기
쿠버네티스 비용 최적화의 핵심 중의 핵심은 리소스 요청(requests)과 제한(limits)을 올바르게 설정하는 것입니다. 이 두 가지 개념을 정확히 이해하는 것이 모든 최적화의 출발점입니다.
Requests vs Limits 이해하기
- Requests: 컨테이너가 보장받아야 하는 최소 리소스량입니다. 스케줄러는 이 값을 기준으로 파드를 노드에 배치합니다.
- Limits: 컨테이너가 사용할 수 있는 최대 리소스량입니다. 이 값을 초과하면 CPU는 스로틀링되고, 메모리는 OOMKilled가 발생합니다.
가장 흔한 문제는 개발자들이 "혹시 모르니까" 라는 이유로 실제 필요량보다 훨씬 큰 requests를 설정하는 겁니다. 예를 들어, 실제로 200m CPU와 256Mi 메모리를 사용하는 서비스에 1 CPU와 2Gi 메모리를 requests로 설정하는 경우가 흔합니다. requests가 크면 스케줄러가 실제로 필요한 것보다 많은 노드를 할당하게 되고, 이것이 바로 과다 프로비저닝의 근본 원인입니다. 하나의 노드에 4개의 파드만 배치할 수 있는 상황이 라이트사이징 후에는 12개의 파드를 수용할 수 있게 되는 거죠.
실제 사용량 분석하기
최적화의 첫 단계는 현재 실제 리소스 사용량을 파악하는 것입니다. metrics-server가 설치되어 있다면 다음 명령어들로 확인할 수 있습니다.
# 네임스페이스별 파드 리소스 사용량 확인
kubectl top pods -n production --sort-by=memory
# 노드별 리소스 사용량 확인
kubectl top nodes
# 특정 파드의 요청량 vs 실제 사용량 비교
kubectl get pods -n production -o custom-columns=\
NAME:.metadata.name,\
CPU_REQ:.spec.containers[0].resources.requests.cpu,\
CPU_LIM:.spec.containers[0].resources.limits.cpu,\
MEM_REQ:.spec.containers[0].resources.requests.memory,\
MEM_LIM:.spec.containers[0].resources.limits.memory
# 클러스터 전체의 리소스 할당 효율 확인
kubectl describe nodes | grep -A 5 "Allocated resources"
VPA(Vertical Pod Autoscaler)로 자동 추천받기
수동으로 모든 워크로드의 적정 리소스를 분석하는 것은 현실적으로 불가능합니다. VPA를 활용하면 실제 사용 패턴을 기반으로 최적의 requests/limits 값을 자동으로 추천받을 수 있습니다.
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-server-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
updatePolicy:
updateMode: "Off" # 추천만 받고 자동 적용하지 않음
resourcePolicy:
containerPolicies:
- containerName: api-server
minAllowed:
cpu: 100m
memory: 128Mi
maxAllowed:
cpu: 4
memory: 8Gi
controlledResources: ["cpu", "memory"]
updateMode: "Off"로 설정하면 VPA가 실제 리소스를 변경하지 않고 추천값만 제공합니다. 이 추천값을 확인한 후 점진적으로 적용하는 것이 안전합니다 (한 번에 다 적용했다가 문제 생기면... 말 안 해도 아시죠?). VPA의 updateMode에는 세 가지 옵션이 있는데, "Off"는 추천만 제공, "Initial"은 파드 생성 시에만 적용, "Auto"는 실행 중인 파드도 재시작하여 리소스를 조정합니다. 프로덕션 환경에서는 먼저 "Off" 모드로 추천값을 검증한 후, 안정성이 확인되면 "Auto" 모드로 전환하는 것이 권장됩니다. 단, VPA와 HPA를 동일한 CPU/메모리 메트릭으로 동시에 사용하면 충돌이 발생할 수 있으므로, HPA는 커스텀 메트릭을, VPA는 CPU/메모리를 담당하도록 분리하는 것이 좋습니다.
# VPA 추천값 확인
kubectl describe vpa api-server-vpa -n production
리소스 설정 모범 사례
- Requests: 실제 사용량의 P50(중앙값) 근처로 설정합니다.
- Limits: 실제 사용량의 P99 근처로 설정합니다.
- 메모리 limits는 requests의 1.5~2배를 넘지 않도록 합니다.
- CPU limits는 상황에 따라 설정하지 않는 전략도 고려합니다 (스로틀링 방지).
- 정기적으로(최소 월 1회) 리소스 사용 패턴을 리뷰합니다.
- 새로운 서비스 배포 시에는 반드시 부하 테스트를 수행하고, 그 결과를 바탕으로 리소스를 설정합니다.
- Java, Go, Node.js 등 런타임 특성에 따라 메모리 패턴이 다르므로, 언어별 특성을 고려합니다. 예를 들어 JVM 기반 애플리케이션은 힙 메모리와 메타스페이스를 포함한 전체 메모리 사용량을 기준으로 설정해야 합니다.
오토스케일링 전략: HPA, VPA, Karpenter
리소스를 올바르게 설정했다면, 다음 단계는 워크로드 변화에 자동으로 대응하는 오토스케일링을 구축하는 겁니다. 쿠버네티스 생태계에서 활용할 수 있는 주요 오토스케일링 도구들을 살펴보겠습니다.
HPA(Horizontal Pod Autoscaler)
HPA는 CPU, 메모리, 또는 커스텀 메트릭을 기반으로 파드 수를 자동으로 늘리거나 줄이는 가장 기본적인 오토스케일러입니다.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-server-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 3
maxReplicas: 50
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 65
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 75
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Percent
value: 50
periodSeconds: 60
behavior 섹션을 활용하면 스케일링 속도를 세밀하게 제어할 수 있습니다. 스케일 다운 시에는 안정화 윈도우를 길게 설정하여 플래핑(flapping)을 방지하는 것이 중요합니다. 위 예제에서는 스케일 다운 시 300초(5분)의 안정화 윈도우와 60초당 최대 10%의 감소율을 설정하여, 일시적인 트래픽 감소에 과잉 반응하지 않도록 했습니다. 반면 스케일 업은 30초의 짧은 안정화 윈도우와 60초당 50%까지의 증가율을 허용하여 트래픽 급증에 빠르게 대응합니다. HPA의 타겟 사용률은 일반적으로 65~75%로 설정하는 것이 비용 효율과 성능 사이의 균형점입니다.
HPA를 효과적으로 활용하려면 커스텀 메트릭을 함께 사용하는 것이 좋습니다.
CPU 사용률만으로는 실제 서비스 부하를 정확히 반영하지 못하는 경우가 많기 때문입니다. 예를 들어, HTTP 요청 수, 큐 깊이, 응답 시간 등의 애플리케이션 수준 메트릭을 Prometheus Adapter를 통해 HPA에 연동하면 더 정확한 스케일링이 가능합니다.
Karpenter: 차세대 노드 오토스케일링
Karpenter는 기존 Cluster Autoscaler를 대체하는 AWS 기반의 오픈소스 노드 프로비저너로, 최근 쿠버네티스 비용 최적화 분야에서 가장 주목받는 도구입니다.
| 비교 항목 | Cluster Autoscaler | Karpenter |
|---|---|---|
| 노드 프로비저닝 시간 | 수 분 | 45~60초 |
| 인스턴스 유형 선택 | 노드 그룹 기반 (수동) | 지능형 자동 선택 |
| 빈 패킹(Bin-packing) | 제한적 | 고급 최적화 |
| 스팟 인스턴스 관리 | 별도 설정 필요 | 네이티브 지원 |
| Consolidation | 수동 | 자동 통합 |
| 클라우드 지원 | 멀티 클라우드 | AWS 중심 (Azure 베타) |
2026년 1월, Salesforce는 1,000개 이상의 EKS 클러스터를 Cluster Autoscaler에서 Karpenter로 마이그레이션했습니다. 2025년 중반 저위험 환경에서 시작하여 테스트와 검증을 거친 후 프로덕션에 적용한 이 대규모 전환 결과, FY2026에 약 5%의 비용 절감을 달성했으며, FY2027에는 빈 패킹과 스팟 인스턴스 최적화가 안정화되면서 추가로 5~10%의 절감이 예상됩니다. 무엇보다 운영 오버헤드가 약 80% 감소하여, 수동 노드 그룹 관리가 자동화된 선언형 노드 풀 설정으로 대체되었습니다. Salesforce 엔지니어들은 사내 Karpenter 전환 도구를 개발하여 노드 로테이션, AMI 검증, 그레이스풀 파드 퇴거를 자동화했으며, 잘못 구성된 PDB(Pod Disruption Budget), 쿠버네티스 레이블 길이 제약, 단일 레플리카 애플리케이션에서의 빈 패킹 충돌 등의 과제를 해결했습니다.
또한 실시간 데이터 플랫폼 기업 Tinybird는 Karpenter와 스팟 인스턴스를 조합하여 AWS 비용을 20% 절감했으며, CI/CD 워크로드에서는 최대 90%까지 비용을 줄였습니다. 일부 기업에서는 Karpenter 도입 후 컴퓨트 비용을 70%까지 절감한 사례도 보고되고 있습니다.
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: default
spec:
template:
metadata:
labels:
environment: production
spec:
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand", "spot"]
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"]
- key: karpenter.k8s.aws/instance-generation
operator: Gt
values: ["5"]
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
limits:
cpu: "1000"
memory: 2000Gi
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 30s
---
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: default
spec:
amiSelectorTerms:
- alias: al2023@latest
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: my-cluster
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: my-cluster
role: KarpenterNodeRole-my-cluster
위 설정에서 주요 포인트를 살펴보겠습니다. consolidationPolicy: WhenEmptyOrUnderutilized는 Karpenter가 활용도가 낮은 노드를 자동으로 통합하여 비용을 절감하도록 합니다. capacity-type에 ["on-demand", "spot"]을 지정하면 Karpenter가 가격과 가용성을 고려하여 최적의 인스턴스 유형을 자동 선택합니다. instance-category에서 c(컴퓨트 최적화), m(범용), r(메모리 최적화) 패밀리를 허용하고, instance-generation을 5세대 이후로 제한하여 최신 세대의 인스턴스만 사용하도록 했습니다 (최신 세대가 가성비가 좋습니다). 최신 세대 인스턴스는 같은 가격에 더 높은 성능을 제공하므로, 세대 제한은 비용 최적화에 효과적입니다. consolidateAfter: 30s는 노드가 비어 있거나 저활용 상태가 30초 이상 지속되면 즉시 통합을 시도하도록 설정합니다.
KEDA: 이벤트 기반 오토스케일링
KEDA(Kubernetes Event-Driven Autoscaling)는 CNCF 졸업(Graduated) 프로젝트로, 큐 길이, 스트림 랙, HTTP 요청 수 등 외부 이벤트 소스를 기반으로 파드를 스케일링합니다. 특히 0까지 스케일 다운이 가능하여, 유휴 시간에 리소스를 완전히 해제할 수 있습니다.
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: order-processor
namespace: production
spec:
scaleTargetRef:
name: order-processor
pollingInterval: 15
cooldownPeriod: 120
minReplicaCount: 0 # 이벤트 없을 때 0으로 축소
maxReplicaCount: 100
triggers:
- type: aws-sqs-queue
metadata:
queueURL: https://sqs.ap-northeast-2.amazonaws.com/123456789/orders
queueLength: "5"
awsRegion: ap-northeast-2
authenticationRef:
name: aws-credentials
KEDA는 65개 이상의 스케일러를 지원하며, RabbitMQ, Kafka, AWS SQS, Azure Service Bus, Prometheus 메트릭 등 다양한 이벤트 소스와 연동할 수 있습니다. 2023년 8월 CNCF 졸업 프로젝트로 인정받은 KEDA는 FedEx, KPMG, Microsoft, Alibaba Cloud, Cisco, Red Hat, Reddit 등 대형 기업에서 프로덕션에 활용되고 있습니다. 배치 처리나 이벤트 처리 워크로드에서 특히 큰 비용 절감 효과를 기대할 수 있으며, 야간이나 주말에 이벤트가 없을 때 파드를 0으로 축소함으로써 해당 시간대의 컴퓨트 비용을 완전히 제거할 수 있습니다 (실제로 필자가 운영하던 배치 처리 워크로드에서는 이것만으로도 월 30% 이상 비용을 절감했습니다). 현재 HTTP 기반 오토스케일링 기능도 실험 단계로 개발 중이어서, 메시지 큐 이외의 워크로드에서도 활용 범위가 넓어지고 있습니다.
스팟 인스턴스와 프리엠프터블 VM 활용
스팟 인스턴스(또는 프리엠프터블 VM)는 클라우드 제공자의 유휴 컴퓨팅 용량을 대폭 할인된 가격으로 사용하는 방식입니다. 클라우드 제공자가 해당 용량을 다시 필요로 하면 2분(AWS) 또는 30초(GCP) 전에 중단 알림을 보내고 인스턴스를 회수합니다. 이러한 중단 리스크가 있지만, 적절히 활용하면 컴퓨트 비용을 극적으로 줄일 수 있습니다. 실제로 많은 기업이 프로덕션 워크로드의 50~70%를 스팟 인스턴스에서 안정적으로 운영하고 있습니다.
- AWS Spot Instances: 온디맨드 대비 최대 90% 할인
- Azure Spot VMs: 온디맨드 대비 최대 90% 할인
- GCP Spot VMs: 온디맨드 대비 최대 91% 할인
스팟에 적합한 워크로드
스팟 인스턴스는 언제든 중단(eviction)될 수 있으므로, 다음 조건을 만족하는 워크로드에 적합합니다.
- 무상태(Stateless) 서비스: 로컬 상태에 의존하지 않는 웹 서버, API 서버
- 내결함성(Fault-tolerant) 워크로드: 여러 레플리카로 실행되어 일부 파드 중단에 대응 가능한 서비스
- 배치(Batch) 처리: 재시도가 가능한 데이터 처리, ETL, ML 학습 작업
- CI/CD 파이프라인: 빌드, 테스트 워크로드
- 개발/스테이징 환경: 프로덕션이 아닌 환경 전체
스팟 친화적 디플로이먼트 구성
노드 어피니티(Node Affinity)와 톨러레이션(Tolerations)을 활용하여 스팟 노드에서 실행될 워크로드를 관리합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: worker-processor
namespace: production
spec:
replicas: 10
selector:
matchLabels:
app: worker-processor
template:
metadata:
labels:
app: worker-processor
spec:
terminationGracePeriodSeconds: 120
tolerations:
- key: "karpenter.sh/capacity-type"
operator: "Equal"
value: "spot"
effect: "NoSchedule"
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: karpenter.sh/capacity-type
operator: In
values:
- spot
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: worker-processor
containers:
- name: worker
image: myapp/worker:latest
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "2"
memory: 1Gi
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- "echo 'Graceful shutdown initiated' && sleep 90"
핵심 설정 포인트는 다음과 같습니다.
- topologySpreadConstraints: 파드를 여러 가용 영역에 분산하여 단일 AZ의 스팟 중단에 대한 영향을 최소화합니다.
- terminationGracePeriodSeconds: 충분한 종료 유예 시간을 설정하여 진행 중인 작업이 완료될 수 있도록 합니다.
- preStop hook: 스팟 중단 신호를 받으면 현재 요청을 처리 완료하고 안전하게 종료할 수 있도록 합니다.
- preferredDuringSchedulingIgnoredDuringExecution: 스팟 노드를 선호하되, 사용 불가 시 온디맨드 노드에도 배치될 수 있도록 합니다.
네임스페이스 기반 비용 할당과 거버넌스
여러 팀이 하나의 클러스터를 공유하는 멀티테넌트 환경에서는 누가 얼마나 사용하는지 추적하고, 과도한 사용을 방지하는 거버넌스 체계가 필수적입니다.
비용 할당 없이 공유 클러스터를 운영하면 "공유지의 비극(Tragedy of the Commons)" 현상이 발생합니다. 어떤 팀도 비용에 대한 책임감이 없으므로 모든 팀이 리소스를 과도하게 사용하게 되고, 결과적으로 전체 클러스터 비용이 통제 불능 상태가 됩니다 (이건 꽤 심각한 문제입니다).
ResourceQuota로 네임스페이스 제한하기
ResourceQuota는 네임스페이스 수준에서 전체 리소스 사용량의 상한을 설정합니다.
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-backend-quota
namespace: team-backend
spec:
hard:
requests.cpu: "40"
requests.memory: 80Gi
limits.cpu: "80"
limits.memory: 160Gi
pods: "100"
persistentvolumeclaims: "20"
services.loadbalancers: "5"
LimitRange로 기본값 설정하기
LimitRange는 개별 컨테이너에 리소스 요청/제한이 지정되지 않았을 때 기본값을 자동으로 적용합니다. 이를 통해 리소스 설정 없이 배포되는 파드가 클러스터 리소스를 무제한 소비하는 것을 방지합니다.
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: team-backend
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 100m
memory: 128Mi
max:
cpu: "4"
memory: 8Gi
min:
cpu: 50m
memory: 64Mi
- type: PersistentVolumeClaim
max:
storage: 50Gi
min:
storage: 1Gi
차지백 vs 쇼백 모델
비용 할당 방식은 조직 문화와 규모에 따라 선택합니다.
- 쇼백(Showback): 각 팀의 리소스 사용량과 비용을 보여주기만 합니다. 비용 인식을 높이는 첫 단계로 적합하며, 도입 저항이 낮습니다.
- 차지백(Chargeback): 각 팀의 예산에서 실제 사용한 만큼을 청구합니다. 비용 절감 동기가 강력하지만, 정확한 비용 산정 체계가 선행되어야 합니다.
레이블 전략
효과적인 비용 추적을 위해 일관된 레이블링 표준을 수립하는 것이 중요합니다.
# 권장 레이블 표준
metadata:
labels:
# 비용 할당 필수 레이블
cost-center: "engineering-platform"
team: "backend"
project: "payment-service"
environment: "production"
# 쿠버네티스 권장 레이블
app.kubernetes.io/name: "payment-api"
app.kubernetes.io/component: "server"
app.kubernetes.io/part-of: "payment-system"
이 레이블들은 비용 모니터링 도구에서 팀별, 프로젝트별, 환경별로 비용을 분류하는 데 활용됩니다. 모든 워크로드에 빠짐없이 적용하려면 OPA/Gatekeeper 또는 Kyverno 같은 정책 엔진을 활용하여 레이블 미설정 시 배포를 차단하는 것이 효과적입니다.
비용 모니터링 도구 비교: OpenCost, Kubecost, 클라우드 네이티브
비용 최적화는 측정에서 시작됩니다. 현재 얼마나 쓰고 있는지 정확히 모르면 절감도 불가능합니다. 주요 쿠버네티스 비용 모니터링 도구들을 비교해보겠습니다.
OpenCost
OpenCost는 CNCF 산하 오픈소스 프로젝트로, 쿠버네티스 워크로드의 실시간 비용 모니터링을 제공합니다. 2025년에만 11개 릴리스를 발표하며 활발하게 발전하고 있으며, 특히 주목할 만한 기능은 MCP(Model Context Protocol) 서버입니다. v1.118부터 기본 내장된 이 기능을 통해 AI 에이전트가 자연어로 비용 데이터를 쿼리할 수 있습니다 (꽤 혁신적이죠?). 2026년 로드맵에는 AI/ML 워크로드 비용 추적, 비용 데이터 보안 강화, KubeModel 프레임워크 개선이 포함되어 있습니다.
Kubecost
Kubecost는 OpenCost를 기반으로 구축된 엔터프라이즈 솔루션으로, 고급 대시보드, 알림, 절감 추천, 멀티 클러스터 지원 등 부가 기능을 제공합니다.
무료 티어에서는 단일 클러스터 모니터링과 15일간의 메트릭 보존을 제공하며, 유료 티어에서는 무제한 클러스터 지원, 통합 비용 뷰, Savings Insights(절감 인사이트), SSO 통합 등의 엔터프라이즈 기능을 사용할 수 있습니다. 쿠버네티스 비용 최적화를 처음 시작하는 조직이라면 OpenCost로 시작하고, 멀티 클러스터 환경이나 고급 분석이 필요해지면 Kubecost로 확장하는 전략이 합리적입니다.
클라우드 네이티브 도구
각 클라우드 제공자도 자체 비용 관리 도구를 제공합니다. AWS는 Cost Explorer와 Split Cost Allocation Data를 통해 EKS 파드 수준 비용 분배를 지원하며, Azure는 Cost Management에서 AKS 클러스터 비용을 네임스페이스 수준까지 분석할 수 있습니다. GCP는 Cloud Billing과 Cost Management를 통해 GKE 워크로드 비용을 추적합니다. 클라우드 네이티브 도구의 가장 큰 장점은 별도 설치가 필요 없다는 점이지만, 데이터 반영에 수 시간의 지연이 있고 파드 수준의 세밀한 분석에는 한계가 있습니다.
| 기능 | OpenCost | Kubecost | 클라우드 네이티브 |
|---|---|---|---|
| 비용 | 무료 (오픈소스) | 무료 티어 + 유료 | 기본 무료 |
| 실시간 모니터링 | 지원 | 지원 | 지연 있음 (수 시간) |
| 파드 수준 비용 할당 | 지원 | 지원 | 제한적 |
| 멀티 클러스터 | 제한적 | 지원 (유료) | 클라우드별 |
| 절감 추천 | 기본 | 고급 | 제한적 |
| AI 기반 쿼리 (MCP) | 지원 (v1.118+) | 미지원 | 미지원 |
| CNCF 지원 | 공식 프로젝트 | 기반 활용 | 해당 없음 |
| 설치 복잡도 | 낮음 | 낮음 | 별도 설정 불요 |
OpenCost Helm 설치
OpenCost는 Helm으로 간단하게 설치할 수 있습니다.
# Prometheus가 이미 설치되어 있다고 가정
helm repo add opencost https://opencost.github.io/opencost-helm-chart
helm repo update
helm install opencost opencost/opencost \
--namespace opencost \
--create-namespace \
--set opencost.prometheus.internal.serviceName=prometheus-server \
--set opencost.prometheus.internal.namespaceName=monitoring \
--set opencost.ui.enabled=true \
--set opencost.exporter.defaultClusterId=my-production-cluster
# 설치 확인
kubectl get pods -n opencost
# UI 포트포워딩
kubectl port-forward -n opencost svc/opencost 9090:9090
실전 사례: 월 500만원 절감 시나리오
자, 이론적인 내용은 충분히 다뤘으니 이제 실제 시나리오에 적용해보겠습니다. 한국 중규모 SaaS 기업 A사의 EKS 클러스터 최적화 과정을 단계별로 살펴봅니다. 이 시나리오는 앞서 소개한 전략들을 종합적으로 적용한 사례로, 12주에 걸쳐 진행되었습니다.
최적화 전 상태
- 50개 노드 (m5.xlarge, 모두 온디맨드)
- 평균 CPU 활용률: 30%
- 평균 메모리 활용률: 35%
- 대부분의 파드가 requests를 실사용량의 3~5배로 설정
- 오토스케일링 미적용
- 월 비용: 약 1,200만원
최적화 단계
- 1단계 - 가시성 확보: OpenCost를 설치하고, 모든 워크로드에 비용 할당 레이블을 적용했습니다.
- 2단계 - 리소스 라이트사이징: VPA 추천을 기반으로 모든 Deployment의 requests를 실사용량에 맞게 조정했습니다. 평균적으로 requests를 60% 줄였습니다.
- 3단계 - Karpenter 도입: Cluster Autoscaler를 Karpenter로 교체하고, 지능형 빈 패킹과 자동 통합을 활성화했습니다.
- 4단계 - 스팟 인스턴스 적용: 무상태 워크로드(전체의 60%)를 스팟 인스턴스로 전환했습니다.
- 5단계 - 네임스페이스 쿼터 적용: 각 팀에 ResourceQuota를 설정하고 월간 비용 리포트를 공유했습니다.
- 6단계 - 모니터링 및 알림: Prometheus + Grafana 기반 비용 대시보드를 구축하고, 과다 프로비저닝 및 유휴 리소스 알림을 설정했습니다.
최적화 전후 비교
| 항목 | 최적화 전 | 최적화 후 | 변화 |
|---|---|---|---|
| 노드 수 | 50개 | 35개 | -30% |
| 평균 CPU 활용률 | 30% | 65% | +117% |
| 평균 메모리 활용률 | 35% | 60% | +71% |
| 온디맨드 비율 | 100% | 40% | -60%p |
| 스팟 비율 | 0% | 60% | +60%p |
| 월 비용 | ~1,200만원 | ~700만원 | -42% |
| 월 절감액 | - | - | ~500만원 |
절감액 산출 근거를 자세히 살펴보겠습니다.
- 노드 수 감소 효과: 50 → 35노드로 30% 절감 = 약 360만원 절감
- 스팟 전환 효과: 35노드 중 21노드를 스팟(평균 65% 할인)으로 전환 = 약 215만원 추가 절감
- OpenCost 비용: 무료 (오픈소스)
- Karpenter 비용: 무료 (오픈소스)
- 순 절감액: 약 500만원/월 (연 약 6,000만원)
주목할 점은 이 절감의 대부분이 오픈소스 도구만으로 달성되었다는 겁니다.
OpenCost(모니터링), Karpenter(노드 오토스케일링), VPA(리소스 추천)는 모두 무료이며, 추가 라이선스 비용 없이 활용할 수 있습니다. 또한 리소스 활용률이 30%에서 65%로 개선되면서, 동일한 인프라에서 향후 서비스 확장을 수용할 수 있는 여유도 확보되었습니다. 비용 최적화가 단순한 비용 절감이 아니라 인프라 효율성 전반의 개선으로 이어진 좋은 사례입니다.
자동화된 비용 최적화 파이프라인 구축
지속 가능한 비용 최적화를 위해서는 일회성 작업이 아닌 자동화된 파이프라인을 구축해야 합니다. 수동 최적화는 담당자가 바뀌거나 시간이 지나면 원래 상태로 되돌아가는 경향이 있습니다 (이른바 "설정 드리프트(configuration drift)"라고 하죠). 이를 방지하기 위해 리소스 정책을 코드로 관리하고, 비용 이상 상황을 자동으로 감지하며, 최적화 조치를 파이프라인에 통합해야 합니다.
GitOps 기반 리소스 최적화
GitOps 워크플로에 리소스 검증을 통합하면, 과다 프로비저닝이 프로덕션에 배포되기 전에 차단할 수 있습니다. ArgoCD나 Flux 같은 GitOps 도구를 사용하고 있다면, Git 저장소에 커밋되는 쿠버네티스 매니페스트의 리소스 설정을 PR 단계에서 자동으로 검증할 수 있습니다. CI/CD 파이프라인에 다음과 같은 리소스 검증 단계를 추가합니다.
# .github/workflows/resource-check.yaml
name: Resource Request Validation
on:
pull_request:
paths:
- 'k8s/**'
- 'helm/**'
jobs:
validate-resources:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check resource requests
run: |
# 리소스 요청이 너무 높은 컨테이너 검출
for file in $(find k8s/ helm/ -name "*.yaml" -o -name "*.yml"); do
# CPU 요청이 4 코어 이상인 경우 경고
if grep -q "cpu:.*[4-9][0-9]*$\|cpu:.*[0-9]*[0-9]$" "$file" 2>/dev/null; then
echo "WARNING: High CPU request found in $file"
fi
# 메모리 요청이 8Gi 이상인 경우 경고
if grep -q "memory:.*[8-9][0-9]*Gi\|memory:.*[0-9][0-9]Gi" "$file" 2>/dev/null; then
echo "WARNING: High memory request found in $file"
fi
done
- name: Validate with Datree/KubeLinter
run: |
kube-linter lint k8s/ --config .kube-linter.yaml
Prometheus + Grafana 비용 대시보드
비용 가시성을 위한 Grafana 대시보드에 다음과 같은 PromQL 쿼리를 활용합니다. 이 쿼리들을 Grafana 대시보드에 패널로 구성하면, 실시간으로 네임스페이스별 리소스 효율, 메모리 낭비량, 노드 활용도, 유휴 파드 등을 한눈에 파악할 수 있습니다.
# 네임스페이스별 CPU 요청 대비 실제 사용 비율
sum(rate(container_cpu_usage_seconds_total{namespace!="kube-system"}[5m])) by (namespace)
/
sum(kube_pod_container_resource_requests{resource="cpu", namespace!="kube-system"}) by (namespace)
* 100
# 네임스페이스별 메모리 낭비량 (GB)
(
sum(kube_pod_container_resource_requests{resource="memory", namespace!="kube-system"}) by (namespace)
-
sum(container_memory_working_set_bytes{namespace!="kube-system"}) by (namespace)
) / 1024 / 1024 / 1024
# 노드별 활용 효율 (CPU)
(1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (node)) * 100
# 유휴 파드 감지 (5분간 CPU 사용량 0에 가까운 파드)
sum(rate(container_cpu_usage_seconds_total[5m])) by (pod, namespace)
< 0.001
and on(pod, namespace)
kube_pod_status_phase{phase="Running"} == 1
비용 이상 감지 알림
비용이 급격히 증가하거나 리소스가 장시간 유휴 상태로 방치되는 상황을 자동으로 감지하여 알림을 보내는 PrometheusRule을 설정합니다. 이 알림들은 Slack, PagerDuty, 이메일 등으로 전달되도록 Alertmanager와 연동합니다.
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: cost-anomaly-alerts
namespace: monitoring
spec:
groups:
- name: cost-optimization
interval: 5m
rules:
# CPU 과다 프로비저닝 경고 (사용률 20% 미만)
- alert: CPUOverProvisionedNamespace
expr: |
(
sum(rate(container_cpu_usage_seconds_total{namespace!="kube-system"}[1h])) by (namespace)
/
sum(kube_pod_container_resource_requests{resource="cpu", namespace!="kube-system"}) by (namespace)
) < 0.2
for: 24h
labels:
severity: warning
category: cost-optimization
annotations:
summary: "{{ $labels.namespace }} 네임스페이스의 CPU 사용률이 20% 미만입니다"
description: "24시간 동안 CPU 요청 대비 사용률이 {{ $value | humanizePercentage }}입니다. 리소스 라이트사이징을 검토하세요."
# 메모리 과다 프로비저닝 경고
- alert: MemoryOverProvisionedNamespace
expr: |
(
sum(container_memory_working_set_bytes{namespace!="kube-system"}) by (namespace)
/
sum(kube_pod_container_resource_requests{resource="memory", namespace!="kube-system"}) by (namespace)
) < 0.3
for: 24h
labels:
severity: warning
category: cost-optimization
annotations:
summary: "{{ $labels.namespace }} 네임스페이스의 메모리 사용률이 30% 미만입니다"
description: "24시간 동안 메모리 요청 대비 사용률이 {{ $value | humanizePercentage }}입니다."
# 노드 저활용 경고
- alert: NodeUnderutilized
expr: |
(1 - avg(rate(node_cpu_seconds_total{mode="idle"}[1h])) by (node)) < 0.2
and
(sum(kube_pod_info) by (node)) > 0
for: 6h
labels:
severity: info
category: cost-optimization
annotations:
summary: "노드 {{ $labels.node }}의 CPU 활용률이 6시간 이상 20% 미만입니다"
description: "Karpenter 통합 또는 노드 축소를 검토하세요."
마무리: 쿠버네티스 FinOps 로드맵
쿠버네티스 비용 최적화는 한 번에 완성되는 프로젝트가 아니라 지속적으로 개선하는 프로세스입니다. FinOps(Financial Operations)는 엔지니어링, 재무, 비즈니스 팀이 협력하여 클라우드 비용을 최적화하는 문화적 실천입니다. 다음 4단계 로드맵을 참고하여 체계적으로 접근하시기 바랍니다. 각 단계를 건너뛰지 않고 순서대로 진행하는 것이 중요합니다 (가시성 없이 최적화를 시도하면 효과를 측정할 수 없고, 자동화 없이 최적화를 유지하려면 지속적인 수동 노력이 필요하기 때문입니다).
Phase 1: 가시성 확보 (1~2주차)
- OpenCost 또는 Kubecost를 설치하여 현재 비용 구조를 파악합니다.
- 모든 워크로드에 팀, 프로젝트, 환경 레이블을 부여합니다.
- 네임스페이스별 비용 리포트를 생성하고 팀에 공유합니다.
- 현재 리소스 사용률 베이스라인을 측정합니다.
Phase 2: 최적화 실행 (3~6주차)
- VPA 추천을 기반으로 리소스 requests/limits를 라이트사이징합니다.
- HPA를 주요 워크로드에 적용하여 수요 기반 스케일링을 구현합니다.
- Karpenter를 도입하여 노드 수준 오토스케일링을 최적화합니다.
- 적합한 워크로드부터 스팟 인스턴스를 적용합니다 (개발/스테이징 우선, 이후 프로덕션).
Phase 3: 자동화 구축 (7~12주차)
- CI/CD 파이프라인에 리소스 검증 단계를 통합합니다.
- 비용 이상 감지 알림을 설정합니다.
- OPA/Gatekeeper 또는 Kyverno를 통해 리소스 정책을 강제합니다.
- 정기적인 비용 리뷰 프로세스를 수립합니다.
Phase 4: 거버넌스 체계화 (지속적)
- 쇼백에서 시작하여 점진적으로 차지백 모델을 도입합니다.
- 팀별 비용 예산을 설정하고 추적합니다.
- 분기별 비용 최적화 리뷰를 시행합니다.
- 새로운 도구와 기법을 지속적으로 평가하고 도입합니다.
- 비용 최적화 문화를 조직 전체에 확산합니다.
핵심 요약
쿠버네티스 비용 최적화의 핵심을 정리하면 다음과 같습니다.
- 측정이 우선입니다. OpenCost 같은 도구로 현재 상태를 정확히 파악하지 못하면 개선도 불가능합니다.
- 리소스 라이트사이징이 가장 효과적입니다. 전체 비용 낭비의 70%가 과다 프로비저닝에서 발생합니다. VPA 추천을 적극 활용하세요.
- Karpenter는 게임 체인저입니다. 지능형 빈 패킹, 자동 통합, 네이티브 스팟 지원으로 노드 수준 최적화를 자동화합니다.
- 스팟 인스턴스를 두려워하지 마세요. 적절한 설계(멀티 AZ 분산, 그레이스풀 셧다운)를 적용하면 프로덕션에서도 안전하게 활용할 수 있습니다.
- 거버넌스가 지속 가능성을 보장합니다. 네임스페이스 쿼터, 정책 엔진, 비용 알림을 통해 최적화된 상태를 유지하세요.
- 자동화가 핵심입니다. 수동 최적화는 시간이 지나면 다시 원래 상태로 돌아갑니다. CI/CD 통합, 알림, 정책 자동화를 반드시 구축하세요.
쿠버네티스 비용 최적화는 단순히 비용을 줄이는 것이 아니라, 같은 비용으로 더 많은 가치를 만들어내는 것입니다.
올바르게 수행된 비용 최적화는 비용을 줄이면서도 시스템의 안정성과 성능을 오히려 향상시킵니다. 과다 프로비저닝을 줄이면 리소스 할당의 정확도가 높아지고, 오토스케일링을 도입하면 트래픽 변화에 더 빠르게 대응할 수 있으며, 모니터링을 강화하면 문제를 더 일찍 발견할 수 있기 때문입니다.
이 가이드에서 소개한 전략들을 단계적으로 적용하여, 여러분의 클러스터를 효율적이고 비용 효과적인 인프라로 전환하시기 바랍니다. 68%의 파드가 3~8배의 메모리를 낭비하고 있는 현실에서, 최적화를 시작하지 않을 이유가 없습니다. 가장 중요한 것은 오늘 당장 첫 단계를 시작하는 겁니다. OpenCost를 설치하고, kubectl top 명령어로 현재 리소스 사용률을 확인하는 것부터 시작하세요. 작은 한 걸음이 연간 수천만원의 절감으로 이어질 수 있습니다.