Skip to content

Setting up Django app with Postgres database and health check

In Part 1 of this series, we installed all tools required for developing our Django application on local Kubernetes with Skaffold. In this part, we'll create the Django application. In the next part, we'll finally get to the fun part: defining Kubernetes manifests and Skaffold configuration file.

Since this tutorial is not about Django but about Skaffold and Kubernetes, I'll move quickly in this part. If you have any questions, please add them in the comments and I'll do my best to answer! As before, you can find all code in the accompanying GitHub repository.

Creating Django project

We'll create a Django project named store in src/store with the startproject command of django-admin:

# In the root of repository
$ cd src
$ mkdir store
$ cd store
$ django-admin startproject store .

Add the following to requirements.txt:

# src/store/requirements.txt
django
gunicorn
psycopg2

Here, gunicorn is used for serving the application and psycopg2 is the Postgres driver.

Now, let's create an app that we'll use for status-checking:

# Inside src/store
$ python manage.py startapp status

This creates the status/ folder in src/store. Add the following to store/urls.py:

# src/store/store/urls.py
from django.urls import path, include

urlpatterns = [path("status/", include("status.urls"))]

This will boostrap the status app to status/ endpoint.

Define a view in the root of status/ path:

# src/store/status/urls.py
from django.urls import path

from . import views

urlpatterns = [
    path("", views.index, name="index"),
]

In status/views.py, add a view that returns 200 for successful database connection and 500 otherwise:

# src/store/status/views.py
from django.db import connection
from django.http import JsonResponse

def index(request):
    try:
        with connection.cursor() as cursor:
            cursor.execute("SELECT 1")
        return JsonResponse({ "message": "OK"}, status=200)
    except Exception as ex:
        return JsonResponse({ "error": str(ex) }, status=500)

We check if the database cursor can execute a SELECT 1 statement and return an error for any exception.

With this, we have created a Django application with status/ endpoint that checks the database connection. Now we still need to setup Postgres.

Setting up Postgres

To setup Postgres, modify src/store/settings.py as follows:

# src/store/settings.py
import os

# Keep everything else as-is

POSTGRES_CONFIG = {
    "username": os.environ.get("POSTGRES_USER", "postgres"),
    "db_name": os.environ.get("POSTGRES_DB", "store"),
    "host": os.environ.get("POSTGRES_HOST", "127.0.0.1"),
    "password": os.environ.get("POSTGRES_PASSWORD", ""),
    "port": os.environ.get("POSTGRES_PORT", 5432),
}

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": POSTGRES_CONFIG["db_name"],
        "USER": POSTGRES_CONFIG["username"],
        "PASSWORD": POSTGRES_CONFIG["password"],
        "HOST": POSTGRES_CONFIG["host"],
        "PORT": POSTGRES_CONFIG["port"],
    }
}

Here we read the Postgres settings from environment variables and set sane defaults.

Dockerfile

To deploy the application to Kubernetes, we'll need to create a Dockerfile:

# src/store/Dockerfile
FROM python:3
ENV PYTHONUNBUFFERED 1
RUN mkdir /store
WORKDIR /store
COPY . /store/
RUN pip install -r requirements.txt
CMD ["gunicorn", "store.wsgi"]

We use gunicorn for serving the app, starting from store/wsgi.py created by the django-admin startproject command.

Nginx

In any production environment, we need to run Django behind a reverse proxy or ingress, so we'll create an nginx proxy. Add the following to Dockerfile.nginx:

# src/store/Dockerfile.nginx
FROM nginx:1.16.1
COPY nginx.conf /etc/nginx/nginx.conf

Add the following setup from gunicorn documentation to nginx.conf:

# src/store/nginx.conf
# https://docs.gunicorn.org/en/latest/deploy.html#nginx-configuration
user nobody nogroup;
# 'user nobody nobody;' for systems with 'nobody' as a group instead
error_log  /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024; # increase if you have lots of clients
    accept_mutex off; # set to 'on' if nginx worker_processes > 1
}
http {
    upstream store {
        server localhost:8000;
    }

    server {
        listen 8080;

        location / {
            proxy_pass http://store;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_redirect off;
        }
    }
}

Here we simply hard-code the proxy to forward requests to localhost:8000, where our Django app will be running. In production usage, we would read the address from environment variables at deploy time.

Conclusion

This concludes Part 2 of our tutorial for local development on Skaffold. In the next part, we'll get to deploying our application and database on Minikube with Skaffold. See you then!