K8s VPA 리소스 자동 튜닝

K8s VPA란? 왜 필요한가

Kubernetes 클러스터를 운영하다 보면 Pod의 CPU·메모리 requests/limits를 얼마로 설정해야 할지 고민됩니다. 너무 크게 잡으면 리소스 낭비, 너무 작게 잡으면 OOMKill이나 CPU 쓰로틀링이 발생합니다. Vertical Pod Autoscaler(VPA)는 실제 사용량을 관측하여 최적의 requests/limits를 자동으로 추천·적용해주는 컨트롤러입니다.

HPA(Horizontal Pod Autoscaler)가 Pod 수를 늘리고 줄이는 수평 확장이라면, VPA는 개별 Pod의 리소스 할당량을 조정하는 수직 확장입니다. 두 가지를 적절히 조합하면 비용 효율과 안정성을 동시에 달성할 수 있습니다.

VPA 아키텍처: 3가지 컴포넌트

VPA는 세 개의 독립적인 컴포넌트로 구성됩니다:

  • Recommender — Metrics Server(또는 Prometheus)에서 과거 사용량 데이터를 수집하고, 히스토그램 기반 알고리즘으로 최적 requests/limits를 계산합니다.
  • Updater — 현재 실행 중인 Pod의 requests가 추천 범위를 크게 벗어나면 해당 Pod를 evict하여 새 값이 적용되도록 합니다.
  • Admission Controller — 새로 생성되는 Pod에 추천된 requests/limits를 mutating webhook으로 주입합니다.
# VPA 컴포넌트 흐름도
Metrics Server → Recommender → VPA 오브젝트(추천값 저장)
                                    ↓
                              Updater (기존 Pod evict)
                              Admission Controller (새 Pod에 주입)

VPA 설치 방법

VPA는 Kubernetes 기본 내장이 아니므로 별도 설치가 필요합니다. 공식 autoscaler 레포지토리에서 제공하는 스크립트를 사용합니다:

# 공식 레포 클론
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler

# 설치 (Recommender + Updater + Admission Controller)
./hack/vpa-up.sh

# 설치 확인
kubectl get pods -n kube-system | grep vpa
# vpa-admission-controller-xxx   1/1  Running
# vpa-recommender-xxx            1/1  Running
# vpa-updater-xxx                1/1  Running

Helm 차트로도 설치할 수 있습니다:

helm repo add fairwinds-stable https://charts.fairwinds.com/stable
helm install vpa fairwinds-stable/vpa 
  --namespace vpa --create-namespace

VPA 오브젝트 작성: updateMode 이해

VPA 리소스의 핵심은 updatePolicy.updateMode입니다. 세 가지 모드를 지원합니다:

모드 동작 Pod 재시작 사용 시나리오
Off 추천만 제공, 적용 안함 없음 운영 초기 관측용
Initial Pod 생성 시에만 적용 없음(기존 Pod 유지) 롤링 업데이트 시 반영
Auto 추천값 자동 적용 있음(evict 발생) 완전 자동화

운영 팁: 처음에는 반드시 Off 모드로 시작하여 추천값이 합리적인지 검증한 뒤, Auto로 전환하세요.

실전 예제: Spring Boot 앱에 VPA 적용

다음은 Spring Boot 기반 API 서버에 VPA를 적용하는 전체 매니페스트입니다:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-server
  template:
    metadata:
      labels:
        app: api-server
    spec:
      containers:
      - name: api
        image: myregistry/api-server:v2.1.0
        resources:
          requests:
            cpu: "200m"
            memory: "256Mi"
          limits:
            cpu: "1000m"
            memory: "1Gi"
---
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
      minAllowed:
        cpu: "100m"
        memory: "128Mi"
      maxAllowed:
        cpu: "2000m"
        memory: "2Gi"
      controlledResources: ["cpu", "memory"]

VPA 추천값 확인하기

VPA를 Off 모드로 배포한 뒤, kubectl describe vpa로 추천값을 확인할 수 있습니다:

kubectl describe vpa api-server-vpa -n production

# 출력 예시
Status:
  Recommendation:
    Container Recommendations:
    - Container Name: api
      Lower Bound:
        Cpu:     150m
        Memory:  200Mi
      Target:                    # ← 이 값이 최적 추천
        Cpu:     350m
        Memory:  450Mi
      Uncapped Target:
        Cpu:     350m
        Memory:  450Mi
      Upper Bound:
        Cpu:     800m
        Memory:  900Mi

