Dockerizar un proyecto Django
Tags: python, docker
Published: 2021-04-25 19:39:52

# Introduccion

Docker es una herramienta que como desarrolladores debemos estar dispuestos a aprender nos permite hacer el despliegue de nuestras aplicaciones de una manera rápida y sencilla ademas de hacer que nuestros proyectos sean fáciles de distribuir. A continuación vamos a descubrir como dockerizar nuestra app en Django.

## Elegir nuestras imágenes y el espacio de trabajo

Lo primero es saber que imágenes necesitamos y para ello lo primero es una imagen de  con la que podremos usar el lenguaje.

[python - Docker Hub](https://hub.docker.com/_/python)

Lo siguiente es una imagen para montar nuestra base de datos, para este ejercicio decidí usar mysql en su versión 5.7

[mysql](https://hub.docker.com/_/mysql)

Creamos una carpeta la cual va a contener nuestro proyecto

```bash
~/ $ mkdir dockerproject
~/ $ cd dockerproject
~/dockerproject $
```

## Definir dependencias

Para nuestro proyecto en Django necesitamos las librerías correspondientes ademas del interpretador de lenguaje Python, asi que necesitamos definir las dependencias de nuestro proyecto.

Creamos un archivo de nombre requirements.txt en nuestro espacio de trabajo el cual tiene que contener la versión de Django que vamos a usar y el cliente para mysql.

```
Django>=3.0,<4.0
mysqlclient>=2.0
```

El cliente de mysql es necesario ya que no se encuentra en las librerías por defecto para la versión 3 de Python, asi que lo agregaremos manualmente.

## Nuestro servidor de Python

Dentro de la carpeta vamos a crear un archivo de nombre Dockerfile, esta sera nuestra imagen local de Python en la cual podremos definir una serie de instrucciones necesarias para el inicio de nuestro proyecto.

Definimos la imagen base.

```docker
FROM python:3
```

Creamos nuestro directorio de trabajo.

```docker
WORKDIR /code
```

Ahora necesitaremos instalar Django y el cliente de mysql para Python, en este paso usaremos el archivo de requirements.

```docker
COPY requirements.txt /code/
```

En este punto podemos instalar las dependencias con la siguiente instrucción.

```docker
RUN pip install -r requirements.txt
```

Y poner una copia de nuestro código en la carpeta en la que nos encontramos dentro de nuestro directorio de trabajo en el contenedor de Docker

```docker
COPY . /code/
```

Al final deberíamos de tener un archivo como el siguiente Dockerfile

```docker
FROM python:3

WORKDIR /code

COPY requirements.txt /code/

RUN pip install -r requirements.txt

COPY . /code/
```

Con el siguiente comando en la terminal podemos comprobar que nuestra imagen se puede procesar correctamente.

```bash
~/dockerproject $ docker build .
[+] Building 0.4s (10/10) FINISHED
 => [internal] load build definition from Dockerfile                                                       0.0s
 => => transferring dockerfile: 31B                                                                        0.0s
 => [internal] load .dockerignore                                                                          0.0s
 => => transferring context: 2B                                                                            0.0s
 => [internal] load metadata for docker.io/library/python:3                                                0.0s
 => [1/5] FROM docker.io/library/python:3                                                                  0.0s
 => [internal] load build context                                                                          0.1s
 => => transferring context: 2.43kB                                                                        0.0s
 => CACHED [2/5] WORKDIR /code                                                                             0.0s
 => CACHED [3/5] COPY requirements.txt /code/                                                              0.0s
 => CACHED [4/5] RUN pip install -r requirements.txt                                                       0.0s
 => CACHED [5/5] COPY . /code/                                                                             0.0s
 => exporting to image                                                                                     0.1s
 => => exporting layers                                                                                    0.0s
 => => writing image sha256:13e36e422c69799feb5105373d4b04d78c683c7527294be29c80a7940a6374b4               0.0s
```

Ahora podemos comprobar que las dependencias se instalaron correctamente con el hash de la imagen que nos devuelve el comando anterior y con la siguiente instrucción. Si todo esta correcto Python debería de regresar la versión de Django instalada

```bash
~/dockerproject $ docker run 13e36e422c69799feb5105373d4b04d78c683c7527294be29c80a7940a6374b4 python -m django --version
3.2
```

## Crear el proyecto

Para el siguiente paso podemos crear el proyecto desde nuestro contenedor asi que ejecutaremos el siguiente comando. El comando puede variar en el parámetro directorio-actual y el cual te dejo ejemplos para Linux y Windows.

```bash
~/dockerproject $ docker run -it --volume <directorio-actual>:/code <hash-de-la-imagen> python -m django startproject dockerproject .
```

```bash
Windows $ docker run -it --volume %cd%:/code <hash-de-la-imagen> python -m django startproject dockerproject .
Linux $ docker run -it --volume $(pwd):/code <hash-de-la-imagen> python -m django startproject dockerproject .
```

Al finalizar obtendremos un nuevo directorio con el nombre del proyecto que contiene los archivos de configuración del proyecto y un archivo de nombre [manage.py](http://manage.py) que nos permitirá ejecutar comando de Django de nuestro proyecto.

Ahora tenemos que decirle a Django que conexión de base de datos va a usar y esto lo podremos hacer con el archivo de configuración /dockerproject/setting.py en la linea 76 con la constante DATABASES.

```python
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'project',
        'USER': 'root',
        'PASSWORD': 'password',
        'HOST': 'docker_python_db',
        'PORT': 3306
    }
}
```

## Docker compose

Para una gestión mas limpia de los contenedores de nuestro proyecto usaremos docker compose.

Creamos el archivo docker-compose.yml en el cual definiremos los servicios para nuestro proyecto.

Primero definimos que versión de compose vamos a usar.

```yaml
version: '3.8'
```

Después definimos los servicios que vamos a usar, en primer lugar nuestro servidor web.

```yaml
# version
web:
    container_name: ${PROJECT_NAME}_web
    build: .
    working_dir: /code
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - '8085:8000'
    expose:
      - '8000'
    links:
      - mysql
    networks:
      backend:
        ipv4_address: 192.20.0.5
```

Explicando un poco la definición de este servicio vamos a poner como nombre del contenedor el nombre del proyecto mas la terminación _web. El contenedor se va a genera desde el contexto del mismo directorio en el cual se encuentra nuestra imagen en Dockerfile. El directorio de trabajo sera /code y el comando que se estará ejecutando el cual va a ser:

```yaml
command: python manage.py runserver 0.0.0.0:8000
```

Este comando iniciara el servidor de test que provee Django, este sera el comando que se estará ejecutando en el ciclo de vida del contenedor. Después con volumes vamos a pedir a Docker compose que cree una copia de nuestro directorio dentro del directorio de trabajo del contenedor y esto nos servirá para la ejecución del código en el contenedor.

Con ports le indicaremos el mapa de puertos de nuestro host y el contenedor, en este caso mapeamos el puerto 8085 de nuestro host al puerto 8000 del contenedor y el cual es usado para el servidor.

La instrucción expose nos permite agregar metadata que para saber en que puerto se estará publicando la aplicación.

La instrucción links nos permite ligar a otro servicio el que se esta definiendo, en otras palabras nos permite definir que el servicio web va a interactuar con mysql.

Finalmente networks define la red por la cual se comunicaran los diferentes servicios. En esta parte especificamos una dirección ip fija.

Ahora podemos añadir el servicio de mysql para nuestra base de datos.

```yaml
# web
mysql:
    container_name: ${PROJECT_NAME}_db
    image: 'mysql:5.7'
    volumes:
      - './database/data:/var/lib/mysql'
    ports:
      - '3308:3306'
    networks:
      backend:
        ipv4_address: 192.20.0.6
    environment:
      MYSQL_DATABASE: project
      MYSQL_ROOT_PASSWORD: password
```

Aquí definimos el nombre del contenedor, que usa la imagen mysql:5.7 y que estará montando un volume de la carpeta ./database/data en /var/lib/mysql este paso nos permitirá mantener la base de datos que se cree en el contenedor de mysql y asi no tendremos que definir la base de datos cada que iniciamos los servicios. Mapeamos el puerto 3308 al 3306 que usa mysql en el contenedor y definimos la red a la que estará conectada con su respectiva ip. Finalmente para el contenedor de mysql podemos definir dos variables de entorno MYSQL_DATABASE nos permite definir la base de datos que se creara cuando el servicio inicie y ademas MYSQL_ROOT_PASSWORD que define la contraseña del usuario root.

Como ultimo con docker-compose definimos la red backend que se uso en los servicios anteriores. La red backend usara el driver bridge y una configuración donde las direcciones ip tendrán una mascara de 24 y empezaran con 192.20.0.

```yaml
# version
# services
networks:
  backend:
    driver: bridge
    ipam:
      config:
        - subnet: 192.20.0.0/24
```

Al final tendrías un archivo docker-compose.yml como el siguiente:

```yaml
version: '3.8'

services:

  web:
    container_name: ${PROJECT_NAME}_web
    build: .
    working_dir: /code
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - '8085:8000'
    expose:
      - '8000'
    links:
      - mysql
    networks:
      backend:
        ipv4_address: 192.20.0.5

  mysql:
    container_name: ${PROJECT_NAME}_db
    image: 'mysql:5.7'
    volumes:
      - './database/data:/var/lib/mysql'
    ports:
      - '3308:3306'
    networks:
      backend:
        ipv4_address: 192.20.0.6
    environment:
      MYSQL_DATABASE: project
      MYSQL_ROOT_PASSWORD: password

networks:
  backend:
    driver: bridge
    ipam:
      config:
        - subnet: 192.20.0.0/24
```

Necesitamos definir nuestra variable de entorno para el nombre del proyecto. Para esto creamos un archivo .env en nuestro directorio de trabajo el cual tiene que contener lo siguiente:

```
###< docker ###
PROJECT_NAME=docker_python
###> docker ###
```

Para comprobar que todo funciona correctamente iniciando los servicios con el comando:

```bash
~/dockerproject $ docker-compose up -d
```

Si todo esta correcto al ir a nuestro navegador e introducir [localhost:8085](http://localhost:8085) podremos ver una pantalla como la siguiente:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ve4h7g5phrxq5qnit93a.PNG)

## Migraciones

Como ultimo podemos correr las migraciones en la base de datos, estas migraciones serán las migraciones del modulo admin de Django, para este paso podremos usar el siguiente comando:

```bash
~/dockerproject $ docker exec -it docker_python_web bash
```

El cual ejecuta el comando bash en el contenedor web, desde el que podremos ejecutar las migraciones con:

```bash
root@<hash>: /code# python manage.py migrate
```

# Conclusion

Docker es siempre una excelente opción para la distribución de nuestras aplicaciones, podemos personalizar una imagen local y adecuarla a nuestras necesidades, ademas con solo instalar Docker tendremos una serie de herramientas poderosas para un desarrollo mas rápido y en este caso sin tener que instalar Python directamente en nuestra computadora podremos iniciar un proyecto en Django.

Puedes consultar el código en:

{% github AdGARAY/docker-python no-readme %}