Skip to content

Instantly share code, notes, and snippets.

@mikesparr
Last active April 12, 2024 04:26
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save mikesparr/db2f5cb7d7980cf4970c7d5c8b2194e5 to your computer and use it in GitHub Desktop.
Save mikesparr/db2f5cb7d7980cf4970c7d5c8b2194e5 to your computer and use it in GitHub Desktop.
Demonstrating how you can deploy Cloud Run (serverless) or Compute Engine instance groups across regions and balance with global load balancer
#!/usr/bin/env bash
#####################################################################
# REFERENCES
# - https://cloud.google.com/run/docs/multiple-regions
# - https://cloud.google.com/compute/docs/instance-groups/distributing-instances-with-regional-instance-groups
# - https://cloud.google.com/load-balancing/docs/https/setup-global-ext-https-compute
# - https://cloud.google.com/load-balancing/docs/backend-service#named_ports
#####################################################################
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_USER=$(gcloud config get-value core/account) # set current user
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
export IDNS=${PROJECT_ID}.svc.id.goog # workflow identity domain
export GCP_REGION="us-central1" # CHANGEME (OPT)
export GCP_ZONE="us-central1-a" # CHANGEME (OPT)
export NETWORK_NAME="default"
# enable apis
gcloud services enable compute.googleapis.com \
storage.googleapis.com \
cloudbuild.googleapis.com \
run.googleapis.com \
artifactregistry.googleapis.com
# configure gcloud sdk
gcloud config set compute/region $GCP_REGION
gcloud config set compute/zone $GCP_ZONE
##########################################################
# Demo App Initialization
##########################################################
# declare demo app name
export APP_NAME="my-app"
export APP_REGION_1="us-central1"
export APP_REGION_2="australia-southeast1"
export APP_IMAGE_URL="gcr.io/google-samples/zone-printer:0.2"
##########################################################
# Load Balancing
##########################################################
export DOMAIN="my-app.msparr.com" # CHANGE ME TO DESIRED DOMAIN
export EXT_IP_NAME="global-ip"
export BACKEND_NAME="$APP_NAME-backend"
export SERVERLESS_NEG_NAME_1="$APP_NAME-neg-us-2"
export SERVERLESS_NEG_NAME_2="$APP_NAME-neg-aus-2"
export HTTP_KEEPALIVE_TIMEOUT_SEC="610" # default
# create global public IP
gcloud compute addresses create --global $EXT_IP_NAME
export EXT_IP=$(gcloud compute addresses describe $EXT_IP_NAME --global --format="value(address)")
echo "** remember to update DNS for $DOMAIN -> $EXT_IP **"
# create backend service
gcloud compute backend-services create $BACKEND_NAME \
--global
# create URL map
gcloud compute url-maps create $APP_NAME-url-map \
--default-service=$BACKEND_NAME
# create managed SSL cert
gcloud beta compute ssl-certificates create $APP_NAME-cert \
--domains $DOMAIN
# create target HTTPS proxy
gcloud compute target-https-proxies create $APP_NAME-https-proxy \
--ssl-certificates=$APP_NAME-cert \
--url-map=$APP_NAME-url-map
# create forwarding rule using static IP (classic LB, use EXTERNAL_MANAGED for global ALB)
gcloud compute forwarding-rules create $APP_NAME-fwd-rule \
--load-balancing-scheme=EXTERNAL \
--target-https-proxy=$APP_NAME-https-proxy \
--global \
--ports=443 \
--address=$EXT_IP_NAME
# create target HTTP proxy
gcloud compute target-http-proxies create $APP_NAME-http-proxy \
--url-map=$APP_NAME-url-map
# create forwarding rule using static IP (classic LB, use EXTERNAL_MANAGED for global ALB)
gcloud compute forwarding-rules create $APP_NAME-fwd-rule-http \
--load-balancing-scheme=EXTERNAL \
--target-http-proxy=$APP_NAME-http-proxy \
--global \
--ports=80 \
--address=$EXT_IP_NAME
##########################################################
# Cloud Run Serverless Runtime
##########################################################
export SERVERLESS_NEG_NAME="$APP_NAME-neg"
export SERVERLESS_BACKEND_NAME="$APP_NAME-run-backend"
# deploy app to 2 regions
gcloud run deploy $APP_NAME \
--allow-unauthenticated \
--ingress=internal-and-cloud-load-balancing \
--image=$APP_IMAGE_URL \
--region=$APP_REGION_1
gcloud run deploy $APP_NAME \
--allow-unauthenticated \
--ingress=internal-and-cloud-load-balancing \
--image=$APP_IMAGE_URL \
--region=$APP_REGION_2
# create serverless network endpoint groups for each region
gcloud compute network-endpoint-groups create $SERVERLESS_NEG_NAME \
--network-endpoint-type=serverless \
--cloud-run-service=$APP_NAME \
--region=$APP_REGION_1
gcloud compute network-endpoint-groups create $SERVERLESS_NEG_NAME \
--network-endpoint-type=serverless \
--cloud-run-service=$APP_NAME \
--region=$APP_REGION_2
# add custom backend for Cloud Run apps (assuming legacy)
gcloud compute backend-services create $SERVERLESS_BACKEND_NAME \
--global
# add serverless negs to backend for each region
gcloud compute backend-services add-backend $SERVERLESS_BACKEND_NAME \
--global \
--network-endpoint-group=$SERVERLESS_NEG_NAME \
--network-endpoint-group-region=$APP_REGION_1
gcloud compute backend-services add-backend $SERVERLESS_BACKEND_NAME \
--global \
--network-endpoint-group=$SERVERLESS_NEG_NAME \
--network-endpoint-group-region=$APP_REGION_2
# update existing load balancer URL map to point to new backend
gcloud compute url-maps set-default-service $APP_NAME-url-map \
--default-service=$SERVERLESS_BACKEND_NAME \
--global
# test from different regions and confirm backend switches
##########################################################
# (OPTIONAL ALTERNATIVE) Compute Engine Runtime
##########################################################
export INSTANCE_TEMPLATE_NAME="$APP_NAME-template"
export INSTANCE_GROUP_NAME="$APP_NAME-rmig"
export MIG_BACKEND_NAME="$APP_NAME-rmig-backend"
export HEALTH_CHECK_NAME="http-basic-check"
# create instance template that runs container image
gcloud compute instance-templates create-with-container $INSTANCE_TEMPLATE_NAME \
--container-image $APP_IMAGE_URL \
--tags "$APP_NAME,allow-health-check,allow-ssh"
# create regional migs for each region
gcloud compute instance-groups managed create $INSTANCE_GROUP_NAME \
--base-instance-name $INSTANCE_GROUP_NAME \
--size 1 \
--template $INSTANCE_TEMPLATE_NAME \
--region $APP_REGION_1
gcloud compute instance-groups managed create $INSTANCE_GROUP_NAME \
--base-instance-name $INSTANCE_GROUP_NAME \
--size 1 \
--template $INSTANCE_TEMPLATE_NAME \
--region $APP_REGION_2
# add named port (internal)
gcloud compute instance-groups set-named-ports $INSTANCE_GROUP_NAME \
--named-ports http:80,$APP_NAME:8080 \
--region $APP_REGION_1
gcloud compute instance-groups set-named-ports $INSTANCE_GROUP_NAME \
--named-ports http:80,$APP_NAME:8080 \
--region $APP_REGION_2
# enable firewall rules
gcloud compute firewall-rules create fw-allow-health-check \
--network=default \
--action=allow \
--direction=ingress \
--source-ranges=130.211.0.0/22,35.191.0.0/16 \
--target-tags=allow-health-check \
--rules=tcp
gcloud compute firewall-rules create fw-allow-ssh \
--network=default \
--action=allow \
--direction=ingress \
--source-ranges=0.0.0.0/0 \
--target-tags=allow-ssh \
--rules=tcp:22
# create health check
gcloud compute health-checks create http $HEALTH_CHECK_NAME \
--use-serving-port \
--global
# create custom backend for MIGs
gcloud compute backend-services create $MIG_BACKEND_NAME \
--load-balancing-scheme=EXTERNAL \
--protocol=HTTP \
--port-name=$APP_NAME \
--health-checks=$HEALTH_CHECK_NAME \
--global
# add instance groups to MIG backend for each region
gcloud compute backend-services add-backend $MIG_BACKEND_NAME \
--global \
--instance-group=$INSTANCE_GROUP_NAME \
--instance-group-region=$APP_REGION_1
gcloud compute backend-services add-backend $MIG_BACKEND_NAME \
--global \
--instance-group=$INSTANCE_GROUP_NAME \
--instance-group-region=$APP_REGION_2
# update existing load balancer URL map to point to MIG backend
gcloud compute url-maps set-default-service $APP_NAME-url-map \
--default-service=$MIG_BACKEND_NAME \
--global
# test from different regions and confirm backend switches
@mikesparr
Copy link
Author

@RamyaMagesh10734605 this is example how I set this up in my environment, with a domain name (msparr.com) that I own. You would have to set up with your own domain/dns and in your own GCP project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment