Chapter 9: Building Real GCP Infrastructure
Topic 60

Compute Engine and Managed Instance Groups

ComputeMIG

A single google_compute_instance is a pet; production Compute Engine on GCP is a managed instance group stamping identical VMs from an instance template, healed and scaled automatically. For Hatch's VM-based workloads you write one google_compute_instance_template, wrap it in a regional google_compute_region_instance_group_manager, attach an autoscaler and a health check, and roll new versions by changing the template — the VMs themselves are disposable.

That disposability is the whole design. No VM is special, none holds irreplaceable state, and any one of them can vanish and be recreated from the template without a human. Once you accept that the template is the source of truth and the running instances are cattle, the rest of the MIG model — autoscaling, autohealing, rolling updates — falls out naturally.

The Instance Template

A google_compute_instance_template is the immutable blueprint: machine type, boot image, disks, network tags, service account, and startup script. It cannot be edited in place. A change means a new template version, which feels like friction until you see it is exactly the property that makes rollouts safe — every version is a distinct, named, reproducible artifact.

Try to mutate a template and Terraform forces a replacement; trying to treat it as editable blocks every rollout. The right mental model is that you never change a template — you create the next one and point the MIG at it.

An immutable instance template
resource "google_compute_instance_template" "app" {
  project      = "hatch-app-prod"
  name_prefix  = "app-"            # new template = new version, never edited in place
  machine_type = "e2-standard-2"
  tags         = ["https-server"]   # picks up the firewall rule from Topic 58

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

  network_interface {
    subnetwork = "projects/hatch-net-host/regions/us-central1/subnetworks/app-uc1"
    # no access_config block = no external IP
  }

  lifecycle {
    create_before_destroy = true   # so a referenced template can be replaced
  }
}

Regional vs Zonal MIG

A google_compute_region_instance_group_manager spreads its instances across the zones of us-central1 and survives a single-zone outage. The zonal google_compute_instance_group_manager pins every instance to one zone, so a routine zone failure takes the entire group down. For anything with an SLO, the choice is regional — the zonal manager is for cases where you genuinely want everything in one zone.

This is where the regional-subnet model from Topic 56 pays off: because the subnet is regional, a regional MIG places instances in any zone of us-central1 on the same subnet with no per-zone networking to set up. The platform spreads the group for you.

The Autoscaler

A google_compute_region_autoscaler scales the MIG between min_replicas and max_replicas on CPU, load-balancer utilization, or a custom metric. Once an autoscaler is attached, the MIG owns the instance count — you never set it by hand. A hand-set target_size on an autoscaled MIG just gets overwritten on the next scaling decision and produces endless plan churn as Terraform and the autoscaler fight over the number.

Pick a metric that actually reflects load. CPU is the default but a request-rate or load-balancer-utilization signal often tracks real demand better for an HTTP workload. Set the floor and ceiling deliberately, and let the autoscaler choose everything in between.

A regional autoscaler driving the MIG count
resource "google_compute_region_autoscaler" "app" {
  project = "hatch-app-prod"
  name    = "app-autoscaler"
  region  = "us-central1"
  target  = google_compute_region_instance_group_manager.app.id

  autoscaling_policy {
    min_replicas = 3
    max_replicas = 10
    cpu_utilization {
      target = 0.6          # the autoscaler owns the count — never hand-set it
    }
  }
}

Health Checks and Autohealing

A google_compute_health_check drives two things at once: load-balancer membership and MIG autohealing. When an instance fails the check, autohealing recreates it from the template, so the group self-repairs instead of paging someone at 3 a.m. Omit the health check and autohealing has nothing to act on — a hung VM stays in rotation serving errors until a human notices.

Tune the check to detect a genuinely broken instance, not a momentary blip, because an over-eager check recreates healthy VMs and an over-lax one leaves broken ones serving. The health check is the signal the whole self-healing loop depends on; it is worth getting right.

Rolling Updates by Template Change

Deployment is a template swap. Point the MIG's version at a new instance template and the update_policy — surge and max-unavailable — rolls instances over gradually instead of all at once. This is the entire deploy mechanism on a MIG: new template, controlled rollout, and rollback by reverting the template, with no separate deploy tool.

Mind the capacity math on a small group. An aggressive update_policy with zero max-unavailable and no surge stalls the rollout, because there is no spare capacity to bring replacement instances up into before taking old ones down. Allow some surge so the rollout always has room to move.

The MIG self-healing loop
Template
Immutable blueprint. A new version is a new template; the MIG points its version at it.
Autoscaler
Owns the instance count between min and max on a load metric. Never hand-set the count.
Health check
Drives load-balancer membership and autohealing — failed instances are recreated from the template.
Common Mistakes
  • Trying to edit an instance template in place and being surprised it is immutable — you create a new template and update the MIG's version, so treating the template as mutable blocks every rollout.
  • Running a zonal MIG for production and losing the whole group in a routine zone outage — use a regional MIG so instances span the zones of us-central1.
  • Setting the instance count manually on a MIG that has an autoscaler — the autoscaler owns the count, so a hand-set target_size just gets overwritten and creates plan churn.
  • Omitting a health check, so autohealing has nothing to act on and a hung VM stays in rotation serving errors until a human notices.
  • Putting an aggressive update_policy — zero max-unavailable, no surge — on a small MIG and stalling the rollout because there is no spare capacity to replace instances into.
Best Practices
  • Use a regional MIG over a zonal one for any production workload so a single zone failure does not take the group down.
  • Drive scaling with an autoscaler on a meaningful metric and never set the instance count by hand.
  • Attach a health check and enable autohealing so failed instances are recreated from the template automatically.
  • Roll deployments by creating a new instance template and updating the MIG version with a surge-based update_policy, so rollouts are gradual and reverting the template rolls back.
  • Set create_before_destroy on the template so a referenced template can be replaced without tearing the MIG down first.
Comparable tools AWS launch templates + Auto Scaling Groups — the direct analog Config Connector ComputeInstanceTemplate / ComputeInstanceGroupManager Packer for baking the image the template boots

Knowledge Check

Why does the immutability of an instance template make rollouts safer rather than harder?

  • Every version is a distinct, reproducible artifact, so a deploy is a template swap and a rollback is reverting to the prior template
  • Immutable templates apply faster because Terraform skips the diff
  • It lets you patch the boot disk, metadata, and machine type of a running VM in place, so the MIG updates each instance without ever recreating it
  • It removes the need for a health check during rollout

An autoscaler is attached to the Hatch MIG. What happens if you also set target_size by hand?

  • The autoscaler overwrites it on the next scaling decision, producing constant plan churn
  • The hand-set value wins, the autoscaler is disabled for the rest of the apply, and the MIG holds that fixed instance count
  • The MIG averages the two values
  • Terraform refuses to apply until the autoscaler is removed

Why run a regional MIG instead of a zonal one for Hatch's production VMs?

  • A regional MIG spreads instances across the zones of the region and survives a single-zone outage
  • A zonal MIG cannot attach an autoscaler
  • Regional MIGs are billed at a lower rate
  • Only a regional MIG can be built from an instance template; a zonal MIG must enumerate each VM by hand instead

What is the consequence of omitting a health check on a MIG?

  • Autohealing has no signal to act on, so a hung VM stays in rotation serving errors until a human notices
  • The MIG refuses to create any instances
  • The autoscaler reads the missing check as zero healthy instances and immediately scales the group up to max_replicas
  • Rolling updates run instantly with no surge

You got correct