본문 바로가기

Kubernetes

Kubernetes HPA가 동작하지 않는 이유

반응형

Deployment에 HPA를 설정했는데 트래픽이 몰려도 Pod 수가 그대로입니다. kubectl get hpa를 확인하면 TARGETS 열에 <unknown>/50%이 표시되거나, 분명히 CPU가 높은데 replicas가 변하지 않습니다. HPA가 동작하지 않는 원인은 대부분 메트릭 수집 실패, resource requests 누락, 설정 오류 중 하나입니다.

핵심 요약

원인 증상 확인 방법
metrics-server 미설치/장애 TARGETS: <unknown> kubectl top nodes 응답 여부
resource requests 미설정 TARGETS: <unknown>, missing request 이벤트 Pod spec에 requests 필드 확인
HPA minReplicas = maxReplicas replicas 변동 없음 kubectl get hpa MIN/MAX 값 비교
Deployment replicas 필드 충돌 apply 시 원래 수로 되돌아감 manifest에서 spec.replicas 제거 여부
autoscaling API 버전 문제 HPA 생성 실패 또는 필드 무시 kubectl api-versions 확인
stabilization window scale-down이 5분간 지연 kubectl describe hpa Events
ScaleTargetRef 불일치 REFERENCE 없음 또는 에러 kubectl describe hpa scaleTargetRef

1. HPA 동작 원리 요약

HPA는 Controller Manager 안에서 동작하는 컨트롤 루프입니다. 기본 15초 간격으로 메트릭을 조회하고, 현재 값과 목표 값의 비율을 계산해서 replica 수를 조정합니다.

HPA 동작 흐름
HPA 동작 흐름

스케일링 공식:

desiredReplicas = ceil(currentReplicas × (currentMetricValue / desiredMetricValue))

예를 들어, 현재 CPU 사용률이 80%이고 목표가 50%라면: ceil(2 × (80 / 50)) = ceil(3.2) = 4 → Pod를 4개로 늘립니다.

이 과정에서 메트릭 수집, 계산, 스케일링 결정 중 어느 단계에서든 문제가 생기면 HPA는 동작하지 않습니다.

2. 원인 1: metrics-server가 없거나 동작하지 않음

가장 흔한 원인입니다. HPA가 CPU/메모리 메트릭을 기반으로 스케일링하려면 metrics-server가 클러스터에 설치되어 있어야 합니다. metrics-server는 각 노드의 kubelet에서 리소스 사용량을 수집하고, metrics.k8s.io API를 통해 제공합니다.

증상

$ kubectl get hpa
NAME      REFERENCE        TARGETS         MINPODS   MAXPODS   REPLICAS
my-app    Deployment/app   <unknown>/50%   2         10        2

TARGETS 열에 <unknown>이 표시됩니다.

진단 명령어

# metrics-server가 설치되어 있는지 확인
kubectl get deployment metrics-server -n kube-system

# metrics API가 응답하는지 확인
kubectl top nodes

# API 서비스 등록 상태 확인
kubectl get apiservice v1beta1.metrics.k8s.io -o yaml

kubectl top nodeserror: Metrics API not available 또는 error: metrics not available yet를 반환하면 metrics-server 문제입니다.

해결 방법

매니지드 Kubernetes를 사용하는 경우:

플랫폼 기본 설치 여부 확인/활성화 방법
EKS 기본 미설치 kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
AKS 기본 설치됨 kubectl get pods -n kube-system -l k8s-app=metrics-server
GKE 기본 설치됨 자동 관리, 별도 조치 불필요

EKS에서 metrics-server 설치:

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

설치 후에도 동작하지 않는 경우 — metrics-server Pod 로그를 확인합니다:

kubectl logs -n kube-system deployment/metrics-server

흔한 에러: x509: cannot validate certificate — kubelet TLS 인증서를 검증하지 못하는 경우입니다. 테스트 환경에서는 --kubelet-insecure-tls 옵션으로 우회할 수 있지만, 운영 환경에서는 인증서를 올바르게 구성해야 합니다.

3. 원인 2: Pod에 resource requests가 설정되지 않음

HPA가 CPU 사용률(utilization) 기반으로 스케일링할 때, 사용률은 현재 사용량 / requests 값으로 계산됩니다. requests가 없으면 분모가 없어서 사용률을 계산할 수 없습니다.

