Amazon EKS — HPA and VPA

2 minute read

HPA (Horizontal Pod Autoscaler) scales the number of pod replicas. VPA (Vertical Pod Autoscaler) adjusts the resource requests of individual pods. Both are essential for efficient scaling on EKS.


Metrics Server

Both HPA and VPA require the Metrics Server:

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

# Verify
kubectl get pods -n kube-system -l k8s-app=metrics-server
kubectl top nodes
kubectl top pods

Horizontal Pod Autoscaler (HPA)

Basic HPA (CPU-Based)

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Pods
          value: 4
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 10
          periodSeconds: 60

HPA with Custom Metrics (Prometheus Adapter)

Install Prometheus Adapter:

helm install prometheus-adapter prometheus-community/prometheus-adapter \
  --namespace monitoring \
  --set prometheus.url=http://kube-prometheus-kube-prome-prometheus.monitoring.svc \
  --set prometheus.port=9090

HPA using custom metric:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: queue-processor-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: queue-processor
  minReplicas: 1
  maxReplicas: 50
  metrics:
    - type: Pods
      pods:
        metric:
          name: messages_in_queue
        target:
          type: AverageValue
          averageValue: "10"

HPA Commands

# Create HPA imperatively
kubectl autoscale deployment web-app -n production --min=2 --max=20 --cpu-percent=70

# Check HPA status
kubectl get hpa -n production
kubectl describe hpa web-app-hpa -n production

# Watch scaling events
kubectl get hpa -w

Vertical Pod Autoscaler (VPA)

VPA analyzes pod resource usage and recommends (or auto-applies) optimal CPU and memory requests.

Install VPA

git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
./hack/vpa-up.sh

VPA Configuration

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: web-app-vpa
  namespace: production
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  updatePolicy:
    updateMode: "Off"    # Recommend only — don't auto-apply
  resourcePolicy:
    containerPolicies:
      - containerName: app
        minAllowed:
          cpu: 50m
          memory: 64Mi
        maxAllowed:
          cpu: 2
          memory: 4Gi

VPA Update Modes

Mode Behavior Use Case
Off Recommendations only — no changes Production review
Initial Set requests at pod creation only New deployments
Auto Update requests on running pods (restarts pod) Non-critical workloads

Check Recommendations

kubectl describe vpa web-app-vpa -n production

Output:

Recommendation:
  Container Recommendations:
    Container Name: app
    Lower Bound:    Cpu: 50m, Memory: 128Mi
    Target:         Cpu: 250m, Memory: 512Mi     # Use this
    Upper Bound:    Cpu: 1, Memory: 2Gi
    Uncapped Target: Cpu: 250m, Memory: 512Mi

HPA + VPA Interaction

Scenario Works? Notes
HPA on CPU + VPA on memory Yes Different metrics, no conflict
HPA on CPU + VPA on CPU No Both try to control CPU — conflicts
HPA on custom metric + VPA on CPU/memory Yes Different scopes
VPA in Off mode + HPA Yes VPA only recommends, doesn’t interfere

Best practice: Use VPA in Off mode for recommendations, then manually update Deployment resource requests. Use HPA for scaling replicas.


Scaling Behavior Tuning

Aggressive Scale-Up (Latency-Sensitive)

behavior:
  scaleUp:
    stabilizationWindowSeconds: 0        # Scale up immediately
    policies:
      - type: Percent
        value: 100                       # Double replicas per period
        periodSeconds: 15

Conservative Scale-Down (Cost vs Stability)

behavior:
  scaleDown:
    stabilizationWindowSeconds: 600      # Wait 10 min before scale down
    policies:
      - type: Pods
        value: 1                         # Remove max 1 pod per period
        periodSeconds: 120

Best Practices

  1. Always set resource requests — HPA uses requests for utilization calculation
  2. Use behavior to control flapping — stabilization windows prevent rapid scale up/down
  3. Use VPA in Off mode to right-size, then apply recommendations manually
  4. Don’t use HPA and VPA on the same metric — they’ll fight each other
  5. Start conservative — scale down slowly, scale up aggressively
  6. Monitor HPA events — kubectl describe hpa shows scaling decisions

Updated: