January 20, 2020

Creating an Instance Group (AutoScaling) on GCP with terraform

How to setup autoscaling with terraform on GCP. Set up a managed autoscale cluster on GCP

Creating an Instance Group (AutoScaling) on GCP with terraform

The follow is a demonstration of how to start up an Instance group with Terraform (AutoScaling) on GCP.

The script provided will generate the following:

  • An Instance Group
  • Two VPC's
  • Two subnets (one per vpc) with ranges of 172.16.8.0/24 and 172.16.0.0/24
  • Two Firewall rules to controll ingress/egress
    • tcp:80,443,22,icmp on the external facing subnet
    • all ports/protocls on the internal facing subnet
  • An instance Group template for autoscaling
    • Instances will have two nics
  • An Autoscaler to determine when to scale in or out (More or less vms)
  • An autoscale healthcheck to determine if the instance is healthy

If you don't have Terraform installed you can follow the instructions here: https://learn.hashicorp.com/terraform/getting-started/install.html

Clone the Repo

The first thing you will need to do is clone the repo from github

git clone https://github.com/doublebracket/gcpInstanceGroupTerraform.git

Create an Authentication token in GCP

To do this navigate to your GCP console and select IAM & admin -> Service accounts. Create a service account if you don't already have one. The default Compute engine account will be sufficient. Then create a key.

Copy  the key as account.json to the cloned folder

mv ~/Downloads/<key> ./account.json

Open the vars.tf folder and edit the projects var  to include your GCP project

variable "project" {
  type    = "string"
  default = "<your project>"
}

By default the script uses your public key at ~/.ssh/id_rsa.pub change this if you need to under

variable "public_key_path" {
  type    = "string"
  default = "~/.ssh/id_rsa.pub"
}

Next run terraform init

terraform init

Run terraform apply

terraform apply

Plan: 11 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

Terraform will begin to create the autoscale group.

You will be able to ssh as [email protected]<ip address> to the VM

Find the IP by navigating to compute engine and looking up autoscaledemo

Destroy the cluster

Destroy the cluster with the terraform destroy command

terraform destroy

Understanding the script

The script is split into three pieces

main.tf - This contains the main resources including the autoscale group and the image template

vars.tf - This contains just the editable variables, such as subnet ranges, regions etc.

vpc.tf - This contains the info on subnets firewalls and the VPCs

The Instance Template

When you spin up an autoscale cluster it needs to have a base image from which to launch VMs from. This is where we specify startup scripts, ssh keys and our base image. In this case we are using a Centos 7 image.

resource "google_compute_instance_template" "default" {
  name        = "${var.cluster_name}-template-${random_string.random_name_suffix.result}"
  description = "This template is used to create app server instances."
  instance_description = "AutoScale Demo"
  machine_type         = "${var.instance}"
  can_ip_forward       = true

  scheduling {
    automatic_restart   = true
    on_host_maintenance = "MIGRATE"
  }

  # Create a new boot disk from an image
  disk {
    source_image = "${var.autoscale_demo_image}"
    auto_delete  = true
    boot         = true
  }
  # Secondary Disk
  disk {
    auto_delete = true
    boot        = false
    disk_size_gb = 30
  }
# Public facing managment port with public IP.
  network_interface {
    subnetwork = "${google_compute_subnetwork.public_subnet.self_link}"
    # Add a public IP (default ephemeral)
    access_config {
      nat_ip = ""
    }
  }
   # Secondary Interface for internal traffic etc.
      network_interface {
     subnetwork = "${google_compute_subnetwork.internal_subnet.self_link}"
   }

  # Cloud-init user data can be specificed here
  # A linter may pick up the : as an illegal character. It should still work regardless.
  metadata = {
    startup-script = "${file(var.cloud_init_data)}"
    ssh-keys="admin:${file(var.public_key_path)}"
  }


}

Cloud-init or startup data can be specifed like this:

metadata = {
    startup-script = "startup-script:${file(var.cloud_init_data)}"
    ssh-keys="admin:${file(var.public_key_path)}"
  }

In this example we passed a file that installs apache  and starts a basic web server.

Disks are set to auto_delete = true. This stops disks from accumulating when the groups scale in and out. Multiple disks can be specified

disk {
    source_image = "${var.autoscale_demo_image}"
    auto_delete  = true
    boot         = true
  }
  # Secondary Disk
  disk {
    auto_delete = true
    boot        = false
    disk_size_gb = 30
  }

You can specifiy a different VM image like ubuntu in the vars.tf file under:

variable "autoscale_demo_image" {
  type = "string"
  default = "projects/centos-cloud/global/images/centos-7-v20191210"
}

It can be difficult on GCP to find the image you are looking for especially if it is a cutom image. The following shows you how to easily find an image on GCP https://doublebracket.io/find-marketplace-image-names-on-gcp-for-terraform/

The AutoScaler

The autoscaler allows you to specify when to scale in or out (more or less vms) in this case we specify cpu_utlization as the trigger to scale out at. In this case the vars.tf looks like this:

variable "cpu_utilization" {
  type    = number
  default = 0.80 
}

This means the cluster will target an 80% cpu usage. Meaning it will scale in and out to try to get the usage around this number up to a max of 2 vms in this case.

resource "google_compute_region_autoscaler" "default" {
  project  = "${var.project}"
  #Name needs to be in lowercase
  name   = "${var.cluster_name}-autoscaler-demo-${random_string.random_name_suffix.result}"
  region = "${var.region}"
  target = "${google_compute_region_instance_group_manager.InstanceGroup.self_link}"


  autoscaling_policy {
    max_replicas    = "${var.max_replicas}"
    min_replicas    = "${var.min_replicas}"
    cooldown_period = "${var.cooldown_period}"

    cpu_utilization {
      target = "${var.cpu_utilization}"
    }
  }
}