Django avec Docker et Compose

Quand on commence un projet avec Django le serveur de développement par défaut et une base de données SQLite sont en général suffisant. Le développeur se contentera d’isoler les dépendances aux librairies Python avec, par exemple, Virtualenv.

Lorsque l’utilisation de la base de données SQLite n’est plus possible, parce que le projet utilise des requêtes plus complexes, ou des extensions qui n’ont pas d’équivalents SQLite comme PostGIS, les choses se compliquent. Docker et Compose permettent de résoudre ces problématiques.

Docker et Compose

Grace à Docker nous pouvons créer des containers pour faire tourner des bases de données, des serveurs de cache, et toutes les autres parties de nos infrastructures de production. Compose permettra quand à lui de faire un lien entre les différents containers.

Docker va également améliorer les développements en permettant aux développeurs d’utiliser en local une configuration identique à celle des serveurs de production, évitant ainsi les erreurs d’incompatibilité qui apparaissent lors de la mise en production de l’application.

Cela leur permettra aussi d’avoir une application fonctionnelle dès le clone, ce qui se traduira souvent par de nombreuses heures gagnées.

Un projet PostGIS

Pour illustrer l’utilisation de Docker et Compose dans le développement d’une application Django, je vais créer un projet utilisant PostGIS, une extension PostgreSQL qui ajoute le support d’objet géographique.

Création du projet:

$ django-admin startproject plop
$ cd plop/

On va ensuite éditer le fichier plop/settings.py pour ajouter ‘django.contrib.gis’ dans INSTALLED_APPS et configurer la base de données

DATABASES = {
    'default': {
        'ENGINE': 'django.contrib.gis.db.backends.postgis',
        'NAME': 'plop',
        'USER': 'plop',
        'PASSWORD': 'secret',
        'HOST': 'db',
        'PORT': 5432
    }
}

Pour finir, on va ajouter un fichier requirements.txt avec les dépendances Python

Django==1.8.5
psycopg2==2.6.1
geopy==1.11.0

A ce stade on peut essayer de créer une application

$ python manage.py startapp map
...
django.core.exceptions.ImproperlyConfigured: Error loading psycopg2 module: No module named psycopg2

Mais nous n’avons pas installé les dépendances et surtout nous n’avons pas de base de données.

Docker et Compose

Nous allons maintenant créer le DOCKERFILE qui va définir le container qui fera tourner notre application.

FROM python:2.7

# On ne veut pas de buffer pour stdin, stdout et stderr.
ENV PYTHONUNBUFFERED 1

# On installe certaines dépendances.
RUN apt-get update -y
RUN apt-get install -y binutils libproj-dev gdal-bin

# On crée le répertoire qui accueillera l'application.
RUN mkdir /app
WORKDIR /app

# On installe les dépendances
ADD requirements.txt /app/
RUN pip install -r requirements.txt

# Et on ajoute le code
ADD . /app/

Ensuite on ajoute le fichier docker-compose.yml. Ce fichier va définir l’ensemble des containers du projets et les liens entre les containers.

# Containeur 1: La base de données
# La base de données utilise une image disponible sur le hub docker
# Elle sera accessible depuis l'application en utilisant le host 'db'
db:
    image: geelweb/postgis:9.3
    environment:
        - POSTGRES_USER=plop
        - POSTGRES_PASSWORD=secret
# Containeur 2: Le serveur web
# C'est notre application Django
web:
    # On utilise notre DOCKERFILE pour construire le container
    build: .
    # On lance le serveur de développement au démarrage du container
    command: python manage.py runserver 0.0.0.0:8000
    # On indique où l'on a mis le code
    volumes:
        - .:/app
    # On indique les forwards de ports
    ports:
        - "8000:8000"
    # On précise le lien avec notre container de base de données, la db sera accessible via l'host "db"
    links:
        - db

Il est temps de démarrer les containers

$ docker-compose up
Creating plop_db_1...
Building web...
...
Successfully built ca4e0c01ebd3
Creating plop_web_1...
Attaching to plop_db_1, plop_web_1
...
db_1 | PostgreSQL init process complete; ready for start up.
db_1 |
db_1 | LOG: database system was shut down at 2015-10-17 08:38:15 UTC
db_1 | LOG: MultiXact member wraparound protections are now enabled
db_1 | LOG: autovacuum launcher started
db_1 | LOG: database system is ready to accept connections
web_1 | Performing system checks...
web_1 |
web_1 | System check identified no issues (0 silenced).
web_1 |
web_1 | You have unapplied migrations; your app may not work properly until they
are applied.
web_1 | Run 'python manage.py migrate' to apply them.
web_1 | October 17, 2015 - 08:38:29
web_1 | Django version 1.8.5, using settings 'plop.settings'
web_1 | Starting development server at http://0.0.0.0:8000/
web_1 | Quit the server with CONTROL-C.

On peut exécuter d’autres commandes que runserver, comme par exemple syncdb et migrate:

$ docker-compose run web python manage.py syncdb
Starting plop_db_1...

Operations to perform:
 Synchronize unmigrated apps: staticfiles, gis, messages
 Apply all migrations: admin, contenttypes, auth, sessions
Synchronizing apps without migrations:
 Creating tables...
 Running deferred SQL...
 Installing custom SQL...
Running migrations:
 Rendering model states... DONE
 Applying contenttypes.0001_initial... OK
 Applying auth.0001_initial... OK
 Applying admin.0001_initial... OK
 Applying contenttypes.0002_remove_content_type_name... OK
 Applying auth.0002_alter_permission_name_max_length... OK
 Applying auth.0003_alter_user_email_max_length... OK
 Applying auth.0004_alter_user_username_opts... OK
 Applying auth.0005_alter_user_last_login_null... OK
 Applying auth.0006_require_contenttypes_0002... OK
 Applying sessions.0001_initial... OK

You have installed Django's auth system, and don't have any superusers defined.
Would you like to create one now? (yes/no): no
$ docker-compose run web python manage.py migrate
Operations to perform:
 Synchronize unmigrated apps: staticfiles, gis, messages
 Apply all migrations: admin, contenttypes, auth, sessions
Synchronizing apps without migrations:
 Creating tables...
 Running deferred SQL...
 Installing custom SQL...
Running migrations:
 No migrations to apply.

Il est maintenant possible de créer une app:

$ docker-compose run web python manage.py startapp map
Starting plop_db_1...
gluchet@localhost plop (step4) $ ls map/
__init__.py admin.py migrations models.py tests.py views.py

Se connecter à la base de données

Si vous êtes arrivé ici parce que vous voulez utiliser PostgreSQL pour vos développements, vous aurez peut-être besoin de vous y connecter:

$ docker-compose start db
$ docker-compose run db sh -c 'exec psql -h $DB_PORT_5432_TCP_ADDR -U$POSTGRES_USER'

Resources

About the Author: Guillaume Luchet

Guillaume Luchet est Directeur de la R&D et Lead Développeur chez Bilendi Technology, entrepreneur et développeur freelance.