This post will demonstrate how to use the Google Cloud Run platform in a CI/CD pipeline. The pipeline will test the application’s code, build a Docker image, and deploy the image as a Google Cloud Run service on the Google Cloud Platform.

Technologies used

This post assumes a basic understanding of the following;

Add a project to CircleCI

The project used in this demo can be found in this repo. If you would like to follow along, fork the repo. Then, signup for a free CircleCI account if you don’t already have one. Connect the project to CircleCI by following the instructions for setting up your build on CircleCI.

Google Cloud setup

Let’s create the necessary credentials needed to interact with the Google Cloud Run platform. These credentials will give our CI/CD pipeline the access needed to execute commands on the Google Cloud Platform (GCP).

Create a GCP project

Create a new project in the GCP console. Give your project a memorable name. We want to make it readily identifiable so that it is easy to tear down later. After creating it, be sure to copy the project id as it is different from the project name.

How to find your project id.

Getting your project’s credentials

Next, set up a service account key which you will use to create and manage resources in your GCP project. Follow the steps here to create service account keys. Select JSON as the key type, and click Create. Save this .json file locally.

Important security note: Protect your Google Cloud credentials from being published and exposed in a public GitHub repository. You must be very cautious with the data in this file because, if exposed, anyone with this information can log into your account, create resources, and run up charges. Add the credential’s .json filename to the project’s .gitignore file as a layer of protection against accidentally publishing this sensitive data.

CirceCI pipeline setup

Next, we need to update our pipeline configuration file in order to use the Google Cloud Run platform in our CI/CD pipeline.

Encoding the Google service account file

The service account file must be encoded into a base64 value in order to store this data as an environment variable on CircleCI. Run the following command in a terminal to encode the values and get the results:

base64 cicd_demo_gcp_creds.json

The results of this command will look similar to this:

ewogICJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIsCiAgInByb2plY3RfaWQiOiAiY2ljZC13b3Jrc2hvcHMiLAogICJwcml2YXRlX2tleV9pZCI6ICJiYTFmZDAwOThkNTE1ZTE0NzE3ZjE4NTVlOTY1NmViMTUwNDM4YTQ4IiwKICAicHJpdmF0ZV9rZXkiOiAiLS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tXG5NSUlFdlFJQkFEQU5CZ2txaGtpRzl3MEJBUUVGQUFTQ0JLY3dnZ1NqQWdFQUFvSUJBUURjT1JuRXFla3F4WUlTXG5UcHFlYkxUbWdWT3VzQkY5NTE1YkhmYWNCVlcyZ2lYWjNQeFFBMFlhK2RrYjdOTFRXV1ZoRDZzcFFwWDBxY2l6XG5GdjFZekRJbXkxMCtHYnlUNWFNV2RjTWw3ZlI2TmhZay9FeXEwNlc3U0FhV0ZnSlJkZjU4U0xWcC8yS1pBbjZ6XG5BTVdHZjM5RWxSNlhDaENmZUNNWXorQmlZd29ya3Nob3BzLmlhbS5nc2VydmljZWFjY291bnQuY29tIgp9Cg==

Copy the result into your clipboard. You’ll be using it in the next section.

Create project variables

In order for this CI/CD pipeline to execute commands on GCP, we have to create project-level environment variables on CircleCI. These environment variables will be used later in the config.yml file.

Create the following project-level environment variables using the CircleCI dashboard:

  • GOOGLE_PROJECT_ID: The Project ID for your Google Cloud project. This value can be retrieved from the project card in the Google Cloud Dashboard.
  • GCP_PROJECT_KEY: The base64 encoded result from the previous section.
  • GOOGLE_COMPUTE_ZONE: The value of the region to target your deployment.

Google Cloud Run (fully managed) vs Google Cloud Run on GKE with Anthos

Google Cloud Run allows you to run services on a fully managed environment or on a Google Kubernetes Engine (GKE) cluster with Anthos. In this post, I’ll address running Google Cloud Run fully managed as well as running Google Cloud Run on a GKE cluster with Anthos. In both cases, I’ll demonstrate how to integrate Google Cloud Run deployments into your CI/CD pipelines using the Google Cloud Run orb.

Creating a CI/CD pipeline with the Google Cloud Run (fully managed) service

Now that you have all of the elements required to use the Google Cloud Run platform in a CircleCI pipeline, update your project’s config.yml file with the following configuration syntax.

version: 2.1
orbs:
  gcp-gcr: circleci/gcp-gcr@0.6.1
  cloudrun: circleci/gcp-cloud-run@1.0.0
jobs:
  build_test:
    docker:
      - image: circleci/python:3.7.4
    steps:
      - checkout
      - run:
          name: Install Python Dependencies
          command: |
            echo 'export PATH=~$PATH:~/.local/bin' >> $BASH_ENV && source $BASH_ENV
            pip install --user -r requirements.txt
      - run:
          name: Run Tests
          command: |
            pytest
  build_push_image_cloud_run_mangaged:
    docker:
      - image: circleci/python:3.7.4
    steps:
      - checkout
      - setup_remote_docker:
          docker_layer_caching: false
      - run:
          name: Build app binary and Docker image
          command: |
            echo 'export PATH=~$PATH:~/.local/bin' >> $BASH_ENV
            echo ${GCP_PROJECT_KEY} | base64 --decode --ignore-garbage > $HOME/gcloud-service-key.json
            echo 'export GOOGLE_CLOUD_KEYS=$(cat $HOME/gcloud-service-key.json)' >> $BASH_ENV
            echo 'export TAG=${CIRCLE_SHA1}' >> $BASH_ENV
            echo 'export IMAGE_NAME=$CIRCLE_PROJECT_REPONAME' >> $BASH_ENV && source $BASH_ENV
            pip install --user -r requirements.txt
            pyinstaller -F hello_world.py
            docker build -t us.gcr.io/$GOOGLE_PROJECT_ID/$IMAGE_NAME -t us.gcr.io/$GOOGLE_PROJECT_ID/$IMAGE_NAME:$TAG .
      - gcp-gcr/gcr-auth:
          gcloud-service-key: GOOGLE_CLOUD_KEYS
          google-project-id: GOOGLE_PROJECT_ID
          google-compute-zone: GOOGLE_COMPUTE_ZONE
      - gcp-gcr/push-image:
          google-project-id: GOOGLE_PROJECT_ID
          registry-url: "us.gcr.io"
          image: $IMAGE_NAME
      - cloudrun/deploy:
          platform: "managed"
          image: "us.gcr.io/$GOOGLE_PROJECT_ID/$IMAGE_NAME"
          service-name: "orb-gcp-cloud-run"
          region: $GOOGLE_COMPUTE_ZONE
          unauthenticated: true
workflows:
  build_test_deploy:
    jobs:
      - build_test
      - build_push_image_cloud_run_mangaged:
          requires:
            - build_test

Cloud Run (fully managed) config breakdown

Let’s breakdown the pipeline syntax that implements the Google Cloud Run orb and deploys the application using the Google Cloud Run (fully managed) service.

version: 2.1
orbs:
  gcp-gcr: circleci/gcp-gcr@0.6.1
  cloudrun: circleci/gcp-cloud-run@1.0.0

The snippet above declares 2.1 as the version of CircleCI’s platform to use. The orbs: key specifies the orbs to include in this pipeline. In this example, I’ll be using the circleci/gcp-gcr@0.6.1 and circleci/gcp-cloud-run@1.0.0 orbs. I included the gcp-gcr orb to demonstrate implementing multiple orbs into your pipeline.

jobs:
  build_test:
    docker:
      - image: circleci/python:3.7.4
    steps:
      - checkout
      - run:
          name: Install Python Dependencies
          command: |
            echo 'export PATH=~$PATH:~/.local/bin' >> $BASH_ENV && source $BASH_ENV
            pip install --user -r requirements.txt
      - run:
          name: Run Tests
          command: |
            pytest
  build_push_image_cloud_run_mangaged:
    docker:
      - image: circleci/python:3.7.4
    steps:
      - checkout
      - setup_remote_docker:
          docker_layer_caching: false
      - run:
          name: Build app binary and Docker image
          command: |
            echo 'export PATH=~$PATH:~/.local/bin' >> $BASH_ENV
            echo ${GCP_PROJECT_KEY} | base64 --decode --ignore-garbage > $HOME/gcloud-service-key.json
            echo 'export GOOGLE_CLOUD_KEYS=$(cat $HOME/gcloud-service-key.json)' >> $BASH_ENV
            echo 'export TAG=${CIRCLE_SHA1}' >> $BASH_ENV
            echo 'export IMAGE_NAME=$CIRCLE_PROJECT_REPONAME' >> $BASH_ENV && source $BASH_ENV
            pip install --user -r requirements.txt
            pyinstaller -F hello_world.py
            docker build -t us.gcr.io/$GOOGLE_PROJECT_ID/$IMAGE_NAME -t us.gcr.io/$GOOGLE_PROJECT_ID/$IMAGE_NAME:$TAG .

