¿Qué es Docker y por qué emplearlo en nuestros proyectos?

 Tiempo de lectura: 20 minutos.

Público objetivo: Desarrolladores de software

Nivel:  Básico.

Fecha publicación: 8 de Marzo de 2022

Hola a todos y todas, me alegra que visiten nuevamente nuestro blog, aunque he de confesarles que no soy uno de sus bloggers habituales, hemos decidido dejar descansar a nuestro querido Jhon J Valero R durante un tiempo y aproveché la oportunidad para compartirles un tema el cual me tiene interesado hace un largo tiempo. Sin más preámbulos ni confesiones adicionales, los invito a entrar libremente y por su propia voluntad y dejar parte de la felicidad que traen con ustedes.

Montar un entorno de desarrollo… (Estoy seguro que alguno hizo una mueca con solo leerlo) todo aquel que haya realizado este proceso debe saber que es una tarea indispensable y realmente tediosa a la hora de correr nuestros proyectos, debemos verificar versiones, programas, librerías, instalar y configurar herramientas de entornos de versiones, instalar dependencias y finalmente correr el proyecto, todo eso para (En algunas ocasiones) encontrarnos con errores que no tuvieron tus compañeros y que no existen en producción… Esto se traduce a horas de producción pérdidas por parte de un desarrollador.

Aquí entra Docker, una herramienta que no solo nos va a permitir hacer este proceso una sola vez, compartirlo con todo el equipo de desarrollo y emplearlo en producción, además reducirá el ya conocido por todos “En local funcionaba bien, no se que paso” al permitirnos crear una imagen con todo lo necesario para nuestro proyecto, almacenado en un repositorio.

La documentación de Docker nos dice:

“ La imagen del contenedor Docker es un paquete de software ejecutable independiente y liviano que incluye todo lo necesario para ejecutar una aplicación: código, tiempo de ejecución, herramientas del sistema, bibliotecas del sistema y configuraciones. ”

Alguien puede estar leyendo este texto y pensar que es básicamente una máquina virtual, ¿por qué no emplear VirtualBox en lugar de usar Docker? Pero es importante hablar del elefante en la habitación, supongamos que su aplicación tiene entre 100 o 200 MB, pero un sistema operativo completo puede pasar fácilmente a GigaBytes.

Docker lo resuelve usando contenedores para ejecutar las aplicaciones. Primero, Docker crea una imagen de su aplicación y luego la ejecuta como contenedores. De esta manera, en lugar de usar un sistema operativo completo para una sola aplicación, usa algunos recursos compartidos comunes y ejecuta su aplicación en contenedores empleando Docker Engine, el cual se encarga de eliminar la dependencia del sistema operativo completo de su aplicación.

“Dockerizando” un proyecto:

