Spotted a bug? Have a great idea? Help us make google.dev great!

Running websites can be difficult, with all of the overhead of creating and managing VMs/ Clusters/Pods/Services/etc. This is fine for larger, multi-tiered applications, but if you are just trying to get your website deployed and visible, it is a lot of overhead.

With Cloud Run, GCP's implementation of Google's KNative framework, you can manage and deploy your website without any of the infrastructure overhead you experience with VM or pure kubernetes based deployments. Not only is this a simpler approach from a management perspective, it also gives you the ability to "scale to zero" when there are no requests coming into your website.

Not only does Cloud Run bring "serverless" development to containers, it can be run either on your own Google Kubernetes Engine (GKE) clusters or on a fully managed PaaS solution provided by Cloud Run. We will be testing the latter scenario in this Code Lab.

The exercises are ordered to reflect a common cloud developer experience:

  1. Create a Docker container from your application
  2. Deploy the container to Cloud Run
  3. Modify the website
  4. Rollout a new version with zero downtime

Architecture diagram

Below you can see the flow of the deployment & Cloud Run hosting.

We begin with a Docker image created via Cloud Build, which we trigger from within Cloud Shell. We then deploy this image out to Cloud Run from a command in Cloud Shell.

What you'll learn

  • How to build a Docker image using Cloud Build and upload it to gcr.io
  • How to deploy Docker images to Cloud Run
  • How to manage Cloud Run deployments
  • How to setup an endpoint for an application on Cloud Run

Prerequisites

  • A Google Cloud Platform account with administrative access to create projects or a project with Project Owner role
  • If you're not familiar with Docker, you can familiarize yourself using Docker's "Getting Started" page.

Self-paced environment setup

If you don't already have a Google Account (Gmail or Google Apps), you must create one. Sign-in to Google Cloud Platform console (console.cloud.google.com) and create a new project:

Remember the project ID which is automatically populated under your Project Name. This Project ID is a unique name across all Google Cloud projects (the name above has already been taken and will not work for you, sorry!). It will be referred to later in this codelab as PROJECT_ID.

Next, you'll need to enable billing in the Developers Console in order to use Google Cloud resources and Enable the Cloud Run API.

Enabling the Cloud Run API

From the APIs & Services dashboard, click on the "+ Enable APIs and Services" link.

Search for the "Cloud Run API" and enable this API.

Running through this codelab shouldn't cost you more than a few dollars, but it could be more if you decide to use more resources or if you leave them running (see "cleanup" section at the end of this document). Google Container Engine pricing is documented here.

New users of Google Cloud Platform are eligible for a $300 free trial.

Google Cloud Shell

While Google Cloud and Cloud Run can be operated remotely from your laptop, in this codelab we will be using Google Cloud Shell, a command line environment running in the Cloud. This environment is pre-configured with all of the client libraries and frameworks that you will need.

This Debian-based virtual machine is loaded with all the development tools you'll need. It offers a persistent 5GB home directory, and runs on the Google Cloud, greatly enhancing network performance and authentication. This means that all you will need for this codelab is a browser (yes, it works on a Chromebook).

To activate Google Cloud Shell, from the developer console simply click the button on the top right-hand side (it should only take a few moments to provision and connect to the environment):

activateCloudShell.png

Click the "Start Cloud Shell" button:

Screen Shot 2017-06-14 at 10.13.43 PM.png

Once connected to the cloud shell, you should see that you are already authenticated and that the project is already set to your PROJECT_ID :

gcloud auth list

Command output

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

Command output

[core]
project = <PROJECT_ID>

Cloud Shell also sets some environment variables by default which may be useful as you run future commands.

echo $GOOGLE_CLOUD_PROJECT

Command output

<PROJECT_ID>

If for some reason the project is not set, simply issue the following command :

gcloud config set project <PROJECT_ID>

Looking for your PROJECT_ID? Check out what ID you used in the setup steps or look it up in the console dashboard:

Project_ID.png

IMPORTANT: Finally, set the default zone and project configuration:

gcloud config set compute/zone us-central1-f

You can choose a variety of different zones. Learn more in the Regions & Zones documentation.

Since we are deploying an existing website, we will just need to clone the source from our git repo, so we can focus on creating Docker images and deploying to Cloud Run.

Run the following commands to clone the git repo to your Cloud Shell instance and change to the appropriate directory. We will also install the NodeJS dependencies so we can test our application before deploying.

cd ~
git clone https://github.com/googlecodelabs/monolith-to-microservices.git
cd ~/monolith-to-microservices
./setup.sh

This will clone our Github repo, change to the directory and install the dependencies needed to run our application locally. It may take a few minutes for this script to run.

Let's do our due diligence and test our application, run the following command to start our web server:

