GCP for DevOps — Compute Exercises

4 minute read

Practice Exercises — Compute

Exercise 1 — Create a Web Server VM

Task: Create an e2-small VM named web-demo in us-central1-a running Debian 12, with a startup script that installs and starts Nginx, and a firewall rule allowing HTTP traffic.

Show Answer
# Create the VM with a startup script
$ gcloud compute instances create web-demo \
    --zone=us-central1-a \
    --machine-type=e2-small \
    --image-family=debian-12 \
    --image-project=debian-cloud \
    --tags=http-server \
    --metadata=startup-script='#!/bin/bash
apt-get update
apt-get install -y nginx
systemctl enable nginx
systemctl start nginx
echo "<h1>Hello from $(hostname)</h1>" > /var/www/html/index.html
'

# Create a firewall rule for HTTP
$ gcloud compute firewall-rules create allow-http \
    --network=default \
    --direction=INGRESS \
    --action=ALLOW \
    --rules=tcp:80 \
    --source-ranges=0.0.0.0/0 \
    --target-tags=http-server

# Verify — get the external IP and test
$ gcloud compute instances describe web-demo --zone=us-central1-a \
    --format="get(networkInterfaces[0].accessConfigs[0].natIP)"

34.68.123.45

$ curl http://34.68.123.45
<h1>Hello from web-demo</h1>

Key Points:

  • Tags connect VMs to firewall rules
  • Startup scripts run as root on every boot
  • Use --image-family instead of specific image names for latest versions

Exercise 2 — Create a Custom Image

Task: Create a VM, install Docker and kubectl, then create a custom image from it.

Show Answer
# Step 1: Create a builder VM
$ gcloud compute instances create image-builder \
    --zone=us-central1-a \
    --machine-type=e2-standard-2 \
    --image-family=ubuntu-2204-lts \
    --image-project=ubuntu-os-cloud \
    --boot-disk-size=30GB

# Step 2: SSH and install tools
$ gcloud compute ssh image-builder --zone=us-central1-a

# Inside the VM:
$ sudo apt-get update
$ sudo apt-get install -y docker.io apt-transport-https ca-certificates curl
$ sudo systemctl enable docker
$ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
$ sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
$ kubectl version --client
$ exit

# Step 3: Stop the VM
$ gcloud compute instances stop image-builder --zone=us-central1-a

# Step 4: Create the image
$ gcloud compute images create devops-tooling-v1 \
    --source-disk=image-builder \
    --source-disk-zone=us-central1-a \
    --family=devops-tooling \
    --description="Ubuntu 22.04 with Docker and kubectl"

# Step 5: Verify and clean up
$ gcloud compute images describe devops-tooling-v1 \
    --format="table(name,family,status,diskSizeGb)"

$ gcloud compute instances delete image-builder --zone=us-central1-a --quiet

Exercise 3 — Set Up Auto-Scaling MIG

Task: Create a MIG with auto-scaling (min 2, max 6, target CPU 50%) and auto-healing with an HTTP health check.

Show Answer
# Step 1: Create an instance template
$ gcloud compute instance-templates create app-template \
    --machine-type=e2-medium \
    --image-family=debian-12 \
    --image-project=debian-cloud \
    --boot-disk-size=20GB \
    --tags=http-server,health-check \
    --metadata=startup-script='#!/bin/bash
apt-get update && apt-get install -y nginx
systemctl enable nginx && systemctl start nginx
echo "OK" > /var/www/html/health
'

# Step 2: Create a health check
$ gcloud compute health-checks create http app-health-check \
    --port=80 \
    --request-path=/health \
    --check-interval=30s \
    --timeout=10s \
    --healthy-threshold=2 \
    --unhealthy-threshold=3

# Step 3: Create the MIG
$ gcloud compute instance-groups managed create app-mig \
    --zone=us-central1-a \
    --template=app-template \
    --size=2

# Step 4: Set auto-scaling
$ gcloud compute instance-groups managed set-autoscaling app-mig \
    --zone=us-central1-a \
    --min-num-replicas=2 \
    --max-num-replicas=6 \
    --target-cpu-utilization=0.50 \
    --cool-down-period=60

