Building Resilient Networks with Azure Networking Services Part 3: Understanding Azure Load Balancing Solutions

Resilient Networking in Azure - Load Balancing

Introduction

In modern cloud architectures, effective load balancing is crucial for ensuring high availability and optimal performance. Azure provides a suite of load balancing solutions that address different layers of the OSI model:

  • Azure Load Balancer operates at layer 4 (TCP/UDP), ideal for fast, high-throughput distribution of network traffic among virtual machines.
  • Azure Application Gateway, on the other hand, operates at layer 7 (HTTP/HTTPS) and is specifically designed for web applications, offering advanced features like SSL termination, cookie-based session affinity, and URL-based routing.
  • Azure Traffic Manager is a DNS-based traffic distribution solution that directs users to the most appropriate endpoint globally—it’s particularly useful for multi-region deployments and optimizing user experience via geographic routing.

In this post, we’ll walk you through the practical aspects of deploying load balancing solutions for both layer 4 and layer 7 traffic. You’ll see how to set these up using the Azure Portal and using Infrastructure-as-Code (IaC) via Bicep.

Comparing Azure Load Balancing Solutions

Azure Load Balancer (Layer 4)

  • What It Does:
    Operates at the transport layer to efficiently distribute incoming TCP/UDP traffic among healthy backend instances.
  • Use Cases:
    Ideal for scalable, high-performance workloads where low latency is required.
  • Implementation:
    Configure frontend IPs, backend pools, health probes, and load balancing rules.

Azure Application Gateway (Layer 7)

  • What It Does:
    Inspects traffic at the HTTP/HTTPS level and provides features like URL-based routing, SSL offloading, and session affinity.
  • Use Cases:
    Best suited for web application scenarios where you need to load balance and secure HTTP traffic.
  • Implementation:
    Define configurations for frontend IPs, listeners, HTTP settings, backend pools, and request routing rules.

Azure Traffic Manager

  • What It Does:
    Uses DNS-based routing to direct clients to optimal endpoints based on performance, geographic location, or failover policies.
  • Use Cases:
    Excellent for global distribution scenarios, disaster recovery, and performance optimization across regions.
  • Implementation:
    Managed at the DNS level, with health checks and endpoint monitoring.
ℹ️
Note: While Traffic Manager is not directly “in-path” like Load Balancer or Application Gateway, it ensures that users reach the right endpoint before the traffic even enters your Azure environment.

Setting Up Load Balancing for Layer 4 Traffic

Using the Azure Portal

  1. Create a Public Load Balancer:

    • Sign in to the Azure Portal.
    • Click Create a resource and search for Load Balancer.
    • Choose Public as the SKU.
    • In the Basics tab, provide a unique name (e.g., MyPublicLB), select your subscription and resource group, and specify your region.
    • Configure the Frontend IP Configuration by selecting or creating a new Public IP.
    • Click Review + create and then Create once validation passes.
  2. Configure the Backend Pool and Health Probe:

    • After deployment, navigate to your Load Balancer resource.
    • Under Settings, select Backend pools; add your virtual machines or scale set instances.
    • Under Health Probes, define a probe (e.g., TCP on port 80) to monitor the health of your backend resources.
  3. Set Up Load Balancing Rules:

    • Under Load balancing rules, create a rule that associates the frontend configuration and the backend pool, specifying ports and protocols (e.g., forwarding TCP port 80).

Using Bicep

Below is a sample Bicep template that provisions an Azure Load Balancer for layer 4 traffic along with a virtual network and backend linux server.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Parameters
@description('Location for all resources')
param location string = resourceGroup().location

@description('Azure Virtual Network Name')
param vnetName string = 'myVNet'

@description('Azure Virtual Network Subnet Name')
param subnetName string = 'mySubnet'

@description('Name of the Load Balancer')
param loadBalancerName string = 'myOutboundLoadBalancer'

@description('Azure LoadBalancer Public IP Name')
param lbPublicIPName string = 'myOutboundLB-PublicIP'

@description('Name of the Backend Pool')
param backendPoolName string = 'myBackendPool'

param adminUsername string = 'azureuser'
@secure()
param adminPassword string

// 1. Virtual Network with a default subnet
resource vnet 'Microsoft.Network/virtualNetworks@2024-05-01' = {
  name: vnetName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.0.0.0/16'
      ]
    }
    subnets: [
      {
        name: subnetName
        properties: {
          addressPrefix: '10.0.0.0/24'
        }
      }
    ]
  }
}

