Skip to content

10.5 Load Balancer Setup

Overview

The Azure Load Balancer distributes incoming traffic across multiple VMs, providing high availability and scalability. This configuration uses a Standard SKU public load balancer with health probes and load balancing rules.

Load Balancer Architecture

                    Internet
                       ↓
              [Public IP: Static]
                       ↓
            ┌──────────────────────┐
            │   Azure Load Balancer │
            │   (Standard SKU)      │
            └──────────────────────┘
                       ↓
            ┌──────────┴──────────┐
            ↓                     ↓
    [Health Probe]        [LB Rule: Port 80]
    (Port 80)             (TCP)
            ↓                     ↓
    ┌───────────────────────────────┐
    │    Backend Address Pool       │
    └───────────────────────────────┘
            ↓                     ↓
         [VM 0]               [VM 1]
      (10.0.1.x)           (10.0.1.y)

Load Balancer Resource

Terraform Configuration

resource "azurerm_lb" "example" {
  name                = var.load_balancer_name
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
  sku                 = "Standard"

  frontend_ip_configuration {
    name                 = var.public_ip_name
    public_ip_address_id = azurerm_public_ip.example.id
  }
}

Key Properties

Property Value Description
name test-lb Load balancer identifier
sku Standard Standard SKU (required for zones, outbound rules)
frontend_ip_configuration Public IP Entry point for traffic

SKU Comparison

Feature Basic Standard
Backend Pool Size Up to 300 Up to 1000
Health Probes HTTP, TCP HTTP, HTTPS, TCP
Availability Zones ❌ ✅
Outbound Rules ❌ ✅
SLA None 99.99%
Cost Free Paid
Security Open by default Closed by default

Why Standard?: Better features, SLA, and required for production workloads.

Backend Address Pool

Terraform Configuration

resource "azurerm_lb_backend_address_pool" "example" {
  loadbalancer_id = azurerm_lb.example.id
  name            = "test-pool"
}

Purpose

The backend pool is a collection of VMs that receive traffic from the load balancer. VMs are added via NIC associations (configured in section 10.3).

Backend Pool Members

Backend Pool: "test-pool"
├── VM 0 (via NIC 0)
│   └── Private IP: 10.0.1.x
└── VM 1 (via NIC 1)
    └── Private IP: 10.0.1.y

Health Probe

Terraform Configuration

resource "azurerm_lb_probe" "example" {
  loadbalancer_id = azurerm_lb.example.id
  name            = "test-probe"
  port            = 80
}

How Health Probes Work

  1. Probe Interval: Load balancer sends HTTP request to port 80 every 15 seconds (default)
  2. Success Criteria: HTTP 200 OK response
  3. Failure Threshold: 2 consecutive failures mark VM as unhealthy (default)
  4. Action: Unhealthy VMs removed from rotation

Health Probe Flow

Load Balancer
    ↓ (every 15s)
GET / HTTP/1.1
    ↓
VM responds
    ↓
┌─────────────────┐
│ HTTP 200 OK?    │
└─────────────────┘
    ↓           ↓
   Yes         No
    ↓           ↓
 Healthy    Unhealthy
(in pool)  (removed)

Probe Configuration Options

resource "azurerm_lb_probe" "example" {
  loadbalancer_id     = azurerm_lb.example.id
  name                = "test-probe"
  protocol            = "Http"           # Http, Https, or Tcp
  port                = 80
  request_path        = "/"              # For HTTP/HTTPS probes
  interval_in_seconds = 15               # Default: 15
  number_of_probes    = 2                # Failures before unhealthy
}

Load Balancing Rule

Terraform Configuration

resource "azurerm_lb_rule" "example_rule" {
  loadbalancer_id                = azurerm_lb.example.id
  name                           = "test-rule"
  protocol                       = "Tcp"
  frontend_port                  = 80
  backend_port                   = 80
  disable_outbound_snat          = true
  frontend_ip_configuration_name = var.public_ip_name
  probe_id                       = azurerm_lb_probe.example.id
  backend_address_pool_ids       = [azurerm_lb_backend_address_pool.example.id]
}

