API 서버의 메모리 사용량이 매주 늘어나는데 requests 값은 배포 첫날 그대로입니다. 너무 낮은 requests는 노드 압박을 만들고, 너무 높은 requests는 비용 낭비로 이어집니다. VPA는 실제 사용량을 기반으로 리소스 요청값을 조정하는 방법을 제공합니다.
VPA — 수직 파드 오토스케일러
팀에서 새로운 마이크로서비스를 배포할 때마다 이런 대화가 반복됩니다. "CPU requests를 얼마로 설정해야 하죠?" "일단 250m으로 해보죠." "메모리는요?" "512Mi?" 운이 좋으면 파드가 잘 돌아가지만, 실제 사용량은 requests의 30%에 불과해 자원이 낭비되거나, 반대로 limits에 걸려 파드가 OOM으로 죽기를 반복합니다. VPA(Vertical Pod Autoscaler)는 이 문제를 해결합니다. 파드의 실제 CPU/메모리 사용 패턴을 분석하여 최적의 requests와 limits를 자동으로 조정합니다. HPA가 "파드를 몇 개 띄울지" 결정한다면, VPA는 "각 파드가 얼마나 큰지"를 결정합니다. 이 모듈에서는 VPA의 세 가지 운영 모드와 실무에서 가장 안전하게 사용하는 방법을 다룹니다.
- 1VPA 아키텍처: Recommender, Updater, Admission Controller
- 2Off / Initial / Auto 모드 비교와 사용 시나리오
- 3VPA 설정: minAllowed, maxAllowed, updatePolicy
- 4HPA vs VPA: 선택 기준과 동시 사용 주의사항
- 5추천값 분석으로 현재 리소스 설정 최적화
- 6VPA Auto 모드의 파드 재시작 최소화 전략
kubectl get deployment -n kube-system | grep vpakubectl get crd | grep verticalpodautoscalerkubectl top nodeskubectl apply -f https://github.com/kubernetes/autoscaler/releases/latest/download/vpa-v1-crd-gen.yamlVPA 아키텍처
VPA는 세 개의 컴포넌트로 동작합니다.
┌─────────────────────────────────────────────────────┐
│ VPA 동작 흐름 │
│ │
│ Metrics Server ──► VPA Recommender │
│ (실제 사용량 수집) (분석 + 추천값 계산) │
│ │ │
│ VPA 오브젝트 (추천값 저장) │
│ ├── VPA Updater ─────────────► │
│ │ (파드 Evict + 재시작) 파드 │
│ └── VPA Admission Controller ► │
│ (새 파드 생성 시 requests 주입) │
└─────────────────────────────────────────────────────┘
Off / Initial / Auto 모드 비교
신규 서비스 배포 후 "requests를 얼마로 설정해야 하죠?"라는 질문이 항상 나옵니다. 과도하게 높으면 노드 자원이 낭비되고, 너무 낮으면 OOM이 발생합니다. VPA는 세 가지 모드로 이 문제에 접근합니다. Off 모드는 실제 사용량을 분석해 추천값만 제시하고 직접 건드리지 않습니다. Initial 모드는 파드 시작 시 한 번만 적용합니다. Auto 모드는 지속적으로 모니터링하며 자동으로 파드를 재시작해 최적값을 유지합니다. 처음 VPA를 도입할 때는 Off 모드로 시작해 추천값을 검토한 후 단계적으로 적용하는 것이 안전합니다.
# 1. Off 모드 — 추천만, 적용 안 함 (안전한 시작점)
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: 4Gi
controlledResources: ["cpu", "memory"]

