Compartir comentarios
Las respuestas se generan en base a la documentación.

Aplicaciones multicontenedor

Hasta este punto, has estado trabajando con aplicaciones de un solo contenedor. Pero ahora agregarás MySQL a la pila de la aplicación. A menudo surge la siguiente pregunta: "¿Dónde se ejecutará MySQL? ¿Se instala en el mismo contenedor o se ejecuta por separado?". En general, cada contenedor debe hacer una sola cosa y hacerla bien. A continuación se presentan algunas razones para ejecutar el contenedor por separado:

  • Es muy probable que tengas que escalar las API y las interfaces frontend de manera diferente a las bases de datos.
  • Los contenedores separados te permiten gestionar versiones y actualizarlas de forma aislada.
  • Si bien puedes usar un contenedor para la base de datos de manera local, es posible que desees utilizar un servicio gestionado para la base de datos en producción. En ese caso, no querrás distribuir tu motor de base de datos junto con tu aplicación.
  • Ejecutar múltiples procesos requerirá un gestor de procesos (el contenedor solo inicia un proceso), lo que agrega complejidad al inicio y apagado del contenedor.

Y existen más razones. Por lo tanto, como se muestra en el siguiente diagrama, lo mejor es ejecutar tu aplicación en múltiples contenedores.

Aplicación de tareas conectada al contenedor MySQL

Redes de contenedores

Recuerda que los contenedores, por defecto, se ejecutan de forma aislada y no saben nada acerca de otros procesos o contenedores en la misma máquina. Entonces, ¿cómo permites que un contenedor se comunique con otro? La respuesta son las redes. Si colocas los dos contenedores en la misma red, podrán comunicarse entre sí.

Iniciar MySQL

Existen dos formas de colocar un contenedor en una red:

  • Asignar la red al iniciar el contenedor.
  • Conectar un contenedor que ya está en ejecución a una red.

