Scaling up and down
More copies, fewer copies, on demand.
A lunch counter at noon has six people working behind it. By three in the afternoon, two. Same menu, same kitchen, same griddle. The only thing that changed is how many pairs of hands are on the line. Scaling in Kubernetes is that idea, boiled down to a single number you can type.
You built a Deployment a couple of lessons back. A Deployment is the Kubernetes object that runs your app and keeps an agreed number of copies alive. Each copy is a Pod, the smallest thing Kubernetes runs (one Pod is one wrapped-up running instance of your app). The number of copies you asked for has a name: the replica count. Scaling means changing that number, then letting Kubernetes add or delete Pods until what is running matches what you asked for.
Turning the number up and down
There are two ways to change the count. The quick way is one command. You name the Deployment, name the new number, and Kubernetes handles the rest. Say hello is running 3 copies and the queue outside says you need 5.
kubectl scale deployment/hello --replicas=5
deployment.apps/hello scaled
That word scaled means one thing only: Kubernetes wrote down the new target. It says nothing about whether the Pods are up and serving traffic. So go and look. The -l app=hello part narrows the list to Pods carrying the label app=hello, which is how you ask for this app's copies and nothing else.
kubectl get pods -l app=hello
NAME READY STATUS RESTARTS AGEhello-7d9c6f8b4-2xk9p 1/1 Running 0 6mhello-7d9c6f8b4-8ftll 1/1 Running 0 6mhello-7d9c6f8b4-c4v2m 1/1 Running 0 6mhello-7d9c6f8b4-lp7qz 1/1 Running 0 15shello-7d9c6f8b4-wm5rn 1/1 Running 0 15s
Five Pods. The columns read like this. READY 1/1 means the one container inside that Pod is up. STATUS Running is what you want to see. AGE is where the story sits: three Pods have been alive 6 minutes, two for 15 seconds. Those two youngsters are the copies Kubernetes started the moment you asked for 5.
Here is the machinery under that command. A Deployment does not handle Pods itself. It owns a ReplicaSet, a small controller with one job: keep an agreed number of Pods running. You moved that number to 5. The ReplicaSet counted 3, saw it was two short, and started two more. Scale the other way and it deletes the surplus instead. Compare what you want against what you have, close the gap, repeat. That loop is the whole of scaling.
The quick command comes with a catch, and it is better to meet it here than at 2am. Your hello.yaml file on disk still says replicas: 3. (YAML is the plain-text file format Kubernetes uses to describe things.) Run apply against that file again and the count drops straight back to 3, because making the cluster match the file is exactly what apply is for. That is the declarative habit: the file is the source of truth, and the cluster gets dragged into line with it. So for a change you want to keep, edit the file. Here is the full Deployment with the count moved to 5.
apiVersion: apps/v1kind: Deploymentmetadata:name: hellolabels:app: hellospec:replicas: 5selector:matchLabels:app: hellotemplate:metadata:labels:app: hellospec:containers:- name: helloimage: nginxdemos/hello:plain-textports:- containerPort: 80resources:requests:cpu: 100mlimits:cpu: 250m
kubectl apply -f hello.yaml
deployment.apps/hello configured
The word configured tells you the Deployment was already there and Kubernetes edited it to match your file.
Now a wall you will hit sooner or later. A replica count is a number you type, but every copy has to land on a real machine. Ask for more than your cluster has room for and Kubernetes will not argue with you. It writes the number down and then quietly fails to place them all.
kubectl scale deployment/hello --replicas=8
deployment.apps/hello scaled
The same cheerful scaled line, because all the command did was record a target. Now look at the Pods.
kubectl get pods -l app=hello
NAME READY STATUS RESTARTS AGEhello-7d9c6f8b4-2xk9p 1/1 Running 0 18mhello-7d9c6f8b4-8ftll 1/1 Running 0 18mhello-7d9c6f8b4-c4v2m 1/1 Running 0 18mhello-7d9c6f8b4-lp7qz 1/1 Running 0 12mhello-7d9c6f8b4-wm5rn 1/1 Running 0 12mhello-7d9c6f8b4-q4h2n 1/1 Running 0 22shello-7d9c6f8b4-rb8dz 0/1 Pending 0 22shello-7d9c6f8b4-t7k2s 0/1 Pending 0 22s
Six copies are Running and two are sitting at Pending. Pending means the Pod exists on paper but no machine has been picked to run it. To find out why, describe one of the stuck Pods and read the Events at the bottom of the output.
kubectl describe pod hello-7d9c6f8b4-rb8dz
Events:Type Reason Age From Message---- ------ ---- ---- -------Warning FailedScheduling 24s default-scheduler 0/1 nodes are available: 1 Insufficient cpu. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.
The scheduler is the part of Kubernetes that picks a machine for each Pod. It walked every node, found none with a spare 100m of CPU (the amount each copy requests), and stopped there. The Pod is not broken. It is unplaced, and it will keep waiting. You clear it by making room: add a node, lower the CPU request, or scale back down now the rush is over.
kubectl scale deployment/hello --replicas=2
deployment.apps/hello scaled
Scaling changes the replica count on a Deployment (or on a ReplicaSet directly). It works sideways, which people call horizontal: you get more copies, never a bigger copy. Making one Pod bigger means raising its requests and limits, which is vertical sizing and a lesson of its own. For a stateless web app, reach for more copies when traffic spikes rather than pouring more CPU into a single one.
Autoscaling, which you set up at the end of this lesson, is the same knob with a machine turning it, driven by measurements instead of by your judgement. Typing kubectl scale by hand is still what you do in an incident when you need capacity in the next thirty seconds. Write down why you scaled, in the ticket or the incident channel, so the next person on call does not inherit a replica count of 12 with no story attached to it.
One warning for flash-sale day. Adding Pods to your app adds nothing to the database sitting behind it. If 200 connections already have that database on its knees, going from 6 copies to 20 moves the outage one hop down the chain instead of ending it. Scale the app, then go and watch how saturated everything it depends on is.
Try this
Do this on your own cluster. Scale a Deployment up, then back down, and watch the Pod count follow. Keep an eye on Ready against Desired while the new Pods come up.
$ kubectl create deployment scale --image=nginx:1.27 --replicas=1deployment.apps/scale created$ kubectl scale deployment/scale --replicas=4deployment.apps/scale scaled$ kubectl get deploy,pods -l app=scaleNAME READY UP-TO-DATE AVAILABLE AGEdeployment.apps/scale 4/4 4 4 20sNAME READY STATUS RESTARTS AGEpod/scale-… 1/1 Running 0 20s... (4 pods)$ kubectl scale deployment/scale --replicas=2deployment.apps/scale scaled$ kubectl get pods -l app=scaleNAME READY STATUS RESTARTS AGEscale-… 1/1 Running 0 45sscale-… 1/1 Running 0 45sscale-… 0/1 Terminating 0 25s$ kubectl delete deployment scaledeployment.apps "scale" deleted
Takeaway
kubectl scale moves one number: how many copies of your app run. It buys you more Pods, never a roomier Pod. Use it on purpose during an incident, trust the Ready count rather than the word scaled, and expect whatever your app talks to be the next thing that buckles.
Let the cluster scale for you
Turning the dial by hand is fine until you have to do it at every hour of the day. A thermostat solves the same problem for heating. You do not stand next to the boiler flipping a switch. You set a target temperature and the thermostat holds it, firing up when the room gets cold and easing off when it warms. A Horizontal Pod Autoscaler (HPA for short, the controller that changes your replica count for you) is a thermostat for Pods. You hand it a target, a floor and a ceiling, and it adds or removes replicas on its own to hold that target.
The one below watches CPU use across your Pods. CPU means the processor, so this is a reading of how hard your app is working. The HPA tries to hold that average near 60 percent, and it is fenced in on both sides: never fewer than 2 Pods, never more than 10. It is a complete file you can apply as it stands, and it aims at the hello Deployment by name.
apiVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata:name: hellospec:scaleTargetRef:apiVersion: apps/v1kind: Deploymentname: hellominReplicas: 2maxReplicas: 10metrics:- type: Resourceresource:name: cputarget:type: UtilizationaverageUtilization: 60
kubectl apply -f hello-hpa.yaml
horizontalpodautoscaler.autoscaling/hello created
kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGEhello Deployment/hello cpu: 12%/60% 2 10 2 45s
Read the TARGETS column as current over goal. Here cpu: 12%/60% says the average Pod is using 12 percent of the CPU it asked for, against a goal of 60. Load is quiet, so the HPA parks on its floor of 2 Pods. Send real traffic at the app and that first number climbs. Once it crosses 60, the HPA starts adding replicas, up to its ceiling of 10, then takes them away again as the traffic drains. That is real elasticity: you run the capacity today needs, not the capacity one busy Friday a year might need.
Two things have to be true already or the HPA will sit there doing nothing at all. Your Pods need a CPU request (the resources.requests.cpu line in hello.yaml), because 60 percent has to be 60 percent of something. And your cluster needs metrics-server, a small add-on that reports each Pod's CPU and memory use. Without the request the HPA has no baseline to divide by. Without metrics-server it cannot see usage in the first place. Both are already in the files above.
You have a pool of copies sharing the work now, and their names change every time that number moves. Nobody calling your app should have to keep track of that. What they need is one steady address sitting in front of all the copies, which is exactly what a Service is, and setting one up is next.