cd ~/monolith-to-microservices/monolith
npm start

Output:

Monolith listening on port 8080!

You can preview your application by clicking the web preview icon and selecting Preview on port 8080.

This should open a new window where you can see our Fancy Store in action!

You can close this window after viewing the website and to stop the web server process, press CTRL+C in the terminal window.

Now that we have our source files ready to go, it is time to Dockerize our application!

Normally you would have to take a two step approach that entails building a docker container and pushing it to a registry to store the image for GKE to pull from. But we can make life easier, we can use Google Cloud Build to build the Docker container and put the image in the Google Cloud Container Registry with a single command! This allows us to issue a single command to build and move our image to the container registry. To view the manual process of creating a docker file and pushing it you can go here.

Google Cloud Build will compress the files from the directory and move them to a Google Cloud Storage bucket. The build process will then take all the files from the bucket and use the Dockerfile which is present in the same directory to run the Docker build process. Since we specified the --tag flag with the host as gcr.io for the Docker image, the resulting Docker image will be pushed to the Google Cloud Container Registry.

First we need to make sure we have the Cloud Build API enable, run the following command to enable it:

gcloud services enable cloudbuild.googleapis.com

After the API is enabled, run the following command in your cloud shell terminal to start the build process:

gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:1.0.0 .

This process will take a few minutes, but after it is completed, there will be output in the terminal similar to the following:

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ID                                    CREATE_TIME                DURATION  SOURCE                                                                                  IMAGES                              STATUS
1ae295d9-63cb-482c-959b-bc52e9644d53  2019-08-29T01:56:35+00:00  33S       gs://<PROJECT_ID>_cloudbuild/source/1567043793.94-abfd382011724422bf49af1558b894aa.tgz  gcr.io/<PROJECT_ID>/monolith:1.0.0  SUCCESS

To view your build history or watch the process in real time, you can go to the Google Cloud console. Click the menu button on the top left and scroll down to Cloud Build and click History. Here you can see a list of all your previous builds, there should only be 1 that you just created.

If you click on the build id, you can see all the details for that build including the log output.

From the build details page you can view the container image that was created by clicking on the image name in the build information section.

Now that we have containerized our website and pushed our container to the Google Container Registry, it is time to deploy to Cloud Run!

There are two approaches for deploying on top of Cloud Run:

Managed Cloud Run: The Platform as a Service model where all container lifecycle is managed by the Cloud Run product itself. We'll be using this approach in our codelab.

Cloud Run on GKE: Cloud Run with an additional layer of control which allows you to bring your own clusters & pods from GKE. You can check it out here.

Command-Line examples will be directly in the Cloud Shell, using the environment variables you set up earlier.

Command-Line

The "gcloud run" command is currently available in the beta version of gcloud, which is where a lot of early-access commands are first made available.

We will deploy the image that we built earlier, and we will choose the managed version of Cloud Run by specifying "--platform managed".

Run the following command to deploy your application:

gcloud beta run deploy --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:1.0.0 --platform managed 

You will be asked to specify which region you'd like to run in. Just select the region closest to you, then accept the default suggested Service name (it will be "monolith").

For testing purposes, we will allow unauthenticated requests into the application. Select "Y" at the prompt:

Verify Deployment

To verify the Deployment was created successfully, run the following command, it may take a few moments for the pod status to be Running:

gcloud beta run services list

Select the first option: [1] Cloud Run (fully managed)

Output:


SERVICE   REGION    URL  LAST DEPLOYED BY          LAST DEPLOYED AT
✔  monolith  us-east1 <your url>  <your email>  2019-09-16T21:07:38.267Z

This output shows us several things. We can see our Deployment, as well as the user that deployed it (your email) and the URL you can use to access the app. Looks like everything was created successfully!

Open the URL provided in the list of Services in your web browser and you should see the same website you previewed locally.

Now, deploy your application again, but this time let's adjust one of the parameters.

By default, a Cloud Run application will have a Concurrency value of "80", meaning that each container instance will serve up to 80 requests at a time. This is a big departure from the Functions-as-a-Service model, where one instance handles one request at a time.

Here, we will re-deploy the same container image with a Concurrency value of 1 (just for testing), and see what happens.

gcloud beta run deploy --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:1.0.0 --platform managed --concurrency 1

Answer the subsequent questions just as you did the first time. Once the command is successful, check the Cloud Console to see the result.

From the Cloud Run dashboard, click on the "monolith" Service to see the details:

On the Service Details page, click on the "Revisions" tab. You should now see 2 revisions created. Click on the "monolith-00002" Revision and review the details on the right hand side. You will see that the Concurrency value has been reduced to "1".

]

Although this configuration is sufficient for testing, in most production scenarios you will have containers supporting multiple concurrent requests.

