Deploy Flask application on GCP Cloud Run
Introduction
GCP Cloud Run is a service that allows us to deploy any container application to the cloud in a serverless manner. It is like the middle service between Cloud Functions and Kubernetes (GKE). In my opinion, it is best suited for a stand-alone application that is running on some framework. In order to connect Cloud Run to other GCP services, we need to create a proxy server first and that costs extra money.
The official document says that Cloud Run allows users to deploy their own legacy old code with containers to the cloud. Also, it says, it is easy to migrate between Cloud Run and Kubernetes because essentially, Cloud Run is running on top of the GKE. I will write about migration later.
In this blog, I show usage of Cloud Run and how to automate deployment with a simple script.
Pricing
GCP Cloud Run is a relatively cheaper service if you choose to use it for the right usage. It is actually free for a specific amount of usage. Basically, 1 vCPU usage for 180,000 seconds = 50 hours is free every month. If all usage plan pattern is defined, we can calculate the cost amount using the GCP Price Calculator. For instance, in the case of a simple API which runs about 1 second and it receives 10 requests per minute, it is actually free (see the following image).
Flask
Flask is python based light framework. Its simplicity of development and lightweight feature is attracted many developers.
Flask-based small to mid-sized applications is a very suitable application for Cloud Run.
In this guide, I will show a guide to deploy the flask REST API application on Cloud Run and how to automate it with a simple method.
Create Cloud Run
First, we need to make sure Container Registry is ready to use because Cloud Run retrieves Docker images from there.
Push Dockerfile to container registry GCR
The flask application I am using for this blog is here https://github.com/huchka/flaskproject/tree/v0.0. Tag v0.0
is for this blog.
To push docker image to GCR, follow these steps:
- Make sure Container Registry API is enabled on the GCP.
- Make sure the current cloud account is selected. You can check this by running
gcloud auth list
. - Then make sure the docker command is able to push images to GCR by running
gcloud auth configure-docker
. - Build docker image with
docker build
ordocker-compose build
command. Docker image should start withgcr.io/{PROJECT_ID}/{IMAGE_NAME}
. For example, build command is as follows in this blog case:
docker build -t gcr.io/{PROJECT_ID}/flaskproject:latest ./
- Now push the image, if necessary enable Container registry API
docker push gcr.io/{PROJECT_ID}/flaskproject:latest
PROJECT_ID
is project id of the project on GCP.
Deploy cloud run
Create Cloud Run service on GCP:
Cloud Run can scale-out automatically and the maximum number of instances can be configured on Maximum number of instances
.
If an application receives a connection request from outside select Allow all traffic
and if there is no authentication required select Allow unauthenticated invocations
.
Container Port
should be Dockerfile’s expose port number, in another word it is the port number Flask application is running. Cloud Run maps 443/80 port to this port.
Container command
is for defining start-up commands. Usually preparing entrypoint.sh
is a good manner because we can add Cloud Run environment-specific commands that should be run first. In this case, I need to make sure all database (tables) is created before the application run.
Automation of deployment
We always need 3 steps for deploying changed/fixed web application to Cloud Run.
- Build docker image with new source code
- Push docker image to GCR
- Deploy a new version of Cloud Run that uses the docker image pushed on step 2.
This deployment could happen a lot of times on some projects. So automating those 3 steps could save time and helps to eliminate the human error of the deployment process.
GCP already provides us with Cloud Build to automate processes. With Cloud Build, all we need to do is push our code to the repository and it will be deployed to Cloud Run by itself. Once we configured Cloud Build, it is easy to use. However, only Cloud Repository (GCP’s git repo), Github, and Bitbucket repositories are integrated with Code Build Trigger. Meaning, it is a little bit hard to use if we are using a different repository like Gitlab or others.
So, here I am going to introduce a new simple way by using Makefile.
Automate with Makefile
Makefile is a tool for automating tasks. This time, we need to automate 3 tasks which are build, push, and deploy. Source code can be found here. Here is Makefile
code:
include .envDOCKER_IMAGE_GCR_URI := gcr.io/${PROJECT_ID}/${CLOUD_RUN_SERVICE_NAME}:latest.PHONY: all build push deployall: build push deploybuild:
docker buildx build --platform linux/amd64 -t ${DOCKER_IMAGE_GCR_URI} ./push:
docker push ${DOCKER_IMAGE_GCR_URI}deploy:
gcloud run deploy ${CLOUD_RUN_SERVICE_NAME} --region asia-northeast1 --image=${DOCKER_IMAGE_GCR_URI}
First, we need to create .env
file and write PROJECT_ID
and CLOUD_RUN_SERVICE_NAME
values there. Makefile uses it as variables during the command lines.
As the tasks say, build
is for building Docker image, push
is for pushing Docker image to GCR, and deploy
is for actually deploying the new image to Cloud Run. With this Makefile
, I can deploy my new source code with only this command.
make all
Summary
Showed how to create Cloud Run service and deploy Flask container onto it. Then I have shown a much simpler way to deploy automation without using Cloud Build. Of course, Cloud Build could be a better choice depending on your projects. For instance, if your project is using Cloud Repository, GitHub, or BitBucket, it is very easy to integrate with it. If you are using a different repository and still want to use Cloud Build, one common way is to create a mirror Repository on Cloud Repository, or others. Then you can use Cloud Build automation.
One thing I have thought about using Cloud Run is actually it allows us to deploy PHP applications easily to serverless services. It is not that easy in AWS Lambda.