Introducción a Docker Compose

Nota: por motivos de que "alguien" encontró el proyecto en GitHub y mando una copia a mi misma empresa para un puesto similar al mio, he decidido eliminar de GitHub el código y está disponible en mega a través del enlace de descarga que hay a la derehca. (Abajo en versiones móviles)

En este post vamos a ver la continuación de los post anteriores en el que vimos qué es Docker, así como un ejemplo de como correr nuestra api en docker. Y como correr nuestros test de integración contra una base de datos alojada en Docker.

 

En este vamos a ver la continuación del mismo, y cómo implementar un sistema de múltiples contenedores en nuestra aplicación.

Antes de continuar, decir que docker-compose se instala automáticamente cuando instalas Docker en la máquina, ya sea windows o mac.  

Nota: El código está disponible en GitHub, es de una entrevista que hice para una empresa. 

 

 

 

1 - Qué es docker compose?

Como vimos en el post anterior para ejecutar nuestra aplicación en docker creamos un fichero llamado Dockerfile y este fichero contiene diferente configuración, esta configuración varía dependiendo de qué queremos poner en el contenedor, ya que no es lo mismo poner una página web, que una base de datos. 

 

Este proceso, de crear todos los Dockerfile y ejecutarlos puede ser bastante tedioso, ya que debemos pensar que una aplicación de tamaño mediano es probable que tenga un front end, un back end, quizá background-workers así como la base de datos, sistema de caché, sistema de colas o de message-broker, etc, cada uno de nuestros servicios será un contenedor diferente 

 

Por lo tanto, crear múltiples Dockerfile y ejecutarlos todo en un script queda largo y feo.

 

Aquí es donde entra docker copose el cual es una herramienta que nos permite definir y correr múltiples contenedores en Docker, estos múltiples contenedores se definen en un fichero denominado docker-compose con la extensión .yml.

 

Por lo tanto Docker compose es una herramienta de orquestación de contenedores

 

 

 

2 - Configuración de un fichero docker-compose

Para poder crear estos contenedores debemos hacerlo en nuestro fichero docker-compose.yml por norma general en los proyectos se suele poner en la raíz del mismo, y yo personalmente siempre lo pongo ahí. 

 

El fichero consta de varias partes:

  • version: Debemos indicar que versión de docker-compose vamos a utilizar.
    • Por ejemplo es importante tener en cuenta que hoy en dia para relacionar contenedores se utiliza la configuración links pero anteriormente había que crear una red en la que todos los contenedores estuvieran en la misma red.
  • services: Aquí es donde indicaremos los diferentes servicios que vamos a utilizar y cada uno de ellos será convertido en un contenedor.
  • volumes:  Podemos definir el apartado volumes el cual quiere decir que la información del contenedor se preservara cuando destruyamos el mismo, así la siguiente vez que lo creemos, toda esa información permanecerá ahí (por ejemplo información de una base de datos).

 

version: "3.8"
services:
    rabbitmq:
       ###Configuración del contenedor
    mysql:
       ###Configuración del contendor

 

