Lesson 13 HA & Autoscaling
Theme:
Lesson 13 · Slide 1

Lesson 13 — HA & Autoscaling

Scale the API under load. Survive node drains. Blue/green deploys with one Helm value.

Cloud Infra & DevOps Module 5 HPA PDB Blue/Green
Lesson 13 · Slide 2

The Module-5 arc

L12 made the app observable

We can now see latency, errors, traffic, saturation. The four golden signals are live in Grafana. Alerts fire before users notice.

L13 uses those signals to react

HPA scales replicas when traffic climbs. PDB protects capacity during node drains. Topology spread keeps replicas off the same host. Blue/green flips traffic with one Helm value.

Lesson 13 · Slide 3

HPA in 30 seconds

HorizontalPodAutoscaler watches a metric and adjusts the Deployment’s replicas between minReplicas and maxReplicas to keep the metric at target. Two metric sources:

HPA is a control loop. It doesn’t know what your service does — it knows one number and one knob.
Lesson 13 · Slide 4

HPA on CPU (the boring default)

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: api }
spec:
  scaleTargetRef: { kind: Deployment, name: url-shortener-api }
  minReplicas: 2
  maxReplicas: 8
  metrics:
  - type: Resource
    resource: { name: cpu, target: { type: Utilization, averageUtilization: 70 } }

Target 70% CPU utilisation, scale 2–8. Requires requests.cpu set on the Deployment — otherwise “utilisation” is undefined and the HPA does nothing.

Lesson 13 · Slide 5

HPA on a custom metric (the right default)

  metrics:
  - type: Pods
    pods:
      metric: { name: http_requests_per_second }
      target: { type: AverageValue, averageValue: "100" }

“100 rps per pod” reads directly from real traffic. Requires the prometheus-adapter to expose http_requests_per_second via the Kubernetes metrics API. CPU lags traffic by 30–60 s on bursty workloads; request rate doesn’t.

Best of both: include CPU and request-rate metrics in the same HPA. Kubernetes takes the max desired-replicas across all metrics.
Lesson 13 · Slide 6

PDB — protect capacity during drains

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: { name: api }
spec:
  minAvailable: 50%
  selector: { matchLabels: { app: api } }

When a node drains, kubectl drain honours the PDB and refuses to evict pods that would drop available count below 50%. Without a PDB, a rolling node upgrade can take you to 0 replicas.

Common mistake: minReplicas: 1 + PDB minAvailable: 1 — drain blocks forever because evicting the only pod violates the budget.
Lesson 13 · Slide 7

topologySpreadConstraints — don’t co-locate replicas

topologySpreadConstraints:
- maxSkew: 1
  topologyKey: topology.kubernetes.io/zone
  whenUnsatisfiable: ScheduleAnyway
  labelSelector: { matchLabels: { app: api } }

Prefer to put pods in different AZs; if not possible, still schedule (ScheduleAnyway vs DoNotSchedule). One zone failure no longer eats all your replicas.

Combine with a second constraint on kubernetes.io/hostname for node-level spread. Both constraints use ScheduleAnyway so they work on a single-node kind cluster.
Lesson 13 · Slide 8

Blue/green via Helm values

# values-prod.yaml
api:
  image:
    repository: ghcr.io/me/url-shortener-api
    tag: blue           # flip to green for cut-over
service:
  api:
    selector: { app: api, color: blue }   # also flipped

Two Deployments (api-blue, api-green), one Service whose selector picks one. The flip is --set api.color=green. Rollback is --set api.color=blue. No Ingress change; no DNS change.

Lesson 13 · Slide 9

Picking minReplicas and maxReplicas

HPA decides how many pods. Cluster autoscaler decides how many nodes. They work at different layers and must both be tuned.
Lesson 13 · Slide 10

requests vs limits for the HPA

HPA reads CPU utilisation = used / requests. If requests.cpu = 100m and the pod uses 500m, utilisation = 500% — HPA scales out aggressively. If requests.cpu = 1000m and the pod uses 500m, utilisation = 50% — HPA may scale in.

Tune requests to the steady-state usage, then HPA’s averageUtilization is meaningful.

Don’t set limits.cpu below requests.cpu. Many platforms ban limits.cpu outright — it causes CPU throttling under burst even when nodes have spare capacity.
Lesson 13 · Slide 11

The L13 diff

sample-app/helm/templates/
  api-deployment.yaml    # + topologySpreadConstraints, + color label, split blue/green
  worker-deployment.yaml # + topologySpreadConstraints
  hpa.yaml               # NEW: HPA on api, CPU + request-rate metrics
  pdb.yaml               # NEW: PDB minAvailable=50% on api + worker
  api-service.yaml       # selector includes color: {{ .Values.api.color }}
sample-app/helm/values.yaml
  api: { color: blue, minReplicas: 2, maxReplicas: 8 }
sample-app/helm/values-prod.yaml
  api: { color: blue }   # flip here for cut-over
Lesson 13 · Slide 12

What we’re not doing

Lesson 13 · Slide 13

Production checklist

Before claiming HA

  • minReplicas: ≥ 2
  • PDB present for every stateless workload
  • topologySpreadConstraints against zone and hostname
  • requests.cpu set realistically
  • HPA behavior.scaleUp bounded to ≤ 100% per 60 s (no stampedes)
  • HPA behavior.scaleDown.stabilizationWindowSeconds: 300 (no flapping)
  • Blue/green Deployments named with explicit color labels
Lesson 13 · Slide 14

Recap & what’s next

Coming up: Lesson 14 — ArgoCD: stop running helm upgrade by hand; git push is the only deploy. Drift detection. Sync waves. App-of-Apps.