Rule Breakdown

Property Value Description
name test-rule Rule identifier
protocol Tcp TCP protocol
frontend_port 80 Port on load balancer
backend_port 80 Port on VMs
disable_outbound_snat true Use outbound rule instead
frontend_ip_configuration_name Public IP name Which frontend IP to use
probe_id Health probe Which probe determines health
backend_address_pool_ids Backend pool Which VMs receive traffic

Traffic Flow

Client Request
    ↓
http://PUBLIC_IP:80
    ↓
Load Balancer (Frontend Port 80)
    ↓
[Check Health Probe]
    ↓
Distribute to Healthy VMs
    ↓
┌─────────┴─────────┐
↓                   ↓
VM 0:80           VM 1:80
(if healthy)      (if healthy)

Load Distribution Algorithm

Default: 5-tuple hash (source IP, source port, destination IP, destination port, protocol)

Result: Same client typically goes to same VM (session affinity)

Alternative: 2-tuple (source IP, destination IP) or 3-tuple (source IP, destination IP, protocol)

resource "azurerm_lb_rule" "example_rule" {
  # ... other configuration ...
  load_distribution = "SourceIP"  # 2-tuple hash
}

Session Affinity Options

Mode Hash Tuple Use Case
Default 5-tuple Stateless apps
SourceIP 2-tuple Session persistence
SourceIPProtocol 3-tuple Protocol-specific persistence

Outbound Rule

Terraform Configuration

resource "azurerm_lb_outbound_rule" "example" {
  name                    = "test-outbound"
  loadbalancer_id         = azurerm_lb.example.id
  protocol                = "Tcp"
  backend_address_pool_id = azurerm_lb_backend_address_pool.example.id

  frontend_ip_configuration {
    name = var.public_ip_name
  }
}

Purpose

Outbound rules enable VMs in the backend pool to access the internet using the load balancer's public IP.

Why Needed?

When disable_outbound_snat = true in the load balancing rule, you must explicitly configure outbound connectivity.

Outbound Traffic Flow

VM needs to access internet
    ↓
VM → Subnet → Load Balancer
    ↓
Load Balancer applies SNAT
(Source NAT)
    ↓
Outbound traffic uses Public IP
    ↓
Internet sees: PUBLIC_IP

SNAT Port Allocation

Default: 1024 ports per VM Calculation: Total ports / Number of VMs

Public IP: 64,000 ports available
2 VMs: 32,000 ports each
10 VMs: 6,400 ports each

Advanced Outbound Configuration

resource "azurerm_lb_outbound_rule" "example" {
  name                    = "test-outbound"
  loadbalancer_id         = azurerm_lb.example.id
  protocol                = "All"                    # All, Tcp, or Udp
  backend_address_pool_id = azurerm_lb_backend_address_pool.example.id
  allocated_outbound_ports = 10000                   # Ports per VM
  idle_timeout_in_minutes  = 4                       # Default: 4, Max: 30

  frontend_ip_configuration {
    name = var.public_ip_name
  }
}

Complete Load Balancer Flow

Inbound Request

1. Client → http://PUBLIC_IP
2. Load Balancer receives on port 80
3. Health probe checks VM status
4. Load balancer selects healthy VM (hash algorithm)
5. Forwards to VM:80
6. VM processes request
7. VM responds
8. Load balancer forwards response to client

Outbound Request

1. VM needs to access internet
2. VM sends packet to load balancer
3. Load balancer applies SNAT
4. Packet sent with Public IP as source
5. Response returns to Public IP
6. Load balancer forwards to VM

Variables

variable "load_balancer_name" {
  type        = string
  default     = "test-lb"
  description = "Name of the Load Balancer."
}

variable "public_ip_name" {
  type        = string
  default     = "test-public-ip"
  description = "Name of the Public IP."
}