En los siguientes pasos, primero crearás la red y luego conectarás el contenedor MySQL al iniciar.

  1. Crea la red.

    $ docker network create todo-app
    
  2. Inicia un contenedor MySQL y conéctalo a la red. También vas a definir algunas variables de entorno que la base de datos utilizará para inicializarse. Para obtener más información sobre las variables de entorno de MySQL, consulta la sección "Environment Variables" en la página de MySQL en Docker Hub.

    $ docker run -d \
        --network todo-app --network-alias mysql \
        -v todo-mysql-data:/var/lib/mysql \
        -e MYSQL_ROOT_PASSWORD=secret \
        -e MYSQL_DATABASE=todos \
        mysql:8.0
    
    $ docker run -d `
        --network todo-app --network-alias mysql `
        -v todo-mysql-data:/var/lib/mysql `
        -e MYSQL_ROOT_PASSWORD=secret `
        -e MYSQL_DATABASE=todos `
        mysql:8.0
    $ docker run -d ^
        --network todo-app --network-alias mysql ^
        -v todo-mysql-data:/var/lib/mysql ^
        -e MYSQL_ROOT_PASSWORD=secret ^
        -e MYSQL_DATABASE=todos ^
        mysql:8.0
    

    En el comando anterior, puedes ver la bandera --network-alias. En una sección posterior aprenderás más sobre esta bandera.

    Tip

    Notarás un volumen llamado todo-mysql-data en el comando anterior que está montado en /var/lib/mysql, que es donde MySQL almacena sus datos. Sin embargo, nunca ejecutaste un comando docker volume create. Docker reconoce que deseas usar un volumen con nombre y crea uno automáticamente por ti.

  3. Para confirmar que tienes la base de datos en funcionamiento, conéctate a ella y verifica que la conexión se establezca.

    $ docker exec -it <mysql-container-id> mysql -u root -p
    

    Cuando aparezca la solicitud de contraseña, escribe secret. En la consola de MySQL, enumera las bases de datos y verifica que veas la base de datos todos.

    mysql> SHOW DATABASES;
    

    Deberías ver una salida parecida a esta:

    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    | todos              |
    +--------------------+
    5 rows in set (0.00 sec)
  4. Sal de la consola de MySQL para regresar a la terminal de tu máquina.

    mysql> exit
    

    Ahora tienes una base de datos todos lista para usar.

Conectar a MySQL

Ahora que sabes que MySQL está en funcionamiento, puedes usarlo. Pero, ¿cómo lo usas? Si ejecutas otro contenedor en la misma red, ¿cómo encuentras ese contenedor? Recuerda que cada contenedor tiene su propia dirección IP.

Para responder a las preguntas anteriores y comprender mejor las redes de contenedores, vas a utilizar el contenedor nicolaka/netshoot, que incluye una gran cantidad de herramientas útiles para solucionar problemas o depurar redes.

  1. Inicia un nuevo contenedor utilizando la imagen nicolaka/netshoot. Asegúrate de conectarlo a la misma red.

    $ docker run -it --network todo-app nicolaka/netshoot
    
  2. Dentro del contenedor, vas a utilizar el comando dig, que es una herramienta de DNS muy útil. Buscarás la dirección IP para el nombre de host mysql.

    $ dig mysql
    

    Deberías obtener una salida como la siguiente.

    ; <<>> DiG 9.18.8 <<>> mysql
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32162
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
    
    ;; QUESTION SECTION:
    ;mysql.				IN	A
    
    ;; ANSWER SECTION:
    mysql.			600	IN	A	172.23.0.2
    
    ;; Query time: 0 msec
    ;; SERVER: 127.0.0.11#53(127.0.0.11)
    ;; WHEN: Tue Oct 01 23:47:24 UTC 2019
    ;; MSG SIZE  rcvd: 44

    En la sección "ANSWER SECTION", verás un registro A para mysql que se resuelve en 172.23.0.2 (es muy probable que tu dirección IP tenga un valor diferente). Aunque normalmente mysql no es un nombre de host válido, Docker pudo resolverlo a la dirección IP del contenedor que tenía ese alias de red. Recuerda que utilizaste --network-alias anteriormente.

    Lo que esto significa es que tu aplicación simplemente necesita conectarse a un host llamado mysql para comunicarse con la base de datos.

Ejecutar tu aplicación con MySQL

La aplicación de tareas admite la configuración de algunas variables de entorno para especificar los parámetros de conexión a MySQL. Estas son:

  • MYSQL_HOST: el nombre de host del servidor MySQL en ejecución.
  • MYSQL_USER: el nombre de usuario que se utilizará para la conexión.
  • MYSQL_PASSWORD: la contraseña que se utilizará para la conexión.
  • MYSQL_DB: la base de datos que se utilizará una vez conectado.
Note

Si bien el uso de variables de entorno para configurar la conexión es generalmente aceptado en desarrollo, se desaconseja totalmente al ejecutar aplicaciones en producción. Diogo Mónica, exjefe de seguridad de Docker, escribió un artículo de blog fantástico explicando los motivos.

Un mecanismo más seguro es utilizar el soporte de secretos proporcionado por tu framework de orquestación de contenedores. En la mayoría de los casos, estos secretos se montan como archivos en el contenedor en ejecución. Verás que muchas aplicaciones (incluida la imagen de MySQL y la aplicación de tareas) también admiten variables de entorno con el sufijo _FILE para apuntar a un archivo que contiene la variable.

Por ejemplo, configurar la variable MYSQL_PASSWORD_FILE hará que la aplicación use el contenido del archivo referenciado como la contraseña de conexión. Docker no hace nada especial para soportar estas variables de entorno de forma nativa. Tu aplicación deberá estar diseñada para buscar la variable y leer el contenido del archivo.

Ahora puedes iniciar tu contenedor preparado para desarrollo.

  1. Especifica cada una de las variables de entorno anteriores, además de conectar el contenedor a la red de tu aplicación. Asegúrate de estar en el directorio getting-started-app cuando ejecutes este comando.

    $ docker run -dp 127.0.0.1:3000:3000 \
      -w /app -v ".:/app" \
      --network todo-app \
      -e MYSQL_HOST=mysql \
      -e MYSQL_USER=root \
      -e MYSQL_PASSWORD=secret \
      -e MYSQL_DB=todos \
      node:24-alpine \
      sh -c "npm install && npm run dev"
    

    En Windows, ejecuta este comando en PowerShell.

    $ docker run -dp 127.0.0.1:3000:3000 `
      -w /app -v ".:/app" `
      --network todo-app `
      -e MYSQL_HOST=mysql `
      -e MYSQL_USER=root `
      -e MYSQL_PASSWORD=secret `
      -e MYSQL_DB=todos `
      node:24-alpine `
      sh -c "npm install && npm run dev"

    En Windows, ejecuta este comando en Command Prompt (Símbolo del sistema).

    $ docker run -dp 127.0.0.1:3000:3000 ^
      -w /app -v "%cd%:/app" ^
      --network todo-app ^
      -e MYSQL_HOST=mysql ^
      -e MYSQL_USER=root ^
      -e MYSQL_PASSWORD=secret ^
      -e MYSQL_DB=todos ^
      node:24-alpine ^
      sh -c "npm install && npm run dev"
    
    $ docker run -dp 127.0.0.1:3000:3000 \
      -w //app -v "/.:/app" \
      --network todo-app \
      -e MYSQL_HOST=mysql \
      -e MYSQL_USER=root \
      -e MYSQL_PASSWORD=secret \
      -e MYSQL_DB=todos \
      node:24-alpine \
      sh -c "npm install && npm run dev"
    
  2. Si observas los registros del contenedor (docker logs -f <container-id>), deberías ver un mensaje similar al siguiente, que indica que está utilizando la base de datos MySQL.

    [nodemon] 3.1.11
    [nodemon] to restart at any time, enter `rs`
    [nodemon] watching path(s): *.*
    [nodemon] watching extensions: js,mjs,cjs,json
    [nodemon] starting `node src/index.js`
    Waiting for mysql:3306.
    Connected!
    Connected to mysql db at host mysql
    Listening on port 3000
    
  3. Abre la aplicación en tu navegador y agrega algunos elementos a tu lista de tareas.

  4. Conéctate a la base de datos MySQL y demuestra que los elementos se están escribiendo en la base de datos. Recuerda que la contraseña es secret.

    $ docker exec -it <mysql-container-id> mysql -p todos
    

    Y en la consola de MySQL, ejecuta lo siguiente:

    mysql> select * from todo_items;
    +--------------------------------------+--------------------+-----------+
    | id                                   | name               | completed |
    +--------------------------------------+--------------------+-----------+
    | c906ff08-60e6-44e6-8f49-ed56a0853e85 | Do amazing things! |         0 |
    | 2912a79e-8486-4bc3-a4c5-460793a575ab | Be awesome!        |         0 |
    +--------------------------------------+--------------------+-----------+
    

    Tu tabla se verá diferente porque tendrá tus elementos. Pero deberías verlos almacenados allí.

Resumen

En este punto, tienes una aplicación que ahora almacena sus datos en una base de datos externa que se ejecuta en un contenedor separado. Aprendiste un poco sobre redes de contenedores y descubrimiento de servicios utilizando DNS.

Información relacionada:

Siguientes pasos

Es muy probable que comiences a sentirte un poco abrumado con todo lo que necesitas hacer para iniciar esta aplicación. Tienes que crear una red, iniciar contenedores, especificar todas las variables de entorno, exponer puertos y más. Eso es mucho que recordar y ciertamente dificulta el compartirlo con otra persona.

En la siguiente sección, aprenderás sobre Docker Compose. Con Docker Compose, puedes compartir las pilas de tu aplicación de una manera mucho más sencilla y permitir que otros las pongan en marcha con un comando único y simple.

Usar Docker Compose