Cloud Run is a serverless platform from Google Cloud to deploy and run containers. It’s fully managed, autoscallable, and has a generous free tier. Cloud Run can be used to serve Restful web APIs, WebSocket applications, or microservices connected by gRPC. It also integrates well with other Google Cloud solutions such as Cloud Tasks, Cloud Scheduler, and Pub/Sub.
Terraform is a popular open-source tool for running infrastructure as code. Terraform lets you manage and deploy infrastructure from multiple providers, one of them being Google Cloud. If this is your first time reading about Terraform, you might wanna check this introduction first.
In this article, you will see how to deploy a Cloud Run service to Google Cloud using Terraform. All the infrastructure will be written using HCL, the native syntax for Terraform’s language. By the end of the tutorial, you should have a service up and running on Cloud Run and a URL to access it.
Prerequisites
To follow this tutorial you will need:
- Terraform CLI. I recommend using the latest version, currently v0.14. Instructions to download and install Terraform can be found here.
- Google Cloud SDK. The most recent version should also work well for this tutorial. Installation instructions here.
- A Google Cloud account. If you don’t have one, create it here.
Initial setup
Start by authenticating the SDK to Google Cloud:
# Authenticate to Google Cloud
gcloud auth application-default login
Follow the web flow to obtain the access credentials. By the end of this step, you will be able to execute commands on the SDK similar to a service account.
Create a new project where your Cloud Run service will be deployed. Replace PROJECT_ID
and PROJECT_NAME
with the desired values:
gcloud projects create "PROJECT_ID" --name="PROJECT_NAME"
Create a folder with a HCL file inside:
mkdir my-service && cd my-service
touch main.tf
For simplicity, all the Terraform code in the next steps will be added to main.tf
.
Creating your first service
Begin by adding the requirements for Terraform and the Google provider on main.tf
:
# main.tf
terraform {
required_version = ">= 0.14"
required_providers {
# Cloud Run support was added on 3.3.0
google = ">= 3.3"
}
}
This will require the Terraform version to be the latest and the Google provider to be at least on version 3.3
– when Cloud Run support was added.
Now add the Google provider configuration. Replace PROJECT_ID
with the value from the previous step:
# main.tf
provider "google" {
# Replace `PROJECT_ID` with your project
project = "PROJECT_ID"
}
The Cloud Run API doesn’t come enabled on projects by default. Add the following resource to enable it:
# main.tf
# Enables the Cloud Run API
resource "google_project_service" "run_api" {
service = "run.googleapis.com"
disable_on_destroy = true
}
Now create the Cloud Run service in the us-central1
region:
# main.tf
# Create the Cloud Run service
resource "google_cloud_run_service" "run_service" {
name = "app"
location = "us-central1"
template {
spec {
containers {
image = "gcr.io/google-samples/hello-app:1.0"
}
}
}
traffic {
percent = 100
latest_revision = true
}
# Waits for the Cloud Run API to be enabled
depends_on = [google_project_service.run_api]
}
Let’s stop for a while and check what the code above is doing:
name
: the name of your service. It will be displayed in the public URL.location
: the region where your service will run. See all the options here.image
: The Docker image that will be used to create the container. Cloud Run has direct support for images from the Container Registry and Artifact Registry.traffic
: controls the traffic for this revision. Thepercent
property indicates how much traffic will be redirected to this revision.latest_revision
specifies that this traffic configuration needs to be used for the latest revision.depends_on
: waits for a resource to be ready, in this case, the Cloud Run API.
Invoking the service
By default, Cloud Run services are private and secured by IAM. To access them, you would need valid credentials with at least the Cloud Run Invoker permission set.
Let’s change that and make the service publicly available through an HTTP endpoint.
There are other ways than HTTP requests to trigger a service. For example, they can be accessed by gRPC requests, WebSockets, and other Google Cloud products like Cloud Scheduler.
Add the following code on main.tf
to expose your service:
# main.tf
# Allow unauthenticated users to invoke the service
resource "google_cloud_run_service_iam_member" "run_all_users" {
service = google_cloud_run_service.run_service.name
location = google_cloud_run_service.run_service.location
role = "roles/run.invoker"
member = "allUsers"
}
The resource above is adding the permission to invoke the service to anyone on the internet. The allUsers
identifier is a special value that represents authenticated and unauthenticated users.
To display the service URL in the Terraform command output, add this output to the configuration:
# main.tf
# Display the service URL
output "service_url" {
value = google_cloud_run_service.run_service.status[0].url
}
Deploying the infrastructure
At this point, you have all it takes to deploy the infrastructure to Google Cloud using Terraform.
Start by initializing the configuration. Run the following command in your terminal:
# Initialize Terraform configuration
terraform init
Run terraform plan
to verify the changes that will be applied:
# Plan the changes
terraform plan
If everything is correct, you will see that 3 resources will be created and the service URL will be displayed.
Plan: 3 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ service_url = (known after apply)
Run terraform apply
to apply all the changes:
# Apply all the changes
terraform apply
If everything goes well, you will see this at the end of the output:
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Outputs:
service_url = https://app-cvmew7554a-uc.a.run.app
You can check if the service is running using curl
:
curl https://app-cvmew7554a-uc.a.run.app
Hello, world!
Version: 1.0.0
Hostname: localhost
Updating the service
Terraform can be used not just to push your initial infrastructure to Cloud Run, but also to update it.
Cloud Run works with revisions. When a configuration is changed or a new image is added, a new revision is created as a result. You can then redirect all the traffic to the new revision and start serving your updated application.
To update your service, simply change the value in the image
property and pass it a new image:
# main.tf
resource "google_cloud_run_service" "run_service" {
name = "app"
# ...
template {
spec {
containers {
# Change `hello-app:1.0` to `hello-app:2.0` 👇
image = "gcr.io/google-samples/hello-app:2.0"
}
}
}
# ...
}
With this, Terraform will create a new revision on Cloud Run. The previous revision is preserved, but because of the traffic
options defined previously, it won’t recieve any traffic.
Run terraform apply
to deploy the changes:
# Apply all the changes
terraform apply
You can check that the new image is live using curl
:
curl https://app-cvmew7554a-uc.a.run.app
Hello, world!
Version: 2.0.0 # Now serving 2.0.0
Hostname: localhost
Cleaning up
To delete all resources created with Terraform, run the following command and confirm the prompt:
# Destroy all the infrastructure created by Terraform
terraform destroy
This will disable the Cloud Run API, delete the Cloud Run service and its permissions.
The project was created using the gcloud
CLI tool, so you will need to delete it manually. For that, you can run:
# Remove the Google Cloud project
gcloud projects delete PROJECT_ID