How to Deploy to Google Cloud Run
Google Cloud Run is a managed compute platform that lets you run containers on Google’s scalable infrastructure.
This How To guide will show you how to use Docker to deploy your Deno app to Google Cloud Run.
First, we’ll show you how to deploy manually, then we’ll show you how to automate it with GitHub Actions.
Pre-requisites:
- Google Cloud Platform account
docker
CLI installedgcloud
installed
Manual Deployment
Dockerfile
and docker-compose.yml
Create To focus on the deployment, our app will simply be a main.ts
file that returns
a string as an HTTP response:
import { Application } from "https://deno.land/x/oak/mod.ts";
const app = new Application();
app.use((ctx) => {
ctx.response.body = "Hello from Deno and Google Cloud Run!";
});
await app.listen({ port: 8000 });
Then, we’ll create two files – Dockerfile
and docker-compose.yml
– to
build the Docker image.
In our Dockerfile
, let’s add:
FROM denoland/deno
EXPOSE 8000
WORKDIR /app
ADD . /app
RUN deno cache main.ts
CMD ["run", "--allow-net", "main.ts"]
Then, in our docker-compose.yml
:
version: '3'
services:
web:
build: .
container_name: deno-container
image: deno-image
ports:
- "8000:8000"
Let’s test this locally by running docker compose -f docker-compose.yml build
,
then docker compose up
, and going to localhost:8000
.
It works!
Set up Artifact Registry
Artifact Registry is GCP’s private registry of Docker images.
Before we can use it, go to GCP’s
Artifact Registry and click
“Create repository”. You’ll be asked for a name (deno-repository
) and a region
(us-central1
). Then click “Create”.
Build, Tag, and Push to Artifact Registry
Once we’ve created a repository, we can start pushing images to it.
First, let’s add the registry’s address to gcloud
:
gcloud auth configure-docker us-central1-docker.pkg.dev
Then, let’s build your Docker image. (Note that the image name is defined in our
docker-compose.yml
file.)
docker compose -f docker-compose.yml build
Then, tag it with
the new Google Artifact Registry address, repository, and name. The image name
should follow this structure:
{{ location }}-docker.pkg.dev/{{ google_cloudrun_project_name }}/{{ repository }}/{{ image }}
.
docker tag deno-image us-central1-docker.pkg.dev/deno-app-368305/deno-repository/deno-cloudrun-image
If you don’t specify a tag, it’ll use :latest
by default.
Next, push the image:
docker push us-central1-docker.pkg.dev/deno-app-368305/deno-repository/deno-cloudrun-image
More info on how to push and pull images to Google Artifact Registry.
Your image should now appear in your Google Artifact Registry!
Create a Google Cloud Run Service
We need an instance where we can build these images, so let’s go to Google Cloud Run and click “Create Service”.
Let’s name it “hello-from-deno”.
Select “Deploy one revision from an existing container image”. Use the drop down
to select the image from the deno-repository
Artifact Registry.
Select “allow unauthenticated requests” and then click “Create service”. Make
sure the port is 8000
.
When it’s done, your app should now be live:
Awesome!
gcloud
Deploy with Now that it’s created, we’ll be able to deploy to this service from the gcloud
CLI. The command follows this structure:
gcloud run deploy {{ service_name }} --image={{ image }} --region={{ region }} --allow-unauthenticated
.
Note that the image
name follows the structure from above.
For this example, the command is:
gcloud run deploy hello-from-deno --image=us-central1-docker.pkg.dev/deno-app-368305/deno-repository/deno-cloudrun-image --region=us-central1 --allow-unauthenticated
Success!
Automate Deployment with GitHub Actions
In order for automation to work, we first need to make sure that these both have been created:
- the Google Artifact Registry
- the Google Cloud Run service instance
(If you haven’t done that, please see the section before.)
Now that we have done that, we can automate it with a GitHub workflow. Here’s the yaml file:
name: Build and Deploy to Cloud Run
on:
push:
branches:
- main
env:
PROJECT_ID: {{ PROJECT_ID }}
GAR_LOCATION: {{ GAR_LOCATION }}
REPOSITORY: {{ GAR_REPOSITORY }}
SERVICE: {{ SERVICE }}
REGION: {{ REGION }}
jobs:
deploy:
name: Deploy
permissions:
contents: 'read'
id-token: 'write'
runs-on: ubuntu-latest
steps:
- name: CHeckout
uses: actions/checkout@v3
- name: Google Auth
id: auth
uses: 'google-github-actions/auth@v0'
with:
credentials_json: '${{ secrets.GCP_CREDENTIALS }}'
- name: Login to GAR
uses: docker/login-action@v2.1.0
with:
registry: ${{ env.GAR_LOCATION }}-docker.pkg.dev
username: _json_key
password: ${{ secrets.GCP_CREDENTIALS }}
- name: Build and Push Container
run: |-
docker build -t "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE }}:${{ github.sha }}" ./
docker push "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE }}:${{ github.sha }}"
- name: Deploy to Cloud Run
id: deploy
uses: google-github-actions/deploy-cloudrun@v0
with:
service: ${{ env.SERVICE }}
region: ${{ env.REGION }}
image: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi>e</mi><mi>n</mi><mi>v</mi><mi mathvariant="normal">.</mi><mi>G</mi><mi>A</mi><msub><mi>R</mi><mi>L</mi></msub><mi>O</mi><mi>C</mi><mi>A</mi><mi>T</mi><mi>I</mi><mi>O</mi><mi>N</mi></mrow><mo>−</mo><mi>d</mi><mi>o</mi><mi>c</mi><mi>k</mi><mi>e</mi><mi>r</mi><mi mathvariant="normal">.</mi><mi>p</mi><mi>k</mi><mi>g</mi><mi mathvariant="normal">.</mi><mi>d</mi><mi>e</mi><mi>v</mi><mi mathvariant="normal">/</mi></mrow><annotation encoding="application/x-tex">{{ env.GAR_LOCATION }}-docker.pkg.dev/</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">e</span><span class="mord mathnormal">n</span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="mord">.</span><span class="mord mathnormal">G</span><span class="mord mathnormal">A</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0077em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">L</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.07153em;">OC</span><span class="mord mathnormal">A</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mord mathnormal" style="margin-right:0.07847em;">I</span><span class="mord mathnormal" style="margin-right:0.10903em;">ON</span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">oc</span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mord mathnormal" style="margin-right:0.02778em;">er</span><span class="mord">.</span><span class="mord mathnormal">p</span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord">.</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="mord">/</span></span></span></span>{{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE }}:${{ github.sha }}
- name: Show Output
run: echo ${{ steps.deploy.outputs.url }}
The environment variables that we need to set are (the examples in parenthesis are the ones for this repository)
PROJECT_ID
: your project id (deno-app-368305
)GAR_LOCATION
: the location your Google Artifact Registry is set (us-central1
)GAR_REPOSITORY
: the name you gave your Google Artifact Registry (deno-repository
)SERVICE
: the name of the Google Cloud Run service (hello-from-deno
)REGION
: the region of your Google Cloud Run service (us-central1
)
The secret variables that we need to set are:
GCP_CREDENTIALS
: this is the service account json key. When you create the service account, be sure to include the roles and permissions necessary for Artifact Registry and Google Cloud Run.
Check out more details and examples of deploying to Cloud Run from GitHub Actions.
For reference: https://github.com/google-github-actions/example-workflows/blob/main/workflows/deploy-cloudrun/cloudrun-docker.yml