증상

$ kubectl describe hpa my-app
...
Conditions:
  Type            Status  Reason            Message
  ----            ------  ------            -------
  ScalingActive   False   FailedGetMetrics  the HPA was unable to compute the replica count: missing request for cpu

진단 명령어

# Pod의 resource requests 확인
kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].resources.requests}'

출력이 비어 있으면 requests가 설정되지 않은 것입니다.

해결 방법

Deployment spec에 resource requests를 추가합니다:

spec:
  containers:
  - name: app
    resources:
      requests:
        cpu: "200m"
        memory: "256Mi"
      limits:
        cpu: "500m"
        memory: "512Mi"

주의할 점:

  • 모든 컨테이너에 requests를 설정해야 합니다. 사이드카 컨테이너(예: Istio proxy, log collector)에 requests가 없으면 동일한 문제가 발생합니다.
  • requests 값은 실제 사용량 기반으로 설정해야 합니다. 너무 높으면 사용률이 낮게 계산되어 스케일 업이 안 되고, 너무 낮으면 불필요한 스케일링이 발생합니다.
HPA 메트릭 수집 실패 원인
HPA 메트릭 수집 실패 원인

4. 원인 3: minReplicas와 maxReplicas 설정 오류

minReplicas = maxReplicas

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
  minReplicas: 3
  maxReplicas: 3  # 스케일링 불가

이 경우 HPA는 항상 3개를 유지합니다. 스케일 업도, 스케일 다운도 일어나지 않습니다. 의도적인 경우가 아니라면 maxReplicas를 minReplicas보다 크게 설정해야 합니다.

maxReplicas가 너무 낮음

목표 사용률이 50%인데 현재 Pod 2개가 90%를 사용 중이라면, 공식에 따라 4개가 필요합니다. 하지만 maxReplicas가 3이면 3개까지만 스케일됩니다. 이후에도 부하가 높으면 HPA 이벤트에 DesiredWithinRange 메시지가 나타납니다.

진단 명령어

kubectl get hpa my-app -o jsonpath='{.spec.minReplicas}/{.spec.maxReplicas}'

5. 원인 4: scaleTargetRef 불일치

HPA의 scaleTargetRef가 실제 존재하는 Deployment/StatefulSet과 이름이 다르면 스케일링 대상을 찾지 못합니다.

증상

$ kubectl describe hpa my-app
...
Reference:          Deployment/my-apps   # 오타: my-apps vs my-app
...
Conditions:
  Type            Status  Reason              Message
  ScalingActive   False   FailedGetScale      the HPA controller was unable to get the target's current scale

진단 명령어

# HPA가 참조하는 대상 확인
kubectl get hpa my-app -o jsonpath='{.spec.scaleTargetRef}'

# 실제 Deployment 이름과 비교
kubectl get deployment -n <namespace>

해결 방법

HPA의 scaleTargetRef.name을 실제 리소스 이름과 일치시킵니다. namespace도 동일해야 합니다.

6. 원인 5: Deployment manifest에 spec.replicas가 남아있음

Deployment YAML에 spec.replicas가 명시되어 있으면, kubectl apply를 실행할 때마다 HPA가 조정한 replica 수가 원래 값으로 되돌아갑니다.

시나리오

  1. HPA가 Pod를 5개로 스케일 업
  2. 개발자가 이미지 버전을 변경하고 kubectl apply -f deployment.yaml 실행
  3. manifest에 replicas: 2가 있으므로 2개로 줄어듦
  4. HPA가 다시 계산해서 5개로 늘림 → 불필요한 thrashing 발생

해결 방법

HPA를 사용하는 Deployment에서는 spec.replicas 필드를 제거합니다:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  # replicas: 2  ← 제거
  selector:
    matchLabels:
      app: my-app
  template:
    ...

주의: spec.replicas를 제거하면 기본값 1이 적용될 수 있습니다. 제거 전에 HPA가 먼저 활성화되어 있어야 안전합니다.

7. 원인 6: autoscaling API 버전 문제

Kubernetes 1.26부터 autoscaling/v2beta2가 제거되었습니다. 현재 안정 API는 autoscaling/v2입니다.

