11.3. App Updates for Postgres#

Your application is not ready to use Postgres yet. This lab will take you through the steps to update it.

Install Postgres Support#

You should also install the pyscopg2 package in your development environment. The psycopg2 package provides Python programs a way to access a Postgres database:

$ pip install psycopg2

Change the Database Setting#

In the mysite/settings.py file you should update your database configuration to match this:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': os.environ['POSTGRES_DB'],
        'USER': os.environ['POSTGRES_USER'],
        'PASSWORD': os.environ['POSTGRES_PASSWORD'],
        'HOST': os.environ['POSTGRES_HOSTNAME'],
    },
}

This creates four new mandatory environment variables that have to be set in order to run your application.

Test the Updates#

The development copy of your app is now set to use the Postgres database instead of the built-in SQLite database. But how to test it? There are a few options, you could create a Postgres database in your dev environment. However, since we already have one running in Kubernetes let’s use the kubeclt port-forward command to make that database accessible to the dev environment:

$ kubectl port-forward service/postgres-postgresql 5432:5432

In a separate terminal set all of the necessary environment variables and run the development server:

$ export POSTGRES_DB="mysite"
$ export POSTGRES_USER="mysiteuser"
$ export POSTGRES_PASSWORD="this-is-a-bad-password"
$ export POSTGRES_HOSTNAME="localhost"
$ python ./manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
...

If you see the usual output from migrate you can also run the createsuperuser command:

$ python ./manage.py createsuperuser

Once you are able to perform the migrate and createsuperuser commands you have completed the setup of your Postgres database. The changes will stick as long as you don’t delete the PersistentVolumeClaim associated with Postgres.

Note

Remember this procedure. You will need to initialize the database using the development server again.

Rebuild the Container#

Now that you have updated your application it’s important to carry those updates through to your Dockerfile so that new cloud versions of your app see the changes. Update your Dockerfile to contain the new environment variables:

ENV POSTGRES_DB="mysite"
ENV POSTGRES_USER="mysiteuser"
ENV POSTGRES_PASSWORD="this-is-a-bad-password"
ENV POSTGRES_HOSTNAME="localhost"

You can remove the DATA_DIR environment variable. It will not be used anymore. Also make sure that the new image is built with psycopg2 by updating the pip install line:

RUN pip install Django==4.2.9 psutil psycopg2

Rebuild your container with the changes you’ve made to the Dockerfile:

$ docker build -t mysite 

Building is the easy part. But how do you test? It’s more complicated now that the site requires a database to be present. This is one of the harder things that you do in Kubernetes. To properly test your container we need to do two things:

  1. Open a tunnel to Postgres in your cluster:

    $ kubectl port-forward service/postgres-postgresql 5432:5432
    Forwarding from 127.0.0.1:5432 -> 5432
    Handling connection for 5432
    
  2. Launch the container using host mode networking so the container can “see” localhost:

    $ docker run -it --rm --network host mysite /bin/bash 
    root@cs-1000282563330-default:/mysite# python manage.py migrate 
    

Once you push the new code to GitHub your container will be ready to scale!

Update Configuration and Secrets#

Add these environment variables to your deployment/secret.yaml file:

POSTGRES_PASSWORD: "this-is-a-bad-password"
POSTGRES_USER: "mysiteuser"

Add these environment variables to your deployment/config.yaml file:

POSTGRES_DB: "mysite"
POSTGRES_HOSTNAME: "postgres-postgresql"

Note

In Kubernetes each Service is reachable by a hostname that matches the name key in the service. You can find the name of a service from kubectl get all. For example:

$ kubectl get all 
NAME                                     READY   STATUS    RESTARTS      AGE
pod/mysite-deployment-69b9b76c87-25sz2   1/1     Running   5 (46m ago)   51m
pod/postgres-postgresql-0                1/1     Running   0             47m

NAME                             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/kubernetes               ClusterIP   34.118.224.1     <none>        443/TCP    25h
service/postgres-postgresql      ClusterIP   34.118.237.116   <none>        5432/TCP   47m
service/postgres-postgresql-hl   ClusterIP   None             <none>        5432/TCP   47m

The name of the Postgres service is postgres-postgresql so the hostname inside of other containers is postgres-postgresql.

Don’t forget to use kubectl apply to update the secrets in your cluster.