Kubernetes HPA: 스케일링 공식

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가지

  1. CPU와 메모리를 동시에 targetUtilization으로 설정: 메모리는 GC 기반 언어(Java, Go)에서 할당 후 반환이 느려 사실상 항상 높게 측정됩니다. 메모리 기반 HPA는 AverageValue(절대값)를 사용하거나, CPU 단독 + 메모리 limits/requests 적절 설정이 더 안정적입니다.
  2. minReplicas=1: 단일 Pod에서 스케일 업까지 15~30초 + Pod 시작 시간이 걸립니다. 갑작스러운 트래픽 증가에 응답 지연이 불가피합니다. 프로덕션에서는 최소 2~3으로 설정합니다.
  3. maxReplicas를 너무 높게 설정: 메트릭 수집 오류나 일시적 스파이크에 대형 스케일 업이 발생하면 노드 자원이 고갈됩니다. Cluster Autoscaler와 연동할 때 노드 프로비저닝 시간(수 분)까지 고려해야 합니다.
  4. behavior 미설정: 기본 scaleDown stabilizationWindow는 5분이지만, 트래픽 패턴에 따라 더 길게(10~15분) 설정해야 축소 후 즉시 재확장하는 flapping을 방지할 수 있습니다.
  5. 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와 함께 설계해야 실질적으로 안정적인 오토스케일링이 가능합니다.

참고 자료

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux