Deploy a website with Cloud Run

1. Before you begin

Running websites can be difficult with all the overhead of creating and managing Virtual Machine (VM) instances, clusters, Pods, services, and more. That's fine for larger, multi-tiered apps, but if you're only trying to get your website deployed and visible, then it's a lot of overhead.

With Cloud Run, the Google Cloud implementation of Knative, you can manage and deploy your website without any of the overhead that you need for VM- or Kubernetes-based deployments. Not only is that a simpler approach from a management perspective, but it also gives you the ability to scale to zero when there are no requests coming to your website.

Not only does Cloud Run bring serverless development to containers, but it can also be run either on your own Google Kubernetes Engine (GKE) clusters or on a fully managed platform as a service (PaaS) solution provided by Cloud Run. You'll test the latter scenario in this codelab.

The following diagram illustrates the flow of the deployment and Cloud Run hosting. You begin with a Docker image created via Cloud Build, which you trigger in Cloud Shell. Then, you deploy that image to Cloud Run with a command in Cloud Shell.

db5f05c090d5ebcb.png

Prerequisites

What you'll learn

  • How to build a Docker image with Cloud Build and upload it to gcr.io
  • How to deploy Docker images to Cloud Run
  • How to manage Cloud Run deployments
  • How to set up an endpoint for an app on Cloud Run

What you'll build

  • A static website that runs inside a Docker container
  • A version of this container that lives in Container Registry
  • A Cloud Run deployment for your static website

What you'll need

  • A Google Account with administrative access to create projects or a project with project-owner role

2. Environment setup

Self-paced environment setup

If you don't already have a Google Account, then you must create one. Then, sign into the Google Cloud Console and click Project > Create project.

53dad2cefdae71da.png

faab21976aabeb4c.png

Remember the project ID, which is automatically populated under your project name. The project ID is a unique name across all Google Cloud projects, so the name in the screenshot has already been taken and will not work for you. It will be referred to later as PROJECT_ID.

Next, you need to enable billing in the Cloud Console to use Google Cloud resources and enable the Cloud Run API.

Enable the Cloud Run API

Click Navigation menu ☰ > APIs & Services > Dashboard > Enable APIs And Services. .

5dbb2e6e27a55fcf.png

Search for "Cloud Run API," then click Cloud Run API > Enable.

f1fd486174a744cf.png

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 Clean up at the end). For more information, see Pricing.

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

Cloud Shell

While Google Cloud and Cloud Run can be operated remotely from your laptop, you'll use Cloud Shell, a command-line environment running in Google Cloud. The environment is preconfigured with all the client libraries and frameworks that you 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 in 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).

  1. To activate Cloud Shell from the Cloud Console, simply click Activate Cloud Shell fEbHefbRynwXpq1vj2wJw6Dr17O0np8l-WOekxAZYlZQIORsWQE_xJl-cNhogjATLn-YxLVz8CgLvIW1Ncc0yXKJsfzJGMYgUeLsVB7zSwz7p6ItNgx4tXqQjag7BfWPcZN5kP-X3Q (it should only take a few moments to provision and connect to the environment).

I5aEsuNurCxHoDFjZRZrKBdarPPKPoKuExYpdagmdaOLKe7eig3DAKJitIKyuOpuwmrMAyZhp5AXpmD_k66cBuc1aUnWlJeSfo_aTKPY9aNMurhfegg1CYaE11jdpSTYNNIYARe01A

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

Once connected to 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>

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 Cloud Console dashboard:

R7chO4PKQfLC3bvFBNZJALLTUiCgyLEq_67ECX7ohs_0ZnSjC7GxDNxWrJJUaoM53LnqABYamrBJhCuXF-J9XBzuUgaz7VvaxNrkP2TAn93Drxccyj2-5zz4AxL-G3hzxZ4PsM5HHQ

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>
  1. Finally, set the default zone and project configuration.
gcloud config set compute/zone us-central1-f

You can choose a variety of different zones. For more information, see Regions & Zones.

3. Clone source repository

Given that you're deploying an existing website, you only need to clone the source from your repository, so you can focus on creating Docker images and deploying to Cloud Run.

Run the following commands to clone the repository to your Cloud Shell instance and change to the appropriate directory. You'll also install the Node.js dependencies so that you can test your app before deployment.

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

That clones your repository, changes to the directory, and installs the dependencies needed to locally run your app. It may take a few minutes for the script to run.

Do your due diligence and test your app. Run the following command to start your web server:

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

Output:

Monolith listening on port 8080!

You can preview your app by clicking Web Preview acc630712255c604.png and selecting Preview on port 8080.

5869738f0e9ec386.png

That opens a new window where you can see your Fancy Store in action!

9ed25c3f0cbe62fa.png

You can close this window after viewing the website. To stop the web-server process, press CONTROL+C (Command+C on Macintosh) in the terminal window.

