Cluster Autoscaling
A comprehensive guide to Kubernetes cluster autoscaling covering Cluster Autoscaler, HPA, VPA, resource optimization, cost management, and practical implementation strategies for efficient cluster operations.
Autoscaling is essential for running Kubernetes cost-effectively while maintaining performance and availability. It automatically adjusts resources based on demand, ensuring:
- Cost Optimization: Scale down during low demand to save costs
- Performance: Scale up during peak demand to maintain responsiveness
- Availability: Handle traffic spikes without manual intervention
- Resource Efficiency: Right-size resources for workloads
- Operational Simplicity: Reduce manual scaling operations
- Cluster Autoscaler: Scales the number of nodes in the cluster
- HPA (Horizontal Pod Autoscaler): Scales the number of pod replicas
- VPA (Vertical Pod Autoscaler): Scales pod resource requests/limits
Cluster Autoscaler
Horizontal Pod Autoscaler (HPA)
Vertical Pod Autoscaler (VPA)
Metrics Server
Cluster Autoscaler automatically adjusts the number of nodes in your cluster based on the resource requirements of pending pods and node utilization.
# Install Cluster Autoscaler (AWS)
apiVersion: apps/v1
kind: Deployment
metadata:
name: cluster-autoscaler
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: cluster-autoscaler
template:
metadata:
labels:
app: cluster-autoscaler
spec:
serviceAccountName: cluster-autoscaler
containers:
- image: k8s.gcr.io/autoscaling/cluster-autoscaler:v1.28.0
name: cluster-autoscaler
command:
- ./cluster-autoscaler
- --v=4
- --stderrthreshold=info
- --cloud-provider=aws
- --skip-nodes-with-local-storage=false
- --expander=least-waste
- --balance-similar-node-groups
- --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/my-cluster
env:
- name: AWS_REGION
value: us-east-1
resources:
requests:
cpu: 100m
memory: 300Mi
# Install Cluster Autoscaler (GCP)
# --cloud-provider=gce
# --node-group-auto-discovery=mig:name-starting-with=gke-my-cluster
# Install Cluster Autoscaler (Azure)
# --cloud-provider=azure
# --node-group-auto-discovery=azure:resourceGroup=my-rg,vmss=my-vmss
# Cluster Autoscaler Configuration
# Asg tags for AWS:
# - k8s.io/cluster-autoscaler/enabled: true
# - k8s.io/cluster-autoscaler/my-cluster: owned
# Check Cluster Autoscaler status
kubectl get pods -n kube-system -l app=cluster-autoscaler
kubectl logs -n kube-system -l app=cluster-autoscaler --tail=50
# Manual scale-up (to test)
# Create a deployment with many replicas
kubectl scale deployment busybox --replicas=50
# Manual scale-down (remove pods)
kubectl scale deployment busybox --replicas=5
# Cluster Autoscaler logs
# "Scale up" events
kubectl get events | grep -i scaling
kubectl describe pod -n kube-system cluster-autoscaler-pod
- Use node group auto-discovery for dynamic scaling
- Set
--balance-similar-node-groupsfor even distribution - Use
--expanderto control scaling behavior (least-waste, priority) - Enable
--skip-nodes-with-local-storagefor pods with PVCs - Configure pod disruption budgets for critical workloads
- Use
cluster-autoscaler.kubernetes.io/safe-to-evict: falseannotation for critical pods
HPA automatically scales the number of pod replicas based on observed metrics. It supports CPU, memory, and custom metrics.
# Install Metrics Server
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
# Verify Metrics Server
kubectl get pods -n kube-system -l k8s-app=metrics-server
kubectl top nodes
kubectl top pods
# Basic HPA - CPU based
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: app-hpa
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
# HPA with Memory
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: app-hpa-memory
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
# HPA with Custom Metrics (Prometheus)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: app-hpa-custom
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: 100
# HPA with External Metrics (e.g., SQS queue length)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: queue-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: queue-worker
minReplicas: 1
maxReplicas: 20
metrics:
- type: External
external:
metric:
name: sqs_queue_length
target:
type: AverageValue
averageValue: 10
# Check HPA status
kubectl get hpa
kubectl describe hpa app-hpa
# HPA events
kubectl get events --field-selector involvedObject.kind=HorizontalPodAutoscaler
- HPA requires Metrics Server or custom metrics adapter
- Set appropriate min and max replicas
- Use behavior configuration for fine-grained control
- Test HPA with load testing before production
- Monitor HPA decisions to avoid thrashing
VPA automatically adjusts CPU and memory requests/limits for pods based on historical usage patterns. It's particularly useful for stateful applications and workloads with variable resource needs.
# Install Vertical Pod Autoscaler
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
./hack/vpa-up.sh
# Verify VPA installation
kubectl get pods -n kube-system -l app=vpa
# VPA Configuration (Recommender mode)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: app-vpa
namespace: default
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Off" # Off, Initial, Recreate, Auto
resourcePolicy:
containerPolicies:
- containerName: '*'
minAllowed:
cpu: 100m
memory: 100Mi
maxAllowed:
cpu: 2000m
memory: 2Gi
controlledResources: ["cpu", "memory"]
# VPA with Auto mode (recommended for production)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: app-vpa-auto
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Auto" # Automatically apply recommendations
resourcePolicy:
containerPolicies:
- containerName: '*'
minAllowed:
cpu: 100m
memory: 100Mi
maxAllowed:
cpu: 4000m
memory: 4Gi
# VPA with Initial mode (set initial resources)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: app-vpa-initial
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Initial" # Apply only at pod creation
resourcePolicy:
containerPolicies:
- containerName: '*'
minAllowed:
cpu: 200m
memory: 256Mi
# Check VPA recommendations
kubectl describe vpa app-vpa
# View VPA recommendations
kubectl get vpa app-vpa -o yaml | grep -A 10 recommendation
# VPA with Prometheus (for better recommendations)
# Configure VPA to use Prometheus as metric source
# --metric-provider=prometheus --prometheus-address=http://prometheus:9090
- HPA: Scales the number of pods (horizontal)
- VPA: Scales the resources per pod (vertical)
- Use HPA for stateless applications with variable load
- Use VPA for stateful applications or when pods can't be easily replicated
- Use both cautiously - they can conflict
# HPA Behavior Configuration (Kubernetes 1.18+)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: app-hpa-behavior
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # Wait 5 minutes before scaling down
policies:
- type: Percent
value: 10
periodSeconds: 60 # Max 10% scale down per minute
- type: Pods
value: 2
periodSeconds: 60
selectPolicy: Min
scaleUp:
stabilizationWindowSeconds: 0 # Scale up immediately
policies:
- type: Percent
value: 100
periodSeconds: 15 # Double pods every 15 seconds
- type: Pods
value: 4
periodSeconds: 15
selectPolicy: Max
# Cluster Autoscaler expanders
# --expander=least-waste (default)
# --expander=random
# --expander=most-pods
# --expander=price
# --expander=priority
# Priority expander example
apiVersion: v1
kind: ConfigMap
metadata:
name: cluster-autoscaler-priority-expander
namespace: kube-system
data:
priorities: |
10:
- .*-spot.*
5:
- .*-ondemand.*
1:
- .*-reserved.*
- Use stabilization windows to prevent thrashing
- Set different policies for scale-up (faster) and scale-down (slower)
- Use percent-based policies for large deployments
- Test scaling behavior with load testing
- Monitor scaling events for anomalies
Spot Instances
Right-Sizing
Scheduled Scaling
Utilization Monitoring
# KEDA ScaledObject with schedule
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: scheduled-scaling
spec:
scaleTargetRef:
name: my-app
kind: Deployment
minReplicaCount: 0
maxReplicaCount: 10
triggers:
- type: cron
metadata:
timezone: America/New_York
start: 0 9 * * *
end: 0 17 * * *
desiredReplicas: "5"
# Cluster Autoscaler with spot instances
# AWS ASG with mixed instances policy
# Use node group with spot price cap
# Configure expander priority for spot
# Kubecost for cost monitoring
helm repo add kubecost https://kubecost.github.io/cost-analyzer
helm repo update
helm install kubecost kubecost/cost-analyzer --namespace kubecost --create-namespace
# Check cost report
kubectl port-forward -n kubecost svc/kubecost-cost-analyzer 9090:9090
# Resource optimization tips
# - Use KubeCost to identify waste
# - Right-size with VPA recommendations
# - Use spot instances for fault-tolerant workloads
# - Implement HPA for demand-based scaling
# - Use committed use discounts for steady workloads
- Use spot instances for stateless, fault-tolerant workloads
- Right-size resource requests with VPA recommendations
- Schedule scaling for predictable traffic patterns
- Monitor and alert on cost anomalies
- Use committed use discounts for baseline capacity
- Regularly review and optimize resource usage
| Feature | Cluster Autoscaler | HPA | VPA | KEDA |
|---|---|---|---|---|
| Scaling Level | Nodes | Pods (replicas) | Pods (resources) | Pods (replicas) |
| Metrics | Pending pods, utilization | CPU, Memory, Custom | Historical usage | Events, Queues, Schedules |
| Scale-up Speed | Slow (minutes) | Fast (seconds) | Slow (hours) | Fast |
| Scale-down | Yes | Yes | Yes | Yes |
| Use Case | Infrastructure | Stateless apps | Stateful apps | Event-driven |
| Cost Impact | High | Medium | Medium | Medium |
| Complexity | Medium | Low | Medium | Medium |
stabilizationWindowSeconds). This prevents rapid scale-down and reduces thrashing. Use scaleDown policies to control the rate of scale-down.Autoscaling is essential for running Kubernetes cost-effectively while maintaining performance. Implement these best practices to optimize resource usage, reduce costs, and ensure your applications scale seamlessly with demand.