Files
hunfabric/blueprints/serverless/cloud-run-microservices

Cloud Run Microservices

Introduction

This blueprint contains all the necessary Terraform modules to deploy two microservices running in Cloud Run and communicating with each other.

The content of this blueprint corresponds to the chapter 'Microservices architectures - Developing Microservices applications' of the Serverless Networking Guide. This guide is an easy to follow introduction to Cloud Run, where a couple of friendly characters will guide you from the basics to more advanced topics with a very practical approach and in record time! The code here complements this learning and allows you to test the scenarios presented and your knowledge.

If you are interested in following this guide, take a look to the chapters' blueprints:

Prerequisites

Depending on the use case, you will need one or two projects with billing enabled and a user with the “Project owner” IAM role on those projects. You can use existing projects or let the blueprint create them for you but in that case you will need to add extra information for each project. E.g.:

# Create the main project
main_project = {
  billing_account_id = "ABCDE-12345-ABCDE"
  parent             = "organizations/0123456789"
  project_id         = "spiritual-hour-331417"
}

How to set this information is explained below.

Spinning up the architecture

General steps

  1. Clone the repo to your local machine or Cloud Shell:
git clone https://github.com/GoogleCloudPlatform/cloud-foundation-fabric
  1. Change to the directory of the blueprint:
cd cloud-foundation-fabric/blueprints/serverless/cloud-run-microservices

You should see this README and some terraform files.

  1. To deploy a specific use case, you will need to create a file in this directory called terraform.tfvars and follow the corresponding instructions to set variables. Values that are meant to be substituted will be shown inside brackets but you need to omit these brackets. E.g.:
main_project = {
  project_id = "[project_id]"
}

may become

main_project = {
  project_id = "spiritual-hour-331417"
}

Use cases are self-contained so you can deploy any of them at will.

  1. The usual terraform commands will do the work:
terraform init
terraform plan
terraform apply

It will take a few minutes. When complete, you should see an output stating the command completed successfully, a list of the created resources, and some output variables with information to access your services.

Congratulations! You have successfully deployed the use case you chose based on the variables configuration.

Use case 1: Service to service communication in the same project

This use case deploys two Cloud Run services in the same project. Service B is protected as an internal only service and communication between Cloud Run services, even in the same project, is not considered internal by default. But there are ways to configure it, and one option is to use PSC/PGA (the second option is shown in the following use case). We will use a PSC endpoint and a Serverless VPC connector to reach it. A DNS record for the PSC endpoint is created.

Service A uses an application with a GUI to test connectivity. You can find its source code in:

https://github.com/willypalacin/vpc-network-tester/tree/main

Build an image and push it to Artifact Registry. Set the corresponding image variable to its URL, and the main project ID, in terraform.tfvars. E.g.:

main_project = {
  project_id = "[main-project-id]"
}
prefix       = "[prefix]"
svc_a_image  = "us-docker.pkg.dev/[project-id]/[repo-name]/[tester-app]"

Note that final project ids will be of the form "[prefix]-[main-project-id]".

The service B default URL is automatically created and shown as a terraform output variable. It will be similar to the one shown in the picture above. Get into service A and try to reach service B URL as shown below:

You can see service A is resolving service B to an internal IP, 10.0.0.100, the PSC endpoint. Public access is restricted, if you try to e.g. curl from your laptop you will get an error.

Use case 2: Service to service communication in different projects

The second option for internal service to service communication is to use an internal Application Load Balancer (ALB). This use case extends the previous architecture using a Shared VPC and a service project. Instead of a VPC connector, it uses Direct VPC Egress which allows Cloud Run to directly use IPs from a subnet. And the ALB allows you to use a custom domain.

Set the following in terraform.tfvars:

main_project    = {
  project_id = "[main-project-id]" # Used as host project
}
prefix          = "[prefix]"
service_project = "[service-project-id]"
svc_a_image     = "us-docker.pkg.dev/[project-id]/[repo-name]/[tester-app]"

The blueprint uses an HTTP connection to the ALB to avoid management of SSL certificates. Try to reach service B custom URL as shown below:

Cleaning up your environment

The easiest way to remove all the deployed resources is to run the following command:

terraform destroy

The above command will delete the associated resources so there will be no billable charges afterwards. Projects are removed from Terraform state but not deleted from Google Cloud.

Files

name description modules resources
alb.tf Internal Application Load Balancer resource. net-lb-app-int
cloudrun.tf Cloud Run services. cloud-run google_cloud_run_v2_service · google_cloud_run_v2_service_iam_policy · google_vpc_access_connector
dns.tf DNS resources. dns
main.tf Project resources. project
outputs.tf Module outputs.
psc.tf Private Service Connect resources. net-address google_compute_global_forwarding_rule
variables.tf Module variables.
vpc.tf VPC resources. net-vpc

Variables

name description type required default
main_project Main (or host) project. object({…})
prefix Prefix used for project names. string
svc_a_image Container image to deploy in service A. string
custom_domain Custom domain for the Load Balancer. string "service-b.acme.org"
ip_ranges IP ranges or IPs used by the VPC. map(string) {…}
region Cloud region where resources will be deployed. string "europe-west1"
service_project Service project. object({…}) {}
svc_b_image Container image to deploy in service B. string "us-docker.pkg.dev/cloudrun/container/hello"

Outputs

name description sensitive
custom_domain Custom domain for the Application Load Balancer.
default_URL_svc_a Cloud Run service A default URL.
default_URL_svc_b Cloud Run service B default URL.
load_balancer_ip Load Balancer IP address.

Tests

module "test" {
  source = "./fabric/blueprints/serverless/cloud-run-microservices"
  main_project = {
    billing_account_id = "ABCDE-12345-ABCDE"
    parent             = "organizations/0123456789"
    project_id         = "main-project-id"
  }
  prefix      = "prefix"
  svc_a_image = "cloud-run-image"
}

# tftest modules=6 resources=22
module "test" {
  source = "./fabric/blueprints/serverless/cloud-run-microservices"
  main_project = { # Used as host project
    billing_account_id = "ABCDE-12345-ABCDE"
    parent             = "organizations/0123456789"
    project_id         = "main-project-id"
  }
  prefix = "prefix"
  service_project = {
    billing_account_id = "ABCDE-12345-ABCDE"
    parent             = "organizations/0123456789"
    project_id         = "service-project-id"
  }
  svc_a_image = "cloud-run-image"
}

# tftest modules=9 resources=32