Your Cloud Run service just got a lot more dynamic.
Let’s say you have a simple Python Flask app:
# main.py
from flask import Flask
import os
app = Flask(__name__)
@app.route('/')
def hello_world():
message = os.environ.get('MESSAGE', 'Hello World!')
return f"<h1>{message}</h1>"
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))
And a requirements.txt:
Flask==2.3.2
gunicorn==20.1.0
To deploy this to Cloud Run, you’d typically do something like:
gcloud run deploy my-app \
--image gcr.io/my-project/my-app:latest \
--platform managed \
--region us-central1 \
--allow-unauthenticated
But that’s manual. A CI/CD pipeline automates this. We’ll use Cloud Build to trigger a build and deployment whenever code changes.
Here’s how it works: You push code to a repository (like Cloud Source Repositories, GitHub, or Bitbucket). Cloud Build detects the push, runs a series of build steps defined in a cloudbuild.yaml file, and then deploys the resulting container image to Cloud Run.
First, you need a cloudbuild.yaml file in your repository’s root. This tells Cloud Build what to do.
steps:
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA', '.']
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA']
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: gcloud
args:
- 'run'
- 'deploy'
- 'my-app'
- '--image'
- 'gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA'
- '--platform'
- 'managed'
- '--region'
- 'us-central1'
- '--allow-unauthenticated'
images:
- 'gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA'
Let’s break this down:
docker build: This step uses thedockerbuilder image to build your application’s container image. It tags the image with the Google Container Registry (GCR) path, using your project ID ($PROJECT_ID) and a unique tag derived from the commit SHA ($COMMIT_SHA). This ensures each deployment is tied to a specific code version.docker push: After building, this step pushes the newly tagged image to GCR. Cloud Build has built-in permissions to push to GCR in your project.gcloud run deploy: This is the core deployment step. It uses thecloud-sdkbuilder image to executegcloudcommands. It tells Cloud Run to deploy a new revision of yourmy-appservice, using the image that was just pushed. We specify the region and make it publicly accessible.images: This section is crucial. It tells Cloud Build which built images should be retained in GCR after the build completes.
To set this up, you’ll need to:
- Enable the Cloud Build API in your Google Cloud project.
- Connect your source code repository (e.g., Cloud Source Repositories) to Cloud Build. You can do this via the Cloud Build console under "Triggers." Create a new trigger, select your repository, and specify that builds should run on pushes to your main branch.
- Ensure the Cloud Build service account (
[PROJECT_NUMBER]@cloudbuild.gserviceaccount.com) has the "Cloud Run Admin" and "Storage Admin" roles. The "Storage Admin" role is needed for Cloud Build to push images to GCR.
Now, when you push a change to your repository, Cloud Build will automatically:
- Pull your code.
- Build the Docker image.
- Push the image to GCR.
- Deploy the new image to your Cloud Run service.
You can then set an environment variable to customize the greeting. For example, to deploy a version that says "Hello Gophers!", you could add a step to your cloudbuild.yaml before the deploy step:
steps:
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA', '.']
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA']
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: gcloud
args:
- 'run'
- 'deploy'
- 'my-app'
- '--image'
- 'gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA'
- '--platform'
- 'managed'
- '--region'
- 'us-central1'
- '--allow-unauthenticated'
- '--set-env-vars'
- 'MESSAGE=Hello Gophers!'
images:
- 'gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA'
With this, the next push will update your Cloud Run service to display "Hello Gophers!".
The most counterintuitive part of this setup is that Cloud Build doesn’t actually "run" your application code during the build process itself. It only builds the container image that will run your application. The deployment step then instructs Cloud Run to pull and execute that image.
The next logical step is to introduce a staging environment, perhaps deploying to a different Cloud Run service for testing before promoting to production.