Skip to content

02 · Load Balancing & Auto Scaling

Module 1 built a VPC with redundant subnets across zones — but redundant subnets don't help if only one VSI is actually running in them. This module adds the two pieces that turn "multiple zones" into "actually survives losing one": an Application Load Balancer for VPC that spreads traffic across healthy instances, and an instance group with an auto scaling policy that adds or removes instances as load changes.

Why a load balancer instead of a floating IP

Module 3 in Level 1 attached a floating IP directly to one VSI — fine for SSH access to a single test instance, wrong for anything serving real traffic. A floating IP points at exactly one network interface; if that instance dies, so does every connection to it. A load balancer instead:

  • Exposes one stable front-end address (or hostname).
  • Health-checks every backend member and stops sending traffic to unhealthy ones automatically.
  • Spreads load across members in multiple zones, so losing a zone loses only the capacity in it, not the whole service.

Create the Application Load Balancer

# Public ALB spanning the public subnets from Module 1
ibmcloud is load-balancer-create ha-app-alb \
  --subnets public-subnet-z1,public-subnet-z2 \
  --public \
  --resource-group-name mastery-path

# Provisioning takes a few minutes -- poll until it's "active"
ibmcloud is load-balancer ha-app-alb

An ALB needs at least one subnet per zone it should have a presence in; each zone's subnet gets its own front-end IP, and IBM Cloud handles the DNS-level distribution across them under the load balancer's hostname.

Backend pool and health monitor

