¿Cómo dockerizar un proyecto hecho en Django?

Cuando decimos dockerizar, lo que queremos lograr es empaquetar nuestra aplicación para que sea fácil de distribuir y ejecutar (lo cúal lo haremos con Docker). Antes de dockerizar la aplicación, debemos tener bien clara su arquitectura, en nuestro caso vamos a usar el proyecto de las frases, donde su arquitectura es:

Acá se puede ver que básicamente necesitamos un servicio web y una base de datos para poder servir nuestra aplicación. Seguramente te estas preguntando ¿Cómo le digo a mi aplicación a cúal base de datos conectarse?. Buena pregunta, y para contestarla, pues con variables de variables de entorno. Las variables de entorno nos permite comunicarnos con la aplicación (desde el sistema host), para modificar su comportamiento, si así lo dispone la aplicación; O en otras palabras, con las variables de entorno podemos parametrizar nuestra aplicación.

En este artículo vamos a usar la base de datos Sqlite basada en archivos que nos provee Django por defecto por simplicidad.

Para lograr esa comunicación entre el sistema operativo host y la aplicación por medio de variables de entorno, usaremos la biblioteca OS. Y un ejemplo muy sencillo de su uso sería:

import osSETTING_01 = os.environ.get('MY_VARIABLE', 'DEFAULT_VALUE')

Ahora desde el sistema operativo host (linux en nuestro caso), se usarían así:

Si lo piensan por un minuto, no es práctico hacer esa exportación de variables una por una, como estandar se hace esta carga de variables de entorno transparente usando el archivo .env. Ya que lo sabemos vamos a crear el archivo .env en la raíz de nuetro directorio y agregemos la variable del ejemplo anterior.

Vamos a mejorar las configuraciones de nuestro proyecto usando variables de entorno. Esto mejorará la seguridad, porque no quedaran expuestas configuraciones como el SECRET_KEY en nuestro código fuente, por ejemplo.

Usualmente se acostumbra como buena practica tener un archivo de nombre .env.example en la raíz de nuestro proyecto, donde se estipulan las variables de entorno mínimas, que necesita el proyecto para funcionar. Vamos a crear ese archivo con las variables vacías y una breve descripción, para mantener esta buena practica.

En nuestro caso Docker nos permitirá la encapsulación y la fácil ejecución. Debemos entender entonces las dos etapas y sus respectivas salidas, nos vamos apoyar del siguiente gráfico:

Como se logra ver en el gráfico, tenemos la etapa de construcción, donde necesitamos el archivo Dockerfile que precisa la receta necesaria para generar una imagen, y las variables ARGs que permite enviar valores que se pueden utilizar en el Dockerfile (y así poder variar la imagen generada). Esta etapa de construcción genera como salida una imagen con nuestro proyecto encapsulado (código fuente, ambiente de ejecución como sistema operativo y bibliotecas). Con esta imagen y las variables de entorno generamos un contenedor (o los contenedores que queramos) con nuestro proyecto en ejecución.

Docker provee el comando docker buildpara construir imagenes y el comando docker run <image> para crear contenedores (o mejor dicho mi imagen en ejecución).

By docker.com

El archivo que necesita Docker para crear la imagen se llama Dockerfile. En el Dockerfile se especifica las instrucciones de instalación de las dependencias del proyecto, se exponen puertos y se especifica el punto de entrada de la aplicación que en nuestro caso es un servicio web que expone un API REST.

La configuración del dockerfile que usaremos es la siguiente:

FROM python:3.8-alpine AS Builder# Install dependencies
RUN pip install pipenv
COPY Pipfile* /
RUN pipenv lock --requirements > requirements.txt
RUN apk add --update --no-cache --virtual .tmp gcc libc-dev
RUN pip install -r requirements.txt
# Copy sources files
WORKDIR /code
COPY . .
# Default port
ARG ARG_DEFAULT_PORT=8000
EXPOSE $ARG_DEFAULT_PORT
ENV DEFAULT_PORT=${ARG_DEFAULT_PORT}
# Install migrations
RUN python manage.py migrate
# Run server
ENTRYPOINT python manage.py runserver 0.0.0.0:${DEFAULT_PORT}

Lo primero que debemos especificar es la imagen base con el comando FROM, usaremos la versión alpine que viene depurada, es decir sin pruebas y documentación por ende pesa menos. Luego vamos a instalar las dependencias, seguimos copiando los archivos fuentes a la imagen. También aprovechamos el manejo de las variables ARGs en el proceso de construcción para tener la flexibilidad del puerto a exponer para acceder al servicio web. Para terminar corremos las migraciones y establecemos como punto de entrada el servidor web que nos provee django por defecto para desarrollo.

Otro archivo que debemos agregar es el .dockerignore el cúal sigue la anología del .gitignore, y nos permite ignorar archivos o carpetas que no queramos dejar accesible en nuestro Dockerfile. Acá usaremos este:

__pycache__
*.pyc
*.pyo
*.pyd
.Python
env
.env
pip-log.txt
pip-delete-this-directory.txt
.tox
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
*.log
.git
db.sqlite3

Ya con el Dockerfile y el .dockerignore podremos construir nuestras imágenes, para esto usaremos el comando docker build, en nuestro caso usaremos:

$>docker build --build-arg ARG_DEFAULT_PORT=8000 -t prhases_img .

En el comando anterior con la opción --build-arg le especificamos el puerto por defecto que estará escuchando la imagen y el servidor web (según lo especificamos en el Dockerfile), además con la opción -t le agregamos una etiqueta para identificar nuestra imagen. Para verificar la imagen creada podemos usar el comando docker images

Ya con nuestra imagen este es un template que podremos ejecutar las veces que queramos, estas ejecuciones se llaman contenedores ó instancias. El comando que vamos a utilizar para crear los contenedores es el docker run. En nuestro caso usaremos el comando:

$>docker run -it -p 8000:8000 \
-e DJANGO_DEBUG=True \
-e DJANGO_SECRET_KEY='SuperS3cr3t' \
-e DJANGO_ALLOWED_HOSTS='*' \
phrases_img

En el comando anterior con la opción -it le decimos que el contenedor nos dé salida interactiva, -p le especificamos el puerto que debe mapear del contenedor con el de nuestra máquina host, con las opciones -e le estamos expecificando las variables de entorno y bueno ya como parametro le estamos especificando la imagen. Ya solo queda abrir el navegador y verificar que efectivamente tenemos acceso a nuestro contenedor.

También podemos revisar los contenedores creados con el comando docker container ls,también se puede utilizar el comando docker ps.

Pueden revisar el código fuente en el repositorio medium-django rama add-docker.

Innovador por naturaleza, desarrollador de software de profesión y futbolista de corazón.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store