How to Deploy a Web Application to Cloud Run Automatically | by Lynn Kwong | Apr, 2022

Apply CI/CD to your web applications

Image by kreatikar in Pixabay

Using some web Python web frameworks like Flask, Django, or FastAPI, you can develop your APIs or web applications easily in Python. As a Python developer, these frameworks are native and easy to get started because you don’t need to learn a whole new front-end language like JavaScript plus its frameworks such as React, Angular, Vue, etc.

This article is not about how to develop APIs or web applications with a Python framework. However, it’s focused on how to deploy a web application to Cloud Run continuously. The Flask framework is chosen because it’s simple to use and has a large community. The procedures introduced in this post are applicable to all Python frameworks.

Install Flask locally

Before deploying our code to the Cloud in a production environment, we need to develop it locally and get everything ready.

The first step is to install the framework on your computer. It’s recommended to install Flask in a virtual environment so it won’t interfere with system libraries. Besides, you can install different versions of Flask in different virtual environments.

We can use the Python native venv library to manage your virtual environments. Alternatively, you can use conda because you can install different versions of Python with conda conveniently. Besides, you can activate your virtual environments from anywhere and don’t need to worry about the metadata for the virtual environment. Check here if you want to learn a bit more about conda.

(base)$ conda create --name flask python=3.10
(base)$ conda activate flask
(flask)$ pip install Flask==2.1.1

Besides Flask, several dependencies required by Flask are installed automatically. Now we can start to create a Flask application locally.

Prepare the Flask application locally

We will create a super simple “Hello World!” Flask application because the focus is the configurations for continuous deployment (CD) as will be introduced soon.

Create a folder called my_app, and navigate into it. Then create a file named app.py. You can give it any name you like, but it’s common to name it app.py or wsgi.py so some local web servers can find it easily. Add the following code to app.py:

Since we are reading the name of the application from an environment variable, let’s set it first:

$ export APP_NAME='My fun flask App'

Later we will see how to set an environment in Cloud Run.

$ python app.py# OR$ export FLASK_ENV=development
$ flask run -p 5000

If port 5000 is used, you can find and stop the current application if you want:

$ sudo lsof -i -P -n | grep -ie 'b5000b'
$ kill <PID>

Alternatively, you can specify a different port for app.run or flask run as shown above. When Flask is run, you can see the following information in the console:

If you go to http://127.0.0.1:5000, you will the greeting from our application:

Test gunicorn locally

Since we will use gunicorn to serve our Flask application in Cloud Run later, let’s install it locally and make sure the application can be served by gunicorn.

gunicorn can be installed with pip in your virtual environment:

$ pip install gunicorn==20.1.0

Now we can serve the application with gunicorn:

$ gunicorn --bind :5001 app:app

If you didn’t stop the Flask application run above, you would need to specify a different port here. The --bind option takes the form of IP:port. Note that IP can be omitted but the colon must be present. If IP is omitted, then gunicorn is bound to 127.0.0.1.

Besides, the application is specified in the form of APP_MODULE:APP_INSTANCE_NAME. In our application, both the module and application instance are named as app, but they can be different in your application. Especially, if you create the application instance through an application factory as demonstrated in the Flask tutorial, you would need to start the application in the form of APP_MODULE:APP_FACTORY_FUNC()namely, you need to explicitly call the application factory to create an application instance.

Create requirements.txt

We have installed some libraries in the local virtual environment. These libraries need to be installed in Cloud Run as well for Flask to work properly there. To do this, we need to put all the required libraries in a file called requirements.txtwhich is a standard file required by Python to specify library dependencies.

We need to put Flask and gunicorn in requirements.txt:

# requirments.txt
Flask==2.1.1
gunicorn==20.1.0

If your application requires other libraries, they should also be added. Besides, if you need to install some helper libraries in your local virtual environments like mypy and pylint, but don’t want to install them in the production requriements.txtyou can create an additional file called requirements-dev.txt and put them there. The name of this file is not critical, but requirements-dev.txt is a commonly used one.

