CI/CD with Cloud Run and Artifact Registry

Using cloudbuild.yaml to configure a CI/CD pipeline

Author
Published

May 22, 2024

Below is an example cloudbuild.yaml file that will:

  1. Build a Docker image using Cloud Build

  2. Push the built image to Artifact Registry

  3. Deploy the image to Cloud Run

There may be some additional setup/service account permissions that need to be specified (I’ll update this later once I sort these out), but at the very least, we need to create a repository in Artifact Registry first. To do that, run the following in gcloud:

gcloud artifacts repositories create REPO_NAME --format docker --region us-east4

From there, the cloudbuild.yaml file should look roughly like this:

substitutions:
  _REPO_NAME: 'my-repo'
  _IMAGE_NAME: 'my-image'
  _REGION: 'us-east4'
  _TAG: 'prod'
  _SERVICE: 'my-service'

steps:
  #docker build 
  - name: 'gcr.io/cloud-builders/docker'
    args: 
      - 'build'
      - '-t'
      - '${_REGION}-docker.pkg.dev/${PROJECT_ID}/${_REPO_NAME}/${_IMAGE_NAME}:${_TAG}'
      - '.'

  #docker push to artifact registry
  - name: 'gcr.io/cloud-builders/docker'
    args: 
      - 'build'
      - '-t'
      - '${_REGION}-docker.pkg.dev/${PROJECT_ID}/${_REPO_NAME}/${_IMAGE_NAME}:${_TAG}'
      - '.'
  
  #deploy the container to cloud run
  - name: 'gcr.io/cloud-builders/gcloud'
    args:
      - 'run'
      - 'deploy'
      - '${_SERVICE}'
      - '--image=${_REGION}-docker.pkg.dev/${PROJECT_ID}/${_REPO_NAME}/${_IMAGE_NAME}:${_TAG}'
      - '--region=${_REGION}'
      - '--max-instances=1'
      - '--min-instances=0'
      - '--tag=${_TAG}'
      - '--cpu=1'
      - '--memory=2Gi'
      - '--allow-unauthenticated'
      - '--set-secrets=/my_mount/MY_SECRET=MY_SECRET:latest'
      - '--concurrency=80'
      - '--port=8080'

#optional but prob worth setting
timeout: 3600s

options:
  logging: CLOUD_LOGGING_ONLY

Walking through what some of the above does…

The substitutions section allows us to set variables to pass to other steps in the build process. We can set default values for these variables, but we can also substitute different values if we need to. Like, we could configure our CI/CD to substitute different values in for prod and test environments.

I think the Docker stuff is fairly straightforward – it’s just building a Docker image (step 1) and pushing the built image to Google’s Artifact Repository (step 2).

Step 3 – where we’re deploying the image to Google Cloud Run – is more involved. We’re setting various options here, such as allocating 2GB of memory to our instance, allocating 1 CPU, ensuring we have a maximum of 1 instance available and a minimum of 0 instances, etc. Even though we’re doing quite a few things here, most of the options are kind of obvious (and there are lots more we could specify but don’t).

The one option that’s a bit confusing is the --set-secrets option. Here, we’re mounting a secret to /my_mount/MY_SECRET. We define the secret (which is managed in Google Secret Manager in this case) by setting /my_mount/MY_SECRET=MY_SECRET:latest, which specifies that we want to use the latest version of MY_SECRET.