Load Balanced Vms

Architecture

Load Balanced Vms is an infrastructure-building script that sets up a cluster of VMs and configures a Load Balancer to expose a public route to the VMs:

  • Compute - VMs - Compute Engine
  • Compute - Cluster - Managed Instance Group
  • Compute - Machine Template - Instance Template
  • Networking - Load Balancing - Cloud Load Balancer

This example application sets up a simple static HTML site using NGINX, and load balances across your cluster.


Get Started

Click on the following link to a copy of the source code in Cloud Shell. Once there, a single command will spin up a working copy of the application in your project..

Open in Cloud Shell

View source code on GitHub


Load Balanced Vms components

The Load Balanced Vms architecture makes use of several products. The following lists the components, along with more information on the components, including links to related videos, product documentation, and interactive walkthroughs.
Video Docs Walkthroughs
Compute Engine Compute Engine is Google Cloud's Virtual technology. With it you can spin up many different configurations of VM to fit the shape of whatever computing needs you have.
Cloud Load Balancing Google Cloud Load Balancer allows you to place a load balancer in front of the Storage Bucket - allowing you to use SSL certificates, Logging and Monitoring.

Scripts

The install script uses an executable written in go and Terraform CLI tools to take an empty project and install the application in it. The output should be a working application and a url for the load balancing IP address.

./main.tf

Enable services

Google Cloud Services are disabled in a project by default. Activate the following required services:

  • Compute Engine - Virtual machines and networking services (like Load Balancer)
variable "gcp_service_list" {
  description = "The list of apis necessary for the project"
  type        = list(string)
  default = [
      "compute.googleapis.com",
  ]
}

resource "google_project_service" "all" {
  for_each                   = toset(var.gcp_service_list)
  project                    = var.project_number
  service                    = each.key
  disable_dependent_services = false
  disable_on_destroy         = false
}

Create instance exemplar on which to base managed VMs

The following command creates a VM, installs the required software on it, and deploys code.

resource "google_compute_instance" "exemplar" {
    name         = "${var.basename}-exemplar"
    machine_type = "n1-standard-1"
    zone         = var.zone
    project      = var.project_id

    tags                    = ["http-server"]
    metadata_startup_script = "apt-get update -y \n apt-get install nginx -y \n  printf '${data.local_file.index.content}'  | tee /var/www/html/index.html \n chgrp root /var/www/html/index.html \n chown root /var/www/html/index.html \n chmod +r /var/www/html/index.html"

    boot_disk {
        auto_delete = true
        device_name = "${var.basename}-exemplar"
        initialize_params {
        image = "family/debian-10"
        size  = 200
        type  = "pd-standard"
        }
    }

    network_interface {
        network = "default"
        access_config {
        // Ephemeral public IP
        }
    }

    depends_on = [google_project_service.all]
}

Create snapshot and disk image

The following command uses the VM to create a snapshot. The snapshot is then used to create a Disk Image. All VMs in the cluster are based on this image.

resource "google_compute_snapshot" "snapshot" {
    project           = var.project_id
    name              = "${var.basename}-snapshot"
    source_disk       = google_compute_instance.exemplar.boot_disk[0].source
    zone              = var.zone
    storage_locations = ["${var.region}"]
    depends_on        = [time_sleep.startup_completion]
}

resource "google_compute_image" "exemplar" {
    project         = var.project_id
    name            = "${var.basename}-latest"
    family          = var.basename
    source_snapshot = google_compute_snapshot.snapshot.self_link
    depends_on      = [google_compute_snapshot.snapshot]
}

Create instance template

The following command creates a template with the settings and configuration for all of the VMs in the cluster. It uses the disk image created in the previous command.

The command includes a startup script that customizes the HTML that is served up by the VMs in the cluster.

 resource "google_compute_instance_template" "default" {
    project     = var.project_id
    name        = "${var.basename}-template"
    description = "This template is used to create app server instances."
    tags        = ["httpserver"]

    metadata_startup_script = "sed -i.bak \"s/\{\{NODENAME\}\}/$HOSTNAME/\" /var/www/html/index.html"

    instance_description = "BasicLB node"
    machine_type         = "n1-standard-1"
    can_ip_forward       = false

    // Create a new boot disk from an image
    disk {
        source_image = google_compute_image.exemplar.self_link
        auto_delete  = true
        boot         = true
    }

    network_interface {
        network = "default"
    }

    depends_on = [google_compute_image.exemplar]
}

Create Managed Instance Group

Requests N number of machines of the Instance Template to be managed as part of this group.

resource "google_compute_instance_group_manager" "default" {
    project            = var.project_id
    name               = "${var.basename}-mig"
    zone               = var.zone
    target_size        = var.nodes
    base_instance_name = "${var.basename}-mig"


    version {
        instance_template = google_compute_instance_template.default.id
    }

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

    depends_on = [google_compute_instance_template.default]
}

Create External IP Address

Needed to bind a host to the domain name, and communicate in general on the Internet.

resource "google_compute_global_address" "default" {
    project    = var.project_id
    name       = "${var.basename}-ip"
    ip_version = "IPV4"
}

Create load balancer

The following command creates a load balancer and implements health checks and backend services. It configures the load balancer to connect to the instance group.

resource "google_compute_health_check" "http" {
    project = var.project_id
    name    = "${var.basename}-health-chk"

    tcp_health_check {
        port = "80"
    }
}

resource "google_compute_firewall" "allow-health-check" {
    project       = var.project_id
    name          = "allow-health-check"
    network       = local.defaultnetwork
    source_ranges = ["130.211.0.0/22", "35.191.0.0/16"]

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

resource "google_compute_backend_service" "default" {
    project               = var.project_id
    name                  = "${var.basename}-service"
    load_balancing_scheme = "EXTERNAL"
    protocol              = "HTTP"
    port_name             = "http"
    backend {
        group = google_compute_instance_group_manager.default.instance_group
    }

    health_checks = [google_compute_health_check.http.id]
    }

    resource "google_compute_url_map" "lb" {
    project         = var.project_id
    name            = "${var.basename}-lb"
    default_service = google_compute_backend_service.default.id
}

Enable HTTP

The following command configures networking rules that point port 80 on the load balancer to the service.

resource "google_compute_target_http_proxy" "default" {
    project = var.project_id
    name    = "${var.basename}-lb-proxy"
    url_map = google_compute_url_map.lb.id
}

resource "google_compute_forwarding_rule" "google_compute_forwarding_rule" {
    project               = var.project_id
    name                  = "${var.basename}-http-lb-forwarding-rule"
    provider              = google-beta
    region                = "none"
    load_balancing_scheme = "EXTERNAL"
    port_range            = "80"
    target                = google_compute_target_http_proxy.default.id
    ip_address            = google_compute_global_address.default.id
}

Conclusion

Once run you should now have a simple web site running on multiple Compute Engine instances, fronted by a load balancer in your project. Additionally you should have all of the code to modify or extend this solution to fit your environment.