Skip to content

Terraform Lab 07 — Layer 4 vs. Layer 7 Load Balancing

Load Balancers act as intelligent traffic dispatchers. This lab covers the difference between Layer 4 (Transport / TCP/UDP) and Layer 7 (Application / HTTP/HTTPS) Load Balancing.

Lab contract

Item This lab
Execution model Standalone focused scenario with a new working directory and state
Starts from Only a GCP project ID; all backend infrastructure is created by this lab
Creates VPC, subnet, firewall, two instance templates, two managed instance groups, and the complete Layer 7 load-balancing chain
Cost note Global forwarding and backend resources may incur charges; destroy after verification
Next Lab 08 — IPsec VPN & BGP

Resource summary

Terraform block Count Purpose
google_compute_network / google_compute_subnetwork 2 Provides an isolated backend network
google_compute_instance_template 2 Defines the web and API VM configurations
google_compute_instance_group_manager 2 Creates one managed web VM and one managed API VM
google_compute_health_check.http_health 1 Determines backend eligibility
google_compute_backend_service 2 Separate web and API backend pools
google_compute_url_map.l7_url_map 1 Sends /api/* to API and other paths to web
google_compute_target_http_proxy.http_proxy 1 Terminates the HTTP frontend and uses the URL map
google_compute_global_forwarding_rule.forwarding_rule 1 Creates the public TCP 80 entry point
output.load_balancer_ip 1 Exposes the assigned frontend address for verification

Layer 7 load-balancing chain from global forwarding rule through proxy and URL map to web and API backend services

Comparison Matrix: Layer 4 vs. Layer 7

Feature Layer 4 (Network Load Balancer) Layer 7 (Application Load Balancer)
OSI Layer Layer 4 (TCP / UDP) Layer 7 (HTTP / HTTPS / gRPC)
Inspection Capability IP addresses and Port numbers only URLs, HTTP headers, Cookies, Query parameters
Routing Capability Distributes to a single backend pool Content-based routing (/api → API pool, /images → Storage)
SSL/TLS Termination Pass-through (Client connects directly to VM) Offloads TLS certificates at the edge; talks HTTP internally
Performance Extreme throughput, lowest latency Rich traffic management, URL rewrites, and security

Complete Terraform Configuration: L7 Cloud HTTP Load Balancer

terraform {
  required_version = ">= 1.5.0"

  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
  }
}

provider "google" {
  project = var.project_id
  region  = var.region
}

variable "project_id" {
  description = "GCP project ID to deploy into"
  type        = string
}

variable "region" {
  description = "GCP region for the load-balancer backends"
  type        = string
  default     = "us-central1"
}

resource "google_compute_network" "lb_vpc" {
  name                    = "lb-vpc"
  auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "lb_subnet" {
  name          = "lb-backend-subnet"
  ip_cidr_range = "10.20.0.0/24"
  region        = var.region
  network       = google_compute_network.lb_vpc.id
}

resource "google_compute_firewall" "allow_health_checks" {
  name    = "allow-lb-health-checks"
  network = google_compute_network.lb_vpc.id

  allow {
    protocol = "tcp"
    ports    = ["80"]
  }

  source_ranges = ["35.191.0.0/16", "130.211.0.0/22"]
  target_tags   = ["lb-backend"]
}

resource "google_compute_instance_template" "web" {
  name_prefix  = "web-template-"
  machine_type = "e2-micro"
  tags         = ["lb-backend"]

  disk {
    source_image = "debian-cloud/debian-12"
    auto_delete  = true
    boot         = true
  }

  network_interface {
    subnetwork = google_compute_subnetwork.lb_subnet.id
  }

  metadata_startup_script = <<-EOF
    #!/bin/bash
    install -d /opt/web
    echo "Web backend response" > /opt/web/index.html
    echo "healthy" > /opt/web/health
    python3 -m http.server 80 --directory /opt/web >/var/log/web-backend.log 2>&1 &
  EOF

  lifecycle {
    create_before_destroy = true
  }
}

resource "google_compute_instance_template" "api" {
  name_prefix  = "api-template-"
  machine_type = "e2-micro"
  tags         = ["lb-backend"]

  disk {
    source_image = "debian-cloud/debian-12"
    auto_delete  = true
    boot         = true
  }

  network_interface {
    subnetwork = google_compute_subnetwork.lb_subnet.id
  }

  metadata_startup_script = <<-EOF
    #!/bin/bash
    install -d /opt/api/api
    echo "API backend response" > /opt/api/api/users
    echo "healthy" > /opt/api/health
    python3 -m http.server 80 --directory /opt/api >/var/log/api-backend.log 2>&1 &
  EOF

  lifecycle {
    create_before_destroy = true
  }
}

resource "google_compute_instance_group_manager" "web" {
  name               = "web-mig"
  base_instance_name = "web"
  zone               = "${var.region}-a"
  target_size        = 1

  version {
    instance_template = google_compute_instance_template.web.id
  }

  named_port {
    name = "http"
    port = 80
  }
}

resource "google_compute_instance_group_manager" "api" {
  name               = "api-mig"
  base_instance_name = "api"
  zone               = "${var.region}-a"
  target_size        = 1

  version {
    instance_template = google_compute_instance_template.api.id
  }

  named_port {
    name = "http-api"
    port = 80
  }
}

# 1. Health Check (Probes backend VMs on port 80)
resource "google_compute_health_check" "http_health" {
  name               = "app-http-health-check"
  check_interval_sec = 5
  timeout_sec        = 3
  healthy_threshold   = 2
  unhealthy_threshold = 3

  http_health_check {
    port         = 80
    request_path = "/health"
  }
}

# 2. Backend Services (Groups of VMs serving specific traffic)
resource "google_compute_backend_service" "web_backend" {
  name                  = "web-backend-service"
  protocol              = "HTTP"
  port_name             = "http"
  load_balancing_scheme = "EXTERNAL_MANAGED"
  health_checks         = [google_compute_health_check.http_health.id]

  backend {
    group = google_compute_instance_group_manager.web.instance_group
  }
}

resource "google_compute_backend_service" "api_backend" {
  name                  = "api-backend-service"
  protocol              = "HTTP"
  port_name             = "http-api"
  load_balancing_scheme = "EXTERNAL_MANAGED"
  health_checks         = [google_compute_health_check.http_health.id]

  backend {
    group = google_compute_instance_group_manager.api.instance_group
  }
}

# 3. URL Map (The Layer 7 Routing Brain)
resource "google_compute_url_map" "l7_url_map" {
  name            = "prod-global-url-map"
  default_service = google_compute_backend_service.web_backend.id

  host_rule {
    hosts        = ["*"]
    path_matcher = "allpaths"
  }

  path_matcher {
    name            = "allpaths"
    default_service = google_compute_backend_service.web_backend.id

    # Content-based routing: /api/* routes to dedicated API instances
    path_rule {
      paths   = ["/api", "/api/*"]
      service = google_compute_backend_service.api_backend.id
    }
  }
}

# 4. Target HTTP Proxy
resource "google_compute_target_http_proxy" "http_proxy" {
  name    = "prod-http-proxy"
  url_map = google_compute_url_map.l7_url_map.id
}

# 5. Global Forwarding Rule (Public Entry Point with Anycast VIP)
resource "google_compute_global_forwarding_rule" "forwarding_rule" {
  name                  = "http-global-entry"
  target                = google_compute_target_http_proxy.http_proxy.id
  port_range            = "80"
  load_balancing_scheme = "EXTERNAL_MANAGED"
}

output "load_balancer_ip" {
  description = "Global frontend IP assigned to the HTTP load balancer"
  value       = google_compute_global_forwarding_rule.forwarding_rule.ip_address
}

Apply and verify

Lab 07 creates its VPC, subnet, firewall rule, instance templates, and managed instance groups. Only project_id is required; all resource names and backend group links are derived inside Terraform.

cd docs/terraform/07-load-balancing-l4-l7
export PROJECT_ID="YOUR_PROJECT_ID"

gcloud services enable compute.googleapis.com \
  --project="$PROJECT_ID"

terraform init
terraform fmt -check
terraform validate
terraform plan \
  -input=false \
  -var="project_id=$PROJECT_ID"
terraform apply \
  -var="project_id=$PROJECT_ID"

Wait for both managed instances and load-balancer backends to become healthy, then test path-based routing:

# 1. Fetch the global virtual IP assigned to the load balancer.
VIP="$(terraform output -raw load_balancer_ip)"

# 2. Test the default web backend.
curl "http://$VIP/"
# Expected: Web backend response

# 3. Test the /api path routed to the API backend.
curl "http://$VIP/api/users"
# Expected: API backend response

Cleanup and next step

terraform destroy \
  -var="project_id=$PROJECT_ID"

This destroys the complete standalone Lab 07 stack, including both managed instance groups and their VMs. Continue to Lab 08 — IPsec VPN & BGP.