Manage Kubernetes Resources and Autoscaling

KubernetesBeginner
Practice Now

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.