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

Aplicaciones multicontenedor con Docker Compose

Para aprender a utilizar Docker Compose, la documentación oficial es el mejor recurso debido a la gran cantidad de casos de uso y ejemplos sobre directivas disponibles. Sin embargo, esta guía te enseñará a crear aplicaciones multicontenedor con Docker Compose para recordar los conceptos que detallamos en el artículo anterior y agregar otros. Aunque emplearemos Django y PostgreSQL para ilustrar, los mismos conceptos te serán de utilidad si decides usar otro stack.

Antes de comenzar

  • Asegúrate de instalar Docker y crear un contenedor en Ubuntu 20.04 y de hacer lo mismo con Docker Compose.
  • Agregar una regla en el cortafuegos de Donweb que permita el tráfico entrante a los puertos 8000 y 5432 de TCP. Este último solamente es necesario para hacer consultas a la base de datos PostgreSQL desde un cliente externo, luego de lo cual te aconsejamos cerrarlo. En el artículo Firewall de la sección de ayuda encontrarás las instrucciones para completar este punto.

Crear un Dockerfile y el archivo docker-compose.yml

Como primer paso, 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/

El archivo requirements.txt al que hacemos referencia arriba también debe estar en el mismo directorio que el Dockerfile, de acuerdo a la ruta que especificamos. Créalo y agrega el contenido que aparece a continuación:

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

Finalmente, agrega el archivo 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 apartado siguiente veremos el contenido del archivo YAML con más detenimiento.

Directivas y variables de entorno

Además de version, services, image, ports y volumes (que ya conoces), en docker-compose.yml encontrarás algunas nuevas:

  • command es el comando que se correrá durante la construcción de la imagen. En este ejemplo, estamos ejecutando las migraciones del proyecto de Django para crear la base de datos y llenarla.
  • build indica el contexto para la construcción de la imagen. El punto que aparece aquí indica que este archivo se encuentra en el mismo directorio y que tiene ese nombre. De cualquier forma, también puedes colocarlo en otra ubicación. Para eso, deberías emplear las directivas context para indicar la ruta y dockerfile para especificar el nombre. La documentación muestra varios ejemplos de este punto.
  • 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).
  • environment se require para poder usar variables de entorno en 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 emplear un archivo .env. La sintaxis ${VARIABLE} hace que Docker Compose incluya la variable correspondiente antes de levantar el servicio en cuestión.

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

Recuerda guardar los cambios antes de continuar.

Crear un proyecto inicial de Django

El comando docker-compose run seguido del nombre de uno de los servicios definidos en docker-compose.yml levanta ese contenedor. A continuación, ejecuta django-admin startproject guiadonweb . en este. También creará el directorio guiadonweb ya que estamos utilizando bind mounts, el cual incluirá el esqueleto del proyecto de Django.

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

Los archivos que generó el comando anterior son propiedad de root, por lo que modificaremos el dueño y el grupo:

sudo chown -R $USER:$USER .

La razón por la que django-admin generó recursos propiedad de root es que el contenedor corre como esa cuenta 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.

Conectar la base de datos

Para cumplir con este objetivo, localiza las secciones ALLOWED_HOSTS y DATABASES dentro del archivo guiadonweb/settings.py y haz los cambios que detallamos abajo. 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. Para efectuar la conexión, emplearán las variables de entorno que definiste en el archivo .env. 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' representa el servicio que definiste en el archivo .yml.

Ejecutar el stack

Luego de guardar el archivo settings.py, levanta el stack completo:

sudo docker-compose up

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

aplicaciones multicontenedor con docker compose 1
Aplicaciones multicontenedor con Docker Compose

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

aplicaciones multicontenedor con docker compose 2
Aplicaciones multicontenedor con Docker Compose

Cuando desees detener la aplicación utiliza sudo docker-compose down desde otra terminal.

Para terminar

En esta guía aprendiste los conceptos necesarios para desarrollar aplicaciones multicontenedor con Docker Compose. La gran ventaja de partir desde 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.

Gabriel Cánepa
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.