DEV Community

Cover image for How to deploy a webapp to Google Cloud Run with Cloud Build
Carlos Azaustre
Carlos Azaustre

Posted on • Originally published at carlosazaustre.es

How to deploy a webapp to Google Cloud Run with Cloud Build

This article was originally published in spanish on my blog.

In the past Cloud Next event, Google announced a new product of its services: Cloud Run. This is an evolution of App Engine which let us run any backend language on a Docker container.

In this article, I will describe how to start with this service and run your first Node.js app with it. Let's go!

Create a project on Google Cloud.

Go to Google Cloud Console and create a new project. I'm named hello-cloud-run but you can name it as you want. Keep in mind the ProjectID which we are using later.

Create a new project on google cloud

Create a new project on google cloud

Creating a new project on Google Cloud

Project created

To be able to use Google Cloud Platform you will need a Gmail/GSuite account, and to activate the billing. If it is the first time you sign up, you have $300 free in credits to use for one year.

API Activation

We need to activate some APIs to have not problems. One is the Cloud Run API and another one is Cloud Build API which we will use later.

Google Cloud APIs and Services

Click on Enable APIs and Services and look for Cloud Run
Cloud Run API

Activate the Cloud Run API and do the same with Cloud Build API
Cloud Run API

Cloud Build API

Our App code

I have created this example code. It is a Node.js application that in the root path returns a JSON object with two properties: The today date and the time that the application is running up.

Create a Node project with the following command (remember to have Node.js installed):

$ npm init -y
Enter fullscreen mode Exit fullscreen mode

Then, install express as dependence:

$ npm i express
Enter fullscreen mode Exit fullscreen mode

Create an index.js file with the following content:

const express = require('express');
const app = express();
const port = process.env.PORT || 3000;

const dateStart = Date.now();

app.get('/', (req, res) => {
  const today = new Date();

  res.json({
    date: today,
    up: `${(Date.now() - dateStart) / 1000} seg.` 
  });
});

app.listen(port, () => {
  console.log(`Server running on port: ${port}`);
  console.log('Press CTRL + C to quit');
})
Enter fullscreen mode Exit fullscreen mode

Let's update the package.json file to add the start script:

...
"scripts": {
   "start": "NODE_ENV=production node index.js"
  },
...
Enter fullscreen mode Exit fullscreen mode

On this way, when we execute npm start command the app will run. We can testing locally.

Next step is to create the Dockerfile with this we define the container which contains the application code. Here you have the content:

FROM node:10

WORKDIR /usr/src/app

ENV PORT 8080
ENV HOST 0.0.0.0

COPY package*.json ./

RUN npm install --only=production

# Copy the local code to the container
COPY . .


# Start the service
CMD npm start
Enter fullscreen mode Exit fullscreen mode

With this file we are configuring an environment with Node v10 as a base, the working directory will be /usr/src/app. We are defining as environment variables the PORT: 8080and HOST: 0.0.0.0. We are copying the package.json and package-lock.json to the working directory and installing the dependencies with RUN npm install --only=production.
Finally, we are moving the app code to the container working directory with COPY . . And with the last one CMD npm start the app is run.

We can try if everything is ok so far, generating the image and the start the docker container. Write the following commands on your terminal:

$ docker build --tag hello-cloud-run:01 .
$ docker run -p 8080:8080 hello-cloud-run:01
Enter fullscreen mode Exit fullscreen mode

Keep in mind you need to have Docker installed on your system.

The build command you have created an image following the Dockerfile steps with the name hello-cloud-run:01. The run command let you run the app on http://localhost:8080

If all is ok you should see the following on your browser:
App running on localhost

Automate the container deploying

Once our project is configurated on Google Cloud and the application code is written and containerized, the following step is to upload it to Google Container Registry.

We are going to create a YAML file with the steps to build and deploy the code using Google Cloud Build. This service is similar to TravisCI but customized to Google Cloud.

On this way, every time we push our code to Github (for example) Cloud Build will build the Docker image and upload the new code to Cloud Container Registry and deploy it into Cloud Run. So awesome!

First, we need to create a trigger on Cloud Build:

Cloud Build triggers

Once created, we choose Github as source repository option

Github as source repository hosting option

We need to authenticate on the service chosen (in this case Github) and to choose the repository.

