Deploy a Serverless CI/CD Pipeline on GCP using Cloud Run, Cloud Build & Terraform
Recently Google introduced its serverless offering called Cloud Run which abstracts away all infrastructure management, so you can focus on building apps. By combining Cloud Run, Cloud Build and Cloud Source Repositories, we can build a simple, serverless CI/CD pipeline that automatically deploys an app whenever new commits are pushed. Basically you'll get a Heroku-style interface for much less money and about 20 minutes of work.
Under the hood, we'll deploy all of the resources using Terraform. You can find the example code used in this post on my GitHub. First, let's have a look at the steps involved.
Overview
I've broken this guide down into the following steps:
- Installing the necessary tools
- Enabling the appropriate permissions
- Running Terraform to deploy the resources
- Deploying a custom app
- Viewing the app
- Cleaning up afterward
- Next steps
Install the necessary tools
In order to complete this guide, you'll need to install the following tools:
- Terraform: This guide uses Terraform to deploy the resources and manage all the infrastructure as code.
- Docker: Docker is required for local development and to push Docker images to Google Container Registry.
- Git: Git is used to clone the example code and trigger new deployments.
- GCP: You will need a GCP account with billing enabled. There is a free tier that includes $300 of free credit over a 12 month period. You will also need to install the gcloud command-line tool.
Enable the appropriate permissions
This guide requires certain permissions to be configured correctly for the CI/CD pipeline to work. To make life easier I've put together a small shell script that can do this for you automatically. This script will automatically enable the following APIs:
cloudbuild.googleapis.com
containerregistry.googleapis.com
cloudresourcemanager.googleapis.com
cloudrun.googleapis.com
Then add certain IAM policy bindings so Cloud Build and Cloud Run can work correctly. First ensure you have set the GOOGLE_CLOUD_PROJECT
environment variable to your GCP project ID (e.g: project-foo-123456
). Then, let's execute the script:
Manual Configuration
You can also manually enable the APIs using the following links:
- Enable the Cloud Build API
- Enable the Cloud Run API
- Enable the Container Registry API
- Enable the Cloud Resource Manager API
Note: Be sure to select the appropriate GCP project.
Then to add the IAM policy binding, simply run:
Note: Be sure to swap [PROJECT_NUM]
and [PROJECT_ID]
with your GCP Project ID Number and your GCP Project ID.
Grant Cloud Build Permissions
Lastly, we need to ensure Cloud Build has the appropriate permissions to deploy to the Cloud Run service.
- Visit the Cloud Build settings page.
- In the Service account permissions panel, set the status of the Cloud Run Admin role to Enabled.
If the GCP console opens a modal that says 'Additional steps may be required', you can safely click the 'SKIP' button.
The following Google Cloud Build page has more information on the required permissions.
Deploy the Resources using Terraform
If you haven't already, you should clone the following repository containing the example code, then switch
to the terraform-cloudrun-example
directory:
Next, copy the terraform.tfvars.example
file to terraform.tfvars
. You will need to replace the value of
the project
variable.
Now we can use Terraform to deploy all of the necessary resources. Simply run the following commands:
terraform init
terraform plan
terraform apply
Terraform will then deploy all of the necessary resources for our pipeline including a Cloud Run service, Git repository and a Cloud Build trigger. This step will take approximately 1-2 minutes to complete. After Terraform has completed, you’ll see a bunch of outputs similar to the following:
Apply complete! Resources: 2 added, 0 changed, 1 destroyed.
Outputs:
registry_url = us.gcr.io/your-project-123456
repository_http_url = https://source.developers.google.com/p/your-project-123456/r/sample-node-app
trigger_id = b4d81a09-20e7-4d2c-9902-577d8c160989
url = https://sample-docker-service-s4fqxvtrpq-uc.a.run.app
You can test the sample application by opening the URL in your browser:
You should see something similar to the following:
So far we've launched a Sample Google App, however, in the next step we'll deploy a custom app of our own.
Deploying a Custom App
Because Cloud Run uses Docker under the hood, you can use any of your favourite languages including Go, Python, PHP, Java and Node.JS to write your apps! For the purposes of this guide, we'll use my basic Node app to demonstrate how to deploy custom apps. First, start by cloning it using Git:
Then run the following commands to add our Cloud Source Repository as a Git remote:
Note: You must substitute [PROJECT_ID]
with your GCP Project ID and [REPO_NAME]
with your Cloud Source Repositories repo name. You can find these values by running terraform output repository_http_url
in the terraform-cloudrun-example
directory.
Now it's time to push the app source code to our Cloud Source Repository:
Google Cloud Build will trigger a new build and automatically deploy the resulting Docker container on Cloud Run.
This is handled automatically as my sample Node app contains a cloudbuild.yaml
file in the root of the repository.
Note: As we've deployed a custom app by pushing to Cloud Source Repositories, we should update the image reference in the terraform.tfvars
file otherwise Terraform will deploy the Google sample app next time it's invoked.
View the App
Once again we can open the URL in our web browser to see that the custom app has been deployed:
You should see something similar to the following:
Clean Up
Even though Cloud Run is dirt cheap to run your apps, we can easily clean up using the Terraform destroy
command:
In about 30 seconds, Terraform will undeploy the app and destroy all of the resources we've created.
Next Steps
This guide gave an example of how to deploy a simple CI/CD pipeline using serverless technologies. Under the hood, Cloud Run is built on top of an open standard - Knative which you should read more about. Additionally, you may want to explore adding a relational database using Cloud SQL so you can run stateful apps.