API 버전 상태 비고
autoscaling/v1 사용 가능 CPU만 지원, 기본 기능만
autoscaling/v2 안정 (v1.23+) CPU, 메모리, 커스텀 메트릭, behavior 지원
autoscaling/v2beta2 제거 (v1.26+) v2로 마이그레이션 필요

증상

error: resource mapping not found for name: "my-app" namespace: "" from "hpa.yaml":
no matches for kind "HorizontalPodAutoscaler" in version "autoscaling/v2beta2"

진단 명령어

# 클러스터에서 지원하는 API 버전 확인
kubectl api-versions | grep autoscaling

해결 방법

autoscaling/v2를 사용합니다:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50

8. 원인 7: stabilization window와 tolerance

HPA가 동작하지 않는 것처럼 보이지만, 실제로는 의도적인 지연일 수 있습니다.

scale-down stabilization window

기본값은 5분(300초) 입니다. 메트릭이 목표 이하로 내려가도 5분 동안은 scale-down하지 않습니다. 이는 메트릭 변동으로 인한 불필요한 축소를 방지하기 위한 설계입니다.

tolerance (허용 오차)

기본 tolerance는 10% 입니다. 현재 메트릭 값과 목표 값의 비율이 1.0 ± 0.1 범위 안에 있으면 스케일링을 하지 않습니다.

예: 목표 CPU 50%, 현재 CPU 54% → 비율 1.08 → tolerance 범위 내 → 스케일링 안 함.

Kubernetes 1.33에서 HPAConfigurableTolerance feature gate가 Alpha로 도입되었고, 1.35에서 Beta로 승격되었습니다. spec.behavior.scaleUp.tolerancespec.behavior.scaleDown.tolerance 필드로 방향별 tolerance를 개별 설정할 수 있습니다. 예를 들어 스케일 업은 tolerance 0(즉시 반응), 스케일 다운은 tolerance 0.05(5%)로 설정하여 불필요한 축소를 줄일 수 있습니다.

진단 방법

kubectl describe hpa my-app

Events 섹션에서 스케일링 이벤트 발생 시각과 SuccessfulRescale 메시지를 확인합니다. 이벤트가 아예 없다면 메트릭 수집 문제이고, 이벤트가 있지만 간격이 긴 경우 stabilization window 영향입니다.

HPA 스케일링 지연 원인
HPA 스케일링 지연 원인

9. 원인 8: 커스텀 메트릭 어댑터 미설치

CPU/메모리 이외의 메트릭(RPS, 큐 깊이 등)으로 스케일링하려면 커스텀 메트릭 어댑터가 필요합니다. custom.metrics.k8s.io 또는 external.metrics.k8s.io API를 제공하는 어댑터가 없으면 HPA가 메트릭을 조회할 수 없습니다.

대표적인 커스텀 메트릭 어댑터

어댑터 설명
Prometheus Adapter Prometheus 메트릭을 Kubernetes API로 노출
KEDA 이벤트 기반 오토스케일링, 다양한 소스 지원
Datadog Cluster Agent Datadog 메트릭 기반 스케일링

진단 명령어

# 커스텀 메트릭 API 등록 여부
kubectl get apiservice v1beta1.custom.metrics.k8s.io
kubectl get apiservice v1beta1.external.metrics.k8s.io

등록된 API 서비스가 없거나 상태가 False면 어댑터가 설치되지 않았거나 장애 상태입니다.

10. 원인 9: Node 리소스 부족으로 스케줄링 실패

HPA가 replica 수를 늘리기로 결정해도, 새 Pod를 배치할 노드가 없으면 실제로 Pod가 생성되지 않습니다. HPA 자체는 정상이지만 결과적으로 스케일 업이 되지 않는 것처럼 보입니다.

증상

$ kubectl get hpa
NAME      REFERENCE        TARGETS    MINPODS   MAXPODS   REPLICAS
my-app    Deployment/app   85%/50%    2         10        5

$ kubectl get pods | grep Pending
my-app-7d9c8-xxxxx   0/1   Pending   0   2m

HPA는 10개까지 스케일을 결정했지만, 일부 Pod가 Pending 상태에 머물러 있습니다.

진단 명령어

