← Articles

Busy evenings: scaling your application

Published
Read
6 minutes
Part of
Kubernetes for the rest of us, ch. 04
Tags
Kubernetes · Scaling · Hpa · Docker

Give your application more Pods when things get busy, then let Kubernetes adjust the numbers.

Give your application more Pods when things get busy, then let Kubernetes adjust the numbers.

A quiet evening in the house becomes a party. Two people handling drinks were plenty an hour ago. Now there’s a queue in the hallway. Getting a few more people to help is usually easier than asking those two to move six times faster.

Your application can work the same way. A Deployment can run several copies of a Pod, called replicas. Adding replicas is horizontal scaling. A Service sends traffic to the ready Pods behind it, so callers keep using the same address as the numbers change.

Those extra Pods still need somewhere to run. Scaling a Deployment adds Pods to your existing Nodes. It doesn’t add Nodes or conjure more CPU out of your laptop.

§ 01

Set up a temporary workspace

Use the Docker Desktop cluster from the earlier chapters, with Kubernetes running and kubectl connected to it. The commands below work in macOS Terminal and Windows PowerShell.

For this exercise, I use a small demo from the Kubernetes HPA walkthrough. It does CPU work for each request, which makes changes in load easy to see.

Give it its own namespace:

terminal
kubectl create namespace scaling-demo

Keep the exercise files in a separate folder too. Save this as scaling-demo.yaml:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: scaling-web
  template:
    metadata:
      labels:
        app: scaling-web
    spec:
      containers:
        - name: web
          image: registry.k8s.io/hpa-example
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: 200m
            limits:
              cpu: 500m
---
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: scaling-web
  ports:
    - port: 80
      targetPort: 80

The CPU settings will matter when the autoscaler takes over. For now, apply the file and wait for both replicas to become available:

terminal
kubectl apply -n scaling-demo -f scaling-demo.yaml
kubectl rollout status deployment/web -n scaling-demo
kubectl get pods -n scaling-demo

You should see two Pods with 1/1 in the READY column. All workload commands use -n scaling-demo, so you can leave your current namespace setting alone.

§ 02

Scale it yourself

Ask the Deployment for four replicas:

terminal
kubectl scale deployment/web -n scaling-demo --replicas=4
kubectl get pods -n scaling-demo --watch

Two more Pods should appear and become ready. Press Ctrl+C to stop watching; the Pods keep running.

The Service still has the same name and address. Its selector matches the new Pods too, so they join the pool serving requests. You don’t need another Service for every replica.

Return to two before moving on:

terminal
kubectl scale deployment/web -n scaling-demo --replicas=2

This changes the running Deployment. The replicas value in your saved YAML stays as it was.

§ 03

Let Kubernetes adjust the numbers

A Horizontal Pod Autoscaler, or HPA, watches a metric and adjusts a Deployment’s replica count. Here, that metric is CPU usage. More requests make this demo consume more CPU, giving the HPA a reason to add replicas.

First, it needs measurements.

Install Metrics Server

The HPA needs CPU measurements from Metrics Server. On my MacBook, I had to install it separately in Docker Desktop.

Install it once for your cluster using the commands below. If you already have Metrics Server running and kubectl top pods -n scaling-demo returns CPU and memory figures, skip ahead to “Give the HPA a target”.

This pinned Metrics Server version supports Kubernetes 1.34 and newer, including the 1.36 cluster used in this guide. Check your server version with kubectl version if yours differs.

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

On my Docker Desktop setup, Metrics Server also failed to validate the kubelet certificates. For this local exercise, configure it to skip that check. Save this as metrics-server-local.json:

json
[
  {
    "op": "add",
    "path": "/spec/template/spec/containers/0/args/-",
    "value": "--kubelet-insecure-tls"
  }
]

Apply the configuration and wait for Metrics Server to become ready:

terminal
kubectl patch deployment metrics-server -n kube-system --type=json --patch-file=metrics-server-local.json
kubectl rollout status deployment/metrics-server -n kube-system --timeout=120s

The flag disables verification of kubelet serving certificates. Use it only for this local learning cluster; production needs properly trusted certificates.

Give Metrics Server a minute to collect measurements, then check:

terminal
kubectl top pods -n scaling-demo

You should see CPU and memory figures for the demo Pods. If measurements still fail, or the rollout times out, inspect the logs:

terminal
kubectl logs -n kube-system deployment/metrics-server

Continue once kubectl top pods -n scaling-demo returns measurements. Metrics Server is cluster infrastructure: installing it creates shared resources outside the exercise namespace.

Give the HPA a target

Each Pod requests 200m of CPU. The m stands for millicores: 1000m equals one CPU core, so 200m is one fifth of a core. Our 50% target tells the HPA to aim for half the requested amount (100m) per Pod on average. When usage stays above the target, it adds more Pods to share the work.

Save this as hpa.yaml:

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  minReplicas: 2
  maxReplicas: 6
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50

Apply it:

terminal
kubectl apply -n scaling-demo -f hpa.yaml
kubectl get hpa -n scaling-demo

The HPA now manages the replica count within those bounds. Leave manual scaling alone while it is active. Also avoid reapplying scaling-demo.yaml during the exercise: its replicas: 2 would reset the count until the HPA adjusts it again.

§ 04

Make it a busy evening

Start a load-generator Pod:

terminal
kubectl run load-generator -n scaling-demo --image=busybox:1.37 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://web; done"

The loop runs inside that container, so the same command works from PowerShell or macOS. It sends requests through the demo’s Service.

Watch the HPA:

terminal
kubectl get hpa web -n scaling-demo --watch

The TARGETS column shows measured CPU utilisation alongside the 50% target. As load rises, the replica count should increase above two, up to the maximum of six. It may take a few minutes, and the exact count depends on the load and available resources.

Press Ctrl+C to stop watching, then inspect the Pods:

terminal
kubectl get pods -n scaling-demo

If TARGETS stays <unknown>, check kubectl top pods -n scaling-demo and kubectl describe hpa web -n scaling-demo. If new Pods remain Pending, use kubectl describe pod <pod-name> -n scaling-demo to see whether the cluster lacks capacity. An HPA can request more replicas than your laptop can run.

§ 05

Let the evening settle down

Stop the traffic by deleting the load generator:

terminal
kubectl delete pod load-generator -n scaling-demo
kubectl get hpa web -n scaling-demo --watch

CPU usage should drop, followed by the replica count returning to two. Give it time: the default scale-down stabilisation window is five minutes. That delay helps avoid repeatedly removing and recreating Pods whenever the load briefly dips.

The HPA reacts to measurements. Starting extra Pods also takes time, so autoscaling helps absorb demand without promising an instant response to every traffic spike.

§ 06

Tidy up

Press Ctrl+C to stop watching, then remove the temporary workspace:

terminal
kubectl delete namespace scaling-demo

Deleting the namespace removes its Deployment, Pods, Service, and HPA, including the load generator if you left it running. Your dev and prod resources remain ready for the next exercise.

If you installed Metrics Server, leave it running as shared cluster infrastructure. You can also remove the local exercise folder once you’re finished.