HPA의 동작 원리: 컨트롤 루프와 스케일링 공식
HorizontalPodAutoscaler(HPA)는 Kubernetes API 리소스이자 컨트롤러입니다. kube-controller-manager 내부의 HPA 컨트롤러가 기본 15초 간격(--horizontal-pod-autoscaler-sync-period)으로 루프를 돌며, 관찰된 메트릭과 목표 메트릭을 비교하여 Deployment·StatefulSet의 레플리카 수를 조정합니다.
핵심 스케일링 공식은 다음과 같습니다:
desiredReplicas = ceil(currentReplicas × (currentMetricValue / desiredMetricValue))
예를 들어 현재 CPU 사용률이 80%이고 목표가 50%라면:
desiredReplicas = ceil(3 × (80 / 50)) = ceil(4.8) = 5
반대로 현재 25%라면 ceil(3 × 0.5) = 2로 축소됩니다. 단, 비율이 1.0에 충분히 가까우면(기본 tolerance ±10%) 스케일링을 건너뜁니다. 이 tolerance는 --horizontal-pod-autoscaler-tolerance로 조정 가능합니다.
메트릭 소스 3가지: Resource·Custom·External
HPA v2 API(autoscaling/v2)는 세 가지 메트릭 소스를 지원합니다:
| 메트릭 타입 | 소스 API | 설명 | 예시 |
|---|---|---|---|
| Resource | metrics.k8s.io | Pod의 CPU·메모리 사용률 (request 기준 %) | CPU 평균 50% |
| Pods (Custom) | custom.metrics.k8s.io | Pod별 커스텀 메트릭 평균값 | requests_per_second |
| Object | custom.metrics.k8s.io | 특정 K8s 오브젝트의 메트릭 | Ingress의 hits_per_second |
| External | external.metrics.k8s.io | 클러스터 외부 메트릭 | SQS 큐 깊이, CloudWatch |
중요: Resource 메트릭(CPU/메모리)을 사용하려면 Metrics Server가 클러스터에 설치되어 있어야 하고, 대상 Pod의 컨테이너에 반드시 resources.requests가 설정되어 있어야 합니다. request가 없으면 HPA가 해당 메트릭에 대해 아무 동작도 하지 않습니다.
기본 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: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60 # request 대비 평균 60%
이 설정에서 3개 Pod의 평균 CPU 사용률이 60%를 넘으면 스케일 아웃, 충분히 낮아지면 스케일 인합니다. minReplicas: 3은 아무리 부하가 낮아도 최소 3개를 유지하라는 의미입니다.
다중 메트릭 조합: CPU + 메모리 + 커스텀 메트릭
여러 메트릭을 동시에 지정하면, HPA는 각 메트릭별로 desiredReplicas를 독립 계산한 뒤 가장 큰 값을 채택합니다.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-server-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 3
maxReplicas: 30
metrics:
# 1) CPU 기반
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
# 2) 메모리 기반
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 75
# 3) 커스텀 메트릭: Pod당 초당 요청 수
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "1000"
만약 CPU 기준으로 5개, 메모리 기준으로 4개, RPS 기준으로 7개가 필요하다면, 최종 결정은 7개입니다. 이 방식으로 어떤 병목이든 놓치지 않고 대응할 수 있습니다.
behavior: 스케일링 속도를 정밀하게 제어하는 핵심
HPA의 가장 강력하면서 과소평가된 기능은 spec.behavior입니다. 스케일 업/다운의 속도, 안정화 윈도우, 정책을 세밀하게 조절할 수 있습니다.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-server-hpa
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
behavior:
scaleUp:
stabilizationWindowSeconds: 0 # 즉시 스케일 업
policies:
- type: Percent
value: 100 # 현재 대비 100% 증가 허용
periodSeconds: 60
- type: Pods
value: 10 # 또는 최대 10개씩
periodSeconds: 60
selectPolicy: Max # 두 정책 중 큰 값 채택
scaleDown:
stabilizationWindowSeconds: 300 # 5분간 안정화 후 축소
policies:
- type: Percent
value: 10 # 60초마다 최대 10%씩 축소
periodSeconds: 60
selectPolicy: Min # 가장 보수적인 정책 채택
behavior 주요 필드 정리
| 필드 | 기본값 (scaleUp) | 기본값 (scaleDown) | 설명 |
|---|---|---|---|
stabilizationWindowSeconds |
0 | 300 (5분) | 윈도우 내 계산된 최적값 중 가장 높은(scaleUp) 또는 낮은(scaleDown) 값 채택 |
policies[].type |
— | — | Pods(절대값) 또는 Percent(비율) |
policies[].periodSeconds |
— | — | 해당 정책이 적용되는 시간 창 (최대 1800초) |
selectPolicy |
Max | Max | Max: 가장 많이 변경 / Min: 가장 적게 변경 / Disabled: 해당 방향 비활성화 |
스케일 다운 완전 비활성화
야간 배치 등 절대로 축소해서는 안 되는 워크로드는 다음과 같이 설정합니다:
behavior:
scaleDown:
selectPolicy: Disabled # 스케일 다운 자체를 막음
메트릭 누락·Not Ready Pod 처리: 알고리즘의 보수적 설계
HPA 알고리즘에는 운영자가 반드시 이해해야 할 보수적 보정 로직이 내장되어 있습니다:
- 메트릭 누락 Pod: 스케일 다운 시에는 목표값의 100%를 소비한다고 가정(축소 억제), 스케일 업 시에는 0%를 소비한다고 가정(확장 억제)
- Not Ready Pod: CPU 기반 스케일링에서 아직 Ready가 아닌 Pod는 0% 소비로 간주되어 스케일 업 규모를 억제합니다
- 삭제 중인 Pod: deletionTimestamp가 설정된 Pod는 계산에서 완전히 제외됩니다
- 초기화 기간: Pod가 시작된 후
--horizontal-pod-autoscaler-cpu-initialization-period(기본 5분) 동안은 CPU 메트릭이 안정화되지 않았다고 판단하여 제외합니다
이 보정 로직 때문에 새로 생성된 Pod의 CPU 급등이 즉시 추가 스케일 업을 유발하지 않습니다. 이는 의도된 설계이며, “스케일 업 → 새 Pod CPU 높음 → 다시 스케일 업” 연쇄 반응(flapping)을 방지합니다.
External 메트릭으로 SQS 큐 기반 오토스케일링
CPU/메모리가 아닌 외부 시스템 메트릭으로 스케일링하는 것은 큐 워커(consumer) 패턴에서 매우 유용합니다. AWS SQS 큐 깊이를 기반으로 워커를 스케일링하는 예시입니다:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: queue-worker-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: queue-worker
minReplicas: 1
maxReplicas: 50
metrics:
- type: External
external:
metric:
name: sqs_approximate_number_of_messages
selector:
matchLabels:
queue: order-processing
target:
type: AverageValue # Pod 수로 나눈 값 기준
averageValue: "30" # Pod당 30개 메시지 유지 목표
behavior:
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 200
periodSeconds: 30
scaleDown:
stabilizationWindowSeconds: 600 # 큐가 비어도 10분 대기
주의: External 메트릭을 사용하려면 prometheus-adapter 또는 KEDA 같은 외부 메트릭 어댑터가 설치되어 있어야 합니다. EKS 환경에서는 CloudWatch Metrics Adapter를 사용할 수 있습니다.
resources.requests 미설정 함정: HPA가 작동하지 않는 가장 흔한 원인
HPA가 배포 후 아무 동작도 하지 않는 가장 흔한 원인은 Pod 컨테이너에 resources.requests가 빠져 있는 경우입니다.
# ❌ 잘못된 설정: requests 없음 → HPA CPU 메트릭 undefined
containers:
- name: api
image: myapp:latest
# resources 섹션 자체가 없음
# ✅ 올바른 설정: requests 반드시 명시
containers:
- name: api
image: myapp:latest
resources:
requests:
cpu: "250m" # HPA가 이 값을 100%로 간주
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
CPU Utilization 타입은 request 대비 사용률입니다. request가 250m이고 현재 사용량이 150m이면 utilization은 60%입니다. 따라서 request 값을 얼마로 설정하느냐에 따라 HPA의 스케일링 민감도가 완전히 달라집니다.
| request 값 | 실사용 150m일 때 utilization | targetUtilization=60% 시 스케일링 |
|---|---|---|
| 100m | 150% | 즉시 확장 (과민) |
| 250m | 60% | tolerance 범위 내 유지 |
| 500m | 30% | 축소 시도 (둔감) |
운영 팁: request는 “평상시 부하의 실제 사용량”에 가깝게 설정하고, targetUtilization을 60~70%로 잡는 것이 일반적입니다. 이렇게 하면 평상시에는 안정적이고, 트래픽 증가 시 약 1.5~1.7배에서 스케일 아웃이 시작됩니다.
HPA 디버깅: kubectl 명령과 이벤트 확인
# HPA 현재 상태 확인 (메트릭 값, 현재/목표 레플리카)
kubectl get hpa api-server-hpa -n production
# 상세 정보: 각 메트릭별 현재값, 조건(Conditions), 이벤트
kubectl describe hpa api-server-hpa -n production
# Conditions에서 확인할 핵심 항목:
# - AbleToScale: 스케일링 가능 여부
# - ScalingActive: 메트릭 수집 성공 여부
# - ScalingLimited: min/max에 의해 제한되었는지
자주 마주치는 이벤트 메시지와 대응:
| 이벤트 메시지 | 원인 | 대응 |
|---|---|---|
FailedGetResourceMetric |
Metrics Server 미설치 또는 Pod에 request 미설정 | Metrics Server 확인, resources.requests 추가 |
FailedComputeMetricsReplicas |
메트릭 API 응답 오류 | custom metrics adapter 로그 확인 |
DesiredWithinRange |
계산된 레플리카가 min~max 범위 내 | 정상 동작 |
ScalingLimited |
desiredReplicas가 maxReplicas를 초과 | maxReplicas 상향 또는 Pod 최적화 검토 |
HPA + PodDisruptionBudget + Readiness Probe 조합 설계
HPA 단독으로는 안정적인 오토스케일링이 불가능합니다. 세 가지를 함께 설계해야 합니다:
# 1) Readiness Probe: 새 Pod가 트래픽을 받을 준비가 되었는지 확인
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
# 2) PDB: 스케일 다운·롤링 업데이트 시 최소 가용 Pod 보장
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-server-pdb
spec:
minAvailable: "60%" # 항상 60% 이상 유지
selector:
matchLabels:
app: api-server
# 3) HPA의 minReplicas를 PDB와 정합성 있게 설정
# PDB minAvailable=60%이고 minReplicas=3이면,
# 최소 2개가 항상 Available 상태를 유지해야 함
Readiness Probe가 없으면 새로 생성된 Pod가 아직 초기화 중인데도 트래픽을 받아 에러가 발생하고, HPA는 “부하가 높다”고 판단하여 불필요한 추가 스케일 업이 연쇄 발생할 수 있습니다.
실무 운영 Anti-Pattern 5가지
- CPU와 메모리를 동시에 targetUtilization으로 설정: 메모리는 GC 기반 언어(Java, Go)에서 할당 후 반환이 느려 사실상 항상 높게 측정됩니다. 메모리 기반 HPA는
AverageValue(절대값)를 사용하거나, CPU 단독 + 메모리 limits/requests 적절 설정이 더 안정적입니다. - minReplicas=1: 단일 Pod에서 스케일 업까지 15~30초 + Pod 시작 시간이 걸립니다. 갑작스러운 트래픽 증가에 응답 지연이 불가피합니다. 프로덕션에서는 최소 2~3으로 설정합니다.
- maxReplicas를 너무 높게 설정: 메트릭 수집 오류나 일시적 스파이크에 대형 스케일 업이 발생하면 노드 자원이 고갈됩니다. Cluster Autoscaler와 연동할 때 노드 프로비저닝 시간(수 분)까지 고려해야 합니다.
- behavior 미설정: 기본 scaleDown stabilizationWindow는 5분이지만, 트래픽 패턴에 따라 더 길게(10~15분) 설정해야 축소 후 즉시 재확장하는 flapping을 방지할 수 있습니다.
- VPA와 HPA를 같은 메트릭으로 동시 사용: VerticalPodAutoscaler가 CPU request를 변경하면 HPA의 utilization 계산 기준이 달라져 충돌합니다. 공식 문서에서도 같은 CPU/메모리 메트릭으로 VPA와 HPA를 동시 사용하지 말 것을 명시하고 있습니다.
HPA 설계 체크리스트
| 항목 | 확인 사항 |
|---|---|
| Metrics Server | kubectl top pods가 동작하는지 확인 |
| resources.requests | 모든 컨테이너에 CPU·메모리 request 명시 |
| Readiness Probe | 초기화 완료 후에만 Ready 상태로 전환되도록 설정 |
| behavior.scaleDown | stabilizationWindowSeconds를 트래픽 패턴에 맞게 조정 |
| PDB | minAvailable을 설정하여 축소 시에도 가용성 보장 |
| Cluster Autoscaler 연동 | 노드 자원 부족 시 노드 추가까지 걸리는 시간(2~5분) 감안하여 buffer 설계 |
| 알림 | HPA가 maxReplicas에 도달하면 알림 발송 (Prometheus alerting 등) |
핵심 정리
- HPA의 스케일링 공식은 currentMetricValue / desiredMetricValue 비율 기반이며, ±10% tolerance 내에서는 동작하지 않습니다.
- resources.requests 미설정은 HPA가 작동하지 않는 가장 흔한 원인입니다. 반드시 명시하세요.
spec.behavior로 스케일 업은 공격적으로, 스케일 다운은 보수적으로 설정하는 것이 운영 안정성의 핵심입니다.- 다중 메트릭 지정 시 가장 큰 desiredReplicas가 채택됩니다. CPU 단독보다 비즈니스 메트릭(RPS, 큐 깊이)을 함께 사용하면 실제 부하에 더 정확히 대응합니다.
- HPA는 PDB·Readiness Probe·Cluster Autoscaler와 함께 설계해야 실질적으로 안정적인 오토스케일링이 가능합니다.
참고 자료