4. Create Docker container with Cloud Build

Now that your source files are ready to go, it's time to Dockerize your app!

Normally, you'd 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. However, you can make life easier by using Cloud Build to build the Docker container and put the image in Container Registry with a single command! To view the manual process of creating a Dockerfile and pushing it, see Quickstart for Container Registry.

Cloud Build compresses the files from the directory and moves them to a Cloud Storage bucket. The build process then takes all the files from the bucket and uses the Dockerfile, which is present in the same directory for running the Docker build process. Given that you specified the --tag flag with the host as gcr.io for the Docker image, the resulting Docker image will be pushed to the Container Registry.

First, you need to make sure that you have the Cloud Build API enabled. Run the following command to enable it:

gcloud services enable cloudbuild.googleapis.com

After the API is enabled, run the following command in Cloud Shell to start the build process:

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

That process takes a few minutes, but after it's 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 Cloud Console, then click Navigation menu ☰ > Cloud Build > History. There, you can see a list of all your previous builds, but there should only be the one that you created.

4c753ede203255f6.png

If you click on the Build id, you can see all the details for that build, including the log output. You can view the container image that was created by clicking the link next to Image.

6e88ed1643dfe629.png

5. Deploy container to Cloud Run

Now that you containerized your website and pushed it to Container Registry, it's time to deploy to Cloud Run!

There are two approaches for deploying to Cloud Run:

  • Cloud Run (fully managed) is the PaaS model where the entire container lifecycle is managed. You'll use that approach for this codelab.
  • Cloud Run for Anthos is Cloud Run with an additional layer of control, which allows you to bring your clusters and Pods from GKE. For more information, see Setting up Cloud Run for Anthos on Google Cloud.

Command-line examples will be in Cloud Shell using the environment variables that you set up earlier.

Command line

Run the following command to deploy your app:

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

You'll be asked to specify which region you'd like to run in. Select the region closest to you, then accept the default suggested service name (monolith).

d52d9419c5166674.png

For testing purposes, allow unauthenticated requests to the app. Enter y at the prompt.

3a57b32f133dad61.png

Verify deployment

To verify that the deployment was created successfully, run the following command. It may take a few moments for the Pod status to be Running:

gcloud run services list

Select [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

The output shows you several things. You can see your deployment, as well as the user that deployed it (your email address) and the URL that 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 that you locally previewed.

6. Create new revision with lower concurrency

Now, deploy your app again, but this time adjust one of the parameters.

By default, a Cloud Run app will have a concurrency value of 80, meaning that each container instance will serve up to 80 requests at a time. That's a big departure from the functions as a service (FaaS) model, in which one instance handles one request at a time.

Redeploy the same container image with a concurrency value of 1 (only for testing purposes) and see what happens.

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

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

From the Cloud Run dashboard, click the monolith service to see the details.

7d1eed2e4728a4f2.png

Click the Revisions tab. You should see two revisions created. Click monolith-00002 and review the details. You should see the concurrency value reduced to 1.

217185c0eccc87dd.png]

4ad481b8bcd0343d.png

Although that configuration is sufficient for testing, in most production scenarios you'll have containers supporting multiple concurrent requests.

Now, restore the original concurrency without redeploying. You can set the concurrency value to the default of 80 or 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 in Cloud Shell to update the current revision:

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

Notice that another revision has been created, that traffic has been redirected, and that the concurrency is back to 80.

7. Make changes to the website

Your marketing team asked you to change the home page of your company's website. They think it should be more informative of what the company is and sells. In this section, you'll add some text to the home page to make the marketing team happy!

It looks like one of your developers already created the changes with the filename index.js.new. You can simply copy that file to index.js and your changes should be reflected. Follow the instructions to make the appropriate changes.

Run the following commands, copy the updated file to the correct filename, and 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>
  );
}

You updated the React components, but you 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 your code is updated, you need to rebuild your Docker container and publish it to Container Registry. You can use the same command as earlier, except this time you'll 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, you'll use that image to update your app with zero downtime.

8. Update website with zero downtime

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

Cloud Run treats each deployment as a new revision, which will be brought online then have traffic redirected to it.

Follow the next sets of instructions to update your website.

Command line

From the command line, you can redeploy the service to update the image to a new version with the following command:

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

Verify deployment

Validate your deployment update by running the following command:

gcloud run services describe monolith --platform managed 

The output looks like this:

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

You'll see that your service is now using the latest version of your image deployed in a new revision.

To verify your changes, navigate to the external URL of your Cloud Run service again and notice that your app title has been updated.

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

gcloud run services list

Your website should now display the text that you added to the home page component!

451ca252acae6928.png

9. Clean up

Delete Container Registry images

# 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 Cloud Build artifacts from Cloud Storage

# 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 Cloud Run service

gcloud run services delete monolith --platform managed

10. Congratulations

You deployed, scaled, and updated your website with Cloud Run.

Learn more