- Published on
Kubernetes 오토스케일링 완벽 가이드: HPA·VPA·KEDA 기반 프로덕션 워크로드 자동 확장 전략
- Authors
- Name
- 들어가며
- HPA v2 심층 분석
- VPA 아키텍처와 운영 전략
- KEDA 이벤트 기반 스케일링
- HPA vs VPA vs KEDA 비교 분석
- 복합 스케일링 패턴
- 운영 시 주의사항
- 장애 사례와 복구 절차
- 프로덕션 체크리스트
- 참고자료
- 마치며

들어가며
프로덕션 Kubernetes 클러스터에서 오토스케일링은 선택이 아니라 필수이다. 트래픽 급증 시 Pod가 부족하면 서비스 장애로 이어지고, 과잉 프로비저닝은 매월 수백만 원의 불필요한 클라우드 비용을 발생시킨다. Kubernetes 생태계는 이러한 문제를 해결하기 위해 세 가지 핵심 오토스케일러를 제공한다.
- HPA (Horizontal Pod Autoscaler): CPU, 메모리, 커스텀 메트릭을 기반으로 Pod 레플리카 수를 수평 확장
- VPA (Vertical Pod Autoscaler): 과거 사용량 분석을 토대로 Pod의 리소스 요청/제한값을 자동 조정
- KEDA (Kubernetes Event-Driven Autoscaler): 메시지 큐, HTTP 요청 수, cron 스케줄 등 외부 이벤트 소스 기반으로 워크로드를 0에서 N까지 스케일링
이 글에서는 각 오토스케일러의 아키텍처와 프로덕션 환경에서의 심층 구성 전략을 다루고, 실제 장애 사례와 복구 절차, 그리고 프로덕션 배포 전 반드시 확인해야 할 체크리스트까지 종합적으로 정리한다.
HPA v2 심층 분석
아키텍처와 메트릭 수집 흐름
HPA v2(autoscaling/v2)는 Kubernetes의 기본 수평 확장 메커니즘이다. HPA 컨트롤러는 기본적으로 15초 주기(--horizontal-pod-autoscaler-sync-period)로 메트릭을 수집하고, 목표 사용률과 현재 사용률의 비율을 계산하여 레플리카 수를 결정한다.
메트릭 수집 흐름은 다음과 같다.
- Metrics Server: kubelet의 cAdvisor에서 CPU/메모리 메트릭을 수집하여
metrics.k8s.ioAPI로 노출 - Custom Metrics Adapter: Prometheus, Datadog 등에서 커스텀 메트릭을
custom.metrics.k8s.ioAPI로 노출 - External Metrics Adapter: 클러스터 외부 시스템의 메트릭을
external.metrics.k8s.ioAPI로 노출
스케일링 알고리즘
HPA의 핵심 공식은 다음과 같다.
desiredReplicas = ceil(currentReplicas * (currentMetricValue / desiredMetricValue))
예를 들어, 현재 3개 Pod가 CPU 80%를 사용 중이고 목표가 50%라면 ceil(3 * (80/50)) = ceil(4.8) = 5개로 확장된다. 복수 메트릭이 지정된 경우 HPA는 각 메트릭에 대해 독립적으로 계산한 뒤 가장 큰 값을 채택한다.
프로덕션 HPA v2 매니페스트
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: 60
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 70
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: '1000'
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 50
periodSeconds: 60
- type: Pods
value: 5
periodSeconds: 60
selectPolicy: Max
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 120
selectPolicy: Min
핵심 포인트는 behavior 필드이다.
- scaleUp: 안정화 창을 60초로 설정하고, 1분 동안 최대 50% 또는 5개 Pod 중 큰 값만큼 확장한다.
- scaleDown: 안정화 창을 300초(5분)로 설정하고, 2분 동안 최대 10%씩만 축소한다. 이렇게 보수적으로 축소해야 트래픽이 다시 증가할 때 대응할 수 있다.
Metrics Server 설치 확인
# Metrics Server 동작 확인
kubectl top nodes
kubectl top pods -n production
# HPA 상태 확인
kubectl get hpa api-server-hpa -n production -o yaml
# HPA 이벤트 확인
kubectl describe hpa api-server-hpa -n production | grep -A 20 "Events"
kubectl top nodes가 실패하면 Metrics Server가 설치되지 않았거나 정상 동작하지 않는 것이다. 이 경우 HPA는 메트릭을 수집하지 못해 스케일링이 전혀 작동하지 않는다.
VPA 아키텍처와 운영 전략
VPA 구성 요소
VPA는 세 가지 주요 컴포넌트로 구성된다.
- Recommender: 과거 리소스 사용량과 OOM 이벤트를 분석하여 최적의 CPU/메모리 요청값을 계산한다. 히스토그램 기반 알고리즘으로 P95 사용량을 기준으로 추천값을 도출한다.
- Updater: 현재 Pod의 리소스 설정이 추천값과 크게 벗어나면 Pod를 퇴거(eviction)시켜 새로운 추천값이 적용되도록 한다.
- Admission Controller: 새로 생성되거나 재시작된 Pod에 추천 리소스 값을 자동으로 적용하는 웹훅이다.
운영 모드
VPA는 네 가지 updateMode를 지원한다.
- Off: 추천만 제공하고 실제 변경은 하지 않는다. 프로덕션 도입 초기에 권장한다.
- Initial: Pod 생성 시에만 추천값을 적용한다. 이미 실행 중인 Pod는 변경하지 않는다.
- Recreate: 추천값과 차이가 크면 Pod를 재생성한다. PodDisruptionBudget과 함께 사용해야 한다.
- Auto: Kubernetes 1.27+에서 In-Place Resource Resize를 지원하는 경우 Pod 재시작 없이 리소스를 조정한다.
프로덕션 VPA 매니페스트
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
controlledValues: RequestsAndLimits
프로덕션 환경에서는 반드시 Off 모드로 시작하여 최소 1-2주간 추천값의 안정성을 관찰한 뒤에 Auto나 Recreate 모드로 전환해야 한다. minAllowed와 maxAllowed를 반드시 설정하여 비정상적인 추천값이 적용되는 것을 방지한다.
VPA 추천값 확인
# VPA 추천값 확인
kubectl describe vpa api-server-vpa -n production
# 추천값과 현재 리소스 요청값 비교
kubectl get vpa api-server-vpa -n production -o jsonpath='{.status.recommendation.containerRecommendations[0]}'
KEDA 이벤트 기반 스케일링
KEDA 아키텍처
KEDA는 Kubernetes의 HPA를 확장하여 외부 이벤트 소스 기반으로 스케일링을 가능하게 하는 CNCF Graduated 프로젝트이다. KEDA의 핵심 컴포넌트는 다음과 같다.
- KEDA Operator: ScaledObject/ScaledJob CRD를 감시하고, HPA를 자동 생성/관리한다. 이벤트가 없을 때 Pod를 0으로 축소하고, 이벤트 발생 시 1로 활성화한 뒤 HPA에 제어를 넘긴다.
- Metrics Server (KEDA): 외부 이벤트 소스의 메트릭을 Kubernetes External Metrics API로 노출한다.
- Scalers: 65개 이상의 이벤트 소스(Kafka, RabbitMQ, AWS SQS, Prometheus, PostgreSQL, Cron 등)에 연결하는 어댑터이다.
스케일링 흐름
KEDA의 스케일링은 두 단계로 작동한다.
- Activation 단계: KEDA Operator가 이벤트 소스를 모니터링하다가 트리거 조건이 충족되면 Deployment의 레플리카를 0에서 1로 활성화한다.
- Scaling 단계: 활성화된 이후에는 KEDA가 생성한 HPA가 메트릭 기반으로 1에서 N까지 확장/축소를 담당한다.
KEDA ScaledObject 매니페스트
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: order-processor-scaledobject
namespace: production
spec:
scaleTargetRef:
name: order-processor
pollingInterval: 15
cooldownPeriod: 300
idleReplicaCount: 0
minReplicaCount: 1
maxReplicaCount: 100
fallback:
failureThreshold: 3
replicas: 5
triggers:
- type: kafka
metadata:
bootstrapServers: kafka-broker:9092
consumerGroup: order-processor-group
topic: orders
lagThreshold: '50'
- type: prometheus
metadata:
serverAddress: http://prometheus:9090
metricName: http_requests_total
query: sum(rate(http_requests_total[2m]))
threshold: '100'
핵심 구성 요소를 살펴보면 다음과 같다.
- pollingInterval: 이벤트 소스를 확인하는 주기(초). 기본값 30초이며, 반응성이 중요한 경우 15초로 줄인다.
- cooldownPeriod: 마지막 트리거 활성화 후 0으로 축소하기까지 대기하는 시간(초).
- idleReplicaCount: 이벤트가 없을 때 유지할 레플리카 수. 0으로 설정하면 Scale-to-Zero가 활성화된다.
- fallback: 메트릭 수집 실패 시 안전장치.
failureThreshold만큼 연속 실패하면replicas에 지정된 수로 유지한다.
KEDA 설치 및 확인
# Helm으로 KEDA 설치
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda --namespace keda --create-namespace
# KEDA 상태 확인
kubectl get pods -n keda
kubectl get scaledobjects -n production
kubectl get hpa -n production
# ScaledObject 상세 확인
kubectl describe scaledobject order-processor-scaledobject -n production
HPA vs VPA vs KEDA 비교 분석
| 항목 | HPA | VPA | KEDA |
|---|---|---|---|
| 스케일링 방향 | 수평 (Pod 수 증감) | 수직 (리소스 요청/제한 조정) | 수평 (이벤트 기반 Pod 수 증감) |
| 기본 메트릭 | CPU, 메모리 | CPU, 메모리 사용 이력 | 외부 이벤트 소스 (65+ 스케일러) |
| 커스텀 메트릭 | 지원 (Adapter 필요) | 미지원 | 내장 지원 |
| Scale-to-Zero | 미지원 (minReplicas 1 이상) | 해당 없음 | 지원 |
| Pod 재시작 | 불필요 | 필요 (Recreate 모드) | 불필요 |
| 적합한 워크로드 | 무상태 웹 서비스, API | 모든 워크로드 (리소스 최적화) | 이벤트 기반, 배치 작업, 큐 처리 |
| Kubernetes 내장 | 예 | 별도 설치 필요 | 별도 설치 필요 |
| HPA와의 관계 | - | CPU/메모리 메트릭 충돌 가능 | HPA를 내부적으로 생성/관리 |
| 러닝 커브 | 낮음 | 중간 | 중간~높음 |
| 비용 최적화 | 중간 (과잉 확장 가능) | 높음 (right-sizing) | 높음 (Scale-to-Zero) |
사용 시나리오별 권장
- 일반 웹 서비스: HPA (CPU 기반) + VPA (Off 모드로 모니터링)
- 이벤트 기반 마이크로서비스: KEDA (Kafka/SQS 트리거)
- 배치 작업: KEDA (ScaledJob으로 작업 완료 시 자동 종료)
- API 게이트웨이: HPA (RPS 기반 커스텀 메트릭)
- 레거시 애플리케이션: VPA (수직 확장이 유일한 선택인 경우)
복합 스케일링 패턴
HPA + VPA 조합
HPA와 VPA를 동시에 사용할 때 가장 중요한 원칙은 같은 메트릭으로 충돌하지 않도록 하는 것이다.
# HPA: 커스텀 메트릭(RPS) 기반으로만 수평 확장
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 3
maxReplicas: 30
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: '500'
---
# VPA: CPU/메모리만 수직 조정 (HPA가 커스텀 메트릭만 사용하므로 충돌 없음)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
updatePolicy:
updateMode: 'Auto'
resourcePolicy:
containerPolicies:
- containerName: api-server
controlledResources:
- cpu
- memory
minAllowed:
cpu: '200m'
memory: '256Mi'
maxAllowed:
cpu: '2'
memory: '4Gi'
이 패턴에서 HPA는 RPS(초당 요청 수)만을 기준으로 Pod 수를 조절하고, VPA는 CPU/메모리 리소스를 최적화한다. 양쪽이 같은 메트릭(CPU/메모리)을 사용하면 HPA가 Pod를 늘려 사용률을 낮추고, VPA가 리소스를 줄여 사용률을 다시 높이는 **무한 루프(thrashing)**가 발생한다.
HPA + KEDA 조합
KEDA는 내부적으로 HPA를 생성하므로 별도의 HPA를 만들 필요가 없다. 단, 여러 이벤트 소스를 하나의 ScaledObject에 결합할 수 있다.
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: multi-trigger-scaler
namespace: production
spec:
scaleTargetRef:
name: api-server
minReplicaCount: 2
maxReplicaCount: 50
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus:9090
metricName: nginx_connections_active
query: sum(nginx_ingress_controller_nginx_process_connections)
threshold: '100'
- type: cron
metadata:
timezone: Asia/Seoul
start: 30 8 * * 1-5
end: 30 18 * * 1-5
desiredReplicas: '10'
이 예시는 Prometheus 메트릭 기반 동적 스케일링과 Cron 기반 예약 스케일링을 결합한다. 평일 오전 8시 30분부터 오후 6시 30분까지는 최소 10개 Pod를 유지하면서, 동시에 활성 연결 수에 따라 추가 확장이 가능하다.
운영 시 주의사항
1. 플래핑(Flapping) 방지
HPA가 빈번하게 스케일 업/다운을 반복하는 현상을 플래핑이라 한다. 이를 방지하려면 다음을 확인한다.
behavior.scaleDown.stabilizationWindowSeconds를 300초 이상으로 설정- 메트릭의 변동성이 크면
averageUtilization목표를 10-15% 여유 있게 설정 tolerance값(기본 0.1, 즉 10%)을 이해하고 활용. 현재 메트릭이 목표의 90-110% 범위 안이면 스케일링이 발생하지 않음
2. 리소스 제한(Limits) 설정 누락
Pod에 CPU/메모리 limits가 설정되지 않으면 HPA의 퍼센트 기반 스케일링이 작동하지 않는다. 반드시 resources.requests를 설정해야 한다.
resources:
requests:
cpu: '500m'
memory: '512Mi'
limits:
cpu: '1000m'
memory: '1Gi'
3. 메트릭 지연(Lag)
Metrics Server는 약 15초 지연이 있고, Prometheus 기반 커스텀 메트릭은 scrape interval + adapter 처리 시간까지 합하면 30-60초의 지연이 발생할 수 있다. 트래픽 급증 시 이 지연 동안 기존 Pod에 과부하가 걸릴 수 있으므로 다음을 고려한다.
minReplicas를 평균 트래픽 기준보다 약간 높게 설정- Readiness Probe를 적절히 구성하여 새 Pod가 트래픽을 받을 준비가 되기까지의 시간을 최소화
- 급증이 예측 가능하면 KEDA의 Cron 트리거로 사전 확장
4. VPA와 HPA 동시 사용 시 충돌
VPA가 Auto 또는 Recreate 모드이면서 HPA가 CPU/메모리 메트릭을 사용하면 양쪽이 서로 상충하는 결정을 내린다. 반드시 다음 중 하나를 선택한다.
- HPA는 커스텀 메트릭만 사용, VPA는 CPU/메모리 조정
- VPA를
Off모드로 설정하고 추천값만 참고
5. Cluster Autoscaler와의 연동
HPA가 Pod 수를 늘려도 클러스터에 가용 노드가 없으면 Pod는 Pending 상태에 머문다. Cluster Autoscaler(또는 Karpenter)가 연동되어 있어야 노드 수준의 확장이 이루어진다.
장애 사례와 복구 절차
사례 1: Metrics Server 장애로 HPA 무력화
증상: 모든 HPA의 TARGETS 열에 unknown이 표시되고, Pod 수가 고정된다.
# 진단
kubectl get hpa -A
kubectl top nodes # 실패하면 Metrics Server 문제
# Metrics Server 상태 확인
kubectl get pods -n kube-system | grep metrics-server
kubectl logs -n kube-system deployment/metrics-server --tail=50
복구 절차:
- Metrics Server Pod 재시작:
kubectl rollout restart deployment/metrics-server -n kube-system - APIService 등록 확인:
kubectl get apiservice v1beta1.metrics.k8s.io -o yaml - 지속적으로 문제가 발생하면 Metrics Server 재설치
- 복구될 때까지 수동으로 레플리카 수 조정:
kubectl scale deployment/api-server --replicas=10 -n production
사례 2: OOM Kill 연쇄 발생
증상: VPA의 추천값이 실제 피크 사용량보다 낮게 설정되어 Pod가 반복적으로 OOMKilled 된다.
# OOM 이벤트 확인
kubectl get events -n production --field-selector reason=OOMKilling --sort-by='.lastTimestamp'
# Pod 재시작 횟수 확인
kubectl get pods -n production -o custom-columns=NAME:.metadata.name,RESTARTS:.status.containerStatuses[0].restartCount
복구 절차:
- VPA
updateMode를 즉시Off로 변경하여 추가 Pod 변경 방지 - 영향받는 Deployment의 메모리 요청/제한을 수동으로 상향 조정
- VPA의
maxAllowed값이 실제 피크 사용량을 충분히 커버하는지 확인 - 최소 2주간
Off모드로 추천값 안정성 재관찰
사례 3: 스케일링 스톰(Scaling Storm)
증상: HPA가 빠르게 확장한 뒤 곧바로 축소를 반복하면서 Pod가 계속 생성/삭제된다.
# HPA 이벤트에서 빈번한 SuccessfulRescale 확인
kubectl describe hpa api-server-hpa -n production | grep SuccessfulRescale
복구 절차:
behavior.scaleDown.stabilizationWindowSeconds를 600초 이상으로 증가scaleDown.policies의 축소 비율을 5% 이하로 제한- 메트릭 소스의 변동성을 분석하여 적절한 평활화(smoothing) 적용
- 필요 시 HPA를 일시적으로 비활성화하고 수동 관리로 전환
사례 4: KEDA 메트릭 수집 실패
증상: ScaledObject 상태가 Unknown이고, 연결된 HPA의 메트릭이 수집되지 않는다.
# ScaledObject 상태 확인
kubectl get scaledobject -n production
kubectl describe scaledobject order-processor-scaledobject -n production
# KEDA Operator 로그 확인
kubectl logs -n keda deployment/keda-operator --tail=100 | grep -i error
복구 절차:
- 이벤트 소스(Kafka, Prometheus 등)의 연결 상태 확인
- ScaledObject의
fallback설정이 있는지 확인. 없으면 추가하여 메트릭 실패 시 안전 레플리카 수 유지 - KEDA Operator 재시작:
kubectl rollout restart deployment/keda-operator -n keda - 인증 정보(TriggerAuthentication)가 만료되지 않았는지 확인
프로덕션 체크리스트
배포 전 다음 항목을 반드시 확인한다.
HPA 관련:
- Metrics Server가 정상 동작하고
kubectl top pods가 성공하는가 minReplicas가 평상시 트래픽을 처리할 수 있는 최소값인가maxReplicas가 클러스터 용량과 비용 한도 내에 있는가behavior.scaleDown.stabilizationWindowSeconds가 300초 이상인가- 대상 Deployment에
resources.requests가 설정되어 있는가
VPA 관련:
- 프로덕션 첫 적용 시
updateMode: "Off"로 시작하는가 minAllowed와maxAllowed가 적절하게 설정되어 있는가- PodDisruptionBudget이 구성되어 있는가
- HPA와 동일 메트릭(CPU/메모리)으로 충돌하지 않는가
KEDA 관련:
fallback설정이 메트릭 장애 시 안전 레플리카 수를 보장하는가cooldownPeriod가 워크로드 특성에 맞게 설정되어 있는가- TriggerAuthentication의 인증 정보가 유효한가
- Scale-to-Zero 사용 시 Cold Start 시간이 SLA 내에 있는가
공통:
- Cluster Autoscaler 또는 Karpenter가 노드 수준 확장을 처리하는가
- 오토스케일링 이벤트에 대한 알림(Alerting)이 구성되어 있는가
- Grafana 대시보드에서 레플리카 수, 메트릭 값, 스케일링 이벤트를 모니터링할 수 있는가
- Runbook이 작성되어 있으며 팀원들이 숙지하고 있는가
참고자료
- Kubernetes Horizontal Pod Autoscaling 공식 문서
- Kubernetes Vertical Pod Autoscaling 공식 문서
- KEDA 공식 사이트
- Kubernetes Autoscaling Patterns: HPA, VPA and KEDA - Spectro Cloud
- Scaling Kubernetes the Right Way: HPA, VPA, CA, Karpenter, and KEDA - CloudPilot AI
- HPA vs VPA vs KEDA Performance and Cost Trade-offs - Kubeify
- Kubernetes HPA Troubleshooting - OneUptime
- VPA GitHub Repository - kubernetes/autoscaler
마치며
Kubernetes 오토스케일링은 단순히 HPA 하나를 배포하는 것으로 끝나지 않는다. 프로덕션 환경에서는 메트릭 수집의 안정성, 스케일링 알고리즘의 이해, Cooldown 전략, 장애 시 복구 절차까지 종합적으로 설계해야 한다.
핵심 원칙을 정리하면 다음과 같다.
- 단일 오토스케일러에 의존하지 마라: HPA, VPA, KEDA는 각각의 강점이 있다. 워크로드 특성에 맞게 조합하라.
- 보수적으로 축소하라: 스케일 업은 빠르게, 스케일 다운은 천천히. 트래픽 재증가에 대비해야 한다.
- 메트릭 장애에 대비하라: Metrics Server, Prometheus, 외부 이벤트 소스 모두 장애가 발생할 수 있다. Fallback 전략을 반드시 구성하라.
- 관찰 먼저, 자동화는 나중에: VPA는 Off 모드로 시작하고, HPA의 behavior를 보수적으로 설정한 뒤 점진적으로 자동화 수준을 높여라.
- 모니터링과 알림은 기본이다: 오토스케일링 결정 과정을 Grafana 대시보드로 시각화하고, 비정상적인 스케일링 패턴에 대한 알림을 설정하라.
오토스케일링을 제대로 구축하면 트래픽 급증에도 안정적으로 서비스를 제공하면서 클라우드 비용을 30-50% 절감할 수 있다. 이 글에서 다룬 패턴과 체크리스트가 프로덕션 환경의 오토스케일링 전략 수립에 실질적인 도움이 되길 바란다.