Cada uno de estos servicios tiene diferente configuración pero todos tienen la misma estructura:

  • container_name: nombre para el contenedor, por defecto docker pone nombres feos y yo personalmente prefiero tener los nombres limpios.
  • image: Indicamos qué imagen vamos a utilizar, estas imágenes comúnmente están almacenadas en la página web de docker , aunque muchas empresas Tienen las suyas propias. es normal ir a internet y mirar cual es la que se necesita (Igual que FROM dentro del Dockerfile)
    • build: Opcionalmente a “image” podemos indicar build, el cual es la ruta a un dockerfile con la configuración.
  • ports: Los puertos que van a enrutar desde el exterior del contenedor al interior.
    • por ejemplo en el caso de mysql utiliza el puerto 3306 pero si tenemos instalado mysql en nuestra máquina no podemos utilizar ese puerto, ya que llamaría al mysql de nuestra máquina y no al del contendor, por ello podemos indicar 4306:3306 que lo que hace es, cuando invocamos desde nuestra máquina al puerto 4306 redirecciona en docker al 3306.
  • environment: Podemos enviar variables de entorno a nuestro contenedor, normalmente las imágenes utilizan estas variables para distintos temas de configuración, por ejemplo en mysql, podemos definir un usuario y una contraseña por defecto.
    • Cada imagen/servicio tiene sus propias variables, hay que leerse la documentación para saber cuales son.
  • volumes: Como hemos indicado antes, es el apartado para tener persistencia de datos cuando creamos y destruimos nuestro contenedor, además nos permite enviar información adicional, comúnmente utilizado para crear configuración inicial.
  • depends_on: algunas veces tenemos un contenedor que depende de otro, aqui es donde lo indicamos
  • links: En ciertas ocasiones, necesitamos varios contenedores que hablen entre sí, osea que tengan acceso de uno al otro, anteriormente se hacía con networks, pero hoy en día lo hacemos con la propiedad link, únicamente debemos indicar . 

 

Aquí podemos ver un fichero docker-compose.yml el cual contiene

  1. MySql (base de datos)
  2. RabbitMQ (message-broker)
  3. API en .net
  4. Backgroundworker en .net
version: "3.8"
services:
    rabbitmq:
        container_name: rabbitmq
        ports:
            - 5672:5672
            - 15672:15672
        volumes: 
            - ./Tools/RabbitMQ/rabbitmq.config:/etc/rabbitmq/rabbitmq.config
            - ./Tools/RabbitMQ/definitions.json:/etc/rabbitmq/definitions.json
        image: rabbitmq:3-management
    mysql:
        container_name: mysql
        ports: 
            - 4306:3306
        environment:
            - MYSQL_DATABASE=retailim
            - MYSQL_ROOT_PASSWORD=test
        volumes:
            - ./src/Database/:/docker-entrypoint-initdb.d/
        image: mysql:5.6
    webapi:
        container_name: webapi
        ports:
            - 8080:80
            - 8081:443
        environment:
            - ASPNETCORE_ENVIRONMENT=Production     
        build:
            context: .
            dockerfile: ./src/RetailIM.WebApi/Dockerfile
        restart: on-failure        
        depends_on:
            - rabbitmq
        links:
            - rabbitmq
    orderms:
        container_name: orderms
        ports:
            - 8180:80
            - 8181:443
        environment: 
            - ASPNETCORE_ENVIRONMENT=Production  
        build: 
            context: .
            dockerfile: ./src/RetailIM.OrderMS/Dockerfile
        restart: on-failure
        depends_on:
            - rabbitmq
            - mysql
        links:
            - mysql
            - rabbitmq

Para el ejemplo de hoy, únicamente  nos interesan MySql y RabbitMQ, por lo tanto los otros los podemos borrar. 

 

 

2.1 - Contexto de la aplicación 

Para poner un poco el contexto, la aplicación tiene la siguiente arquitectura:

arquitectura distribuida

 

Consiste en una aplicación que es una API  la cual recibe llamadas externas a través de la web por HTTP. 

Estas request, van a un message-broker (rabbitMQ en docker) y otra aplicación está constantemente leyendo la cola y las inserta en la base de datos (mysql en docker). 

 

En nuestro test de integración queremos probar toda la funcionalidad, por lo que insertamos directamente desde el test al controlador el dato y manualmente ejecutamos el servicio que lee la cola, pero ni la cola ni la base de datos son accionadas manualmente. esta parte la tenemos que replicar con Docker.

 

3 - Montar contenedores con docker-compose

Para montar estos contenedores únicamente debemos ir a la carpeta donde tenemos nuestro fichero docker-compose.yml y ejecutar en la terminal el siguiente comando: 

docker-compose up -d

Nota: Ejecutamos la opción -d para que corra los contenedores en el background (y no estén en la propia ventana de la terminal) 

 

docker compose up ejemplo

 

