Introduction
The scheduler needs resource requests to place Pods, the kubelet enforces limits, and the HorizontalPodAutoscaler needs metrics plus a meaningful utilization baseline. These controls form one capacity-management workflow.
You will configure requests and limits, inspect live usage, create an HPA, generate load, and observe replica changes.
These settings form a dependency chain. The CPU request establishes a baseline, metrics-server reports recent usage, and HPA compares the two to decide whether replica count should change. Skipping the request would make percentage-based CPU utilization meaningless.
Set Requests and Limits
In this step, deploy the Kubernetes HPA example with explicit resources.
A request is the amount used for scheduling and as the autoscaling denominator. A limit is the maximum enforced for the container. CPU uses cores or millicores (100m is 0.1 core); memory uses byte units such as Mi.
cd /home/labex/project/resource-autoscaling
kubectl create namespace practice-capacity
kubectl create deployment cpu-demo -n practice-capacity --image=registry.k8s.io/hpa-example:latest --replicas=1
Add the resource contract to the generated Deployment. kubectl set resources updates the Pod template and therefore triggers a rollout:
kubectl set resources deployment/cpu-demo -n practice-capacity --requests=cpu=100m,memory=32Mi --limits=cpu=200m,memory=64Mi
kubectl expose deployment cpu-demo -n practice-capacity --port=80 --target-port=80
kubectl rollout status deployment/cpu-demo -n practice-capacity --timeout=60s
kubectl get deployment cpu-demo -n practice-capacity -o jsonpath='{.spec.template.spec.containers[0].resources}{"\n"}'
100m means one tenth of a CPU core. Requests influence scheduling and utilization calculations; limits cap container consumption.
The JSONPath output should contain both requests and limits. Declaring a limit alone would not provide the HPA's utilization denominator.
deployment "cpu-demo" successfully rolled out
{"limits":{"cpu":"200m","memory":"64Mi"},"requests":{"cpu":"100m","memory":"32Mi"}}
JSON may print the keys in a different order. Check the four values rather than their position in the line.
Inspect Resource Metrics
In this step, verify the prepared metrics pipeline and inspect usage.
kubectl top is a recent usage snapshot, similar in spirit to the operating-system top command. It does not display configured requests or limits; those remain part of the workload specification.
cd /home/labex/project/resource-autoscaling
kubectl get deployment metrics-server -n kube-system
for attempt in $(seq 1 18); do
if kubectl top nodes >/dev/null 2>&1 && kubectl top pods -n practice-capacity --containers >/dev/null 2>&1; then
break
fi
echo "Waiting for the first metrics sample (${attempt}/18)..."
sleep 5
done
kubectl top nodes
kubectl top pods -n practice-capacity --containers
kubectl top reads recent metrics rather than resource requests. Metrics may take a short time to appear; retry after several seconds if a new Pod has no sample yet.
The loop performs that retry for you. It checks quietly at most 18 times, waits five seconds between attempts, and then runs the two visible top commands once a sample is available. In the checks, >/dev/null discards normal output and 2>&1 sends error output to the same place; this keeps expected “metrics not ready yet” messages out of the terminal. This delay is normal for a recent-usage pipeline; it does not mean the Pod failed.
The node and Pod values will vary, so focus on the presence of CPU and memory columns rather than exact numbers. --containers separates usage when a Pod contains more than one container.
For example, the tested environment produced a node table and a per-container table like these:
NAME CPU(cores) CPU(%) MEMORY(bytes) MEMORY(%)
labex-v135 182m 9% 1602Mi 44%
POD NAME CPU(cores) MEMORY(bytes)
cpu-demo-... hpa-example 1m 9Mi
Your numbers are expected to differ because they are measurements, not configuration values.
Create a HorizontalPodAutoscaler
In this step, target average CPU utilization of 30 percent with one to four replicas.
At a 100m request, a 30% target corresponds to roughly 30m average CPU usage per Pod. HPA adjusts the Deployment's desired replica count but never goes below min=1 or above max=4.
cd /home/labex/project/resource-autoscaling
kubectl autoscale deployment cpu-demo -n practice-capacity --cpu=30% --min=1 --max=4
kubectl get hpa cpu-demo -n practice-capacity
kubectl describe hpa cpu-demo -n practice-capacity
Utilization is measured relative to the 100m CPU request. Without that request, a percentage-based CPU target would lack its denominator.
The current kubectl autoscale syntax writes the percentage explicitly as --cpu=30%. This replaces the older --cpu-percent flag, which Kubernetes 1.35 reports as deprecated.
In the first few seconds, the TARGETS column may show <unknown> while a new metric sample is collected. describe hpa exposes conditions and events that explain whether the controller has usable metrics.
An initial table can therefore look like this without indicating an error:
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS
cpu-demo Deployment/cpu-demo cpu: <unknown>/30% 1 4 0
After the controller's next sync, <unknown> becomes a measured percentage and REPLICAS reflects the Deployment.
Generate Load and Observe Scaling
In this step, run a temporary client that repeatedly requests the Service for up to 60 seconds.
The BusyBox timeout command runs a nested request loop for 60 seconds. The while loop repeatedly calls the CPU-intensive demo endpoint, and >/dev/null discards response bodies so the terminal remains readable. Reaching the time limit normally returns a nonzero status, so || true treats that planned stop as normal completion.
cd /home/labex/project/resource-autoscaling
kubectl run load-generator -n practice-capacity --image=busybox:1.36 --image-pull-policy=IfNotPresent --restart=Never --rm -i -- sh -c 'timeout 60 sh -c "while true; do wget -qO- http://cpu-demo >/dev/null; done" || true'
kubectl get hpa cpu-demo -n practice-capacity
kubectl get deployment cpu-demo -n practice-capacity
HPA reacts asynchronously, so replica changes may continue after the client exits. Inspect HPA conditions and events rather than assuming an immediate scale.
If scaling has not appeared in the first table, run kubectl get hpa cpu-demo -n practice-capacity -w briefly and press Ctrl+C after observing a change. -w watches updates; it does not modify the HPA. Exact timing is deliberately not part of verification because metrics control loops are asynchronous.
In the tested run, HPA observed usage above its target and raised the Deployment to two ready replicas:
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS
cpu-demo Deployment/cpu-demo cpu: 55%/30% 1 4 2
NAME READY UP-TO-DATE AVAILABLE
cpu-demo 2/2 2 2
The exact utilization and replica count may differ. The useful reading is: current 55% exceeded target 30%, so desired capacity increased while remaining within the 1–4 bounds.
Summary
You connected requests, limits, live metrics, utilization targets, and HPA decisions. Capacity management is a feedback loop: declare a baseline, observe usage, define bounds, and inspect controller conditions.


