Build a planet scale, global architecture for modern apps on Google Cloud
Serverless is the best way to build modern, cloud applications — I let Google Cloud handle all of the heavy lifting of server management, operating systems, patching, and scaling so I can focus on building my application.
In my previous post, I showed why Google Cloud Run is the best serverless, compute runtime. In this post, I’ll show you a reference architecture that demonstrates a globally load balanced, self-healing, auto-scaled, and secure environment— all without managing a single server! We will enhance this architecture in part two.
High Level Architecture
There are four major components in this architecture that make this possible. We will deep dive into each component and cover the integration details.
- Cloud DNS — allows you to configure a globally resolvable domain name that points to your Global Load Balancer
- Cloud Armor — provides layer 7 protection against DDoS attacks, OWASP vulnerabilities, and a fully configurable Web Application Firewall
- Cloud Load Balancing — provides auto-scaled, traffic balancing across regions and native integration with the other components
- Cloud Run Functions— provides a fully managed, pay-per-use serverless compute environment to run your application logic
Cloud Run (Functions)
Cloud Run is a regional service and is supported in every region GCP operates in. It’s entirely serverless and requires zero management. I’ll be using a Cloud Run Function (CRF) to act as the compute tier for this application. It will expose a HTTPS endpoint that allows a consumer to invoke my code. The build process to deploy a CRF looks like this:
To get started, let’s deploy a Cloud Run Function. You can use this sample application. It simulates a 3 second workload and uses the Metadata service to return info about where it’s hosted from. The code will retrieve the instance id it’s hosted on, the number of times the specific Run instance has been invoked, and the region it’s running as JSON:
{
"instanceID": "...",
"counter": ###,
"region": "projects/######/regions/region-name"
}
The deployment process submits my source code to Cloud Build (CB) which will build a docker image, store it in Artifact Registry, and deploy to the Cloud Run service. Once deployed, the Run Function is assigned a HTTPs endpoint in the format:
https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
You can do this through the console, gcloud CLI, or Terraform. Here’s the command to deploy:
gcloud functions deploy multi-region-demo
--gen2 --region=us-central1
--runtime=nodejs22 --source=. --entry-point=helloHttp
--trigger-http
--ingress-settings=internal-and-gclb --allow-unauthenticated
This creates a Run Function called multi-region-demo
in the us-central1
region using Node22
based source code.
By default, the CRF endpoint is hosted out of a single region, allows traffic from anywhere, and requires Authentication.
Our goal is to serve all traffic through a Global External Application Load Balancer (GALB) instead of directly from the Cloud Run Function. This will allow the GALB to decide which region to best serve incoming traffic.
- Use the
allow-unauthenticated
flag because GALB does not perform any form of auth to a Cloud Run service - Use the
ingress-settings
flag to restrict traffic to the function so that only internal sources and your GALB are allowed
You can monitor the deployment progress at Cloud Build and view the actual image that was built at Artifact Registry.
Next, deploy the Run Function to every region you want your application to be hosted from. Just run the same gcloud CLI command with a different region specified. Every Run Function will exist independently in its own region and have its own HTTPs endpoint. We will attach all deployed regions to a Global ALB in the next step.
Remember these are regional services so Cloud Build will execute independently in each region and store Artifacts in each region
Cloud Load Balancing
A Global External Application Load Balancer (GALB) will act as a single point of traffic ingress and distribute traffic across all regions based on the user’s location. This is made possible through a single anycast IP address that is presented across all regions. We will use Cloud DNS to create a DNS entry (A record) that resolves to this IP address, then use Certificate Manager to create a matching SSL certificate for our Custom domain.
Let’s look at the 4 main components that make up this architecture.
- A Global External Load Balancer (GALB). This will provide a single static, anycast IP that your Custom DNS A record will point to.
- A Global Backend Service. This acts as the service that your GALB will direct traffic to. Your Backend Service can attach a variety of Backends (such as GCE Managed Instance Groups) — in our case we are going to attach a Serverless Network Endpoint Group (NEG).
- A Serverless NEG represents a Serverless resource such as Cloud Run, App Engine, or API Gateway. You will need to create a Serverless NEG in each region you want to service traffic and point it to the Cloud Run service in that region.
- Cloud Run. This provides the actual compute environment to execute your application logic. We set this up already.
Review the Load Balancer docs for detailed instructions on how to setup this architecture.
Certificate Manager
Create a global, Google Managed SSL certificate for your domain using DNS Authorization. You can create a domain specific cert or a wildcard cart for an entire subdomain. Before Google will activate your Certificate you must validate that you own the domain through a DNS AuthZ process.
Certificate Manager will generate a key-value pair of DNS records that you must add to your Domain. Certificate Manager will lookup your domain and verify that the records match the values it generated. This process establishes ownership of the domain — preventing unauthorized certificate usage.
Cloud DNS
In Cloud DNS, create a public Zone for your Domain. Then add two records to the Zone:
- An A record that points to the static IP for your GALB. This will allow global resolution of your domain to your GALB.
- A CNAME record with matching values from the Certificate Manager step. The verification process usually finishes in under 30 minutes but could take up to 24 hours due to DNS propagation and caching.
For example, my domain is iamgeorge.demo.altostrat.com. I added both records as follows:
Monitor the Certificate status in Certificate manager —once the status shows Active
you are good to go. Note the name for your Certificate as we will use it in the next step.
Now we need to deploy this cert to the GALB. At the time of this post, the only away to do this is via the gcloud CLI. The full process is described in the Certificate Manager docs here. The main steps are:
Create a Certificate Map
gcloud certificate-manager maps create CERTIFICATE_MAP_NAME
Create a config Entry in the Map for your Cert and Domain
gcloud certificate-manager maps entries create CERTIFICATE_MAP_ENTRY_NAME \
--map="CERTIFICATE_MAP_NAME" \
--certificates="Cert name from Certmanager" \
--hostname="Your cert's hostname"
Attach the Certificate Map to your GALB
gcloud compute target-https-proxies create PROXY_NAME \
--certificate-map="CERTIFICATE_MAP_NAME" \
--url-map="The name of your URL Map you created during the GALB step" \
--global
Cloud Armor
You should secure your application by applying Layer 7 WAF rules with Cloud Armor. It’s easy to start with preconfigured rules but you can also create custom rules.
Review your GALB configuration — under the Backend Services section you should see a Backend Security Policy
link. There will be a default policy created that allows all traffic. It looks like this:
Try adding a preconfigured rule such as a check for Cross-Site scripting:
Use Logs Explorer to review how your security policies are enforcing requests. Here’s a basic log query to grab policy evaluation logs:
resource.type:(http_load_balancer)
OR resource.type:(tcp_ssl_proxy_rule)
OR resource.type:(l4_proxy_rule)
AND jsonPayload.enforcedSecurityPolicy.name:(name of your policy here)
Look at the enforcedSecurityPolicy
JSON block to figure out how your policy executed.
Summary
You should now be able issue a HTTP GET against your domain and get a response from the nearest region. You can VPN to a different region or use a GCE instance in a different region to verify that the GALB is routing traffic appropriately.
If you used my code for your CRF, here’s what you should see.
{
"instanceID": "...",
"counter": 1,
"region": "projects/528923850333/regions/us-east4"
}
This architecture allows you to leverage the global Google network to maximize resiliency and cost efficiency, plus:
- It’s 100% serverless — there are zero servers to manage or patch including the compute used in the build and deployment phase
- It autoscales and load balances with traffic and incurs cost per use, instead of incurring cost by uptime (with the exception of the GALB)
- Security is built in at every stage — there is no public entry to any compute, layer 7 security is covered, and container images are auto patched and updated by Google
What’s next
In my next post, I’ll show you how to enhance this architecture with:
- Cloud CDN to serve static assets for faster, cheaper delivery
- Add Identity Aware Proxy on the GALB as another form of Security
- Add and configure various Datastore backends
- Automate deployment of this architecture using Pulumi IaC