Azure CLI Verification

Show Load Balancer

az network lb show \
  --resource-group <resource-group-name> \
  --name test-lb \
  --output json

List Backend Pool Members

az network lb address-pool show \
  --resource-group <resource-group-name> \
  --lb-name test-lb \
  --name test-pool \
  --output json

Check Health Probe Status

az network lb probe show \
  --resource-group <resource-group-name> \
  --lb-name test-lb \
  --name test-probe \
  --output json

View Load Balancing Rules

az network lb rule list \
  --resource-group <resource-group-name> \
  --lb-name test-lb \
  --output table

Test Load Balancer

# Get public IP
PUBLIC_IP=$(az network public-ip show \
  --resource-group <resource-group-name> \
  --name test-public-ip \
  --query ipAddress \
  --output tsv)

# Test HTTP endpoint
curl http://$PUBLIC_IP

# Test multiple times to see load distribution
for i in {1..10}; do
  curl http://$PUBLIC_IP
  echo ""
done

Monitoring and Troubleshooting

Check Backend Health

az network lb show \
  --resource-group <resource-group-name> \
  --name test-lb \
  --query "backendAddressPools[0].backendIpConfigurations[*].id" \
  --output table

View Metrics in Portal

  1. Navigate to Load Balancer in Azure Portal
  2. Click "Metrics"
  3. View:
  4. Data path availability (health probe status)
  5. Byte count
  6. Packet count
  7. SNAT connection count

Common Issues

Issue: No Response from Load Balancer

Causes: - Health probe failing - NSG blocking traffic - VMs not running

Debug:

# Check VM status
az vm list \
  --resource-group <resource-group-name> \
  --show-details \
  --output table

# Check NSG rules
az network nsg rule list \
  --resource-group <resource-group-name> \
  --nsg-name test-nsg \
  --output table

Issue: Traffic Only Goes to One VM

Cause: Session affinity (5-tuple hash)

Solution: Same client will typically hit same VM. Test from different IPs or change distribution mode.

Issue: SNAT Port Exhaustion

Symptoms: Outbound connections fail

Solution: - Increase allocated outbound ports - Add more public IPs to load balancer - Reduce idle timeout

Advanced Configurations

Multiple Frontend IPs

resource "azurerm_public_ip" "example2" {
  name                = "test-public-ip-2"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
  allocation_method   = "Static"
  sku                 = "Standard"
}

resource "azurerm_lb" "example" {
  name                = var.load_balancer_name
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
  sku                 = "Standard"

  frontend_ip_configuration {
    name                 = "frontend1"
    public_ip_address_id = azurerm_public_ip.example.id
  }

  frontend_ip_configuration {
    name                 = "frontend2"
    public_ip_address_id = azurerm_public_ip.example2.id
  }
}

Internal Load Balancer

For internal traffic only:

resource "azurerm_lb" "internal" {
  name                = "internal-lb"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
  sku                 = "Standard"

  frontend_ip_configuration {
    name                          = "internal-frontend"
    subnet_id                     = azurerm_subnet.example.id
    private_ip_address_allocation = "Dynamic"
  }
}

Best Practices

  1. Use Standard SKU: Better features and SLA
  2. Configure Health Probes: Essential for high availability
  3. Monitor Metrics: Track data path availability
  4. Plan SNAT Ports: Avoid port exhaustion
  5. Use Availability Zones: For zone redundancy (requires zone-redundant public IP)
  6. Implement Logging: Enable diagnostic logs
  7. Test Failover: Verify behavior when VMs fail

Cost Considerations

Standard Load Balancer Pricing (East US example): - Rules: $0.025/hour per rule (~$18/month) - Data Processed: $0.005 per GB

Example Monthly Cost: - 1 rule: $18 - 100 GB data: $0.50 - Total: ~$18.50/month

Next Steps

Proceed to 10.6-VM-Extensions.md to learn about installing Nginx on the VMs using VM extensions.