Cómo crear una aplicación multicontenedor con Docker Compose

Cómo crear una aplicación multicontenedor con Docker Compose

La documentación oficial de Docker Compose provee numerosos ejemplos de casos de uso y directivas disponibles. Para recordar los conceptos que detallamos en el artículo anterior y agregar otros, en esta guía aprenderás cómo crear una aplicación multicontenedor con Docker Compose utilizando Django y PostgreSQL. Ya sea que en el futuro elijas utilizar el mismo stack que en esta oportunidad u otro diferente, tendrás las herramientas para hacerlo con éxito.

Requisitos previos

Paso 1: Creación de un Dockerfile y de docker-compose.yml

Para comenzar, crea un nuevo Dockerfile con el siguiente contenido:

nano Dockerfile
FROM python:3
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/

Como notarás, estamos haciendo referencia a un archivo llamado requirements.txt que también debemos agregar en el directorio actual:

nano requirements.txt
Django>=3.0,<4.0
psycopg2>=2.8
django-environ==0.8.0

Finalmente, crea el recurso para Docker Compose:

nano docker-compose.yml

Con estas directivas:

version: "3"

services:
  db:
    image: postgres
    ports:
      - "5432:5432"
    volumes:
      - ./data/db:/var/lib/postgresql/data
    environment:
      - POSTGRES_NAME=${DB_NAME}
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
  web:
    build: .
    command: bash -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000"
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

networks:
  default:
    name: webinar-compose

En el próximo paso examinaremos el contenido del archivo YAML más en detalle.

Paso 2: Explicación de las directivas y variables de entorno

Además de version, services, image, ports y volumes (las variables que ya conoces), en este caso vemos algunas nuevas:

  • build permite especificar el contexto para la construcción de la imagen. En este caso no utilizarás una predefinida desde el Docker Hub, sino que la generarás con el Dockerfile. El punto que aparece aquí indica que este archivo se encuentra en el mismo directorio y que tiene ese nombre. De todas maneras, también es posible colocarlo en otra ubicación y asignarle otro, para lo cual deberías emplear las directivas context para indicar la ruta y dockerfile para especificar el nombre. Aunque no emplearemos esta técnica en esta guía, puedes ampliar más al respecto en la documentación.
  • command especifica el comando que deseamos ejecutar durante el proceso de construcción de la imagen. En este caso puntual, estamos corriendo las migraciones del proyecto de Django para crear y llenar la base de datos asociada.
  • environment es necesaria cuando requerimos el uso de variables de entorno para Docker Compose. Si bien es posible agregarlas en texto plano en docker-compose.yml, es más prudente emplear variables de entorno del sistema operativo o agregarlas en un archivo .env. De una u otra forma, mediante la sintaxis ${VARIABLE}, Docker Compose entenderá que debe realizar el reemplazo por la correspondiente variable antes de levantar el servicio en cuestión. En este ejemplo, en minutos generarás el archivo con las variables y sabrás el motivo por el que este método suele ser el preferido para esta tarea.
  • depends_on indica la dependencia que un servicio o contenedor tiene de otro. En este caso, web depende de db, lo que hace que Docker Compose inicie primero este último (o lo detenga si es lo que deseas luego).

Teniendo en cuenta que ya mencionamos las variables de entorno, agrega las siguientes en el archivo .env dentro del directorio actual:

nano .env
DB_NAME=postgres
DB_USER=postgres
DB_PASSWORD=postgres

Luego de guardar los cambios, procede con el paso 3.

Paso 3: Creación de un proyecto inicial de Django

Para continuar, con el siguiente comando harás la primera aproximación a Docker Compose en esta guía. En pocas palabras, docker-compose run seguido del nombre de uno de los servicios definidos en docker-compose.yml levanta ese contenedor y ejecuta django-admin startproject guiadonweb . en este. Como estamos empleando bind mounts, esto creará el directorio guiadonweb con el esqueleto del proyecto de Django durante el proceso.

sudo docker-compose run web django-admin startproject guiadonweb .

Debido a que los archivos que creó el comando anterior son propiedad de root, cambia el dueño y el grupo:

sudo chown -R $USER:$USER .

La razón por la que django-admin generó recursos con la propiedad original es que el contenedor corre como root por defecto. Esto es un punto a mejorar en lo que a seguridad se refiere, por lo que lo abordaremos de forma integral en un próximo artículo.

Para conectar la base de datos, localiza las secciones ALLOWED_HOSTS y DATABASES dentro del archivo guiadonweb/settings.py y haz los siguientes cambios. Estos te permitirán acceder a la aplicación desde afuera del contenedor y cambiarán la base de datos por defecto (SQLite) por PostgreSQL. Además, harán uso de las variables de entorno que definiste en el archivo .env para efectuar la conexión. Asegúrante de cambiar IP DEL VPS AQUÍ por la dirección de tu host tal como aparece en la lista de servicios dentro de tu cuenta Donweb.

nano guiadonweb/settings.py
ALLOWED_HOSTS = ['IP DEL VPS AQUÍ']
import os
import environ
env = environ.Env(
    # set casting, default value
    DEBUG=(bool, False)
)

# Take environment variables from .env file
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': env('DB_NAME'),
        'USER': env('DB_USER'),
        'PASSWORD': env('DB_PASSWORD'),
        'HOST': 'db',
        'PORT': 5432,
    }
}

En el bloque de arriba, 'HOST': 'db' hace referencia al servicio que definiste en el archivo .yml.

Paso 4: Ejecución del stack

Después de guardar los cambios en el archivo settings.py, puedes levantar el stack completo con el siguiente comando:

sudo docker-compose up

Como resultado, podrás acceder a la pantalla inicial del proyecto a través de un navegador web:

Para finalizar, con sudo docker container ls puedes verificar el listado de los contenedores actuales:

Cuando desees detener el stack, utiliza sudo docker-compose down.

Conclusión

En esta guía aprendiste los fundamentos de una aplicación multicontenedor usando Docker Compose. Al poder construir una imagen propia, puedes adaptar cada servicio según tu gusto y necesidades. Si bien en este artículo utilizamos Django y PostgreSQL, los principios que hemos detallado se aplican a cualquier conjunto de componentes que puedas imaginar.

¿Te resultó útil esta guía?

Imagen por defecto
Gabriel Cánepa
Gabriel trabaja actualmente como desarrollador full-stack en Scalar, una firma que se dedica a hacer valuaciones de empresas. Es Administrador de Sistemas certificado por la Fundación Linux y previamente ha escrito un gran número de artículos y contenidos técnicos sobre el tema para: DigitalOcean, Linode, Carrera Linux Argentina y Tecmint. Tiene una certificación en programación de la Universidad de Brigham Young-Idaho, y está completando las carreras de programador y analista de sistemas en la Universidad Nacional de Villa Mercedes (UNViMe). En su tiempo libre, Gabriel disfruta leyendo libros de Stephen R. Covey, tocando piano y guitarra, y enseñando conocimientos de programación a su dos hijas.