VPA 사용 시 발생할 수 있는 위험 요소

2,017 단어·5 분·원문(.md)

VPA는 Vertical Pod Autoscaler로 pod의 cpu/memory/request/limit 값을 자동으로 조정하는 기능이다.

즉, 애플리케이션의 실제 사용량(HPA가 보는 CPU 사용률 같은 지표 + 메모리 사용 패턴)을 분석해서

이 pod는 지금보다 메모리를 더 많이 줘야한다. CPU는 줄여도 된다. 같은 결론을 내린 후 pod 스펙을 자동으로 업데이트한다.

핵심 동작

  • Recommender: Pod의 자원 사용량을 수집하고 적정 request/limit 값을 계산한다.
  • Updater: 필요시 pod를 재시작해 새로운 자원 스펙을 적용한다.
  • Admission Controller: 새로 만들어지는 Pod에 추천값을 적용한다.

왜 필요한가 #

사람이 request/limit 값을 정확하게 맞추기 어렵다 우리는 보통 휴리스틱하게 자원을 분배하여 맞춰가는 모습을 많이 볼 수 있는데

트래픽 변동, 배치 작업, gc 패턴, 메모리 피크등 대문에 정확한 자원값을 고정적으로 잡는 것은 사실상 불가능하다.

VPA는 실측 기반으로 값을 잡아준다.

Over-provisioning 방지

cpu 메모리를 넉넉하게 잡으면 안정적이지만 노드 자원이 낭비되고 비용이 증가한다.

VPA는 실제 사용치를 기반으로 최소한의 근거있는 Request/Limit 을 유지한다.

Under-provisioning 방지

위와 반대로 메모리 피크를 고려하지 못하면 OOMKilled를 맞을수도 있고 VPA는 메모리 패턴을 분석해

적정 Request를 높여준다.

HPA 상호보완

HPA는 파드 개수를 조절하지만 Request/Limit은 그대로라서 pod 10개를 늘렸는데 실제로 request가

말도 안되게 크게 설정돼 있으면 노드가 부족해 Scale out이 제대로 되지 않는다.

VPA는 바로 이런 비효율을 해결한다.

VPA 사용시 발생할 수 있는 위험 요소 #

운영 환경에서는 이게 제일 중요한데, 대부분의 문제는 Pod 재시작 때문에 발생한다.

  1. Pod 강제 재시작 -> 트래픽 순간 장애(downtime)

updater가 새로운 자원값을 적용하려면 파드 재생성이 필요하다.

무중단 설정(rolling, podDistruptionBudget 최소한의 파드수 유지)을 잘 안해두면 서비스가 순간적으로 끊긴다.

특히 statefulSet은 매우 위험하다.

  1. 메모리 과추천(Over-Recommendation) -> 노드 부족 / 비용 폭증

Recommender는 안전하게 동작하기 위해 메모리 Request를 넉넉하게 추천하는 경향이 있다.

이 경우 다음과 같은 문제가 발생한다.

  • 하나의 파드 메모리 request가 크게 늘어 노드에 스케줄이 불가능하다.
  • 전체 클러스터 자원 압박 -> 비용 증가
  • 불필요한 Scale out
  1. HPA랑 충돌 위험

HPA랑 VPA를 같이 사용하게 되면 아래가 문제다

  • HPA는 cpu 사용률을 기준으로 스케일링 함
  • VPA는 CPU request/limit 자체를 변경함
  • CPU Request가 변경되면 cpu 사용률(usage, request)가 다시 계산되기 때문에 HPA가 오작동하거나 과도하게 파드를 늘릴 수 있음

일반적으로 VPA는 request만 조절, Limit은 그대로 설정을 많이 사용한다.

또는 HPA와 VPA를 동시에 쓰고 싶으면 VPA는 Request만 Update 모드로 둔다.

  1. 순간적 부하 피크에 의해 잘못된 자원값이 추천될 위험

특정 시점의 급격한 메모리 spike가 발생하면, Recommender가 이를 정상 피크로 인식하고 Request를 지나치게 높여버릴 수 있다.

특히 gc pause, 특정 batch job, 비정상적인 장애, 메모리 leak

같은 일시적인 패턴에도 영향을 받는다.

  1. 크리티컬 워크로드에는 예측 불가능성이 증가한다.

VPA 추천값은 통계 기반이라 예측 정확성이 100%가 아니다.

자원이 민감한 워크로드에선 사람이 직접 고정하는게 나을 수 있음.

  1. 노드 스케일링 로직과 충돌 eks cluster auto scaler 환경에서 vpa로 인해 request가 커지면 다음이 동시에 일어남.

  2. 파드가 기존 노드에 스케쥴 안됨

  3. cluster autoscaler가 노드에 스케일 아웃 시도

  4. 노드가 늘어나기전에 vpa가 또 계산

  5. 반복

오토스케일링이 불안정해지고 비용이 증가할 수 있음.

결론 #

vpa는 자원 효율성과 안정성을 크게 올려주는 강력한 기능이지만 재시작 기반이라는 점때문에 조심해야하며

HPA와 공존하면서 충돌때문에 조심해야한다. 그리고 recommender가 너무 과도하게 프로비저닝할수있는 문제가 있다.

모순적이게도 인간의 오버 프로비저닝은 막을 수 있지만, 지가 오버프로비저닝을 spike 트래픽같은것이나 cpu 높은 사용률 배치가 돌아 날수도 있다는것.

그냥 성능 테스트로 파드 수 구하는걸로 하자..

example #

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-app
  labels:
    app: sample-app
spec:
  replicas: 2
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: app
          image: example/sample:latest
          resources:
            requests:
              cpu: "200m"
              memory: "256Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"

롤링 업데이트 디플로이먼트로 다운타임 방지시켜놓기 1

cpu 기반 hpa인데 최대 10개까지 늘어난다.

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

이건 커스텀 hpa고 rps 값을 기반으로 올린다. cpu 변동 영향을 안받음.

qps http latancy 기반이다.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: sample-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: sample-app
  minReplicas: 2
  maxReplicas: 15
  metrics:
    - type: Pods
      pods:
        metric:
          name: http_requests_per_second
        target:
          type: AverageValue
          averageValue: "50"

vpa 예시도 알아보자

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: sample-app-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind:       Deployment
    name:       sample-app
  updatePolicy:
    updateMode: "Auto"  # 자동으로 Request 조정 + 필요한 경우 Pod 재시작
  resourcePolicy:
    containerPolicies:
      - containerName: app
        mode: "Auto"
        controlledValues: "RequestsOnly"   # 핵심: Limit은 변경하지 않도록 강제
        minAllowed:
          cpu: "100m"
          memory: "128Mi"
        maxAllowed:
          cpu: "1"
          memory: "2Gi"

위에처럼 줬다면 pod최소 하나쯤은 살아두게 해두자 podDisruptionBudget 정책을 살려두자

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: sample-app-pdb
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: sample-app

LimitRange로 클러스터 보호겸 네임스페이스 단위로 vpa가 너무 낮거나 높은 request는 추천하지 않도록 제한

apiVersion: v1
kind: LimitRange
metadata:
  name: namespace-limit-range
spec:
  limits:
    - max:
        cpu: "2"
        memory: "4Gi"
      min:
        cpu: "50m"
        memory: "64Mi"
      type: Container
SRE/question/q_7.md