---
title: "From On-Prem to the Cloud: Implementing GKE Horizontal Autoscaling Step by Step"
description: "A step-by-step guide to configuring HPA horizontal autoscaling on GKE and verifying automatic Pod scale-out and scale-in behavior under CPU overload using the K6 load testing tool."
canonical_url: "https://blog.markkulab.net/en/post/gke-autoscaling-step-by-step"
author: "Mark Ku"
author_url: "https://blog.markkulab.net/en/author/mark-ku"
site: "Mark Ku's Tech Notes"
date_published: "2024-11-15 01:01:35 +0800"
category: "Cloud"
tags: ["gke", "kubernetes", "autoscaling", "hpa", "google cloud", "k6", "load testing", "cloud"]
language: "en"
license: "CC BY 4.0"
license_url: "https://creativecommons.org/licenses/by/4.0/"
attribution: "when reusing or quoting, credit the author and link back to the original"
---

# From On-Prem to the Cloud: Implementing GKE Horizontal Autoscaling Step by Step

## What Is Horizontal Scaling?

[Continuing from the previous article on deploying a Next.js app to Google Kubernetes Engine](https://blog.markkulab.net/deploy-nextjs-app-to-google-kubernetes-service/), this post focuses on horizontal scale-out and scale-in in GKE.

Think of it like an organization: even the most capable person can only sustain peak performance for about 16 hours before hitting a wall. When the workload becomes too heavy for one person to handle, you hire more people to share the load — that's horizontal scaling in a nutshell.

The same principle applies in tech. No matter how powerful a single server is, its processing capacity has a ceiling. When load exceeds what the server can handle, simply upgrading the hardware (vertical scaling) may not be enough. That's when horizontal scaling (Scaling Out) becomes the right solution.

## What Are Kubernetes Pods?

In Kubernetes, Pods are the smallest deployable unit, responsible for hosting containerized applications and their runtime environment.

## Which Web Applications Are Good Candidates for Horizontal Scaling?

Not every service is suited for horizontal scaling. Services that scale well are typically **stateless**, such as Web APIs or microservices, where load can be distributed easily across instances.

Services with **local state** — such as databases or session-based web apps not designed for distribution — may face consistency issues that prevent horizontal scaling. In those cases, consider **Vertical Pod Autoscaler (VPA)**, which increases the resources of a single Pod to handle higher load instead.

## Advantages of GKE

Compared to self-managed Kubernetes, Google Kubernetes Engine (GKE) offers far more convenient cluster management. For example, GKE does not require a separate installation of `metrics-server` to monitor CPU and memory usage of containers, significantly lowering the barrier to entry.

## Issue Encountered When Creating a Cluster for Autoscaling

[As mentioned in the previous article](https://blog.markkulab.net/deploy-nextjs-app-to-google-kubernetes-service/), GKE offers two cluster modes:

- **Standard Cluster**: Highly flexible, suitable for users who need fine-grained control, but requires manual resource and configuration management.
- **Autopilot Cluster**: Simplified management with automatic resource adjustment, ideal for teams focused purely on application deployment.

In practice, I found that **Standard Cluster** does not enable Metrics by default. As a result, running `kubectl get hpa` would not return CPU or memory utilization, and autoscaling would not work until metrics were explicitly enabled.

![Standard Cluster Settings](https://blog.markkulab.net/content/markku/posts/gke-autoscaling-step-by-step/images/standard-cluster-settings.png)

![kubectl get hpa in cloud shell](https://blog.markkulab.net/content/markku/posts/gke-autoscaling-step-by-step/images/cannot-get-cpu-usage-rate.png)

## Essential kubectl Commands for Kubernetes Horizontal Scaling

### Check resource usage of containers

The following commands display CPU and memory utilization for nodes or Pods:

```bash
kubectl top nodes
kubectl top pods
```

### Check Horizontal Pod Autoscaler (HPA) status

```bash
kubectl get hpa
```

## Manually Scaling Containers

To scale manually:

```bash
kubectl scale deployment nextjs-blog-deployment --replicas=5
kubectl get pods
```

## Configuring Automatic Horizontal Scaling (HPA)

There are three ways to configure HPA: via a YAML file, via CLI commands, or through the GKE web UI.

### 1. Define HPA in a YAML File

Create a YAML file and apply it:

```bash
kubectl apply -f nextjs-blog-hpa.yaml
```

Example YAML:

```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: nextjs-blog-hpa
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nextjs-blog
  minReplicas: 1
  maxReplicas: 5
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
```

Key fields:
- `scaleTargetRef`: specifies which Deployment the HPA monitors.
- `minReplicas`: always keep at least 1 Pod running.
- `maxReplicas`: scale out to a maximum of 5 Pods.
- `metrics`: trigger scaling when CPU utilization exceeds 50%.

### 2. Configure HPA via CLI

```bash
kubectl autoscale deployment nextjs-blog-deployment --cpu-percent=50 --min=1 --max=5
```

### 3. Configure HPA via the GKE Web UI

![Set up HorizontalPodAutoscaler](https://blog.markkulab.net/content/markku/posts/gke-autoscaling-step-by-step/images/configre-horizontaal-pod-autoscaler.png)

## Testing the Autoscaling Configuration

For load testing I used Grafana's K6 tool.

### Write a Load Test Script

```javascript
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '30s', target: 3000 },  // 負載測試從 0 個虛擬使用者增加到 20 個，並持續30 秒 
    { duration: '1m30s', target: 3000 }, 3000 // 維持 3000 個虛擬使用者持續 1 分 30 秒
    { duration: '20s', target: 0 },
  ]
};
// options 第一階段模擬快速增加負載，第二階段保持穩定負載，第三階段緩慢減少負載。
export default function () {
  const res = http.get('http://your-domain/');
  check(res, { 'status was 200': (r) => r.status == 200 });
  sleep(1);
```

### Run K6 via Docker (no installation needed — `--rm` removes the container after the test completes)

```bash
// Windows 腳本
cat script.js | docker run --rm -i grafana/k6 run -
```

At the same time, run the following command in Google Cloud Shell. The `watch` command prints the current HPA status every two seconds, letting you verify that autoscaling is working correctly:

```bash
 watch -n 2 'date && kubectl get hpa'
```

## Test Results

When container CPU utilization instantly spiked to 102%, the new Pods may not have fully started yet because the load rose too quickly.

![test restul 1](https://blog.markkulab.net/content/markku/posts/gke-autoscaling-step-by-step/images/test-result-1.png)

When CPU utilization reached 150%, the system automatically scaled out to 3 Pods to share the load.

![test restul 2](https://blog.markkulab.net/content/markku/posts/gke-autoscaling-step-by-step/images/test-result-2.png)

When CPU utilization exceeded 200%, the system automatically scaled out to the configured maximum of 5 Pods.

![test restul 3](https://blog.markkulab.net/content/markku/posts/gke-autoscaling-step-by-step/images/test-result-3.png)

After CPU utilization dropped, the system scaled back down to the configured minimum number of Pods.

![test restul 4](https://blog.markkulab.net/content/markku/posts/gke-autoscaling-step-by-step/images/test-result-4.png)

## Reflections

Working with GKE is genuinely exciting. Its flexibility and convenience mean I don't need to manage a complex on-premises infrastructure at all. Looking back at my journey from infrastructure management to software engineering, all those accumulated experiences turned out to make learning cloud technologies and Kubernetes surprisingly approachable.

## References

- [Kubernetes AutoScaling (2) - Horizontal Pod Autoscaler](https://ithelp.ithome.com.tw/articles/10298858)
- [How to do Autoscaling on GKE](https://youtu.be/jP4cg7itW6E?si=h4TDRqLSWULgzIw9)
- [Kubernetes Dynamic Autoscaling in Practice](https://www.aneasystone.com/archives/2022/11/kubernetes-auto-scaling.html)
- [K6 Official Documentation](https://grafana.com/docs/k6/latest/get-started/running-k6/)

---

## About this article and its author

Originally published on [Mark Ku's Tech Notes](https://blog.markkulab.net/en/post/gke-autoscaling-step-by-step)

License: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) — when reusing or quoting, credit the author and link back to the original

### About the author

**[Mark Ku](https://blog.markkulab.net/en/author/mark-ku)** — Software Solution Provider

- 10+ years senior software engineer, now an AI Builder
- Focused on large-platform architecture — North-American e-commerce, AI SaaS subscription billing
- Combining AI Agents and automation to build evolvable product foundations

### Free tools built by the author

All of these are free to use:

- [Free PDF Sign Tool](https://blog.markkulab.net/en/tools/pdf-sign): Online PDF sign tool — draw, type, or upload a signature, then drag, resize, and download. Everything runs in your browser; nothing is uploaded.
- [VS Code Refactory](https://blog.markkulab.net/en/tools/refactory): Refactory is a VS Code refactoring extension: 34 actions plus a 37-rule code-smell inspection layer with a Code Health dashboard, across 18 languages, backed by 534 tests. It learns your repo's conventions: where interfaces live, where DI is registered, whether 'use client' belongs. It ranks files by git churn × complexity so you know what to fix first, and hands any smell to the Claude Code already on your machine. Free to use, and your source never leaves your computer.
- [DB-Kit Database Manager](https://blog.markkulab.net/en/tools/db-kit): DB-Kit is a lightweight, cross-platform database manager built with Tauri + Rust + React. Manage MySQL, MariaDB, PostgreSQL, SQL Server, Oracle, SQLite, MongoDB, Redis, Kafka, Elasticsearch and RabbitMQ from one consistent interface: passwords encrypted in the OS keychain, SSH tunnels, full CRUD, a visual query builder, stacked multi-statement result sets, cross-connection data transfer and compare/sync, Excel / CSV import & export, visualized execution plans, ER diagrams, scheduled backups, SQL stress testing with p50–p99 latency percentiles, a 15-rule SQL review engine, Kafka message browsing with monitoring & alerts, a bilingual UI (Traditional Chinese / English), a built-in AI assistant (natural-language SQL, AI review and tuning advice) and the dbk CLI. Free and open source (MIT), with installers for Windows, macOS and Linux.
- [VS Code Super Mermaid](https://blog.markkulab.net/en/tools/super-mermaid): Super Mermaid is a VS Code extension for beautiful Mermaid diagrams out of the box: auto-colored live preview, mouse pan & zoom, high-res PNG / SVG export, 21 templates and multiple themes. Free and open source (MIT).
- [React Super Mermaid](https://blog.markkulab.net/en/tools/react-super-mermaid): react-super-mermaid is an open-source React component library: render beautiful Mermaid diagrams with a single <MermaidViewer>, with built-in colorful / sketch themes, pan & zoom, in-diagram search, and high-res SVG / PNG export. Lightweight, SSR-safe, fully typed. Free and open source (MIT).
- [Jira / Confluence Super Mermaid](https://blog.markkulab.net/en/tools/jira-super-mermaid): An Atlassian Forge app: write Mermaid syntax directly inside a Jira issue or a Confluence page and get flowcharts, sequence diagrams, state machines and Gantt charts. 11 diagram types, SVG / PNG export, light and dark themes, full CJK support. Runs on Atlassian: your diagrams live in your own site and the app calls no third-party service. Free, coming soon to the Atlassian Marketplace.
- [Mermaid Live Preview](https://blog.markkulab.net/en/tools/mermaid-preview): Write Mermaid in your browser, see it render instantly, and share the whole diagram as a single link. No sign-up, nothing uploaded to a server, and mermaid.live share links work as-is.
- [React Intl Phone Number](https://blog.markkulab.net/en/tools/react-intl-phone-number): react-intl-phone-number is an open-source React component: framework-agnostic and antd-free, with E.164 in/out, a searchable flag / country-code dropdown, configurable validation levels (strict / mobile-strict / loose), themeable CSS, and i18n — phone logic powered by google-libphonenumber. Lightweight and fully typed. Free and open source (MIT).
- [Uptime Kuma Cluster](https://blog.markkulab.net/en/tools/uptime-kuma-cluster): Turn single-node Uptime Kuma into a highly available cluster: OpenResty + Lua smart load balancing, shared MariaDB state, health checks and automatic failover, plus cluster-management REST APIs. One Docker Compose command to start. Free and open source (MIT).
- [Special Education](https://blog.markkulab.net/en/education): Learning materials crafted for special education students

### Daily podcasts

- [Mark's Tech Insights — Daily AI News](https://blog.markkulab.net/en/category/tech-news): Daily curated AI and tech trends. Catch the latest developments via audio summaries — covering AI applications, software architecture, DevOps, and engineering practice. — RSS: https://blog.markkulab.net/feed.xml
- [AI股市蝦聊](https://blog.markkulab.net/en/category/ai-stock-chat): Every trading day, an AI-analyzed take on the Taiwan stock market, delivered as a two-host conversation covering the session and the next-day outlook. — RSS: https://blog.markkulab.net/ai-stock-chat/feed.xml
- [開源好物週報](https://blog.markkulab.net/en/category/open-source-weekly): A weekly two-host pick of free open-source tools surfaced from real Hacker News, GitHub, and Reddit buzz — what pain they solve and the fastest way to get started. — RSS: https://blog.markkulab.net/open-source-weekly/feed.xml

### Newsletter

[Subscribe to the newsletter](https://blog.markkulab.net/en/subscribe) — Be the first to know about new posts. No spam, unsubscribe anytime.
