Getting Started with CloudRun and Terraform - CloudRun 101
Master CloudRun Deployment: A Step-by-Step Guide with Terraform.
CloudRun has become my go-to solution for deploying applications in the cloud due to its simplicity and scalability. It's an ideal choice for various use cases.
In this upcoming blog post series, I'll guide you on how to get started with CloudRun effectively. We'll begin with the quintessential "Hello, World," using Terraform to manage the infrastructure. You can get the complete code on GitHub.
But first, let's take a look at the basics of CloudRun and understand why it's such a powerful tool.
What is CloudRun? ☁️🏃♂️
CloudRun is a fully managed serverless platform on the Google Cloud Platform (GCP). It allows you to deploy containerized applications written in any programming language or framework. The platform automatically scales to handle incoming requests based on demand, making it suitable for a wide range of applications, from small-scale projects to large enterprise applications. CloudRun follows a pay-as-you-go pricing model, making it cost-effective.
You can deploy any Docker container to CloudRun. The platform operates on a request-based model, meaning it starts your container when an HTTP request comes in and processes it. If there are no incoming requests, CloudRun scales down to zero, resulting in zero costs for you.
While there are ways to keep containers alive without requests, the primary model of CloudRun is request-based. It is not designed for long-running operations, especially background processes. For such needs, Google Cloud offers another service called CloudRun Jobs, which is specifically tailored for long-running processes.
Setting Up the Environment
Setting up the environment for deploying applications on Google Cloud Platform (GCP) involves a few key components: a GCP account, the gcloud command-line tool, and Terraform for infrastructure provisioning. I assume that you already have a GCP account and a Google Cloud Project. I also take basic command line skills for granted.
We need to install two essential command line tools:
gcloud is the primary command-line interface for interacting with GCP resources. Install gcloud by following the instructions provided in the GCP documentation.
Terraform is an infrastructure as code tool that allows you to define and provision cloud infrastructure using a declarative configuration language. You can download and install Terraform from the official Terraform website. I recommend using a version manager like tfenv to install Terraform.
Now we are ready to go furthe!
A Quick Terraform Primer 🏗️
Terraform is a tool to manage infrastructure as code. Instead of manually clicking through a web interface to set up resources, Terraform utilizes HCL (HashiCorp Configuration Language), a YAML-like language, to describe what resources you need. HCL is a declarative language (like SQL): instead of describing how to set up your infrastructure, you write what you expect as the end result. So you define how your CloudRun service should look, and Terraform determines what to do and in what order.
For this, Terraform needs to keep a state. This is a plain text file where Terraform saves which resources it has created and details about them. Since Terraform relies on this state, it is important to never change resources created by Terraform without using Terraform.1 Although you could manually change resources and get away with it, it would confuse Terraform and cause issues later. Only do this if you are sure about the changes. Additionally, never cancel a Terraform operation, as it may result in a corrupted state. If you make a mistake, let Terraform complete the operation, correct the mistake, and then run Terraform again.
To interact with Terraform, it is sufficient to know these commands:
terraform init
: Initializes Terraform (similar togit init
). Must be done only once.terraform plan
: Shows what Terraform will do without actually performing any actions (dry run).terraform apply
: Creates the resources.terraform destroy
: Destroys all resources saved in the state.
Since this is not primarily a Terraform tutorial, I aim to balance best practices with simplicity. Check the section at the bottom for a discussion on what could be improved in a real-world scenario.
There are many great resources to learn Terraform, but this overview is sufficient for now.
Lastly, we must enable Terraform to create GCP resources on our behalf. Run the following command to create a credentials file for Terraform. It will prompt you to open a browser and log in with the account you want to use:
gcloud auth application-default login
Now can create our CloudRun service.
Hello, CloudRun!
Now that we have the necessary tools installed and initialized, we can move on to the exciting part—setting up and deploying our CloudRun service. In the following sections, we'll configure Terraform to create and manage our CloudRun infrastructure.
Initialize Terraform
Go to the folder where you want to store the Terraform configuration files and create a file called constants.tf to hold your project settings:
# constants.tf
locals {
# The project ID where the Cloud Run service will be created
project_id = "..."
}
Next, we must tell Terraform to load the Google Cloud provider so it can communicate with GCP. Create a new file called provider.tf:
# provider.tf
terraform {
required_providers {
google = {
version = "~> 5.32.0"
}
}
}
provider "google" {
project = local.project_id
}
Now, use terraform init
to load the provider and create your workspace.
> terraform init
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of hashicorp/google from the dependency lock file
- Using previously-installed hashicorp/google v5.32.0
Terraform has been successfully initialized!
Since we have all the tools ready, we can start setting up the CloudRun.
Adding CloudRun
Before you can use any service on GCP, you first must activate its API. Create a new file called apis.tf:
# apis.tf
resource "google_project_service" "run_api" {
# references the value in constants.tf
project = local.project_id
service = "run.googleapis.com"
disable_on_destroy = false
}
disable_on_destroy
tells Terraform to keep the API activated even if you call terraform destroy
.
Now create cloudrun.tf to add the actual service:
# cloudrun.tf
resource "google_cloud_run_v2_service" "service" {
name = "hello-cloudrun"
location = "europe-west1"
ingress = "INGRESS_TRAFFIC_ALL"
template {
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
}
The location specifies where your service should be hosted. Choose a region that is close to your users. You can use this handy tool to select the region which suits you the most. Ingress describes how your service can be accessed. We choose to accept all traffic. You could also allow only load balancer traffic or only traffic from your internal GCP network, which we will explore in another post.
The template section describes how your service should look. Here, we only specify the Docker image and leave the rest with the defaults.
Next, create a file called outputs.tf to print the URL of the CloudRun service to the terminal after it is created:
# outputs.tf
output "cloudrun_url" {
value = google_cloud_run_v2_service.service.uri
description = "The URL of the Cloud Run service"
}
We are all set now. Run terraform plan
to see what will happen:
> terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
+ create
Terraform will perform the following actions:
# google_cloud_run_v2_service.service will be created
+ resource "google_cloud_run_v2_service" "service" {
...
+ ingress = "INGRESS_TRAFFIC_ALL"
...
+ location = "europe-west1"
+ name = "hello-cloudrun"
...
+ project = "premium-oven-425714-n3"
...
+ uri = (known after apply)
+ template {
...
+ containers {
+ image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
}
# google_project_service.run_api will be created
+ resource "google_project_service" "run_api" {
+ disable_on_destroy = false
+ id = (known after apply)
+ project = "your_project_id"
+ service = "run.googleapis.com"
}
Plan: 2 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ cloudrun_url = (known after apply)
Looks good. Terraform will enable the API, create a CloudRun service, and then print its URL. Now, run terraform apply
to create the resources:
> terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
+ create
Terraform will perform the following actions:
...
Plan: 2 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ cloudrun_url = (known after apply)
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
google_project_service.run_api: Creating...
google_cloud_run_v2_service.service: Creating...
google_project_service.run_api: Creation complete after 4s [id=.../run.googleapis.com]
google_cloud_run_v2_service.service: Still creating... [10s elapsed]
google_cloud_run_v2_service.service: Creation complete after 10s [id=projects/.../locations/europe-west1/services/hello-cloudrun]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
cloudrun_url = "https://hello-cloudrun-...a-ew.a.run.app"
It is possible that you run into an error:
Error: Error creating Service: googleapi: Error 403: Cloud Run Admin API has not been used in project … before or it is disabled.
It may some take some time to really enable the API. If this is the case, just wait a couple of minutes ☕ and run terraform apply
again.
Great! If you now open the URL in the browser, you’ll see the following:
Bummer. Didn’t we allow all traffic via INGRESS_TRAFFIC_ALL
? True, but here comes another handy feature of Cloud Run: secure by default. It allows traffic from the internet, but only authenticated traffic.
If you want to access your service in the browser, you can do two things. First is to provide a correct Authentication header in your request. To do this, you first must install a browser extension that allows you to set request headers. There are plenty to choose from depending on your browser.
To get a valid token, run gcloud auth print-identity-token
in your terminal. This will print a valid OAuth token. Now, use your extension to add the Authorization header, and set it to the value Bearer <token from above>
. If you now open the URL again with the header activated, you’ll see this:
So you did it! There’s your up-and-running CloudRun service! 🥳🍾
As mentioned above, there is a way to access the service without authorization. To do this, you must assign the role run.invoker
to everyone. All users with this role may call your Cloud Run services. To do this, add the following to your cloudrun.tf and run terraform apply
again:
# cloudrun.tf
...
resource "google_cloud_run_v2_service_iam_member" "public_access" {
project = google_cloud_run_v2_service.service.project
location = google_cloud_run_v2_service.service.location
name = google_cloud_run_v2_service.service.name
role = "roles/run.invoker"
member = "allUsers"
}
Now, you (and everyone else on the internet 🧑💻!) can just open the provided URL. Obviously, you should use this with care.
We are done for now. 🎉 Call terraform destroy 💣
to delete the resources created. Next time, we will see how to deploy our own Docker images to CloudRun, using our own registry!
Wrapping Up
Congratulations on deploying your first CloudRun service! 🎉 You’ve taken a big step in leveraging the power of serverless infrastructure. Head over to GitHub to get the complete code!
As mentioned above, this is not a comprehensive Terraform tutorial. In the real world, you would do some things differently.
We saved the Terraform state in a local file. The state contains all information about what Terraform created in the cloud. In a real-world scenario, you would likely save the state in the cloud itself, ensuring that the latest version is accessible to every team member. Terraform self-manages a lock on the state, preventing concurrent modifications.
Thank you for reading! In the next article of this series, we will explore how to deploy our own Docker images to CloudRun using our own registry 📦. Stay tuned for more insights and tips. 🚀
I'd love to hear your thoughts, questions, and feedback in the comments below. 💬 Don't forget to subscribe for more insights 🔔 and share this post with your network! 📢
First rule of the Terraform Club.