Introduction
On August 15, 2018, Google released the Alpha release of Google Cloud Run. Many of us saw the potential and went to work learning this new platform. Everything serverless gets our attention. Cloud Run is Google’s entry into serverless containers. On April 9, 2019, Cloud Run went into Beta and has lit on fire. To say that I am impressed is an understatement.
What is Google Cloud Run? Cloud Run is a managed compute platform for running stateless containers in the cloud. The only serious requirements are that your container must be invocable via HTTP requests and not require long running or background processes as they put the container to sleep in between HTTP requests. Cloud Run also makes deploying HTTPS and SSL certificates easy.
Key feature points:
- Run HTTP apps and services
- Simple developer experience
- Fast auto-scaling
- Scales to zero
- Almost any language, any library, any runtime
- HTTP and HTTPS
- SSL certificates for your domain name
- Integrated logging and monitoring – Stackdriver
- Pay per use – no traffic equals no cost
- Optional authentication
Cloud Run is a big brother to Cloud Functions. Both can do many of the same things. I prefer Cloud Run as I have control over the language, run-time libraries, and operating systems for my application. Just about anything that will run in a Docker container can be deployed to Cloud Run. Cloud Functions is still a favorite for small and tightly controlled microservices.
Cloud Run also supports Kubernetes, which I am not covering in this article. However, redeploying from Cloud Run to Cloud Run on GKE is as simple as adding one command line option. That is what I call elegant simplicity.
In this article, we will create a Python Flask web application and deploy it into a Docker container. Then we will take this container and deploy it to Cloud Run. Once we have everything working, we will create a domain name in our DNS server and deploy an SSL certificate.
This article covers development and deployment from a Windows 10 Professional desktop. Linux is similar but not covered in this article.
We will work with several products and services. Having a basic understanding will be helpful.
The final result will be a simple web application that responds at https://flask-python-example.jhanley.dev. Your endpoint will be different.
Assumptions:
- You have gcloud installed and configured with the correct credentials and project
- You have Docker installed
- You have Python 3 installed
If you do not have Docker or do not want to install it, you can still follow the Google Cloud sections and build and run a container in Cloud Run. You just cannot build and run a local Docker container. You don’t even need Python installed.
Additional information
Google has several good videos to introduce you to Cloud Run. Here are several I like and their running time:
- Build and deploy with Cloud Run – 4:24
- Differences between Cloud Run and Cloud Run on GKE – 5:23
- Cloud Run QuickStart – Docker to Serverless – 7:49
- Run Containers on GCP’s Serverless Infrastructure (Cloud Next ’19) – 48:32
An often overlooked item is the size of your containers. You want them as small as possible to minimize container cold start times. This video is a nice introduction to this:
Do not forget container security. Make sure that your containers have the proper patches and have been scanned for vulnerabilities.
Getting Started
This article assumes that you have the CLI gcloud installed and configured with credentials. This article is CLI based and we will not be using the Google Cloud Console, except for Google Domains. Google’s GUI is excellent, but I prefer the CLI as I can create scripts, create better documentation, etc. Sometimes there are options that are not available in the GUI and you must use the CLI.
Install the Cloud SDK Beta and Alpha components:
1 2 |
gcloud components install beta gcloud components install alpha |
Update the Cloud SDK:
1 |
gcloud components update |
Verify that the correct project is the default project:
1 |
gcloud config list core/project |
If the correct project is not displayed, use this command to change the default project:
1 |
gcloud config set core/project [MY_PROJECT_ID] |
You can list the projects in your account. Some security configurations will not allow you to list projects. In that case, you will need to specify the default project manually as shown above.
1 |
gcloud projects list |
Enable the Cloud Run Service
1 |
gcloud services enable run.googleapis.com |
Set the default region for Cloud Run. This is obvious today, but soon there will be more regions announced:
1 |
gcloud config set run/region us-central1 |
For Cloud Run on GKE:
1 2 |
gcloud config set run/cluster [CLUSTER] gcloud config set run/cluster_location [CLUSTER_LOCATION] |
If you develop for BOTH Cloud Run and Cloud Run on GKE, you cannot set the properties as described above, because they will conflict. Instead, supply the –region parameter as needed in the gcloud command line for Cloud Run, and supply the –cluster and –cluster-location parameters as needed in the gcloud command line for Cloud Run on GKE.
Software Requirements
- Google CLI gcloud – https://cloud.google.com/sdk/
- Python 3.x – https://www.python.org/downloads/
- git – https://git-scm.com/downloads
Download Git Repository
Update: May 16, 2019
I have published the files for this article on GitHub. https://github.com/jhanley-com/google-cloud-run-getting-started-python-flask
License: MIT License
Clone my repository to your system:
1 |
git clone https://github.com/jhanley-com/google-cloud-run-getting-started-python-flask.git |
Develop the Python Flask Application
For this article, we will write a simple Python Flask application that displays “Hello Google Cloud Run World!” with a link to the Cloud Run website. We will write and test this from our Windows 10 desktop. The steps are similar for Linux and are not covered in this article.
For this application we just need Flask. Create the requirements.txt file with this content:
1 |
Flask |
Install/verify that Flask is installed:
1 |
pip install -r requirements.txt |
Let’s look at a simple Flask application. We will not be using this example, this is to simplify understanding Flask. This is a seven-line web server application! This example will listen on 127.0.0.1 port 5000:
1 2 3 4 5 6 7 |
from flask import Flask app = Flask(__name__) @app.route("/") def home(): return "Hello, World!" if __name__ == "__main__": app.run(debug=True) |
We will use an expanded version. Create the application file app.py with this content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import os import logging from flask import Flask # Change the format of messages logged to Stackdriver logging.basicConfig(format='%(message)s', level=logging.INFO) app = Flask(__name__) @app.route('/') def home(): html = """ <html> <head> <title> Google Cloud Run - Sample Python Flask Example </title> </head> <body> <p>Hello Google Cloud Run World!</p> <a href="https://cloud.google.com/run/" target="_blank">Google Cloud Run Website</a> </body> </html> """ return html if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 8080))) |
Execute this code:
1 |
python app.py |
Click this link or open a web browser to http://localhost:8080/
You should see a page similar to this:
Press CTRL-C to kill the Flask web server.
I will not go into detail about Flask as there are extensive documentation and examples on the Internet. At this point, we have created a simple web server and ran it on our local desktop. I like to develop and test as much as possible locally as this is easier and faster than waiting for deployments to complete.
Develop the Docker Container
The next step is to take the website application and deploy it as a Docker container.
Create the Dockerfile file with this content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# Use the official Python 3 image. # https://hub.docker.com/_/python # # python:3 builds a 954 MB image - 342.3 MB in Google Container Registry # FROM python:3 # # python:3-slim builds a 162 MB image - 51.6 MB in Google Container Registry # FROM python:3-slim # # python:3-alpine builds a 97 MB image - 33.2 MB in Google Container Registry FROM python:3-alpine # RUN apt-get update -y # RUN apt-get install -y python-pip COPY . /app # Create and change to the app directory. WORKDIR /app RUN pip install --no-cache-dir -r requirements.txt RUN chmod 444 app.py RUN chmod 444 requirements.txt # Service must listen to $PORT environment variable. # This default value facilitates local development. ENV PORT 8080 # Run the web service on container startup. CMD [ "python", "app.py" ] |
Notice I have some lines commented out. The reason is that I like to start with a full container build (FROM python:3) and when deploying switch to a minimal build (FROM python:3-alpine). Sometimes I need to connect inside the container to debug, install tools, etc. Before deploying production code, build the smallest container you can. This will decrease costs, minimize the testing footprint, reduce data transfer time to start the container, and improve security.
Build the container:
1 |
docker build -t sample-flask-example:latest . |
You will receive a security warning from Docker on Windows:
1 |
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories. |
This issue is handled in the Dockerfile with these lines:
1 2 |
RUN chmod 444 app.py RUN chmod 444 requirements.txt |
The problem is that the Dockerfile COPY statements add execution permission to files. The chmod
commands change the file permissions back to read-only. If you forget to do this, you will have Python execute errors.
Next, run the container and verify that everything works.
1 |
docker run -it --rm -p 8080:8080 sample-flask-example:latest |
Click this link or open a web browser to http://localhost:8080/
You should see the same content again.
Press CTRL-C to kill the container.
Build the Container with Cloud Build
Everything we have done up to this point has not involved Google Cloud. Now we will switch and build our container in the cloud as the first step to deploy with Cloud Run.
Build the container using Cloud Build and store it in Cloud Container Registry. Replace my-project
with your Google Cloud Project ID:
1 |
gcloud builds submit --tag gcr.io/my-project/sample-flask-example |
Once the build completes, you can list the container images in Container Registry:
1 |
gcloud container images list |
Deploy the Container to Cloud Run
Deploy the container from Container Registry to Cloud Run. Replace my-project with your Google Cloud Project ID:
1 |
gcloud run deploy sample-flask-example --image gcr.io/my-project/sample-flask-example --allow-unauthenticated |
When this command completes, you will see a message similar to:
1 |
Service [sample-flask-example] revision [sample-flask-example-00001] has been deployed and is serving traffic at https://sample-flask-example-x5yqob7qaq-uc.a.run.app |
Make a note of the endpoint in the message. Open a new browser and enter the URL.
1 |
https://sample-flask-example-x5yqob7qaq-uc.a.run.app |
Permission Denied
If the deploying identity (user or service account) is missing the permission run.services.setIamPolicy
you must add the Cloud Run Admin IAM role to the identity. The role name is roles/run.admin
. If that is not possible, remove the gcloud run deploy
command line flag --allow-unauthenticated
.
1 |
ERROR: (gcloud.beta.run.services.add-iam-policy-binding) PERMISSION_DENIED: Permission 'run.services.setIamPolicy' denied on resource |
Create a Custom Domain and Certificate
If you are just creating an API, then making your Cloud Run instance easily accessible is not important. For websites, customers will expect a URL that is recognizable. I don’t like to click on weird URLs – you never know what is behind them. So, let’s go thru the process to create a custom domain and SSL certificate and make our website available as a subdomain.
Open a web browser and go to the Google Cloud Run console https://console.cloud.google.com/run. Click on Manage Custom Domains.
Click Add Mapping. Select the Cloud Run service we just deployed. Select your domain name. Enter your subdomain. In this example “flask-python-example”.
If your domain name does not appear, select “Verify a new domain …” and follow the prompts.
Click Continue. The next step is to create a CNAME in your DNS server with the information from this screen:
You will need to wait about ten to thirty minutes for Google to deploy the SSL certificate and for your DNS server to start responding to requests for the new domain name.
Open a web browser and go to your new domain. For this example: https://flask-python-example.jhanley.dev.
In a future article, I will cover HTTPS and SSL for Cloud Run including HTTPS redirection, secure headers, HSTS and some suggestions for Google to improve Cloud Run security.
More Information
Summary
Congratulations, you have deployed a simple containerized website to Google Cloud Run.
There are many more features of Google Cloud Run we have not covered in this introductory article. In future articles, I will cover more details including combining Cloud Run with other Google Cloud services for automation.
We can combine Cloud Run with other Google Cloud services, such as Cloud Storage and Pub/Sub to monitor the security settings on storage objects. This just scratches the surface of what we can do beyond just websites.
Credits
I write free articles about technology. Recently, I learned about Pexels.com which provides free images. The image in this article is courtesy of Pixabay at Pexels.
Date created: May 10, 2019
Last updated: June 3, 2019
I design software for enterprise-class systems and data centers. My background is 30+ years in storage (SCSI, FC, iSCSI, disk arrays, imaging) virtualization. 20+ years in identity, security, and forensics.
For the past 14+ years, I have been working in the cloud (AWS, Azure, Google, Alibaba, IBM, Oracle) designing hybrid and multi-cloud software solutions. I am an MVP/GDE with several.
Leave a Reply