You need to store the application code on it. If you prefer other option like Bitbucket or Cloud Source, go ahead.

You can use this repository as the base project.

create trigger

On settings, to choose Cloud Build Configuration file (yaml or json) as Build configuration, putting the name cloudbuild.yaml which we write later.

Build configuration

All right! On the options, you can choose if you want to dispatch the trigger every time you push to a specific branch repo or with a tag.

build trigger created

Add roles and permissions

Once you have activated the Cloud Run API we need to follow the next steps to bring access from outside to our application.

  1. Grant Cloud Run Admin role to Cloud Build Service account

    1. From Cloud Console, access to IAM Menu
    2. On the members' list, locate and select [PROJECT_NUMBER]@cloudbuild.gserviceaccount.com
    3. Click on EDIT button (pencil icon) to approve the new role.
    4. Click on Add another role
    5. Select Cloud Run and then Cloud Run Admin
    6. Click on Save
  2. Grant IAM Service Account User to Cloud Build Service Account from Cloud Run Runtime service account

    1. From Google Cloud Console, access to Service Accounts
    2. On the members' list, locate and select [PROJECT_NUMBER]-compute@developer.gserviceaccount.com
    3. Click on Show Info Panel up on the right corner.
    4. On Permissions panel, click on Add Member button.
    5. Introduce the Cloud Build service account [PROJECT_NUMBER]@cloudbuild.gserviceaccount.com in the New Member new field.
    6. On the Role dropdown, select Service Accounts and then Service Account User.
    7. Click on Save.

Now in our code, we are going to create the cloudbuild.yaml file which executes the necessary commands to build the docker image, upload it to container registry and deploy it to Cloud Run:

steps:
  # build the container image
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'gcr.io/$PROJECT_ID/hello-cloud-run:${SHORT_SHA}', '.']
  # push the container image to Container Registry
- name: 'gcr.io/cloud-builders/docker'
  args: ['push', 'gcr.io/$PROJECT_ID/hello-cloud-run']
  # deploy container image to Cloud Run
- name: 'gcr.io/cloud-builders/gcloud'
  args: ['beta', 'run', 'deploy', 'hello-cloud-run', '--image', 'gcr.io/$PROJECT_ID/hello-cloud-run:${SHORT_SHA}', '--region', 'us-central1', '--allow-unauthenticated']
  env:
  - 'PORT=8080'
images:
- gcr.io/$PROJECT_ID/hello-cloud-run
Enter fullscreen mode Exit fullscreen mode

Keep in mind that <PROJECT_ID> is your project identifier.

Checking all is working

So now, we will deploy our application code to a repository, in my case I chose Github. (this is my repo for this example). When we made a change and we will push it to master branch, the build configuration will trigger and it will follow all the steps to upload it to Container Registry and then deploy it to Cloud Run!

When you made push to your repo, check inside Google Cloud Console if Cloud Build has triggered an event

Cloud Build triggered an event

If it is Ok, you can go to Container Registry section and check if the Docker image has been created:

Docker image created in Container Registry

And the last, check if in Cloud Run section you have an application running:

Cloud Run application running

One last thing is to let external invocations to the service because for default is private.

Add allUsers to the new members and the Cloud Run > Cloud Run Invoker role.

Grant permissions

You can see a more detail explanation on this post in Dev.to

And yes! You finish! Click on the URL associated to your Cloud Run deployment and if all is OK you can see something similar to this on your browser

Cloud Run deployment is running

References

Top comments (3)

Collapse
 
sub8s profile image
Subrahmanya S M

can get more info on Serving VueJS/reactjs/angularjs Builds via Express.js in index.js file?

res.json({
date: today,
up: ${(Date.now() - dateStart) / 1000} seg.
});

for vue js belove thing is not working:

app.use(express.static('./dist'));

app.get('/*', function(req,res) {

res.sendFile(path.join(__dirname,'/dist/index.html'));
});

Collapse
 
drvaya profile image
Dharam

Hey Carlos.. Great post ..
Quick note - You would need to update the cloudbuild.yaml file to include the --platform flag as that's a new addition in Run.

Collapse
 
marwan01 profile image
Marouen Helali

Do you know if this approach works for Angular apps? I had trouble with the port when I was trying with angular. No problems with nodejs tho.