# Off 모드 추천값 확인
kubectl describe vpa api-server-vpa -n production
# Recommendation:
# Container Recommendations:
# Container Name: api
# Lower Bound:
# Cpu: 150m ← 현재보다 낮추는 것이 가능한 하한
# Memory: 256Mi
# Target:
# Cpu: 380m ← VPA가 권장하는 최적값
# Memory: 512Mi
# Upper Bound:
# Cpu: 800m ← 피크 트래픽 대비 상한 추천
# Memory: 1Gi
# Uncapped Target:
# Cpu: 380m ← minAllowed/maxAllowed 제한 없이 계산한 값
# Memory: 512Mi
# 2. Initial 모드 — 파드 시작 시에만 적용, 이후 변경 안 함
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: batch-job-vpa
namespace: production
spec:
targetRef:
apiVersion: batch/v1
kind: Job
name: etl-job
updatePolicy:
updateMode: "Initial" # 파드 생성 시에만 requests 주입
resourcePolicy:
containerPolicies:
- containerName: etl
minAllowed:
cpu: 500m
memory: 1Gi
maxAllowed:
cpu: 8000m
memory: 32Gi
# 3. Auto 모드 — 지속적으로 추천값 적용 (파드 재시작 포함)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: worker-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: background-worker
updatePolicy:
updateMode: "Auto"
# 최소 업데이트 간격 설정 (기본값은 없음)
minReplicas: 2 # 최소 2개 실행 중일 때만 Evict 허용
resourcePolicy:
containerPolicies:
- containerName: worker
minAllowed:
cpu: 200m
memory: 256Mi
maxAllowed:
cpu: 4000m
memory: 8Gi
# limits를 requests 대비 비율로 자동 설정
controlledResources: ["cpu", "memory"]
HPA vs VPA 선택 기준
트래픽이 몰릴 때 파드를 더 띄울지, 아니면 각 파드에 더 많은 자원을 줄지 판단해야 합니다. 잘못 선택하면 HPA가 파드를 늘리는 동안 VPA가 그 파드를 재시작시키는 충돌이 생길 수도 있습니다. HPA는 스테이트리스 서비스의 인스턴스 수를 조정하고, VPA는 수평 확장이 불가능한 서비스의 파드 크기를 조정합니다. 서비스 특성에 맞는 선택이 불필요한 재시작과 과금을 줄입니다.
HPA 선택 ← 수평 확장 가능한 서비스
┌─────────────────────────────────────┐
│ - 스테이트리스 API 서버 │
│ - 웹 프론트엔드 │
│ - 메시지 컨슈머 (파티션 수만큼 확장) │
│ - 파드 수 늘릴 때 선형 처리량 증가 │
└─────────────────────────────────────┘
VPA 선택 ← 수직 확장이 필요한 서비스
┌─────────────────────────────────────┐
│ - 단일 레플리카 DB (Postgres, Redis) │
│ - 상태 저장 서비스 (Stateful Set) │
│ - 배치 Job (더 큰 메모리로 빠르게) │
│ - 사이드카 컨테이너 (리소스 추정 어려움) │
│ - 초기 requests/limits 설정 최적화 │
└─────────────────────────────────────┘
동시 사용 주의:
HPA는 CPU 사용률 기반으로 파드 수를 조정함
VPA는 requests를 올리면 CPU 사용률이 떨어짐
→ HPA가 파드를 줄이고 VPA가 requests를 올리는 사이클 발생 가능
→ 동시 사용 시: VPA에서 CPU 제어 비활성화하고 메모리만 제어
# HPA + VPA 동시 사용 시 — CPU는 HPA, 메모리는 VPA
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-server-vpa-mem-only
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
updatePolicy:
updateMode: "Auto"
resourcePolicy:
containerPolicies:
- containerName: api
# CPU는 VPA가 건드리지 않음 (HPA가 CPU 메트릭으로 파드 수 조정)
controlledResources: ["memory"]
minAllowed:
memory: 256Mi
maxAllowed:
memory: 8Gi
실습: 리소스 낭비 파드 최적화
# 1. 과도하게 큰 requests를 가진 파드 배포
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: over-provisioned
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: over-provisioned
template:
metadata:
labels:
app: over-provisioned
spec:
containers:
- name: app
image: nginx
resources:
requests:
cpu: 2000m # 실제 사용량은 50m 수준
memory: 4Gi # 실제 사용량은 200Mi 수준
limits:
cpu: 4000m
memory: 8Gi
EOF
# 2. 실제 사용량 확인
kubectl top pods -l app=over-provisioned
# NAME CPU(cores) MEMORY(bytes)
# over-provisioned-aaa 52m 185Mi
# over-provisioned-bbb 48m 192Mi
# ← requests의 2.5%만 사용 중
- NAME—조회 대상 리소스 이름이 예상한 대상과 일치하는지 확인합니다.
- STATUS/READY—Running, Ready, Available처럼 정상 상태를 나타내는 필드가 있는지 봅니다.
- RESTARTS/EVENTS—재시작 횟수나 Warning 이벤트가 증가하지 않는지 확인합니다.
# 3. VPA Off 모드로 추천값 수집 (1-7일 데이터 수집 권장)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: over-provisioned-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: over-provisioned
updatePolicy:
updateMode: "Off"
resourcePolicy:
containerPolicies:
- containerName: app
minAllowed:
cpu: 25m
memory: 64Mi
maxAllowed:
cpu: 500m
memory: 1Gi
kubectl apply -f over-provisioned-vpa.yaml
# 24시간 후 추천값 확인
kubectl describe vpa over-provisioned-vpa
# Target:
# Cpu: 75m ← 2000m → 75m으로 조정 추천
# Memory: 250Mi ← 4Gi → 250Mi로 조정 추천
# 4. 추천값을 Deployment에 수동 적용
kubectl patch deployment over-provisioned --type=json -p='[
{"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/cpu", "value": "75m"},
{"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/memory", "value": "250Mi"},
{"op": "replace", "path": "/spec/template/spec/containers/0/resources/limits/cpu", "value": "200m"},
{"op": "replace", "path": "/spec/template/spec/containers/0/resources/limits/memory", "value": "500Mi"}
]'
# 절감 효과
echo "CPU requests 절감: 2000m → 75m (96% 절감)"
echo "Memory requests 절감: 4Gi → 250Mi (94% 절감)"
API 서버에 VPA Auto 모드를 적용했더니 하루에도 수십 번 파드가 재시작되고 있습니다. 재시작 중에는 서비스 중단이 발생하고, HPA로 파드 수를 늘려도 VPA가 계속 재시작시킵니다.
# 증상 확인
kubectl get pods -l app=api-server -w
# api-server-aaa Running → Terminating → Running (1시간에 3-4회 반복)
kubectl describe pod api-server-aaa | grep -A 5 "Events:"
# Warning Evicted VPA Updater evicted pod api-server-aaa
# to apply resource recommendation
# 원인 분석: VPA 추천값 변화 빈도 확인
kubectl describe vpa api-server-vpa
# Last Update Time: 2024-01-15 14:23:01 ← 빈번한 업데이트
# Last Update Time: 2024-01-15 14:18:44
# Last Update Time: 2024-01-15 14:11:22
# ← 5분마다 추천값이 크게 변동
# 트래픽 패턴 분석
kubectl top pods -l app=api-server --containers
# 09:00 api-server cpu: 50m memory: 200Mi (야간 트래픽)
# 12:00 api-server cpu: 800m memory: 1.2Gi (점심 피크)
# 15:00 api-server cpu: 200m memory: 400Mi (오후)
# ← 낮과 밤의 사용량 차이가 16배 → VPA 추천값이 급변
# 해결책 1: Auto → Initial 모드로 변경 (일일 1회 롤링 업데이트 방식)
kubectl patch vpa api-server-vpa --type=merge -p '
{"spec": {"updatePolicy": {"updateMode": "Initial"}}}'
# 해결책 2: Auto 모드 유지 + minAllowed/maxAllowed 범위 좁히기
# (추천값 변화 범위를 제한하여 Evict 빈도 감소)
kubectl patch vpa api-server-vpa --type=merge -p '
{
"spec": {
"resourcePolicy": {
"containerPolicies": [{
"containerName": "api",
"minAllowed": {"cpu": "200m", "memory": "400Mi"},
"maxAllowed": {"cpu": "1000m", "memory": "2Gi"}
}]
}
}
}'
# 범위를 좁히면 추천값의 최대 변화폭이 줄어들어 재시작 빈도 감소
# 해결책 3: Off 모드로 전환 후 수집한 추천값을 주기적으로 수동 적용
# 주 1회 리뷰 → 허용 변화량 초과 시 Deployment 직접 패치
# 해결책 4 (권장): 트래픽 패턴이 불규칙하면 HPA + 고정 resources 조합
# VPA로 '안정 구간'의 적정 requests 찾은 후 HPA로 파드 수 조정
# 1. VPA Off 모드 1주일 → 야간 최솟값 확인 (e.g., cpu 200m, mem 512Mi)
# 2. requests를 해당 값으로 고정
# 3. HPA로 CPU 80% 기준 파드 수 자동 조정
# → 재시작 없이 트래픽 변화에 대응
# 최종 안정 설정 확인
kubectl top pods -l app=api-server
kubectl get hpa api-server-hpa
핵심 교훈: VPA Auto 모드는 안정적인 트래픽 패턴을 가진 서비스에 적합합니다. 낮과 밤의 사용량 차이가 크거나 이벤트성 트래픽이 있는 서비스는 VPA Off 모드로 최적 기준값을 찾은 후 HPA를 조합하는 것이 더 안정적입니다.
시나리오: 신규 서비스의 requests/limits 초기값 설정
백엔드 팀에서 새로운 Java 마이크로서비스를 K8s에 배포하려는데 "requests를 얼마로 설정해야 하죠?"라는 질문이 왔습니다. 부하 테스트 결과가 없어 추정이 어렵습니다.
# 전략: VPA Off 모드로 스테이징에서 1주일 관찰 → 추천값을 프로덕션에 적용
# 1단계: 스테이징 배포 (보수적 초기값)
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: new-java-service
namespace: staging
spec:
replicas: 2
template:
spec:
containers:
- name: app
image: myrepo/java-service:v1.0
resources:
requests:
cpu: 500m # Java는 시작 시 CPU 소비 많음
memory: 512Mi # JVM 힙 기본 256Mi + 오버헤드
limits:
cpu: 2000m
memory: 2Gi
---
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: java-service-vpa
namespace: staging
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: new-java-service
updatePolicy:
updateMode: "Off" # 관찰만
resourcePolicy:
containerPolicies:
- containerName: app
minAllowed:
cpu: 100m
memory: 256Mi
maxAllowed:
cpu: 4000m
memory: 4Gi
EOF
# 2단계: 1주일 후 추천값 확인
kubectl describe vpa java-service-vpa -n staging | grep -A 10 "Recommendation:"
# 3단계: 추천값 기반으로 프로덕션 초기값 설정
# Target CPU: 320m → requests: 350m, limits: 1000m
# Target Memory: 768Mi → requests: 800Mi, limits: 1.5Gi
# 4단계: 프로덕션 배포 후 VPA Off 모드로 계속 모니터링
실무 포인트: Java 서비스는 JVM 웜업으로 초기 CPU/메모리가 높고, 안정화 후 낮아집니다. VPA는 최근 사용 패턴을 가중치 있게 반영하므로 웜업 기간 이후 데이터가 충분히 쌓인 후의 추천값이 더 정확합니다. 스테이징에서 최소 1주일(주중+주말 패턴 포함) 관찰을 권장합니다.
HPA vs VPA 선택 요약
| 기준 | HPA | VPA |
|---|---|---|
| 확장 방향 | 수평 (파드 수) | 수직 (파드 크기) |
| 적합한 서비스 | 스테이트리스 API | DB, Stateful, 배치 Job |
| 반응 속도 | 빠름 (파드 수 즉시 조정) | 느림 (재시작 필요) |
| 서비스 중단 | 없음 | Auto 모드: 재시작 시 순단 |
| 초기 설정 최적화 | 불가 | Off/Initial 모드로 가능 |
| 동시 사용 | 주의 필요 (CPU 충돌) | 메모리만 VPA 제어 시 가능 |
핵심 요약
| 개념 | 명령/설정 | 실무 사용 빈도 |
|---|---|---|
| VPA 조회 | kubectl get vpa -n <ns> | 리소스 최적화 시 |
| 추천값 확인 | kubectl describe vpa <name> | Off 모드 분석 |
| Off 모드 | updateMode: "Off" | 초기 추천값 수집 |
| Initial 모드 | updateMode: "Initial" | 배치 Job, 안정적 적용 |
| Auto 모드 | updateMode: "Auto" | 안정적 트래픽 서비스 |
| 범위 제한 | minAllowed / maxAllowed | 추천값 급변 방지 |
| CPU+메모리 분리 | controlledResources: ["memory"] | HPA 동시 사용 시 |
| 실제 사용량 확인 | kubectl top pods --containers | VPA 전 현황 파악 |