The snippet above shows the jobs:key which is a list of jobs that represents a grouping of actions to execute within the pipeline. This snippet specifies two jobs: build_test: and build_push_image_cloud_run_mangaged:. The build_test job installs application dependencies. It then executes the project’s unit tests to ensure the app passes before moving onto the next job in the pipeline. The next job listed, build_push_image_cloud_run_mangaged:, creates environment variables, and builds a Docker image that will be deployed to the Google Cloud Run service.

      - gcp-gcr/gcr-auth:
          gcloud-service-key: GOOGLE_CLOUD_KEYS
          google-project-id: GOOGLE_PROJECT_ID
          google-compute-zone: GOOGLE_COMPUTE_ZONE

The snippet above uses the gcp-gcr/gcr-auth: orb to authenticate to Google Container Registry (GCR) using the environment variables set in the previous section.

      - gcp-gcr/push-image:
          google-project-id: GOOGLE_PROJECT_ID
          registry-url: "us.gcr.io"
          image: $IMAGE_NAME

In the snippet above, the push-image command is used to push the newly created image up to GCR for use within GCP.

      - cloudrun/deploy:
          platform: "managed"
          image: "us.gcr.io/$GOOGLE_PROJECT_ID/$IMAGE_NAME"
          service-name: "orb-gcp-cloud-run"
          region: $GOOGLE_COMPUTE_ZONE
          unauthenticated: true

The code snippet above uses the deploy function from the cloudrun orb to create and deploy the Google Cloud Run service that will serve up the newly packaged Docker image. The platform parameter is set to managed which deploys the service to the fully managed environment on GCP.

The next section will demonstrate how to deploy the Docker image to Google Cloud Run for Anthos.

Creating a CI/CD pipeline with the Google Cloud Run service on GKE for Anthos

The previous section demonstrated how to deploy the application to the fully managed Google Cloud Run environment. In this section, I’ll demonstrate how to deploy an application to Google Cloud Run on GKE for Anthos. In the following pipeline example the build_test: job is identical to the previous fully managed Google Cloud Run example, but notice the new job named build_push_image_cloud_run_gke: which deploys an application to a Google Cloud Run service running on a GKE cluster via the Google Cloud Run orb.

version: 2.1
orbs:
  gcp-gcr: circleci/gcp-gcr@0.6.1
  cloudrun: circleci/gcp-cloud-run@1.0.0
jobs:
  build_test:
    docker:
      - image: circleci/python:3.7.4
    steps:
      - checkout
      - run:
          name: Install Python Dependencies
          command: |
            echo 'export PATH=~$PATH:~/.local/bin' >> $BASH_ENV && source $BASH_ENV
            pip install --user -r requirements.txt
      - run:
          name: Run Tests
          command: |
            pytest
  build_push_image_cloud_run_gke:
    docker:
      - image: circleci/python:3.7.4
    steps:
      - checkout
      - setup_remote_docker:
          docker_layer_caching: false
      - run:
          name: Build app binary and Docker image
          command: |
            echo 'export PATH=~$PATH:~/.local/bin' >> $BASH_ENV
            echo ${GCP_PROJECT_KEY} | base64 --decode --ignore-garbage > $HOME/gcloud-service-key.json
            echo 'export GOOGLE_CLOUD_KEYS=$(cat $HOME/gcloud-service-key.json)' >> $BASH_ENV
            echo 'export TAG=${CIRCLE_SHA1}' >> $BASH_ENV
            echo 'export IMAGE_NAME=$CIRCLE_PROJECT_REPONAME' >> $BASH_ENV && source $BASH_ENV
            pip install --user -r requirements.txt
            pyinstaller -F hello_world.py
            docker build -t us.gcr.io/$GOOGLE_PROJECT_ID/$IMAGE_NAME -t us.gcr.io/$GOOGLE_PROJECT_ID/$IMAGE_NAME:$TAG .
      - gcp-gcr/gcr-auth:
          gcloud-service-key: GOOGLE_CLOUD_KEYS
          google-project-id: GOOGLE_PROJECT_ID
          google-compute-zone: GOOGLE_COMPUTE_ZONE
      - gcp-gcr/push-image:
          google-project-id: GOOGLE_PROJECT_ID
          registry-url: "us.gcr.io"
          image: $IMAGE_NAME
      - cloudrun/create_gke_cluster:
          cluster-name: $CIRCLE_PROJECT_REPONAME
          machine-type: "g1-small"
          zone: $GOOGLE_COMPUTE_ZONE
          enable-stackdriver-kubernetes: true
          scopes: "cloud-platform"
      - cloudrun/deploy:
          platform: "gke"
          image: "us.gcr.io/$GOOGLE_PROJECT_ID/$IMAGE_NAME"
          cluster: $CIRCLE_PROJECT_REPONAME
          service-name: $CIRCLE_PROJECT_REPONAME
          cluster-location: $GOOGLE_COMPUTE_ZONE