# Pending Pod의 이벤트 확인
kubectl describe pod <pending-pod-name>
# Events: FailedScheduling - Insufficient cpu/memory

# 노드 리소스 상태 확인
kubectl top nodes
kubectl describe nodes | grep -A 5 "Allocated resources"

해결 방법

  • Cluster Autoscaler 또는 Karpenter 활성화: 노드 풀을 자동으로 확장
  • 노드 리소스 requests 합산이 용량을 초과하지 않도록 Pod requests 조정
  • maxReplicas를 현재 클러스터 용량에 맞게 설정

11. 실무 진단 플로우차트

HPA가 동작하지 않을 때 아래 순서로 확인합니다.

HPA 트러블슈팅 진단 플로우
HPA 트러블슈팅 진단 플로우

Step 1: kubectl get hpa 확인

kubectl get hpa -n <namespace>
  • TARGETS에 <unknown> → Step 2로
  • TARGETS에 숫자가 있지만 REPLICAS 변동 없음 → Step 4로

Step 2: 메트릭 수집 확인

kubectl top nodes
kubectl top pods -n <namespace>
  • 에러 발생 → metrics-server 확인 (원인 1)
  • 정상 응답 → Step 3으로

Step 3: resource requests 확인

kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.spec.containers[*].resources.requests}'
  • 비어 있음 → resource requests 추가 (원인 2)
  • 값이 있음 → 사이드카 컨테이너 포함 모든 컨테이너 확인

Step 4: HPA 상세 확인

kubectl describe hpa <hpa-name> -n <namespace>
  • Conditions의 ScalingActive가 False → scaleTargetRef, API 버전 확인 (원인 5, 7)
  • AbleToScale가 False → min/max 설정 확인 (원인 3)
  • Events에 스케일링 이력 있지만 5분 이내 → stabilization window (원인 7)

12. 운영 환경에서의 HPA 설정 권장사항

resource requests 설계

resources:
  requests:
    cpu: "200m"     # 평상시 사용량 기준
    memory: "256Mi"
  limits:
    cpu: "1000m"
    memory: "512Mi"

requests는 평상시 사용량의 60~80% 수준으로 설정합니다. 이렇게 하면 부하가 약간만 올라도 사용률이 목표를 초과하여 스케일 업이 트리거됩니다.

HPA 설정 예시

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 60
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Percent
        value: 100
        periodSeconds: 15
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 10
        periodSeconds: 60

설계 의도:

설정 이유
averageUtilization 60% 버스트 대응 여유분 확보
scaleUp stabilization 0초 부하 증가 시 즉시 스케일 업
scaleDown stabilization 300초 불필요한 축소로 인한 서비스 불안정 방지
scaleDown rate 10%/분 점진적 축소로 thrashing 방지

CPU vs 메모리 메트릭 선택

기준 CPU 메모리
적합한 워크로드 API 서버, 연산 중심 앱 대부분 부적합
스케일 특성 부하에 따라 오르내림 올라가면 잘 안 내려옴 (GC 의존)
권장 여부 일반적으로 권장 단독 사용 비권장

메모리 메트릭 단독으로 HPA를 구성하면, JVM 기반 앱처럼 GC 전까지 메모리를 반환하지 않는 워크로드에서 무의미한 스케일 업이 발생할 수 있습니다. 메모리 기반 스케일링이 필요하면 CPU 메트릭과 함께 사용하고, OOMKilled 방지는 VPA나 limits 설정으로 처리하는 것이 일반적입니다.

마무리

HPA가 동작하지 않는 원인은 대부분 세 가지로 좁힐 수 있습니다:

  1. 메트릭 수집 실패 — metrics-server 미설치 또는 장애
  2. 계산 불가 — resource requests 미설정
  3. 설정 오류 — scaleTargetRef 불일치, min=max, API 버전 문제

kubectl describe hpa의 Conditions와 Events를 먼저 확인하면 원인을 빠르게 좁힐 수 있습니다. 메트릭이 정상인데도 스케일링이 느리다면 stabilization window와 tolerance 설정을 점검합니다.

Kubernetes 아키텍처 기본 구조와 HPA가 어떤 컴포넌트를 통해 동작하는지 이해하면, 문제 발생 시 어느 레이어를 확인해야 하는지 판단이 빨라집니다.

반응형