태그 보관물: HPA

HPA가 파드를 무한 증식시키거나 계속 죽이고 있다면 — ‘플래핑’과 리소스 설정의 함정

대표 이미지

HPA가 파드를 무한 증식시키거나 계속 죽이고 있다면 — '플래핑'과 리소스 설정의 함정

단순한 CPU 임계값 설정만으로는 부족합니다. 안정화 윈도우와 리소스 요청(Requests)의 상관관계를 통해 예측 가능한 오토스케일링을 구축하는 방법

현장에서 HPA(Horizontal Pod Autoscaler)를 운영하다 보면 정말 당혹스러운 순간이 있어요. 트래픽이 조금 늘어난 것 같은데 파드가 갑자기 수십 개로 폭발하듯 늘어나거나, 반대로 조금 줄어들자마자 파드들이 우수수 삭제되어 서비스가 휘청이는 경우죠. 제가 경험해보니 HPA는 일종의 ‘배포 품질 곱셈기’ 같더라고요. 파드 스펙이 완벽하면 효율을 극대화해주지만, 설정에 작은 결함이라도 있다면 그 결함을 클러스터 전체에 아주 효율적으로 복제하고 증폭시켜 버리거든요 [3].

결국 HPA는 단순히 “부하가 많으면 늘려줘”라고 설정하는 자동 확장 도구가 아닙니다. 잘못 설정된 리소스 요청(Requests)과 안정화 윈도우의 부재는 서비스 전체를 흔드는 ‘플래핑(Flapping)’ 현상을 유발하고, 이는 곧 시스템 전체의 불안정성으로 이어집니다. 오늘은 이 함정들을 어떻게 피하고 예측 가능한 스케일링을 만들 수 있을지 편하게 이야기해 볼게요.

HPA의 작동 원리와 ‘리소스 요청(Requests)’의 결정적 역할

많은 분이 HPA를 설정할 때 “CPU 사용률 70%면 늘려줘”라고 적고 끝내곤 합니다. 하지만 여기서 가장 중요한 건 ‘70%’라는 숫자보다, 그 기준이 되는 리소스 요청(Requests) 값이에요.

HPA가 이용률을 계산하는 공식은 아주 단순합니다. (현재 소비량 / 요청된 리소스)죠 [1]. 여기서 함정이 발생합니다. 만약 실제로는 500m의 CPU가 필요한 앱인데, requests를 너무 낮게(예: 100m) 설정했다고 쳐보세요. 앱이 200m만 써도 HPA는 “와, 이용률이 200%나 되네? 당장 파드를 늘려!”라고 판단합니다. 실제 부하보다 인위적으로 높게 측정된 이용률 때문에 불필요한 스케일 업이 일어나는 거죠.

문제는 여기서 끝나지 않아요. 이렇게 잘못된 스펙을 가진 파드가 계속 복제되면, 클러스터 자원은 빠르게 고갈되지만 정작 개별 파드는 여전히 리소스 부족으로 헐떡이는 악순환에 빠집니다.

“HPA is a multiplier of your deployment’s quality: if your pod spec is flawed, HPA will happily and efficiently multiply that flaw across your cluster.” [3]

HPA는 배포 품질의 곱셈기입니다. 파드 스펙에 결함이 있다면, HPA는 그 결함을 클러스터 전체에 아주 기쁘고 효율적으로 복제할 것입니다.

특히 메모리 기반 스케일링은 더 위험해요. CPU는 임계치를 넘으면 쓰로틀링(Throttling)이 걸리며 느려지지만, 메모리는 한계를 넘는 순간 OOM(Out Of Memory) 킬러가 파드를 바로 죽여버리거든요. 그래서 웬만하면 CPU나 커스텀 메트릭을 우선 고려하시길 추천합니다.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70 # requests 대비 70% 사용 시 확장
---
# HPA가 제대로 작동하려면 반드시 아래와 같이 정확한 requests가 설정되어야 합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
      - name: app-container
        image: my-app:latest
        resources:
          requests:
            cpu: "200m" # 이 값이 HPA 계산의 분모가 됩니다. 너무 낮으면 과잉 확장됩니다.
            memory: "256Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"

서비스를 흔드는 ‘플래핑(Flapping)’ 현상의 정체

혹시 파드 개수가 5개였다가 8개로 늘어났는데, 1분 뒤에 다시 5개로 줄어들고, 또다시 8개로 늘어나는 광경을 보신 적 있나요? 이걸 바로 ‘플래핑(Flapping)’이라고 합니다 [1]. 메트릭이 아주 미세하게 출렁이는데, HPA가 거기에 너무 민감하게 반응해서 파드를 계속 켰다 껐다 하는 상태죠.