Para entender los próximos pasos en la implementación de Docker es necesario establecer el orden del directorio de un proyecto en cuestión y sus características, este proyecto divide el mismo en dos carpetas independientes para el manejo del frontEnd y backEnd, por lo cual se creará un Docker individual para cada proyecto.

  1. Crear Dockerfile para la imagen de backend:
    FROM python:3
    ENV PYTHONUNBUFFERED 1
    WORKDIR /app/api
    COPY requirements.txt ./
    RUN pip install -r requirements.txt
    COPY . ./
    EXPOSE 8000
  2. Crear Dockerfile para la imagen de frontend:
    FROM node:13.12.0-alpine
    WORKDIR /app/frontend
    
    COPY package.json package-lock.json ./
    RUN npm install 
    RUN npm install react-scripts@3.4.1 -g 
    COPY . ./
    EXPOSE 3000
    • Términos empleados en el código:
      • FROM: Establece la imagen base para las instrucciones posteriores. Como tal, un válido Dockerfile debe comenzar con una instrucción de FROM. La imagen puede ser cualquier imagen válida; es especialmente fácil comenzar extrayendo una imagen de los repositorios públicos (Docker Hub).
      • ENV PYTHONUNBUFFERED: Un valor no nulo garantiza que la salida de python se envíe directamente al terminal sin almacenamiento en búfer. Ayuda en la depuración.
      • WORKDIR: Establece su directorio de trabajo en su imagen.
      • COPY: copia un archivo de su directorio local al directorio especificado en la imagen.
      • RUN: Para ejecutar un comando. En este caso instalando las dependencias necesarias
      • EXPOSE: Le dice al contenedor que escuchará un puerto específico.
  3. Configuracion previa:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'bug',
            'USER': config('DB_USER'),
            'PASSWORD': config('DBPASS'),
            'HOST': 'db', # You are supposed to use service name not localhost
            'PORT': '3306',
        }
    }
  4. Crear un archivo Docker-Compose:
    version: "3.2"
    services:
      redis:
        restart: always
        image: redis:5
        ports:
          - "6379:6379"
        networks:
          - db-net
    
      db:
        restart: always
        image: mariadb:5.5
        environment:
          - MYSQL_HOST=localhost
          - MYSQL_PORT=3306
          - MYSQL_ROOT_HOST=%
          - MYSQL_DATABASE=bug
          - MYSQL_USER=<<username>>
          - MYSQL_PASSWORD=<<password>>
          - MYSQL_ROOT_PASSWORD=<<rootpassword>>
        ports:
          - "3302:3306"
        networks:
          - db-net
    
      project:
        restart: always
        container_name: code
        command : bash -c "python check_db.py --service-name db --ip db --port 3306 && 
                          python manage.py migrate &&
                          python manage.py runserver 0.0.0.0:8000"
        env_file:
          - ./project/settings.ini
        build:
          context: ./project/
          dockerfile: Dockerfile
        ports:
          - "8000:8000"
        depends_on:
          - db
          - redis
        networks:
          - db-net
    
      frontend:
        restart: always
        command : npm start
        container_name: front
        build:
          context: ./frontend/
          dockerfile: Dockerfile
        ports:
          - "3000:3000"
        stdin_open: true
        depends_on:
          - project
        networks:
          - db-net
    
    networks:
      db-net:
        driver: bridge​

    En la primera parte se está creando el manejo de la base de datos empleando mariadb y variables de entorno para los datos privados del cliente. Posteriormente establecemos los parámetros necesarios para correr el proyecto de backend y frontend y corremos los comandos para la base de datos, migraciones y finalmente proyecto.

    • Términos empleados en el código:
      • Version: Establece tu versión.

      • services: Define sus servicios. En el código podemos ver servicios de redis, db, project y frontend. Puede definir tantos servicios como desee.

      • Restart: always le dice al contenedor que reinicie el servicio si se apaga inesperadamente.

      • Image: Aquí definimos las imágenes que van a ser utilizadas es posible emplear tanto los Dockerfile que habíamos revisado previamente como imágenes estándar no personalizadas (por ejemplo: mariadb:5.5)

      • Ports: port1:port2 le dice al contenedor que escuche qué puerto. El puerto 1 puede ser cualquier puerto desocupado, pero el puerto 2 debe ser el puerto que habría utilizado si estuviera ejecutando su aplicación en localhost. Es posible emplear el mismo puerto si se tiene la seguridad de estar libre.

      • Command: En este espacio se especifican los comandos para ejecutar el momento de iniciar el contenedor

      • Container_name: Si no define un container_name, la ventana acoplable proporciona un valor predeterminado basado en su carpeta y servicio.

      • Depends_on: Dice a Docker que un determinado servicio depende de otros servicios, por lo que primero debe iniciar sus contenedores antes de iniciar el contenedor actual.

      • Build: 

      • Context: como una ruta relativa para su Dockerfile con respecto a su archivo docker-compose.

      • Dockerfile: es el nombre de su Dockerfile.

Hora de empezar

El primer paso será construir tus imágenes. para ello en la terminal será necesario ubicarse en la carpeta donde esta ubicado el docker compose

docker-compose build

Una vez terminado este proceso será posible ejecutar la aplicación empleando el comando correspondiente. Es posible que deba darle a su aplicación un minuto para correr las migraciones de forma adecuada, configurar el backend y ejecutar los servidores. 

docker-compose up -d

Para comprobar los contenedores que se están ejecutando actualmente

docker-compose ps -a

Visita http://localhost:3000/ según el puerto que se haya configurado y podrás acceder a tu aplicación.

 

Finalmente para detener su aplicación.

docker-compose down

Espero que este pequeño acercamiento a Docker les haya ayudado a despertar un poco la curiosidad y probar esta útil herramienta para el desarrollo y dejar atrás esos tortuosos momentos al montar un entorno desde 0 o el terror que da formatear un equipo en mitad de un desarrollo.

Les deseo un excelente dia y no se preocupen no es un adiós, estaré por aquí en un futuro contándoles alguna otra herramienta de desarrollo que haya probado y me haya ayudado

Autor: Alvaro Juan Pablo Aguirre Sierra

Ingeniero de Software en Elemental Lab

Compartir Compartir Compartir