// 2. Public IP for the Load Balancer frontend
resource lbPublicIP 'Microsoft.Network/publicIPAddresses@2024-05-01' = {
  name: lbPublicIPName
  location: location
  sku: {
    name: 'Standard'
  }
  properties: {
    publicIPAllocationMethod: 'Static'
  }
}

// 3. Load Balancer for outbound access with a backend pool and outbound rule
resource loadBalancer 'Microsoft.Network/loadBalancers@2024-05-01' = {
  name: loadBalancerName
  location: location
  sku: {
    name: 'Standard'
  }
  properties: {
    // Frontend configuration referencing the public IP
    frontendIPConfigurations: [
      {
        name: 'LoadBalancerFrontEnd'
        properties: {
          publicIPAddress: {
            id: lbPublicIP.id
          }
        }
      }
    ]
    // Define the backend pool
    backendAddressPools: [
      {
        name: backendPoolName
      }
    ]
    // Outbound rule so VMs in the backend pool can access the internet
    outboundRules: [
      {
        name: 'LBOutboundRule'
        properties: {
          frontendIPConfigurations: [
            {
              id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', loadBalancerName, 'LoadBalancerFrontend')
            }
          ]
          backendAddressPool: {
            id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', loadBalancerName, backendPoolName)
          }
          protocol: 'All'
          idleTimeoutInMinutes: 5
          allocatedOutboundPorts: 1024
        }
      }
    ]
  }
}

// 4. Network Interface attached to the vNet and associated with the LB backend pool
resource nic 'Microsoft.Network/networkInterfaces@2024-05-01' = {
  name: 'vmNic'
  location: location
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          privateIPAllocationMethod: 'Dynamic'
          subnet: {
            id: '${vnet.id}/subnets/default'
          }
          // Associate NIC with the Load Balancer backend pool for outbound connectivity
          loadBalancerBackendAddressPools: [
            {
              id: '${loadBalancer.id}/backendAddressPools/backendPool'
            }
          ]
        }
      }
    ]
  }
}

// 5. Linux Virtual Machine attached to the NIC
resource linuxVM 'Microsoft.Compute/virtualMachines@2024-07-01' = {
  name: 'vm-linux-01'
  location: location
  properties: {
    hardwareProfile: {
      vmSize: 'Standard_DS1_v2'
    }
    osProfile: {
      computerName: 'vm'
      adminUsername: adminUsername
      adminPassword: adminPassword
      linuxConfiguration: {
        disablePasswordAuthentication: false
      }
    }
    storageProfile: {
      imageReference: {
        publisher: 'Canonical'
        offer: 'UbuntuServer'
        sku: '18.04-LTS'
        version: 'latest'
      }
      osDisk: {
        createOption: 'FromImage'
      }
    }
    networkProfile: {
      networkInterfaces: [
        {
          id: nic.id
        }
      ]
    }
  }
}

Deployment Steps:

  1. Save the template as loadBalancerDeployment.bicep.

  2. Open your terminal and run:

    1
    2
    3
    4
    
    az login
    az deployment group create \
      --resource-group MyResourceGroup \
      --template-file loadBalancerDeployment.bicep
    

Setting Up Load Balancing for Layer 7 Traffic

Using the Azure Portal

  1. Create an Application Gateway:

    • In the Azure Portal, click Create a resource and search for Application Gateway.
    • In the Basics tab, enter details like the name (e.g., MyAppGateway), resource group, region, and select the appropriate SKU (e.g., Standard_v2).
    • Under Settings, configure the Virtual Network and subnet dedicated for the gateway.
    • In Frontend IP configurations, assign a Public IP (create one if necessary).
    • Click Review + create, and then Create.
  2. Configure HTTP Settings and Listeners:

    • After deployment, navigate to your Application Gateway resource.
    • Under Backend pools, define the pool of servers (or VMs) where your application resides.
    • Configure an HTTP setting (including port, protocol, and session affinity settings).
    • Create an HTTP Listener that binds the frontend IP, port, and protocol.
    • Under Rules, create a request routing rule to map the listener to the backend pool and HTTP settings.

Using Bicep

Below is a sample Bicep template that deploys a basic Application Gateway for layer 7 load balancing. (Note: Replace the <subnetResourceId> placeholder with your subnet’s resource ID.)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Parameters
@description('Name of the Application Gateway')
param appGwName string = 'MyAppGateway'