기본적으로 쿠버네티스는 ±10%의 톨러런스(Tolerance)를 둡니다. 즉, 목표치에서 10% 이내의 변동은 무시한다는 뜻이에요. 하지만 대규모 배포 환경에서는 이 10%가 생각보다 큽니다. 파드가 수백 개라면 10% 변동만으로도 수십 개의 파드가 동시에 생성되거나 삭제될 수 있고, 이는 곧 인프라의 엄청난 리소스 낭비와 서비스 불안정으로 이어지죠 [2].

특히 트래픽이 뾰족뾰족하게 튀는 ‘스파이키(Spiky)’한 워크로드에서 이런 현상이 심합니다. 빠르게 반응하게 설정하면 플래핑이 심해지고, 너무 둔하게 설정하면 트래픽 폭주 때 대응이 늦어지는 트레이드오프가 발생해요. 다행히 최신 버전인 K8s 1.33(alpha)부터는 HPAConfigurableTolerance를 통해 이 민감도를 워크로드별로 세밀하게 조정할 수 있게 되었습니다 [2].

해결책: 안정화 윈도우(Stabilization Window) 최적화

그렇다면 이 지긋지긋한 플래핑을 어떻게 잡을까요? 정답은 안정화 윈도우(Stabilization Window) 설정에 있습니다.

안정화 윈도우는 쉽게 말해 “지금 당장 수치가 떨어졌다고 해서 바로 파드를 죽이지 말고, 조금 더 지켜보자”라고 결정하는 대기 시간이에요. HPA가 과거의 권장 상태를 기억했다가, 그 기간 동안의 최댓값을 기준으로 스케일링을 결정하게 만드는 장치죠 [4].

특히 스케일 다운(ScaleDown) 윈도우를 보수적으로 잡는 것이 핵심입니다. 트래픽 스파이크가 잠시 가라앉았다고 해서 바로 파드를 삭제했다가, 1분 뒤에 다시 트래픽이 몰리면 파드를 새로 띄우는 데 걸리는 시간(Cold Start) 때문에 서비스 장애가 날 수 있거든요. 그래서 보통 스케일 다운 윈도우는 300초(5분) 정도로 넉넉하게 설정하는 것을 권장합니다 [3].

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60 # 일시적인 튀는 현상에 너무 빠르게 반응하지 않도록 설정
      policies:
      - type: Percent
        value: 100
        periodSeconds: 15
    scaleDown:
      stabilizationWindowSeconds: 300 # 트래픽 감소 후 최소 5분은 유지하여 성급한 파드 킬 방지
      policies:
      - type: Percent
        value: 10
        periodSeconds: 60

이 설정을 적용하면 트래픽이 요동쳐도 파드 개수가 훨씬 부드럽게 변하는 것을 확인할 수 있을 거예요.

치명적 안티패턴: HPA와 VPA의 위험한 동거

마지막으로 정말 주의해야 할 점이 하나 있습니다. 바로 HPA(수평 확장)와 VPA(수직 확장)를 동시에 사용하는 거예요. 특히 두 도구가 동일한 메트릭(CPU나 메모리)을 바라보고 있을 때 진짜 재앙이 시작됩니다.

상황을 그려볼까요? CPU 부하가 올라가면 HPA는 “파드 수를 늘리자!”라고 합니다. 동시에 VPA는 “파드 하나당 CPU 할당량을 늘리자!”라고 판단하죠. VPA가 리소스를 늘려주면 개별 파드의 CPU 이용률이 뚝 떨어집니다. 그러면 HPA는 “어? 이제 부하가 없네? 파드 수를 다시 줄여야지”라고 생각합니다.

결국 VPA가 리소스를 올리면 HPA가 파드를 줄이고, 다시 부하가 걸리면 HPA가 늘리고 VPA가 올리는… 이른바 ‘데스 스파이럴’에 빠지며 극심한 플래핑이 발생하게 됩니다 [5].

안전하게 쓰고 싶다면 VPA를 Off 모드(추천 모드)로 설정해서 리소스 가이드라인만 확인하시거나, HPA는 CPU로, VPA는 메모리로 설정하는 식으로 서로 간섭하지 않는 메트릭을 사용해야 합니다.

짚고 넘어갈 한계와 주의점

물론 안정화 윈도우가 만능은 아닙니다. 윈도우를 너무 길게 잡으면 트래픽이 급감했을 때도 파드가 계속 살아있어서 클라우드 비용이 낭비될 수 있어요 [1, 2].

또한, 기본 10% 톨러런스는 일반적인 서비스에는 적당하지만, 밀리초(ms) 단위의 레이턴시에 극도로 민감한 서비스에는 너무 둔감할 수 있습니다. 이런 경우에는 앞서 언급한 K8s 1.33+의 맞춤형 톨러런스 설정을 검토해 보시는 것이 좋습니다 [2].

