Google Cloud - Community

A collection of technical articles and blogs published or curated by Google Cloud Developer Advocates. The views expressed are those of the authors and don't necessarily reflect those of Google.

Reference architecture for a globally load balanced & highly available, serverless application

4 steps to supercharge Planet Scale, Global architectures on Google Cloud

7 min readMar 26, 2025

--

In part 1 of this post, I showed you how to create a globally load balanced, self-healing, auto-scaled, and secure architecture on Google Cloud (Serverless of course!). In this post, we will add 3 improvements to this architecture and automate it’s deployment. Here’s what I’ll cover in this post:

  1. Optimize content delivery by adding Cloud CDN
  2. Enhance security posture by adding Identity Aware Proxy (IAP)
  3. Add Google Firestore for global data persistence
  4. Automate deployment via Pulumi IaC

Step 1: Optimize content delivery (static assets)

The previous architecture relies on Cloud Run to respond to all incoming traffic. This is technically feasible, however it’s expensive and inefficient.

The goal is to offload all static assets that don’t require any active compute to be delivered by Cloud CDN instead.

Add Cloud CDN to serve assets

Cloud CDN will serve assets from the Google edge network closest to the user. The Global Load Balancer will integrate with a Backend bucket to store the actual assets. For maximum efficiency, Cloud CDN maintains a cache and attempts to serve directly from that cache instead of reaching to the Google Cloud Storage bucket (GCS).

We will configure all requests to the root of the domain / to be served by CDN. You can enable this at the Load Balancer console.

First, goto the Backend Configuration and create a Backend Bucket. Make sure to enable Cache static content. My bucket name is gmao-web-app. Make sure the Bucket has an index.html file that will be served by default.

Load Balancer config for adding a GCS Bucket backend

Next, goto Routing Rules and add a path that will be served by Cloud CDN. I want the root / of the domain to serve a static web application and the /api path to be served by the global Cloud Run service that we deployed earlier. Here’s how to set that up:

Finally, make sure the bucket has public access enabled and set the default file to be index.html. To set the default file, use gsutil to set the MainPageSuffix to index.html:

gsutil web set -m index.html -e 404.html gs://gmao-web-app

Detailed docs are available here and a full walk through here.

Deploy a Static Website

One of the best ways to create a web application is to build it in React and compile it as a static Single Page Application (SPA) — then deploy to your GCS bucket. Cloud CDN will serve the assets and the entire application will run client side with no web servers necessary.

Here is my sample Github repo that show you how to build a basic React app that can be deployed to any service capable of serving HTTP traffic.

Step 2: Enhance security with Identity Aware Proxy

Identity Aware Proxy (IAP) is a global Google security service that authorizes user requests to applications based on properties in the user’s request context. IAP supports integration with various 3rd party Identity providers such as Google, Facebook, etc as well as GCP project specific IAM users. Plus, it’s #serverless — there are no servers to manage or scale!

The goal is to add context aware security to our application. We will enable IAP on the GALB to protect the application on Cloud Run.

For this reference architecture, we will enable IAP for IAM users, not external Identities. We will protect the application on path /api. Goto the IAP console, locate the Backend for your service, and enable IAP. This will take short duration to provision — just wait until the status is OK.

Then add Principals and assign them the IAP-Secured Web App User role.

Any principal that has this role will be prompted to authenticate when they request path /api

Step 3: Add Global Persistence

Google Cloud provides a wide range of database services that serve different purposes. I choose Google Cloud Firestore to add global persistence to this reference architecture because it is designed to offer global scalability, multi-region replication with 99.999% SLA, strong consistency by default, and no server management. It’s a NoSQL Document database that organizes documents in groups called Collections.

Firestore’s strong consistency allows this architecture to read and write from any region with consistent results. For each user that will use the system, I preload a document with its ID set to the user’s email. The document itself stores user information as follows:

{
"firstName": "George",
"lastName": "Mao",
"email": "user@iamgeorge.altostrat.com"
}

After successful authentication, IAP will inject 3 headers into the downstream request as follows:

Auth context injection from IAP to downstream services

The goal is to extract the authorized user context proved from IAP and use it to pull the associated document from the Firestore backend.

First, we need create a new Service Account that has permissions to interact with Firestore and assign it to the Cloud Run Function. The default service account attached to Cloud Run resources does not provide any permissions. The role we need to add is roles/datastore.user.

If you are using the the console, Create a new Service Account then assign the Cloud Datastore User role.

If you are using the glcoud cli, just execute the following commands:

gcloud iam service-accounts create SERVICE_ACCOUNT_NAME \
--description="DESCRIPTION" \
--display-name="DISPLAY_NAME"

gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:SERVICE_ACCOUNT_NAME@PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/datastore.user"

Second, we need to update the Cloud Run Function code to:

  1. Extract the x-goog-authenticated-user-email header from the request
  2. Lookup the Document in Firestore with the matching ID
let userEmail = req.headers['x-goog-authenticated-user-email'];
console.log(userEmail);
// Remove the accounts.gooogle.com string
userEmail=userEmail.replace("accounts.google.com:", '');

Note: In a real production environment, you should also validate the JWT token in the x-goog-iap-jwt-assertion header

Get the document in Firestore that has the same ID:

let doc = await readDoc(userEmail);

async function readDoc(userEmail) {
try {
console.log(`Reading document: ${userEmail}`)
const document = firestore.collection('person').doc(userEmail);

// Read the document.
const doc = await document.get();
if (!doc.exists) {
console.log('No such document!');
return;
}
console.log('Read the document');
console.log(doc.data());

return doc.data();
}
catch (error) {
console.error('Error retrieving Firestore document:', error);
return null;
}
}

Full updated code is available on in my Github repo here.

Step 4: Automate deployment

Pulumi IaC is a great way to describe your Cloud architecture using common programming languages you already know. It has significant support for GCP and automates all deployment tasks.

The goal is to describe all major components of this architecture using Pulumi NodeJs libraries and fully automated the deployment

I’ll cover some of the key configurations below. To create the Cloud Run resources: Stage the code as a zip file in a GCS bucket, then deploy the Cloud Run Function.

Create a GCS Bucket and push the function code as a Zip file
Deploy a new Cloud Run Function

To create the Firestore Database and add a document:

Create the Firestore DB and add a sample document

The source code that handles the deployment via Pulumi is available here — including configuration of all other resources in this architecture, such as the Global ALB, Cloud CDN, and Firestore.

Run pulumi up to execute the deployment. You should see a diff that shows the resources that will be deployed. Confirm these are accurate and select yes.

Summary

In part 1 of this post, I showed you how to create a globally load balanced architecture.

In this post, we enhanced the architecture by adding global content delivery, security at the API layer, and global persistence capabilities.

Review the source code for:

Reach out to me if you have any questions & Happy Coding :)

--

--

Google Cloud - Community
Google Cloud - Community

Published in Google Cloud - Community

A collection of technical articles and blogs published or curated by Google Cloud Developer Advocates. The views expressed are those of the authors and don't necessarily reflect those of Google.

George Mao
George Mao

Written by George Mao

Head of Specialist Architects @ Google Cloud. I lead a team of experts responsible for helping customers solve their toughest challenges and adopt GCP at scale

No responses yet