Configurando Docker Compose con Nodejs y MySQL
En este tutorial aprenderás a configurar una aplicación Backend de Nodejs y una base de datos de MySQL, cada uno en su propio contenedor de Docker y comunicandose a traves de Docker Compose, todo desde cero y paso a paso.
Requerimientos para el tutorial
- Tener instalado Docker
- Tener intalado Nodejs
Tambien es recomendado haber usado previamente el contenedor de mysql para entener mejor como unirlos a otros contenedores, sin embargo no es un requerimiento e igual será explicado.
Configuracion del proyecto de Backend
Primero vamos a crear un proyecto de ejemplo de Backend en Nodejs:
mkdir nodejs-mysql-docker
cd nodejs-mysql-docker
Abrelo con Visual Studio Code o tu editor favorito:
code .
Luego vamos a inicializar un proyecto de Node:
npm init -y
Instalaremos tambien los modulos principales
npm i express mysql2 dotenv
En este ejemplo, se utilizarán los siguientes paquetes:
- express: Express es un framework Backend minimalista y flexible de Node.js que proporciona un conjunto sólido de características para aplicaciones web y móviles. Se utiliza para construir aplicaciones web, REST APIs y para configurar rutas, middlewares, y manejar solicitudes HTTP.
- mysql2: MySQL2 es un módulo de Node.js que proporciona una implementación del cliente MySQL. Permite a las aplicaciones Node.js interactuar con bases de datos MySQL, realizando consultas y gestionando la base de datos. Ofrece una mejorada compatibilidad con MySQL, mejor rendimiento y soporte para Promesas.
- dotenv: Dotenv es un módulo de Node.js que permite cargar variables de entorno desde un archivo
.env
enprocess.env
. Es útil para manejar configuraciones específicas del entorno, como credenciales de bases de datos y claves de API, sin tener que incluirlas directamente en el código.
Luego tambien instalaremos un modulos adicional como dependencia de desarrollo llamado nodemon
, este modulo permite reiniciar el codigo cada vez que cambiamos algo.
npm i -D nodemon
Luego vamos a crear un archivo que contendrá todo el proyecto en src/index.js
con el siguiente contenido:
import express from 'express'
const app = express()
app.listen(3000)
console.log('Server on port', 3000)
Como en el código anterior estamos usando los Ecmascript Modules (los imports y exports en Javascript) por lo que debemos actualizar nuestro package.json para que use los modulos, ademas tambien crearemos un script para ejecutar en desarrollo con nodemon y en producción con node. Actualiza el codigo del package.json a lo siguiente:
{
"name": "nodejs-mysql-docker",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"start": "node src/index.js",
"dev": "nodemon src/index.js"
},
"license": "ISC",
"dependencies": {
"dotenv": "^16.3.1",
"express": "^4.18.2",
"mysql2": "^3.4.1"
},
"devDependencies": {
"nodemon": "^2.0.22"
}
}
Ahora puedes ejecutar sdes consola los comandos:
npm run dev
para ejecutar con nodemon el proyectonpm start
para ejecutar con node el proyecto
MySQL en Docker compose
Ahora vamos a conectar a nuestra aplicacion de Nodejs a MySQL, y para esto necesitamos tener mysql instalado o tambien podemos usar Docker compose para crear nuestra base de datos.
Primero crea un archivo docker-compose
en la raiz del proyecto, con el siguiente codigo:
version: '3.8'
services:
mysqldb:
image: mysql
environment:
- MYSQL_ROOT_PASSWORD=123456
- MYSQL_DATABASE=fazt_db
ports:
- 3307:3306
Esto creara un contenedor de mysql con un usuario root
con una contraseña 123456
y con una base de datos llamada fazt_db
, ejecutandose internamente en el puerto 3306
y desde nuestro computador en el puerto 3307
.
Por lo general veras muchas configuraciones de puerto con
3306:3306
pero para que se entienda mejor la diferencia he decidido cambiar el puerto expuesto.
Para probar la conexion a tu base de datos puedes usar Clientes de MySQL como Dbeaver, HeidiSQL o MySQL Workbench.
Conexión de Nodejs con Mysql
Ahora que ya tenemos a MySQL ejecutandose vamos a conectar Nodejs con MySQL.
Actualiza el codigo de src/index.js
de esta forma:
import express from "express";
import { createPool } from "mysql2/promise";
const app = express();
const pool = createPool({
host: "localhost",
user: "root",
password: "123456",
port: 3307,
});
pool.on("connection", () => console.log("DB Connected!"));
app.get("/ping", async (req, res) => {
const result = await pool.query("SELECT NOW()");
res.json(result[0]);
});
app.listen(3000);
console.log("Server on port", 3000);
Con este puedes hacer peticiones GET a la ruta http://localhost:3000/api
y obtendras como respuesta la fecha del servidor de MySQL.
Nodejs en Contenedor de Docker
Ahora pasemos a crear un contenedor de Nodejs.
Primero crea un archivo Dockerfile
en la raiz del proyecto, con el siguiente contenido:
FROM node:18
WORKDIR /myapp
COPY package.json .
RUN npm install
EXPOSE 3000
COPY . .
CMD npm start
Luego actualiza el archivo docker-compose.yml de la siguiente forma:
version: '3.8'
services:
mysqldb:
image: mysql
environment:
- MYSQL_ROOT_PASSWORD=123456
- MYSQL_DATABASE=fazt_db
ports:
- 3307:3306
app:
build: .
depends_on:
- mysqldb
links:
- mysqldb
ports:
- 3000:3000
Luego tendremos que actualizar nuestro archivo de src/index.js
porque ahora como nuestro servidor de Nodejs estara conectandose internamente a un servidor de MySQL, los parametros son distintos:
const pool = createPool({
host: "mysqldb",
user: "root",
password: "123456",
port: 3306,
});
Como puedes ver la diferencia notable es el host que ahora tiene el nombre del servicio de Mysql en docker-compose mysqldb
y el puerto es el 3306
debido a que ahora esta en la misma red interna de Docker.
Ahora vamos a ejecutar nuestra nueva configuracion, pero primero vamos a cancelar la ejecucion del contendor anterior presional Ctrl + c dos veces y luego ejecutando:
docker compose down
Seguido de ejecutar docker compose:
docker compose up
Una vez hecho esto ya puedes visistar el http://localhost:300/ping
y deberias ver la misma respuesta.
Si obtenes un error como este
Error: connect ECONNREFUSED 127.0.0.1:3307
Puede que tus parametros de conexion esten mal escritos, asi que asegurate de colocar los que tienes en docker-compose. O si no reinicia los volumes de Docker para que empiecen desde cero, primero ejecutadocker compose down
luegodocker volume rm $(docker volume ls -q)
y finalmente elimina la imagen anteriores condocker rmi $(docker images -aq)
y finalmente ejecuta nuevamentedocker compose up
Variables de Entorno
Ahora como tipicamente vamos a querer cambiar las credenciales de usuario y contraseña o puerto vamos a crear un archivo con variables de entorno para que podamos desde un solo lugar actualizar las variables de docker y las variables de nodejs.
crea un archivo .env con el siguiente contenido:
MYSQLDB_HOST=mysqldb
MYSQLDB_ROOT_PASSWORD=123456
MYSQLDB_DATABASE=fazt_db
MYSQLDB_LOCAL_PORT=3307
MYSQLDB_DOCKER_PORT=3306
NODE_LOCAL_PORT=3000
NODE_DOCKER_PORT=3000
Y luego actualizaremos el archivo docker a lo siguiente:
version: '3.8'
services:
mysqldb:
image: mysql
env_file: ./.env
environment:
- MYSQL_USER=$MYSQLDB_USER
- MYSQL_PASSWORD=$MYSQLDB_PASSWORD
- MYSQL_ROOT_PASSWORD=$MYSQLDB_ROOT_PASSWORD
- MYSQL_DATABASE=$MYSQLDB_DATABASE
ports:
- $MYSQLDB_LOCAL_PORT:$MYSQLDB_DOCKER_PORT
app:
build: .
depends_on:
- mysqldb
links:
- mysqldb
env_file: ./.env
ports:
- $NODE_LOCAL_PORT:$NODE_DOCKER_PORT
ademas que tambien el archivo de nodejs:
import express from "express";
import { createPool } from "mysql2/promise";
import {config} from 'dotenv'
config()
const app = express();
const pool = createPool({
host: process.env.MYSQLDB_HOST,
user: 'root',
password: process.env.MYSQLDB_ROOT_PASSWORD,
port: process.env.MYSQLDB_DOCKER_PORT,
});
pool.on("connection", () => console.log("DB Connected!"));
app.get("/ping", async (req, res) => {
const result = await pool.query("SELECT NOW()");
res.json(result[0]);
});
app.listen(process.env.NODE_DOCKER_PORT || 3000);
console.log("Server on port", process.env.NODE_DOCKER_PORT || 3000);
A partir de aqui puedes visitar la misma ruta /ping
y obtener la misma respuesta. Y listo ya tienes tu proyecto configurado con docker-compose.
Creación de multiples entornos de desarroll
ahora vamos a crear dos entornos para desarrollar y ejecutar en producción:
actualiza el Dockerfile de la siguiente forma:
CMD npm run dev
añade esto al servicio app en docker-compose:
volumes:
- .:/myapp
command: sh -c "npm install && npm run dev"
Luego crearemos un nuevo archivo docker-compose.prod.yml con el siguiente contenido:
version: "3.8"
services:
mysqldb:
image: mysql:5.7
env_file: ./.env
environment:
- MYSQL_USER=$MYSQLDB_USER
- MYSQL_PASSWORD=$MYSQLDB_PASSWORD
- MYSQL_ROOT_PASSWORD=$MYSQLDB_ROOT_PASSWORD
- MYSQL_DATABASE=$MYSQLDB_DATABASE
ports:
- $MYSQLDB_LOCAL_PORT:$MYSQLDB_DOCKER_PORT
logging:
driver: none
app:
depends_on:
- mysqldb
links:
- mysqldb
build: .
# restart: unless-stopped
env_file: ./.env
ports:
- $NODE_LOCAL_PORT:$NODE_DOCKER_PORT
environment:
- DB_HOST=$MYSQLDB_HOST
- DB_USER=$MYSQLDB_USER
- DB_PASSWORD=$MYSQLDB_PASSWORD
- DB_NAME=$MYSQLDB_DATABASE
- DB_PORT=$MYSQLDB_DOCKER_PORT
Ahora debemos usar este comando en el archivo Dockerfile
:
CMD npm start
y puedes ejecutarlo con el comando
docker compose -f docker-compose.prod.yml up --build
Tambien puedes añadir un archivo .dockerignore
con el siguiente contenido:
node_modules
Comandos utiles
Estos son algunos comandos utiles para usar con Docker compose
docker compose up
docker compose up -d
docker compose up --build
docker compose up --build -d
docker compose stop
docker compose rm
docker compose down
docker volume ls
docker volume ls -q
docker volume rm <container_id>
docker volume rm $(docker volume ls -q)
Posibles Errores de Docker Compose con MySQL
- Error de conexion de MySQL, Este es una respuesta de stack overflow a un erro de conexion de mysql desde un Contenedor de docker a un Contenedor de Nodejs, el asunto es que si configuras mal la base de datos inicialmente, y luego lo cambias, este ya creo un volumen con una configuración por defecto, asi que la solucion que menciona aqui es eliminar el volumen, sin embargo puede que no sepas cual es, asi que la solucion es ejecutar el siguiente comando que elimina todos los volumes:
docker volume rm $(docker volume ls -q)
Luego puedes ejecutar tu tipico comandodocker compose up
y deberia funcionar leyendo tu configuracion nueva. Esta solucion fue obtenida de esta respuesta
Más Recursos
- https://millo-l.github.io/Synchronize-docker-compose-nodejs-mysql-execution-order/
- https://learn.microsoft.com/en-us/visualstudio/docker/tutorials/tutorial-multi-container-app-mysql
- Solucion a la conexión de Dbeaver
- https://stackoverflow.com/questions/33663496/docker-bin-bash-nodemon-command-not-found
- Docker Nodejs Debugging