핵심 요약 (Takeaways)

  • HPA의 핵심은 ‘Requests’ 설정입니다. 분모가 되는 요청 값이 부정확하면 HPA는 엉뚱한 계산 결과를 내놓고 잘못된 스케일링을 수행합니다.
  • **플래핑 방지를 위해 stabilizationWindowSeconds 설정은 필수입니다.** 특히 scaleDown 윈도우를 통해 파드의 최소 생존 시간을 확보하세요.
  • HPA와 VPA를 동일한 CPU/메모리 메트릭으로 동시에 돌리지 마세요. 서로의 작동 방식이 상충하여 시스템을 붕괴시킬 수 있습니다.
  • 메모리 기반 스케일링은 CPU보다 위험합니다. 커널의 메모리 관리 방식과 OOM 킬러의 특성 때문에 예측 가능성이 매우 떨어집니다.
  • K8s 1.33+ 사용 시 맞춤형 톨러런스 설정을 검토하세요. 서비스 특성에 맞는 민감도 조절이 가능해졌습니다.

단순히 “자동으로 늘어난다”는 편리함에만 매몰되면, 어느 날 갑자기 클러스터 자원이 바닥나거나 서비스가 출렁이는 경험을 하게 됩니다. HPA는 설정 한 줄로 끝나는 기능이 아니라, 우리 서비스의 트래픽 패턴과 리소스 특성을 깊게 이해하고 맞춰가는 ‘튜닝’의 과정이라는 점을 꼭 기억하셨으면 좋겠어요.


참고 자료 (References)

1. [plural.sh] Kubernetes HPA: Your Guide to Autoscaling — https://www.plural.sh/blog/hpa-kubernetes-guide 2. [anantacloud.com] Preventing Autoscaler Flapping: Kubernetes HPA Tolerance in Depth — https://www.anantacloud.com/post/preventing-autoscaler-flapping-kubernetes-hpa-tolerance-in-depth 3. [scaleops.com] Kubernetes HPA: Use Cases, Limitations & Best Practices — https://scaleops.com/blog/kubernetes-hpa 4. [stackoverflow.com] Kubernetes HPA is flapping replicas regardless of stabilisation window — https://stackoverflow.com/questions/69768955/kubernetes-hpa-is-flapping-replicas-regardless-of-stabilisation-window 5. [palark.com] Best practices for running apps in Kubernetes. Part 2 — https://palark.com/blog/best-practices-kubernetes-part-2

관련 글 추천

  • https://infobuza.com/2026/06/03/20260603-6zmeih/
  • https://infobuza.com/2026/06/03/20260603-av9d99/

FAQ

HPA에서 리소스 요청(Requests) 설정이 왜 중요한가요?

HPA는 '현재 소비량 / 요청된 리소스' 공식을 통해 이용률을 계산하기 때문입니다. 만약 requests 값을 실제 필요한 양보다 너무 낮게 설정하면, 실제 부하가 낮더라도 이용률이 높게 측정되어 불필요한 스케일 업이 발생할 수 있습니다.

HPA의 '플래핑(Flapping)' 현상이란 무엇인가요?

메트릭이 미세하게 변동할 때 HPA가 이에 너무 민감하게 반응하여 파드를 짧은 간격으로 계속 생성했다가 삭제하기를 반복하는 현상을 말합니다.

플래핑 현상을 방지하기 위한 해결책은 무엇인가요?

안정화 윈도우(Stabilization Window)를 설정하는 것입니다. 특히 스케일 다운(scaleDown) 윈도우를 300초(5분) 정도로 넉넉하게 설정하면, 트래픽 감소 후 즉시 파드를 삭제하지 않고 일정 기간 지켜봄으로써 성급한 파드 삭제를 방지할 수 있습니다.

HPA와 VPA를 동시에 사용할 때 주의할 점은 무엇인가요?

두 도구가 동일한 메트릭(CPU나 메모리)을 바라보게 설정하면 안 됩니다. VPA가 리소스를 늘리면 이용률이 떨어져 HPA가 파드 수를 줄이게 되고, 다시 부하가 걸리면 서로 상충하는 동작을 반복하는 '데스 스파이럴'에 빠질 수 있습니다.

메모리 기반 스케일링이 CPU 기반보다 위험한 이유는 무엇인가요?

CPU는 임계치를 넘으면 쓰로틀링이 걸리며 성능이 느려지지만, 메모리는 한계를 넘는 순간 OOM(Out Of Memory) 킬러가 파드를 즉시 종료시키기 때문에 예측 가능성이 매우 떨어집니다.

보조 이미지 1

보조 이미지 2