A pool groups backend members and defines how traffic is balanced and health-checked; a pool member is one backend target (a VSI's private IP, or later, an instance group).

ibmcloud is load-balancer-pool-create web-pool ha-app-alb \
  --algorithm round_robin \
  --protocol http \
  --health-delay 5 \
  --health-retries 2 \
  --health-timeout 2 \
  --health-type http \
  --health-monitor-url /healthz

# Add an existing instance as a pool member (private IP, not floating IP)
ibmcloud is load-balancer-pool-member-create web-pool ha-app-alb \
  --port 8080 \
  --target-address 10.10.1.10

Gotcha: the health-check URL (/healthz above) has to return a 2xx status from the app itself — a load balancer that can reach port 8080 but gets a 500 from /healthz will correctly mark that member unhealthy and stop routing to it, which looks like an outage even though the instance is technically "up."

Listener: what the front end actually accepts

ibmcloud is load-balancer-listener-create ha-app-alb \
  --port 80 \
  --protocol http \
  --default-pool web-pool

For HTTPS, upload a certificate to Secrets Manager first and reference its CRN — the ALB terminates TLS at the listener, so backend members can stay on plain HTTP inside the private network:

ibmcloud is load-balancer-listener-create ha-app-alb \
  --port 443 \
  --protocol https \
  --certificate-instance crn:v1:bluemix:public:secrets-manager:... \
  --default-pool web-pool

Instance groups: the auto-scaled backend

Provisioning VSIs by hand doesn't scale (literally). An instance template describes one instance's configuration; an instance group launches and manages a set of them from that template, and can register its own members directly with the ALB pool.

# Template: the "stamp" every scaled-out instance is cloned from
ibmcloud is instance-template-create web-template \
  --vpc-id $(ibmcloud is vpc ha-app-vpc --output json | jq -r .id) \
  --zone us-south-1 \
  --profile cx2-2x4 \
  --image r134-... \
  --keys mastery-key \
  --primary-network-interface subnet=private-subnet-z1

# Instance group built from the template, spanning both zones' subnets
ibmcloud is instance-group-create web-group \
  --instance-template web-template \
  --membership-count 2 \
  --subnets private-subnet-z1,private-subnet-z2 \
  --load-balancer ha-app-alb \
  --load-balancer-pool web-pool \
  --port 8080

--membership-count 2 is the group's baseline size before any scaling policy kicks in — the group creates that many instances immediately, distributed across the subnets given.

Auto scaling policy

ibmcloud is instance-group-manager-create web-group \
  --name cpu-scaler \
  --policy-type target \
  --min-membership-count 2 \
  --max-membership-count 6 \
  --aggregation-window 90 \
  --cooldown 300

ibmcloud is instance-group-manager-policy-create web-group cpu-scaler \
  --name scale-on-cpu \
  --metric-type cpu \
  --metric-value 70
  • --aggregation-window — how many seconds of sustained metric data triggers a scaling decision (avoids reacting to a one-second spike).
  • --cooldown — minimum seconds between scaling actions, so the group doesn't oscillate up and down while metrics settle after each change.
  • --metric-value 70 — target average CPU percent; the manager adds instances when the group's average is sustained above this and removes them when it's comfortably below.

Gotcha: the min/max bounds are a hard budget ceiling as much as a capacity guarantee — --max-membership-count 6 caps cost under a traffic spike, but also caps how much load the service can actually absorb before requests start queuing or failing. Size the max based on what you can afford to run continuously, not just what you hope never gets hit.

Verify it's actually balancing and scaling

# Confirm pool members are all "healthy"
ibmcloud is load-balancer-pool-members web-pool ha-app-alb

# Watch group membership over time
watch -n 30 ibmcloud is instance-group-memberships web-group

# Hostname to actually hit
ibmcloud is load-balancer ha-app-alb --output json | jq -r .hostname

How It Actually Works

  • An Application Load Balancer for VPC is itself a managed, horizontally-scaled fleet of proxy nodes spread across your chosen zones — not a single appliance — which is why it's given a hostname that resolves via DNS to multiple IPs rather than one static address: the ALB's own capacity scales behind the scenes, and the hostname indirection is what lets IBM add or replace proxy nodes without changing what clients connect to.
  • Health checks are the actual mechanism deciding pool membership in real time — the load balancer independently probes each backend member on the configured interval/path, and a member that fails the configured consecutive-failure threshold is removed from the live-routing set immediately, before any traffic is sent to it, which is why an autoscaled-in instance isn't usable the instant it boots: it has to pass its first health checks before the load balancer routes anything to it.
  • Instance group autoscaling makes decisions from the same metrics pipeline Monitoring uses — it evaluates the group's average CPU (or configured metric) against your target on a polling interval, and scale-out/scale-in are separate control-plane actions that create or delete VSIs from a template, not resource reallocation on existing instances; that's why autoscaling has real lag (metric window + cooldown + VSI boot time) between load rising and capacity actually increasing.
  • Session persistence (sticky sessions) works by the load balancer hashing a cookie or source IP into a consistent backend selection on every request, rather than the backend fleet sharing session state — which is exactly why removing a "sticky" backend member (scale-in, a failed health check) breaks in-flight sessions pinned to it: there's no session data replicated elsewhere for the load balancer to fail over to.

Cheat sheet

Command Purpose
ibmcloud is load-balancer-create <name> --subnets <s1,s2> --public Create a public ALB across zones
ibmcloud is load-balancer-pool-create <pool> <lb> --health-monitor-url <path> Create a backend pool with health checks
ibmcloud is load-balancer-listener-create <lb> --port <p> --default-pool <pool> Front-end listener
ibmcloud is instance-template-create <name> ... Define the instance "stamp" for scaling
ibmcloud is instance-group-create <name> --instance-template <t> --load-balancer <lb> --load-balancer-pool <pool> Create an auto-managed, load-balanced group
ibmcloud is instance-group-manager-create <group> --policy-type target --min-membership-count <n> --max-membership-count <n> Attach an auto scaling manager
ibmcloud is instance-group-manager-policy-create <group> <manager> --metric-type cpu --metric-value <pct> Scale on a target metric
ibmcloud is instance-group-memberships <group> List current group membership

Exercise

Attach the ALB and instance group from this module to the two-zone VPC you built in Module 1's exercise. Set --min-membership-count 2 --max-membership-count 4 with a CPU target of 70%, then generate load against one instance (stress-ng --cpu 2 --timeout 300s works if installed) and watch ibmcloud is instance-group-memberships web-group grow past the baseline within a few minutes. Scale back down by stopping the load, confirm membership shrinks toward the minimum after the cooldown window, then delete the instance group, template, and load balancer before moving on.