@description('Location for all resources')
param location string = resourceGroup().location

@description('Public IP Name for the Application Gateway')
param publicIpName string = 'MyAppGwPublicIP'

@description('Virtual Network Name')
param vnetname string = 'MyVNet'

@description('Subnet Name for the Application Gateway')
param subnetname string = 'MyAppGwSubnet'

// Virtual Network and Subnet pre-existing for your Application Gateway
resource vnet 'Microsoft.Network/virtualNetworks@2024-05-01' existing = {
  name: vnetname
}

resource subnet 'Microsoft.Network/virtualNetworks/subnets@2024-05-01' existing = {
  parent: vnet
  name: subnetname
}

// Public IP for the Application Gateway
resource publicIpAppGw 'Microsoft.Network/publicIPAddresses@2024-05-01' = {
  name: publicIpName
  location: location
  sku: {
    name: 'Standard'
  }
  properties: {
    publicIPAllocationMethod: 'Static'
  }
}

// Application Gateway Resource
resource appGw 'Microsoft.Network/applicationGateways@2024-05-01' = {
  name: appGwName
  location: location
  properties: {
    sku: {
      name: 'Standard_v2'
      tier: 'Standard_v2'
      capacity: 1
    }
    gatewayIPConfigurations: [
      {
        name: 'appGwIpConfig'
        properties: {
          subnet: {
            id: subnet.id
          }
        }
      }
    ]
    frontendIPConfigurations: [
      {
        name: 'appGwFrontend'
        properties: {
          publicIPAddress: {
            id: publicIpAppGw.id
          }
        }
      }
    ]
    frontendPorts: [
      {
        name: 'appGwFrontendPort'
        properties: {
          port: 80
        }
      }
    ]
    backendAddressPools: [
      {
        name: 'appGwBackendPool'
      }
    ]
    backendHttpSettingsCollection: [
      {
        name: 'appGwHttpSettings'
        properties: {
          port: 80
          protocol: 'Http'
          cookieBasedAffinity: 'Disabled'
        }
      }
    ]
    httpListeners: [
      {
        name: 'appGwHttpListener'
        properties: {
          frontendIPConfiguration: {
            id: resourceId('Microsoft.Network/applicationGateways/frontendIPConfigurations', appGwName, 'appGwFrontend')
          }
          frontendPort: {
            id: resourceId('Microsoft.Network/applicationGateways/frontendPorts', appGwName, 'appGwFrontendPort')
          }
          protocol: 'Http'
        }
      }
    ]
    requestRoutingRules: [
      {
        name: 'appGwRoutingRule'
        properties: {
          ruleType: 'Basic'
          priority: 100
          httpListener: {
            id: resourceId('Microsoft.Network/applicationGateways/httpListeners', appGwName, 'appGwHttpListener')
          }
          backendAddressPool: {
            id: resourceId('Microsoft.Network/applicationGateways/backendAddressPools', appGwName, 'appGwBackendPool')
          }
          backendHttpSettings: {
            id: resourceId('Microsoft.Network/applicationGateways/backendHttpSettingsCollection', appGwName, 'appGwHttpSettings')
          }
        }
      }
    ]
  }
}

Deployment Steps:

  1. Save the template as appGatewayDeployment.bicep.

  2. Deploy the template with:

    1
    2
    3
    4
    
    az login
    az deployment group create \
      --resource-group MyResourceGroup \
      --template-file appGatewayDeployment.bicep
    

Setting Up Global Load Balancing Traffic

Using the Azure Portal

  1. Create an Traffic Manager:

    • In the Azure Portal, click Create a resource and search for Traffic Manager.

    • In the Basics tab, enter details like the name (e.g., MyExternalApp) which needs to be globally unique, resource group, region (This is for the resource element only, the configuration is global), and select the routing method (choose from the below options)

      • Performance: Distributes traffic requests to the origin with the lowest network latency, rather than the closest geographic location, ensuring optimal performance
      • Weighted: Distributes traffic based on assigned weights to each origin. This allows you to control the proportion of traffic sent to different origins, which can be useful for load balancing and testing new deployments.
      • Priority: Routes traffic to the primary origin by default. If the primary origin becomes unavailable, traffic is routed to the secondary origin. This ensures high availability and failover capabilities.
      • Geographic: Routes traffic based on the geographic location of the user. This method ensures that users are directed to the nearest origin, reducing latency and improving performance.
      • Multivalue: Returns multiple IP addresses for a single DNS query. This method allows clients to choose the best IP address to connect to, enhancing redundancy and load distribution.
      • Subnet: Routes traffic based on the user’s IP address subnet. This method is useful for directing traffic from specific subnets to designated origins, providing more granular control over traffic routing.
    • Under Settings, configure the Virtual Network and subnet dedicated for the gateway.

    • In Frontend IP configurations, assign a Public IP (create one if necessary).

    • Click Review + create, and then Create.

  2. Configure HTTP Settings and Listeners:

    • After deployment, navigate to your Application Gateway resource.
    • Under Backend pools, define the pool of servers (or VMs) where your application resides.
    • Configure an HTTP setting (including port, protocol, and session affinity settings).
    • Create an HTTP Listener that binds the frontend IP, port, and protocol.
    • Under Rules, create a request routing rule to map the listener to the backend pool and HTTP settings.

