Django on Google Cloud Run

Rahul Sharma
7 min readSep 5, 2021

--

In this tutorial, we will create a fully containerized django app locally (connected to a remote Cloud SQL postgres instance) and deploy it to Cloud Run with CI/CD, credentials management, and static file hosting.

Introduction

I recently had to deploy a django app on GCP and there were quite a few options:

  1. Google Cloud Functions (~ AWS Lambda)
  2. Google App Engine (~ AWS Beanstalk)
  3. Google Compute Engine (~ AWS EC2)
  4. Google Cloud Run
  5. Google Kubernetes Engine (~ AWS EKS)

Coming from AWS, I found the App Engine to be the most suitable for deploying an API service as it’s similar to how Beanstalk works. I also considered Cloud Functions (similar to AWS Lambda) but my use case was slightly complex with the need of defining models, performing migrations, and so on.

GKE was another option but I wanted to focus on launching the app first before worrying about k8s artifacts. It finally came down to App Engine and Cloud Run.

App Engine

  1. Fully managed PaaS from GCP (launched in 2008), battle tested for deploying planet-scale applications
  2. Supports Python, node, and other popular runtimes
  3. Code is stored in a Cloud Storage bucket
  4. Auto scaling based on traffic
  5. Out-of-the-box firewall, traffic splitting, and other key features

Cloud Run

  1. New GCP offering (launched in 2019) that brings the best of both worlds — serverless and containers
  2. Supports any runtime/language/library
  3. Apps are deployed as containers that can scale up or down based on configuration
  4. Code is stored as images on GCR that can be ported to GKE
  5. Integrated logging, monitoring, and CI/CD

Although both have a lot of overlap in terms of features, it’s the container aspect of writing and running applications that led me to Cloud Run in the end.

What we are going to build

We are going to deploy a django app on Cloud Run with:

  1. A Cloud SQL instance (postgres)
  2. CI/CD with Cloud Build
  3. Storing credentials in Secret Manager
  4. Serving static files with Google Cloud Storage
  5. Database for local development with Cloud SQL proxy connection

Step 0 — GCP setup

Before we can start django development, we will need to provision certain resources on GCP.

I. Create a new project

Projects are a way of organizing resources on GCP. Head to your GCP console and create a new project e.g. django-cloudrun-project. It's a standard practice to create one project for dev and another for production.

II. Make sure the project has a billing account attached

III. Download GCP CLI from here

IV. Create a service account

As a best practice, we will create a new service account on GCP to run our Cloud Run instance. Go to the GCP console → IAM & Admin → Service Accounts and create a new service account e.g. svc_app_dev

Next, grant the following permissions to it:

  1. Cloud SQL Client
  2. Service Account User
  3. Cloud Run Invoker
  4. Cloud Run Service Agent
  5. Secret Manager Secret Accessor
  6. Storage Object Admin

Next, go to the KEYS section. Create a new key and store the JSON file as creds.json, we will need it later.

V. Create a Cloud SQL instance

Although we can create a database instance using GCP CLI, it’s helpful to use the GUI if doing for the first time. Head to your GCP console → SQL → Create Instance.

Select a Postgres instance with required capacity e.g. 3.75 GB memory and 10 GB SSD. Give an apt name e.g. django-dev-db. GCP will take up to 5 minutes to create a new instance, it will also create a default user postgres and a default database postgres.

Next, create a new user named djangodevdbadmin and a new database called djangodevdb. Make sure to note down the password for djangodevdbadmin.

VI. Create a storage bucket

We will need to create a Google Cloud Storage bucket to host the static files from django. Head to GCP console → Cloud Storage → Create bucket, name it django_dev_bucket.

Step 1 — Local django setup

In this section, we will create a brand new django app from scratch. We will be using docker to run the app locally (through docker compose).

requirements.txt

Dockerfile

docker-compose.yml

Next, start a new django app named app in src directory.

docker compose run --rm app sh -c "django-admin startproject app ."

Finally, run the app.

# Keep this command window running while development 
# Open a new terminal window to run additional commands in the next sections
docker compose up

You should see the django welcome screen on 127.0.0.1:8000

We are not running command from docker compose because the final deployment to Cloud Run will only use the Dockerfile and so, no option to use compose there.

Step 2 — Setting up env variables

We will be setting up two kinds of environment files:

  1. .env for local development
  2. .env.prod for production

.env

.env.prod

We don’t need proxy in production as it will be taken care by GCP’s network infrastructure. We also don’t need application credentials in production as the service account we created earlier would have all the necessary permissions.

Next, we will store the contents of .env.prod in GCP Secret Manager, which is a secure way of storing sensitive information. We’ll achieve this using the GCP CLI.

At this point, our production app on Cloud Run can fetch the secret values directly from the Secret Manager instance.

Step 3 — Update settings.py

You should now be able to perform migrations and collect static files.

docker compose run --rm app sh -c "python manage.py makemigrations" docker compose run --rm app sh -c "python manage.py migrate" docker compose run --rm app sh -c "python manage.py collectstatic --no-input"

Step 4 — Create superuser

Create a superuser to access the admin dashboard.

docker compose run --rm app sh -c "python manage.py createsuperuser --username=<USER> --email=<EMAIL>"

If your cloud proxy was running locally, the superuser credentials would be stored in the remote CloudSQL instance. Commit the changes and push to your GitHub repo.

Step 5 — Create a new Cloud Run service

It’s time to deploy the application and we will use the GCP console to do so. Once comfortable with the process, you may use the GCP CLI to deploy the future ones.

Head to GCP console → Cloud Run → Create Service. Give it a name and select a region.

Since this is a tutorial app, you can keep the region same as the CloudSQL one to reduce data transfer costs.

In the next step, select SET UP WITH CLOUD BUILD option and connect your GitHub repo, branch to build off, and build type.

In the Advanced settings section, change the port to 8000. You can also set up your container configurations, concurrency settings, and autoscaling parameters.

Next, under the CONNECTIONS tab, add your CloudSQL instance.

Finally, under the SECURITY tab, add the service account (svc_app_dev) created in step 0.

Cloud Run will take 2–5 minutes to deploy the app and return an autogenerated URL to access the app.

Step 6 — Update Cloud Build CI/CD

We will add some django-specific steps in the CI/CD process.

In the root of your project, create a new cloudbuild.yaml file.

Commit and push your changes.

Head to GCP console → Cloud Build → Triggers and update the settings as below.

Also, update the substitution variables (some of them will already be pre-populated).

Trigger a new build through a git push and you should see the build steps under the History tab.

That’s it, you have deployed your django app on Cloud Run!

Next steps

Once you have your base app working, the possible next steps can be:

  1. Expand the django app — add models, views, and other modules.
  2. Create separate triggers for dev and feature branches that deploy a different Cloud Run service.
  3. Connect the Cloud Run service to use other GCP services e.g. Pub/Sub

Thanks for reading!

(Full code available here)

References

  1. Where should I run my stuff?
  2. Official django on Cloud Run docs
  3. Django Cloud Run Codelab
  4. Django with docker compose

--

--