Podemos ver nuestros contenedores siendo ejecutados correctamente tanto si ejecutamos en la terminal el comando docker container ls o si vamos a la aplicación de escritorio de docker y pulsamos en containers .

 

contenedores corriendo en docker con compose

 

Para desmontar los contenedores y que estos desaparezcan de nuestro sistema debemos ejecutar el siguiente comando:

docker-compose down

 

 

4 - Cuando utilizar Docker Compose

4.1 - Entorno de test producción

Un caso de uso podría ser para desplegar todo nuestro entorno en un servidor de test o en producción, pero en aplicaciones empresariales serias esto no lo vais a ver jamás. 

 

Simplemente hay mejores alternativas, como puede ser docker swarm o kubernetes el cual es el que yo personalmente utilizo en la empresa. 

El motivo principal es porque docker-compose es una herramienta de orquestación para un solo host, si quieres más de un host o diferente configuración como puede ser réplicas o escalabilidad, necesitas utilizar docker swarm o kubernetes. 

 

Si puede ser que si la empresa es muy pequeña o alguien de forma personal como proyecto simplemente docker-compose en producción, pero no suele ser lo normal. 

Por ejemplo esta web podría estar configurada con docker-compse y no habría ninguna diferencia de cara al usuario o rendimiento. 

 

4.2 - Automatizar test con docker compose

En este punto es donde pienso que docker compose es el rey, a la hora de incluir nuestros test de integración  dentro de no proceso de continuous delivery y continuous integration.

 

Es debido a que docker compose nos provee una forma muy sencilla de replicar entornos  completamente aislados, que es ideal para realizar nuestros test. 

 

Únicamente debemos tener un script que se ejecute en la pipeline CI/CD que contenga la instrucción de levantar los contenedores, ejecutar los test y finalmente destruir los contenedores:

docker-compose up -d
dotnet test ./test/RetailIM.IntegrationTest/RetailIM.IntegrationTest.csproj
docker-compose down

 

Nota: dentro del script, para asegurarnos de que los contenedores están montados correctamente, podemos esperar un par de segundos, Si el proyecto es grande no hará falta, ya que mientras el proyecto hace la build los contenedores acabaran de montarse, pero cuando es pequeño, es probable que intente empezar antes de la cuenta.

Otra opción es ejecutar los test unitarios entre montar los contenedores y los test de integración, esto nos da tiempo suficiente para que se monten.

 

Y si ejecutamos el script, podemos ver el resultado: 

ejecutar test de integracion con docker compose

 

 

Conclusión

Personalmente utilizo docker compose tanto en mi dia a dia en la oficina como cuando realizo proyectos personales o entrevistas (El ejemplo de hoy, es de una entrevista).

 

Es una herramienta muy útil que nos permitirá correr los test de integración de forma aislada, lo cual es crucial sobre todo en nuestro proceso CI/CD, ya que los resultados serán exactos y certeros. 

Además montar y desmontar el entorno entero es cuestión de unos pocos segundos y todo automatizado.

 

Obligar al código a pasar los test antes de poder hacer un despliegue, sobre todo a producción nos puede ahorrar muchos dolores de cabeza de muchas horas, y como hemos visto, es muy sencillo de configurar. 

 

Uso del bloqueador de anuncios adblock

Hola!

Primero de todo bienvenido a la web de NetMentor donde podrás aprender programación en C# y .NET desde un nivel de principiante hasta más avanzado.


Yo entiendo que utilices un bloqueador de anuncios como AdBlock, Ublock o el propio navegador Brave. Pero te tengo que pedir por favor que desactives el bloqueador para esta web.


Intento personalmente no poner mucha publicidad, la justa para pagar el servidor y por supuesto que no sea intrusiva; Si pese a ello piensas que es intrusiva siempre me puedes escribir por privado o por Twitter a @NetMentorTW.


Si ya lo has desactivado, por favor recarga la página.


Un saludo y muchas gracias por tu colaboración

© copyright 2024 NetMentor | Todos los derechos reservados | RSS Feed

Buy me a coffee Invitame a un café