Home / OCI

OCIHow-toBeginner

OCI first load balancer: backends, health check and listener explained step by step

Build a public flexible load balancer in front of two OCI instances from the CLI. Learn what backend sets, health checks and listeners do, open the right security rules and fix a Critical health status.

Deepak Bhardwaj5 min read

Applies toOCI Flexible Load Balancer, OCI Compute, OCI CLI 3.x

The goal

You have two web servers and want one public address in front of them, so traffic is shared and one server can go down without an outage. This post builds an OCI load balancer (LB) and explains each part as you go.

The parts

Part What it does
Load balancer The public IP and the capacity, measured in bandwidth
Listener Waits for traffic on a port, such as HTTP on 80
Backend set A group of servers plus a balancing policy and health check
Backend One server: an IP address and port
Health check Regularly tests each backend, and stops sending traffic to failed ones

Traffic flows like this: client > listener > backend set > healthy backend.

Before you start

You need:

  • A public subnet for the load balancer.
  • Two instances running a web server on port 80, in a private or public subnet of the same VCN.
  • The CLI set up. See OCI CLI install and setup.

Set variables so the commands stay short:

export C=ocid1.compartment.oc1..aaaaexample
export LB_SUBNET=ocid1.subnet.oc1.ap-mumbai-1.aaaaexample

Step 1: open the network

This is where most first attempts go wrong. Traffic needs to be allowed at each hop.

Where Direction Port From or to
LB subnet Ingress TCP 80 0.0.0.0/0 or your clients' range
LB subnet Egress TCP 80 The backend subnet
Backend subnet Ingress TCP 80 The LB subnet range
Backend instance OS firewall TCP 80 Open it, see below

Oracle Linux images have firewalld turned on by default:

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload
curl -I http://localhost/

For more detail, see when a port is open but the instance still times out.

Step 2: create the load balancer

oci lb load-balancer create \
  --compartment-id "$C" \
  --display-name lb-web \
  --shape-name flexible \
  --shape-details '{"minimumBandwidthInMbps":10,"maximumBandwidthInMbps":100}' \
  --subnet-ids "[\"$LB_SUBNET\"]" \
  --is-private false \
  --wait-for-state SUCCEEDED

Get its ID and public address:

export LB=$(oci lb load-balancer list --compartment-id "$C" --display-name lb-web \
  --query "data[0].id" --raw-output)

oci lb load-balancer get --load-balancer-id "$LB" \
  --query "data.\"ip-addresses\"" 

Load balancer changes run as background work requests. Add --wait-for-state SUCCEEDED to each step, so that the next step doesn't fail because one is still running.

Step 3: create the backend set and health check

oci lb backend-set create \
  --load-balancer-id "$LB" \
  --name bs-web \
  --policy ROUND_ROBIN \
  --health-checker-protocol HTTP \
  --health-checker-port 80 \
  --health-checker-url-path / \
  --health-checker-return-code 200 \
  --wait-for-state SUCCEEDED

The balancing policies:

Policy Sends the next request to
ROUND_ROBIN The next server in turn
LEAST_CONNECTIONS The server with the fewest active connections
IP_HASH The same server each time for a given client IP

Tip

Use a small page such as /health that returns 200 for the health check. A path that redirects returns 301 and counts as a failure unless you change the expected return code.

Step 4: add the backends

for IP in 10.0.2.11 10.0.2.12; do
  oci lb backend create \
    --load-balancer-id "$LB" \
    --backend-set-name bs-web \
    --ip-address "$IP" \
    --port 80 \
    --wait-for-state SUCCEEDED
done

Step 5: create the listener

oci lb listener create \
  --load-balancer-id "$LB" \
  --name http \
  --default-backend-set-name bs-web \
  --port 80 \
  --protocol HTTP \
  --wait-for-state SUCCEEDED

Step 6: test it

oci lb backend-set-health get --load-balancer-id "$LB" --backend-set-name bs-web
oci lb load-balancer-health get --load-balancer-id "$LB"

curl -I http://<lb-public-ip>/

Make each server's page show its hostname, then send several requests and watch the answers alternate:

for i in 1 2 3 4; do curl -s http://<lb-public-ip>/ ; done

Now stop the web server on one backend. Within a minute or so, its health turns Critical and all traffic goes to the other one. Start it again to finish your first failover test.

If health shows Critical

Check for details on one backend. The backend's name is ip:port:

oci lb backend-health get \
  --load-balancer-id "$LB" \
  --backend-set-name bs-web \
  --backend-name 10.0.2.11:80
Health check result Meaning Fix
CONNECT_FAILED Blocked or not listening Check security rules, firewalld and ss -ltn
INVALID_STATUS_CODE The page returned another status Fix the path or the expected code
TIMED_OUT The app is too slow, or packets are dropped Check the app and the routes
UNKNOWN Just created Wait a couple of minutes

The all backends Critical runbook goes further.

Warning

Health checks come from the load balancer's subnet, not from the client. If the backend's rule allows only your office range, every backend turns Critical, even though the server is fine.

Prevent it next time

  • Put each backend in a different fault domain, so one hardware failure can't take down both.
  • Add HTTPS with a certificate from the OCI Certificates service, and track its expiry. See LB certificate expired.
  • Add an alarm for unhealthy backends with Monitoring alarms.
  • Delete test load balancers when you're done, because they keep billing.