Preamble
If you’re a hobbyist developer and you want to host your fun app for next-to-free, you should definitely use Google Cloud Run. Once you set it up, Cloud Run is a dream to work with, but getting started can mean a lot of trial and error.
In this post, I will go step-by-step through setting up GitHub Actions to automatically deploy your cool app to Cloud Run.
Let’s get started.
Prerequisites
1. Install gcloud
and do a local deployment
My initial idea was to write a “how to get started with Cloud Run” post, but it turns out the official Quickstart is excellent.
If you’re just starting out, follow Google’s Quickstart. It covers:
- Installing and initializing the Google Cloud SDK
- Creating your first project on Google Cloud Platform (GCP)
- Building and pushing a container
- Deploying your container to Cloud Run
By the end of the tutorial, you will have installed the gcloud
command-line interface (CLI) and used it to deploy your first container to Cloud Run. This article picks up after the Quickstart material: it’s intended for anyone looking to add a bit more automation to their deployments.
2. Create a GitHub repository
You will also need a GitHub repository — you know, for the GitHub Actions to work. If you’re more of a “see the code” learner, I created an example repo — pcraig3/hello-cr — as part of writing the article. Feel free to clone or fork it.
Service accounts
When you run gcloud
commands locally, you’re probably using your root account (ie, tied to your email address) because that’s the default account you create when you sign up to GCP. For local development, that’s fine: your root account is a superuser with lots of permissions, so you can do whatever you need to.
However, you should really avoid using your root account on other platforms. If your account password is ever stolen, an evildoer can take over your entire G-Cloud setup and use it to host crappy websites, run up a huge tab, or steal your data.
Instead, you should create a "service account": a separate account with limited permissions intended for specialized tasks. If your service account is compromised, the aforementioned evildoer can take down your Cloud Run app, but they won’t be able to mess up too much else.
Creating a service account using the Console
I’m most familiar with creating service accounts by logging in and clicking around, so that’s what I’m going to do here. If you’re smarter than me, you might prefer the command line, but all the same stuff will apply.
- Log in to Google’s backend console
- Using the drop-down selector in the header, pick the project where your Cloud Run app lives. (Mine is called
hello-cr
, it’ll be in a few screenshots)
- Open the left-hand navigation menu and select "IAM & Admin" > "Service Accounts"
- Click "+ Create service account": it will be in the contextual header near the top of the screen
- The first section is titled "Service account details". Enter a "Service account name" (it will also be used for the id). Adding a description is optional. Click "Create" when you are finished.
- Next, assign permissions to your new service account. This part was pretty tricky to figure out, but after a lot of trial and error, I found this combination works.
- Add the following permissions:
Cloud Run Admin
Cloud Run Service Agent
Cloud Build Service Agent
Viewer
- Click "Continue"
- The final section ("Grant users access to this service account") is not relevant to this tutorial, so you can skip it.
Hooray! Now that you’ve created your service account, let’s make sure that everything works as expected by trying a local deployment as your new service account.
Authenticating on the command line as a service account
Show all of your service accounts with
gcloud iam service-accounts list
The account you just created should be listed there.
In order to run commands as the service account, we will need to download a service account key. The service account key is a JSON file containing credentials for the account, so be careful with it (eg, don’t commit it to your repository 😬).
Get the service account key with
gcloud iam service-accounts keys create ./NAME-OF-KEY-FILE.json --iam-account EMAIL-ADDRESS
Replace EMAIL-ADDRESS
with the email for your service account. Also, you probably want to rename the .json
file to something less shouty. If you store it in your app directory, make sure to add it to your .gitignore
file.
Now that you have the key file, you can authenticate yourself as this service account.
Authenticate yourself with
gcloud auth activate-service-account --key-file=NAME-OF-KEY-FILE.json
This way, you can run commands as the service account.
You can see which account you are logged into with
-
gcloud auth list
You can change back to your original account at any time with
gcloud config set account YOUR-EMAIL-ADDRESS
Deploying your app with your service account
You should be able to build and deploy your app using your service account. To do so, follow the steps outlined in the Cloud Run Quickstart.
Build your container image using Cloud Build, by running the following command from the directory containing the Dockerfile:
gcloud builds submit --tag gcr.io/PROJECT-ID/helloworld
where PROJECT-ID is your GCP project ID.
Once built, let’s deploy.
Deploy using the following command:
gcloud run deploy --image gcr.io/PROJECT-ID/helloworld --platform managed
Replace PROJECT-ID with your GCP project ID. You can view your project ID by running the command gcloud config get-value project.
Nice! If the deploy succeeded, you know this service account has the permissions it needs to automate future deployments.
Setting up a "deploy" GitHub Action
Phew, now we’re on the home stretch.
Github Actions automate various parts of your development workflow: you can run tests, do linting, or trigger notifications based on certain conditions. You can also set up rules to deploy your app when your main
branch is updated, which is what we want to do.
GitHub maintains a Marketplace of third-party Actions that we can use to plug into third-party services. Luckily for us, the folks at GCP have built a GitHub Action we can use to log in and run CLI commands: https://github.com/marketplace/actions/setup-gcloud-environment
GitHub Actions’ syntax is .yml
-based, so let’s see what our deploy workflow will look like.
In the root directory of your app, create a file at .github/workflows/deploy.yml
.
# .github/workflows/deploy.yml
name: Deploy to Cloud Run
on:
push:
branches:
- main
env:
PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
RUN_REGION: us-east1
SA_KEY_JSON: ${{ secrets.GCP_SA_KEY_JSON }}
jobs:
deploy:
name: Deploy to Cloud Run
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# Setup gcloud CLI
- uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
with:
version: "290.0.1"
service_account_key: ${{ secrets.GCP_SA_KEY_JSON }}
project_id: ${{ secrets.GCP_PROJECT_ID }}
# Build and push image to Google Container Registry
- name: Build
run: gcloud builds submit --tag gcr.io/$PROJECT_ID/$PROJECT_ID:$GITHUB_SHA
- name: Deploy
run: gcloud run deploy $PROJECT_ID --image gcr.io/$PROJECT_ID/$PROJECT_ID:$GITHUB_SHA --platform managed --region $RUN_REGION
The Build
and Deploy
commands are pretty similar to those we just ran above, although there are a couple of differences. Let’s take a closer look at the build command.
gcloud builds submit --tag gcr.io/$PROJECT_ID/$PROJECT_ID:$GITHUB_SHA
- The dollar-sign syntax (eg,
$PROJECT_ID
) represents an environment variable. All of our env vars are set at the top of the file, except$GITHUB_SHA
, which is a default environment variable referencing the current commit hash -
gcr.io/$PROJECT_ID/$PROJECT_ID
: My project name is also the same as my app name (hello-cr
). If your "project" name is different from your app name, you will need to add a new variable. -
:$GITHUB_SHA
: Adding a colon and a string is a way of tagging containers so we can distinguish between versions. In this case, the git SHA is appended, so that we can identify the current version of the app by referencing our commit history.
Setting up app secrets
If Actions are enabled for your repo, pushing your new deploy.yml
file will attempt a deployment but it will fail because we need to add the secrets for our service account. The secrets we need to add are referred to in the env:
section near the top of the file.
env:
PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
RUN_REGION: us-east1
SA_KEY_JSON: ${{ secrets.GCP_SA_KEY_JSON }}
The RUN_REGION
is hard-coded, but we have to create the other two secrets. Github’s documentation for creating secrets is actually really good: you can add them under "Settings" > "Secrets" > "New secret".
-
GCP_PROJECT_ID
: The project ID is just a string -
GCP_SA_KEY_JSON
: For the service account key, copy the entire JSON file into the textarea. It seems a bit weird but it works just fine.
Secrets are encrypted as soon as they are created, and there’s no way to reveal the original values once saved, so if you are adding sensitive values in the future, make sure to add them as secrets rather than accidentally committing them to your repo.
Once you’ve created your secrets and committed your deploy.yml
file, your next push to the main
branch will trigger an automated deployment. It usually takes a few minutes to deploy a new version of your application (depending mostly on how long it takes to build your container).
pcraig3/hello-cr
on GitHub
While writing this post, I created an example repository using Google’s Node.JS sample application and added my workflow file to it. It’s super simple, and might help you out if you need an tangible example of these concepts. Feel free to clone, fork, or raise an issue if you have any questions.
hello-cr
- Demo URL: https://hello-cr-3bjudp7n7q-ue.a.run.app/
- Blog post: Quickstart: Continuous deployment to Google Cloud Run using Github Actions
Overview
This repository is a reference implementation for using Github Actions to continuously deploy a Node.JS application.
The deployment configuration is described in .github/workflows/deploy.yml
. Whenever the main
branch is updated, it will:
- log in to GCP as a service account
- build and push a container, tagging it with the git
SHA
- deploy the container
The app itself is based on the sample application used in Google's "Build and Deploy" Quickstart for Cloud Run.
The repo is MIT-licensed, so you are free to use or modify it however you like.
All done!
You did it! Get yerself light lager and a pizza. 🍕
Top comments (1)
Great tutorial, thanks Paul. One quick note: I had to add "--allow-unauthenticated" to the gcloud run deploy command to make the service available to the public. Otherwise, it worked perfectly. Thanks again!