DEV Community

Vijaya Rajeev Bollu
Vijaya Rajeev Bollu

Posted on

Can My AWS EKS Cluster Handle 100,000 Users? Load Testing + Autoscaling, Proven Live

Part 2 of a 4-part series — this article covers the repo at tag v2.0-load-testing-autoscaling. Part 1 (EKS deployment, tag v1.0-eks-deployment) is here.


The Setup

Last build: Google's Online Boutique running on a fixed 2-node AWS EKS cluster — 11 services, one Helm release, no autoscaling. The question this time: what actually happens under load? Not "the YAML looks right" — actually driven, actually watched, actually proven.

The Build

Two changes to the cluster from the last video:

kubernetes-manifests-aws/
├── hpa/              → HorizontalPodAutoscaler for 10 services, CPU + memory targets
└── metrics-server/   → the add-on HPA depends on — verified running first

karpenter/
├── nodepool.yaml     → replaces the fixed-size node group
└── ec2nodeclass.yaml → instance requirements Karpenter provisions against
Enter fullscreen mode Exit fullscreen mode

metrics-server is the dependency almost nobody checks first. HPA reads CPU/memory percentages from it — if it's not running, kubectl get hpa just shows <unknown> targets forever, silently. First command in this build: kubectl get deployment metrics-server -n kube-system.

The Load Test

k6 runs in-cluster, ramping virtual users directly against the storefront's internal service address — no external load balancer bottleneck skewing the numbers. The test script targets 100,000 VUs at full scale; for this recorded run it's scaled down to 3,000 VUs, stated openly rather than glossed over, to keep the run inside what this AWS account's free-tier-eligible node ceiling can actually absorb.

kubectl apply -f k6-load-test.yaml
kubectl get pods -n load-testing -w
Enter fullscreen mode Exit fullscreen mode

Watching HPA Respond

kubectl get hpa -n online-boutique -w
Enter fullscreen mode Exit fullscreen mode
NAME                REFERENCE                      TARGETS           MINPODS   MAXPODS   REPLICAS
frontend            Deployment/frontend             45%/60%, 210Mi    2         10        2
cartservice         Deployment/cartservice          78%/60%, 340Mi    2         10        4
checkoutservice     Deployment/checkoutservice      82%/60%, 290Mi    2         10        5
Enter fullscreen mode Exit fullscreen mode

REPLICAS climbs live as CPU/memory cross the target threshold — not a static config, an observed response to the k6 traffic actually landing on these pods.

Watching Karpenter Respond

kubectl get nodes -w
Enter fullscreen mode Exit fullscreen mode

Node count climbs from 2 to 8 as HPA's new replicas outgrow what the original 2 nodes can schedule. Karpenter watches for Pending pods, picks an instance type from the EC2NodeClass constraints, and provisions a new node — no fixed node group ceiling to hit.

kubectl get nodepool,ec2nodeclass
Enter fullscreen mode Exit fullscreen mode

The moment a Pending pod's node comes Ready, the scheduler places it immediately — that transition, pod snapping onto a freshly-provisioned node, is the single clearest proof autoscaling is actually working end to end.

The Dashboard

Grafana at /d/online-boutique-autoscaling plots HPA replica counts, node counts, and CPU/memory per service on one screen — every panel trending upward together during the ramp, and back down together once k6 finishes and Karpenter consolidates idle nodes away.

The Result

kubectl get hpa -n online-boutique
Enter fullscreen mode Exit fullscreen mode

10 services scaled, REPLICAS up across the board.

kubectl get nodes
Enter fullscreen mode Exit fullscreen mode

8 Ready nodes at peak, back to 2 within minutes of load testing stopping — Karpenter's consolidation reclaiming capacity nobody's using anymore.

What I Learned

1. Verify metrics-server before trusting HPA at all.

An HPA with no metrics source doesn't error loudly — it just sits at <unknown> targets and never scales. Check the dependency first, every time.

2. Karpenter removes the node-group ceiling, not the need to think about limits.

No fixed max-node count means no silent capacity wall — but EC2NodeClass constraints (instance types, AZs) still need to match what the account is actually allowed to provision.

3. A load test is worth nothing until you've watched it end to end.

HPA config and a NodePool definition sitting in a repo prove nothing on their own. Watching REPLICAS and node count actually climb under generated traffic is the only real verification.

4. State the scale-down honestly.

3,000 VUs, not the full 100,000-VU script — said on camera, not hidden. Same honesty standard as marking ALB "provisioned, not installed" in the first build: every claim matches what the repo and the run actually did.


Try It

GitHub (this video's exact state): https://github.com/ThinkWithOps/thinkwithops-online-boutique-production/tree/v2.0-load-testing-autoscaling
Demo walkthrough: https://youtu.be/mjGCdLFqZ7k
Tag: v2.0-load-testing-autoscaling

git checkout v2.0-load-testing-autoscaling
kubectl apply -f kubernetes-manifests-aws/hpa/
kubectl apply -f karpenter/
kubectl apply -f k6-load-test.yaml
kubectl get hpa,nodes -w
Enter fullscreen mode Exit fullscreen mode

What's your default — Karpenter or Cluster Autoscaler — for EKS in 2026? Curious what other teams have landed on.


Top comments (0)