각 필드의 의미:

  • Target — VPA가 추천하는 최적 requests 값
  • Lower Bound — 최소한 이 정도는 할당해야 안정적
  • Upper Bound — 피크 시 필요할 수 있는 상한
  • Uncapped Target — minAllowed/maxAllowed 제한 무시한 순수 추천값

VPA + HPA 동시 사용 전략

VPA와 HPA를 같은 CPU 메트릭으로 동시 사용하면 충돌이 발생합니다. VPA가 requests를 올리면 HPA의 사용률(%)이 낮아져 스케일인하고, 다시 부하가 오르면 HPA가 스케일아웃하는 진동 현상이 생깁니다.

해결 전략은 메트릭을 분리하는 것입니다:

# VPA: 메모리만 관리
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: api-server-vpa
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api-server
  updatePolicy:
    updateMode: "Auto"
  resourcePolicy:
    containerPolicies:
    - containerName: api
      controlledResources: ["memory"]   # 메모리만!
      minAllowed:
        memory: "128Mi"
      maxAllowed:
        memory: "2Gi"
---
# HPA: CPU 기반 수평 확장
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-server-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api-server
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

이렇게 하면 CPU는 HPA가 수평으로, 메모리는 VPA가 수직으로 각각 독립적으로 관리합니다. HPA 오토스케일링 심화 가이드도 함께 참고하세요.

운영 시 주의사항과 베스트 프랙티스

1. Pod 재시작 관리

Auto 모드에서 VPA는 추천값과 현재 값의 차이가 클 때 Pod를 evict합니다. PodDisruptionBudget(PDB)을 반드시 설정하여 동시에 너무 많은 Pod가 재시작되지 않도록 보호하세요:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: api-server-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: api-server

2. minAllowed/maxAllowed 반드시 설정

제한 없이 VPA를 사용하면 노드 리소스를 초과하는 추천이 나올 수 있습니다. 반드시 resourcePolicy로 상·하한을 지정하세요.

3. JVM 기반 앱 특별 주의

Java/Spring 앱은 -Xmx 힙 설정이 고정되어 있으면 VPA가 메모리 requests를 줄여도 실제 사용량이 줄지 않습니다. 컨테이너 메모리 인식 JVM 옵션을 사용하세요:

env:
- name: JAVA_OPTS
  value: >-
    -XX:+UseContainerSupport
    -XX:MaxRAMPercentage=75.0
    -XX:InitialRAMPercentage=50.0

4. Goldilocks로 VPA 대시보드 구축

Fairwinds의 Goldilocks는 네임스페이스의 모든 Deployment에 자동으로 VPA(Off 모드)를 생성하고, 추천값을 웹 UI로 보여줍니다:

helm install goldilocks fairwinds-stable/goldilocks 
  --namespace goldilocks --create-namespace

# 네임스페이스 레이블 추가
kubectl label ns production goldilocks.fairwinds.com/enabled=true

# 대시보드 접속
kubectl port-forward -n goldilocks svc/goldilocks-dashboard 8080:80

5. 비용 절감 효과 측정

VPA 도입 전후 리소스 사용량을 비교하여 비용 절감 효과를 정량화하세요:

# 현재 requests 합계 vs 실제 사용량 비교
kubectl top pods -n production --containers
kubectl get vpa -n production -o jsonpath='{.items[*].status.recommendation}'

VPA 도입 로드맵

실제 운영 환경에 VPA를 도입할 때는 단계적으로 접근하세요:

  1. 1주차: Off 모드로 전체 워크로드에 VPA 배포, Goldilocks 대시보드 설정
  2. 2주차: 추천값 분석, minAllowed/maxAllowed 튜닝
  3. 3주차: Staging에서 Auto 모드 테스트, PDB 설정 검증
  4. 4주차: Production에 Initial 모드 적용 → 안정 확인 후 Auto 전환

마치며

VPA는 “리소스 requests를 얼마로 설정하지?”라는 영원한 고민을 데이터 기반으로 해결해줍니다. Off 모드의 추천값만 활용해도 리소스 최적화에 큰 도움이 되며, HPA와 메트릭을 분리하여 조합하면 수평·수직 확장을 동시에 달성할 수 있습니다. 단, Pod 재시작 영향과 JVM 같은 런타임 특성을 고려한 세심한 설정이 필요합니다.

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