Using Bicep

Below is a sample Bicep template that deploys a basic Application Gateway for layer 7 load balancing. (Note: Replace the <subnetResourceId> placeholder with your subnet’s resource ID.)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
// Parameters
param appGwName string = 'MyAppGateway'
param location string = resourceGroup().location
param publicIpName string = 'MyAppGwPublicIP'
param subnetId string  // Provide the resource ID of the subnet for the Application Gateway

// Public IP for the Application Gateway
resource publicIpAppGw 'Microsoft.Network/publicIPAddresses@2020-11-01' = {
  name: publicIpName
  location: location
  sku: {
    name: 'Standard'
  }
  properties: {
    publicIPAllocationMethod: 'Static'
  }
}

// Application Gateway Resource
resource appGw 'Microsoft.Network/applicationGateways@2020-11-01' = {
  name: appGwName
  location: location
  sku: {
    name: 'Standard_v2'
    tier: 'Standard_v2'
  }
  properties: {
    gatewayIPConfigurations: [
      {
        name: 'appGwIpConfig'
        properties: {
          subnet: {
            id: subnetId
          }
        }
      }
    ]
    frontendIPConfigurations: [
      {
        name: 'appGwFrontend'
        properties: {
          publicIPAddress: {
            id: publicIpAppGw.id
          }
        }
      }
    ]
    frontendPorts: [
      {
        name: 'appGwFrontendPort'
        properties: {
          port: 80
        }
      }
    ]
    backendAddressPools: [
      {
        name: 'appGwBackendPool'
      }
    ]
    backendHttpSettingsCollection: [
      {
        name: 'appGwHttpSettings'
        properties: {
          port: 80
          protocol: 'Http'
          cookieBasedAffinity: 'Disabled'
        }
      }
    ]
    httpListeners: [
      {
        name: 'appGwHttpListener'
        properties: {
          frontendIPConfiguration: {
            id: appGw::frontendIPConfigurations[0].id
          }
          frontendPort: {
            id: appGw::frontendPorts[0].id
          }
          protocol: 'Http'
        }
      }
    ]
    requestRoutingRules: [
      {
        name: 'appGwRoutingRule'
        properties: {
          ruleType: 'Basic'
          httpListener: {
            id: appGw::httpListeners[0].id
          }
          backendAddressPool: {
            id: appGw::backendAddressPools[0].id
          }
          backendHttpSettings: {
            id: appGw::backendHttpSettingsCollection[0].id
          }
        }
      }
    ]
  }
}

Deployment Steps:

  1. Save the template as appGatewayDeployment.bicep.

  2. Deploy the template with:

    1
    2
    3
    4
    5
    
    az login
    az deployment group create \
      --resource-group MyResourceGroup \
      --template-file appGatewayDeployment.bicep \
      --parameters subnetId="/subscriptions/<sub-id>/resourceGroups/<rg-name>/providers/Microsoft.Network/virtualNetworks/<vnet-name>/subnets/<subnet-name>"
    

Conclusion

In this post, we explored a range of load balancing solutions available in Azure—from the low-level, high-performance Azure Load Balancer to the feature-rich, application-aware Azure Application Gateway, and the globally distributed Azure Traffic Manager. By examining their differences and deploying examples for both layer 4 and layer 7 traffic, you now have a clearer understanding of how to tailor Azure’s load balancing solutions to meet your application’s needs.

Whether you prefer the hands-on approach using the Azure Portal or a repeatable deployment via Bicep, these tools enhance your network’s resilience and scalability.

Learn More