Now let's just restore the original concurrency without re-deploying. We could set the concurrency value back to the default of "80", or we could just set the value to "0", which will remove any concurrency restrictions and set it to the default max (which happens to be 80 at the time of this writing).

Run the following command from the Cloud Shell to update the current revision:

gcloud beta run deploy --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:1.0.0 --platform managed --concurrency 80

You will notice that another revision has been created, that traffic has now been redirected, and that the concurrency is now back up to 80.

Your marketing team has asked you to change the homepage for your site. They think it should be more informative of who your company is and what you actually sell. In this section, we will add some text to the homepage to make the marketing team happy! It looks like one of our developers already created the changes with the file name index.js.new. We can just copy this file to index.js and our changes should be reflected. Follow the instructions below to make the appropriate changes.

Run the following commands copy the updated file to the correct file name and then print its contents to verify the changes:

cd ~/monolith-to-microservices/react-app/src/pages/Home
mv index.js.new index.js
cat ~/monolith-to-microservices/react-app/src/pages/Home/index.js

The resulting code should look like this:

/*
Copyright 2019 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Paper from "@material-ui/core/Paper";
import Typography from "@material-ui/core/Typography";
const useStyles = makeStyles(theme => ({
  root: {
    flexGrow: 1
  },
  paper: {
    width: "800px",
    margin: "0 auto",
    padding: theme.spacing(3, 2)
  }
}));
export default function Home() {
  const classes = useStyles();
  return (
    <div className={classes.root}>
      <Paper className={classes.paper}>
        <Typography variant="h5">
          Fancy Fashion &amp; Style Online
        </Typography>
        <br />
        <Typography variant="body1">
          Tired of mainstream fashion ideas, popular trends and societal norms?
          This line of lifestyle products will help you catch up with the Fancy trend and express your personal style.
          Start shopping Fancy items now!
        </Typography>
      </Paper>
    </div>
  );
}

We updated the React components, but we need to build the React app to generate the static files. Run the following command to build the React app and copy it into the monolith public directory:

cd ~/monolith-to-microservices/react-app
npm run build:monolith

Now that our code is updated, we need to rebuild our Docker container and publish it to the Google Cloud Container Registry. We can use the same command as we did earlier, except this time, we will update the version label!

Run the following command to trigger a new cloud build with an updated image version of 2.0.0:

cd ~/monolith-to-microservices/monolith

#Feel free to test your application
npm start

gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:2.0.0 .

In the next section we will use this image to update our application with zero downtime.

The changes are completed and the marketing team is happy with your updates! It is time to update the website without interruption to the users.

Cloud Run treats each deployment as a new "Revision" which will be first be brought online, then have traffic redirected to it. NOTE: By default the latest revision will be assigned 100% of the inbound traffic for a service. It is possible to use "Routes" to allocate different percentages of traffic to different revisions within a Service.

Follow the instructions below to update your website.

Command-Line

From the command line, we can re-deploy the Service to update the image to a new version with the following command:

gcloud beta run deploy --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:2.0.0 --platform managed

Verify Deployment

You can validate your deployment update by running the following command:

gcloud beta run services describe monolith --platform managed 

Output:

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  annotations:
    client.knative.dev/user-image: gcr.io/my-cloudrun-codelab/monolith:2.0.0
...

Here you will see that our Service is now using the latest version of our image, deployed in a new revision.

To verify our changes, navigate to the external URL of our CludRun service again and notice that our application title has been updated.

Run the following command to list the services and view the IP address if you forgot it:

gcloud beta run services list

Your web site should now be displaying the text we just added to the homepage component!

If you are planning on doing the other labs in this series, don't do the cleanup now, do it after you are done with all the labs in the series.

Delete Google Container Registry Images

NOTE: If you created other versions, you can use the same syntax to delete those images as well, this codelab assumes you followed exactly and only have two tags

# Delete the container image for version 1.0.0 of our monolith
gcloud container images delete gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:1.0.0 --quiet

# Delete the container image for version 2.0.0 of our monolith
gcloud container images delete gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:2.0.0 --quiet

Delete Google Cloud Build Artifacts from Google Cloud Storage

NOTE: If you used Cloud Build for artifacts other than this codelab, you will have to manually delete your source from Google Cloud Storage bucket gs://<PROJECT_ID>_cloudbuild/source

# The following command will take all source archives from all builds and delete them from cloud storage

# Run this command to print all sources:
# gcloud builds list | awk 'NR > 1 {print $4}' 

gcloud builds list | awk 'NR > 1 {print $4}' | while read line; do gsutil rm $line; done

Delete CloudRun Service

gcloud beta run services delete monolith --platform managed

You successfully deployed, de-scaled and re-scaled, and updated your website on CloudRun.

Additional Resources