workflows:
  build_test_deploy:
    jobs:
      - build_test
      - build_push_image_cloud_run_gke:
          requires:
            - build_test

Cloud Run GKE Config Breakdown

The first job of the pipeline example shown above was covered in the fully managed section, so I’ll skip to the new job build_push_image_cloud_run_gke:.

  build_push_image_cloud_run_gke:
    docker:
      - image: circleci/python:3.7.4
    steps:
      - checkout
      - setup_remote_docker:
          docker_layer_caching: false
      - run:
          name: Build app binary and Docker image
          command: |
            echo 'export PATH=~$PATH:~/.local/bin' >> $BASH_ENV
            echo ${GCP_PROJECT_KEY} | base64 --decode --ignore-garbage > $HOME/gcloud-service-key.json
            echo 'export GOOGLE_CLOUD_KEYS=$(cat $HOME/gcloud-service-key.json)' >> $BASH_ENV
            echo 'export TAG=${CIRCLE_SHA1}' >> $BASH_ENV
            echo 'export IMAGE_NAME=$CIRCLE_PROJECT_REPONAME' >> $BASH_ENV && source $BASH_ENV
            pip install --user -r requirements.txt
            pyinstaller -F hello_world.py
            docker build -t us.gcr.io/$GOOGLE_PROJECT_ID/$IMAGE_NAME -t us.gcr.io/$GOOGLE_PROJECT_ID/$IMAGE_NAME:$TAG .
      - gcp-gcr/gcr-auth:
          gcloud-service-key: GOOGLE_CLOUD_KEYS
          google-project-id: GOOGLE_PROJECT_ID
          google-compute-zone: GOOGLE_COMPUTE_ZONE
      - gcp-gcr/push-image:
          google-project-id: GOOGLE_PROJECT_ID
          registry-url: "us.gcr.io"
          image: $IMAGE_NAME

The snippet above builds the Docker image based on the application and uploads the image to GCR. These actions are also identical to the Docker operations demonstrated in the fully managed section of this post.

      - cloudrun/create_gke_cluster:
          cluster-name: $CIRCLE_PROJECT_REPONAME
          machine-type: "g1-small"
          zone: "us-east1"
          enable-stackdriver-kubernetes: true
          scopes: "cloud-platform"
      - cloudrun/deploy:
          platform: "gke"
          image: "us.gcr.io/$GOOGLE_PROJECT_ID/$IMAGE_NAME"
          cluster: $CIRCLE_PROJECT_REPONAME
          service-name: $CIRCLE_PROJECT_REPONAME
          cluster-location: $GOOGLE_COMPUTE_ZONE
workflows:
  build_test_deploy:
    jobs:
      - build_test
      - build_push_image_cloud_run_gke:
          requires:
            - build_test

In the snippet above, the pipeline deploys the Google Cloud Run app to a GKE cluster using the cloudrun orb. The cloudrun/create_gke_cluster: command created a new GKE cluster where the Google Cloud Run app will be deployed. Next, the cloudrun/deploy: command deploys the application to the newly created GKE cluster. The application is being deployed to a Kubernetes cluster and can take a few minutes, so be patient during this process. It will take a bit longer than the other operations.

Wrapping up

This post shows how to automate the building, testing, and deploying of applications to the Google Cloud Run platform within CI/CD pipelines using the Google Cloud Run orb. Using orbs within CircleCI pipelines offers a clean, concise, and well-tested solution for deploying applications to the Google Cloud Run platform.

Thanks for reading!