Create a Dockerfile

Cloud Run works closely with Cloud Build which builds Docker images from your GitHub repositories automatically. Cloud Run uses the Docker images built by Cloud Build to spin up your applications. Therefore, to use Cloud Run, you should also have the Cloud Build API enabled.

The Dockerfile provided by the official documentation of Cloud Run is a good example and can work as it is in most cases:

This Dockerfile has good documentation and is straightforward to understand. Especially, the PORT virtual environment will be passed in by Cloud Run automatically, as we will see soon when we set up Cloud Run.

Besides, it’s also helpful to specify a .dockerignore file to exclude some metadata or temporary files from your Docker image. It can make your image smaller and look cleaner. A sample .dockerignore file can contain the following contents:

Deploy to Cloud Run

You can deploy your Flask application locally with the following command:

$ gcloud run deploy

Just follow the instructions to deploy your application step by step.

However, if you want to have more control over the deployment procedure, it’s better to use the GCP console. And if you want to set up a continuous deployment (CD) pipeline, it will be much more straightforward to do it in the console.

So let’s head over to the Cloud Run page in GCP Console. You can find Cloud Run on the left navigation bar, or just search for it in the top search box. Then click “CREATE SERVICE” to create a new Cloud Run service:

On the page opened, since we want to set up a continuous deployment pipeline, click the second option to “Continuously deploy new revisions from a source repository”:

Then click “SET UP WITH CLOUD BUILD” to choose a GitHub repository for Cloud Run. This repository will contain the source code and the configurations to build and deploy your application.

If you want to follow, you can fork this repository to your GitHub account and proceed to the following steps. Alternatively, you can add the Dockerfile file as demonstrated above to your own repository.

If it’s the first time you deploy a Cloud Run service from a GitHub repository, you would need to install the “Google Cloud Build” tool in your GitHub repository:

When you click “INSTALL GOOGLE CLOUD BUILD”, a new window will be opened and you will be prompted to install “Google Cloud Build” in all your GitHub repositories or a specific repository. Select the one your want and click “Install” to install it:

You will be asked to authenticate your GitHub account. After that, you will be able to see your GitHub repository in Cloud Build. Select it and accept the agreement for using your GitHub repository in your GCP project:

Click “Next” to specify the “Build Configuration”. You would need to choose the branch against which Cloud Build will run. Besides, you need to specify a Build Type. We will use a Dockerfile to build our application. Both options can be left as default in most cases.

Click “SAVE” to save the configurations for Cloud Build.

Then you need to specify some basic configurations for Cloud Run. Again, the default configurations would be enough in most cases:

The options are quite self-explanatory and have helpful tips as well. This is why the Console is recommended for deployment, especially for beginners. You can have more understanding and more control over what you are doing, rather than treat it as a black box.

Especially, the “CPU allocation and pricing” option can increase the bills. If you choose the default optional “CPU is only assigned during request processing”, then normally you don’t have to pay for it if you don’t have a lot of traffic. However, if you choose “CPU is always assigned”, you would need to pay quite some bucks every month. Please do check Google Cloud Pricing Calculator for more details.

As you may remember, we read an environment variable APP_NAME in our Flash application. Let’s set a value for it and see if the value can be read by Cloud Run. To do this, expand “Container, Variables & Secrets, Connections, Security” and click the “VARIABLES & SECRETS” tab. Add an environment APP_NAME with a value you like, say “My First Cloud Run Service”.

Then click “CREATE” to create your service. It will take less than one minute for Cloud Build to build your Docker container before it can be used in your Cloud Run service.

When the deployment is done, you will land on a page like the one below. You can check the metrics, logs, revisions, etc on this page. If something is wrong with your service, the logs are always the first place to check.

Now if you click on the URL on this page, you should be able to open the simple website built by Flask:

Congratulations! Your CI/CD pipeline for your Flask web application has been built in Cloud Run. Now if you make some changes on the main branch and push the code to GitHub, a new Docker image will be automatically built and a new revision of Cloud Run will be automatically for you.

Leave a Comment