# Step 5: Set auto-healing
$ gcloud compute instance-groups managed set-autohealing app-mig \
    --zone=us-central1-a \
    --health-check=app-health-check \
    --initial-delay=120

# Step 6: Verify
$ gcloud compute instance-groups managed describe app-mig \
    --zone=us-central1-a \
    --format="yaml(autoscaler,autoHealingPolicies)"

$ gcloud compute instance-groups managed list-instances app-mig \
    --zone=us-central1-a

Exercise 4 — Perform a Rolling Update

Task: Create a new instance template with an updated startup script, then perform a zero-downtime rolling update on the MIG.

Show Answer
# Step 1: Create an updated template
$ gcloud compute instance-templates create app-template-v2 \
    --machine-type=e2-medium \
    --image-family=debian-12 \
    --image-project=debian-cloud \
    --boot-disk-size=20GB \
    --tags=http-server,health-check \
    --metadata=startup-script='#!/bin/bash
apt-get update && apt-get install -y nginx
systemctl enable nginx && systemctl start nginx
echo "v2 - OK" > /var/www/html/health
echo "<h1>App v2 running on $(hostname)</h1>" > /var/www/html/index.html
'

# Step 2: Start a rolling update (zero-downtime)
$ gcloud compute instance-groups managed rolling-action start-update app-mig \
    --zone=us-central1-a \
    --version=template=app-template-v2 \
    --max-surge=1 \
    --max-unavailable=0

# Step 3: Monitor the update
$ watch -n 5 gcloud compute instance-groups managed list-instances app-mig \
    --zone=us-central1-a \
    --format="table(instance.basename(),status,currentAction,version.instanceTemplate.basename())"

# Step 4: Verify all instances are updated
$ gcloud compute instance-groups managed describe app-mig \
    --zone=us-central1-a \
    --format="get(status.versionTarget.isReached)"

True

Key Points:

  • --max-surge=1 creates one extra VM during the update
  • --max-unavailable=0 ensures no VM is taken down until its replacement is healthy
  • The update respects the health check — new VMs must be healthy before old ones are removed

Exercise 5 — Secure SSH Access with IAP

Task: Create a VM with no external IP and configure SSH access through Identity-Aware Proxy (IAP).

Show Answer
# Step 1: Create a VM with no external IP
$ gcloud compute instances create private-vm \
    --zone=us-central1-a \
    --machine-type=e2-small \
    --image-family=debian-12 \
    --image-project=debian-cloud \
    --no-address \
    --metadata=enable-oslogin=TRUE

# Step 2: Create a firewall rule allowing SSH from IAP
$ gcloud compute firewall-rules create allow-iap-ssh \
    --network=default \
    --direction=INGRESS \
    --action=ALLOW \
    --rules=tcp:22 \
    --source-ranges=35.235.240.0/20 \
    --target-tags=allow-iap

# Step 3: Add the tag to the VM
$ gcloud compute instances add-tags private-vm \
    --zone=us-central1-a \
    --tags=allow-iap

# Step 4: Grant IAP tunnel access
$ gcloud projects add-iam-policy-binding my-project \
    --member="user:dev@example.com" \
    --role="roles/iap.tunnelResourceAccessor"

# Step 5: SSH via IAP
$ gcloud compute ssh private-vm --zone=us-central1-a --tunnel-through-iap

# Verify no external IP
$ gcloud compute instances describe private-vm --zone=us-central1-a \
    --format="get(networkInterfaces[0].accessConfigs)"

# Should be empty — no external IP

Key Points:

  • IAP source range is 35.235.240.0/20 — this is Google’s IAP proxy range
  • --tunnel-through-iap tells gcloud to route SSH through IAP
  • OS Login integrates SSH access with IAM — no manual SSH key management
  • This is the recommended pattern for production VMs

Cleanup

# Delete all exercise resources
$ gcloud compute instances delete web-demo private-vm --zone=us-central1-a --quiet
$ gcloud compute instance-groups managed delete app-mig --zone=us-central1-a --quiet
$ gcloud compute instance-templates delete app-template app-template-v2 web-template --quiet
$ gcloud compute health-checks delete app-health-check --quiet
$ gcloud compute firewall-rules delete allow-http allow-iap-ssh --quiet
$ gcloud compute images delete devops-tooling-v1 --quiet

Next Chapter → Networking

Updated: