Utiliza contenedores para el desarrollo con Node.js
Requisitos previos
Completa la sección de Contenerizar una aplicación Node.js.
Descripción general
En esta sección, aprenderás a configurar un entorno de desarrollo para tu aplicación contenerizada. Esto incluye:
- Agregar una base de datos local y persistir los datos.
- Configurar tu contenedor para ejecutar un entorno de desarrollo.
- Depurar tu aplicación contenerizada.
Agrega una base de datos local y persiste los datos
La aplicación utiliza PostgreSQL para la persistencia de datos. Agrega un servicio de base de datos a tu configuración de Docker Compose.
Agrega el servicio de base de datos a Docker Compose
Si aún no has creado un archivo compose.yml en la sección anterior, o si necesitas agregar el servicio de base de datos, update tu archivo compose.yml para incluir el servicio de la base de datos PostgreSQL:
services:
# ... servicios de aplicación existentes ...
# ========================================
# Servicio de base de datos PostgreSQL
# ========================================
db:
image: postgres:18-alpine
container_name: todoapp-db
environment:
POSTGRES_DB: '${POSTGRES_DB:-todoapp}'
POSTGRES_USER: '${POSTGRES_USER:-todoapp}'
POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}'
volumes:
- postgres_data:/var/lib/postgresql
ports:
- '${DB_PORT:-5432}:5432'
restart: unless-stopped
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER:-todoapp} -d ${POSTGRES_DB:-todoapp}']
interval: 10s
timeout: 5s
retries: 5
start_period: 5s
networks:
- todoapp-network
# ========================================
# Configuración de volúmenes
# ========================================
volumes:
postgres_data:
name: todoapp-postgres-data
driver: local
# ========================================
# Configuración de red
# ========================================
networks:
todoapp-network:
name: todoapp-network
driver: bridgeActualiza el servicio de tu aplicación
Asegúrate de que el servicio de tu aplicación en compose.yml esté configurado para conectarse a la base de datos:
services:
app-dev:
build:
context: .
dockerfile: Dockerfile
target: development
container_name: todoapp-dev
ports:
- '${APP_PORT:-3000}:3000' # Servidor API
- '${VITE_PORT:-5173}:5173' # Servidor de desarrollo de Vite
- '${DEBUG_PORT:-9229}:9229' # Depurador de Node.js
environment:
NODE_ENV: development
DOCKER_ENV: 'true'
POSTGRES_HOST: db
POSTGRES_PORT: 5432
POSTGRES_DB: todoapp
POSTGRES_USER: todoapp
POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}'
ALLOWED_ORIGINS: '${ALLOWED_ORIGINS:-http://localhost:3000,http://localhost:5173}'
volumes:
- ./src:/app/src:ro
- ./package.json:/app/package.json
- ./vite.config.ts:/app/vite.config.ts:ro
- ./tailwind.config.js:/app/tailwind.config.js:ro
- ./postcss.config.js:/app/postcss.config.js:ro
depends_on:
db:
condition: service_healthy
develop:
watch:
- action: sync
path: ./src
target: /app/src
ignore:
- '**/*.test.*'
- '**/__tests__/**'
- action: rebuild
path: ./package.json
- action: sync
path: ./vite.config.ts
target: /app/vite.config.ts
- action: sync
path: ./tailwind.config.js
target: /app/tailwind.config.js
- action: sync
path: ./postcss.config.js
target: /app/postcss.config.js
restart: unless-stopped
networks:
- todoapp-network
db:
image: postgres:18-alpine
container_name: todoapp-db
environment:
POSTGRES_DB: '${POSTGRES_DB:-todoapp}'
POSTGRES_USER: '${POSTGRES_USER:-todoapp}'
POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}'
volumes:
- postgres_data:/var/lib/postgresql
ports:
- '${DB_PORT:-5432}:5432'
restart: unless-stopped
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER:-todoapp} -d ${POSTGRES_DB:-todoapp}']
interval: 10s
timeout: 5s
retries: 5
start_period: 5s
networks:
- todoapp-network
volumes:
postgres_data:
name: todoapp-postgres-data
driver: local
networks:
todoapp-network:
name: todoapp-network
driver: bridgeLa configuración de la base de datos PostgreSQL se gestiona automáticamente por la aplicación. La base de datos se crea e inicializa cuando la aplicación se inicia, y los datos persisten mediante el volumen
postgres_data.Configura tu entorno copiando el archivo de ejemplo:
$ cp .env.example .envActualiza el archivo
.envcon tus preferencias:# Configuración de la aplicación NODE_ENV=development APP_PORT=3000 VITE_PORT=5173 DEBUG_PORT=9230 # Configuración de la base de datos POSTGRES_HOST=db POSTGRES_PORT=5432 POSTGRES_DB=todoapp POSTGRES_USER=todoapp POSTGRES_PASSWORD=todoapp_password # Configuración de seguridad ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173Ejecuta el siguiente comando para iniciar tu aplicación en modo de desarrollo:
$ docker compose up app-dev --buildAbre un navegador y verifica que la aplicación se esté ejecutando en http://localhost:5173 para el frontend o en http://localhost:3000 para la API. El frontend de React es servido por el servidor de desarrollo de Vite en el puerto 5173, y las llamadas a la API se reenvían al servidor Express en el puerto 3000.
Agrega algunos elementos a la lista de tareas para probar la persistencia de datos.
Después de agregar algunos elementos a la lista de tareas, presiona
CTRL + Cen la terminal para detener la aplicación.Ejecuta la aplicación de nuevo:
$ docker compose up app-devActualiza http://localhost:5173 en tu navegador y verifica que los elementos de la lista de tareas persisten, incluso después de que los contenedores hayan sido eliminados y ejecutados de nuevo.
Configura y ejecuta un contenedor de desarrollo
Puedes utilizar un montaje de tipo bind (bind mount) para montar tu código fuente en el contenedor. De este modo, el contenedor podrá ver los cambios que realices en el código de forma inmediata, en cuanto guardes un archivo. Esto significa que puedes ejecutar procesos, como nodemon, dentro del contenedor para vigilar los cambios en el sistema de archivos y responder a ellos. Para conocer más sobre los montajes de tipo bind, consulta la
descripción general de almacenamiento.
Además de agregar un montaje bind, puedes configurar tu Dockerfile y tu archivo compose.yaml para instalar dependencias de desarrollo y ejecutar herramientas de desarrollo.
Actualiza tu Dockerfile para el desarrollo
Tu Dockerfile debe estar configurado como una construcción multi-etapa con etapas independientes para desarrollo, producción y pruebas. Si seguiste la sección anterior, tu Dockerfile ya incluye una etapa de desarrollo que cuenta con todas las dependencias de desarrollo y ejecuta la aplicación con la recarga en caliente habilitada.
Esta es la etapa de desarrollo de tu Dockerfile multi-etapa:
# ========================================
# Etapa de desarrollo
# ========================================
FROM build-deps AS development
# Establecer el entorno
ENV NODE_ENV=development \
NPM_CONFIG_LOGLEVEL=warn
# Copiar archivos fuente
COPY . .
# Asegurar que todos los directorios tengan los permisos adecuados
RUN mkdir -p /app/node_modules/.vite && \
chown -R nodejs:nodejs /app && \
chmod -R 755 /app
# Cambiar a usuario no root
USER nodejs
# Exponer puertos
EXPOSE 3000 5173 9229
# Iniciar el servidor de desarrollo
CMD ["npm", "run", "dev:docker"]La etapa de desarrollo:
- Instala todas las dependencias, incluidas las de desarrollo (dev dependencies).
- Expone puertos para el servidor API (3000), el servidor de desarrollo de Vite (5173) y el depurador de Node.js (9229).
- Ejecuta
npm run dev, que inicia tanto el servidor Express como el servidor de desarrollo de Vite de forma concurrente. - Incluye comprobaciones de estado para monitorear el estado del contenedor.
A continuación, tendrás que actualizar tu archivo de Compose para utilizar esta nueva etapa.
Actualiza tu archivo de Compose para el desarrollo
Actualiza tu archivo compose.yml para ejecutar la etapa de desarrollo con montajes bind para la recarga en caliente:
services:
app-dev:
build:
context: .
dockerfile: Dockerfile
target: development
container_name: todoapp-dev
ports:
- '${APP_PORT:-3000}:3000' # Servidor API
- '${VITE_PORT:-5173}:5173' # Servidor de desarrollo de Vite
- '${DEBUG_PORT:-9229}:9229' # Depurador de Node.js
environment:
NODE_ENV: development
DOCKER_ENV: 'true'
POSTGRES_HOST: db
POSTGRES_PORT: 5432
POSTGRES_DB: todoapp
POSTGRES_USER: todoapp
POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}'
ALLOWED_ORIGINS: '${ALLOWED_ORIGINS:-http://localhost:3000,http://localhost:5173}'
volumes:
- ./src:/app/src:ro
- ./package.json:/app/package.json
- ./vite.config.ts:/app/vite.config.ts:ro
- ./tailwind.config.js:/app/tailwind.config.js:ro
- ./postcss.config.js:/app/postcss.config.js:ro
depends_on:
db:
condition: service_healthy
develop:
watch:
- action: sync
path: ./src
target: /app/src
ignore:
- '**/*.test.*'
- '**/__tests__/**'
- action: rebuild
path: ./package.json
- action: sync
path: ./vite.config.ts
target: /app/vite.config.ts
- action: sync
path: ./tailwind.config.js
target: /app/tailwind.config.js
- action: sync
path: ./postcss.config.js
target: /app/postcss.config.js
restart: unless-stopped
networks:
- todoapp-networkCaracterísticas clave de la configuración de desarrollo:
- Exposición multipuerto: Servidor API (3000), servidor de desarrollo de Vite (5173) y depurador (9229).
- Montajes bind completos: Código fuente, archivos de configuración y archivos de paquete para recarga en caliente.
- Variables de entorno: Configurables a través del archivo
.envo valores por defecto. - Base de datos PostgreSQL: Base de datos lista para producción con almacenamiento persistente.
- Docker Compose watch: Sincronización automática de archivos y reconstrucción de contenedores.
- Comprobaciones de estado: Monitoreo del estado de salud de la base de datos con gestión automática de dependencias.
Ejecuta tu contenedor de desarrollo y depura tu aplicación
Ejecuta el siguiente comando para iniciar tu aplicación con la configuración de desarrollo:
$ docker compose up app-dev --build
O bien, con observación de archivos para actualizaciones automáticas:
$ docker compose up app-dev --watch
Para el desarrollo local sin Docker:
$ npm run dev:with-db
O inicia los servicios por separado:
$ npm run db:start # Iniciar el contenedor de PostgreSQL
$ npm run dev # Iniciar tanto el servidor como el cliente
Uso de Task Runner (alternativa)
El proyecto incluye un archivo Taskfile.yml para flujos de trabajo avanzados:
# Desarrollo
$ task dev # Iniciar el entorno de desarrollo
$ task dev:build # Construir la imagen de desarrollo
$ task dev:run # Ejecutar el contenedor de desarrollo
# Producción
$ task build # Construir la imagen de producción
$ task run # Ejecutar el contenedor de producción
$ task build-run # Construir y ejecutar en un solo paso
# Pruebas
$ task test # Ejecutar todas las pruebas
$ task test:unit # Ejecutar pruebas unitarias con cobertura
$ task test:lint # Ejecutar análisis estático (linting)
# Kubernetes
$ task k8s:deploy # Desplegar en Kubernetes
$ task k8s:status # Comprobar el estado del despliegue
$ task k8s:logs # Ver los logs de los pods
# Utilidades
$ task clean # Limpiar contenedores e imágenes
$ task health # Comprobar el estado de salud de la aplicación
$ task logs # Ver los logs del contenedor
La aplicación se iniciará con el servidor API Express y el servidor de desarrollo de Vite:
- Servidor API: http://localhost:3000 - Backend de Express.js con API REST
- Frontend: http://localhost:5173 - Servidor de desarrollo de Vite con reemplazo de módulos en caliente (hot module replacement)
- Comprobación de estado (Health Check): http://localhost:3000/health - Estado de salud de la aplicación
Cualquier cambio en los archivos fuente de la aplicación en tu máquina local se reflejará inmediatamente en el contenedor en ejecución gracias a los montajes bind.
Intenta realizar un cambio para probar la recarga en caliente:
Abre
src/client/components/TodoApp.tsxen un IDE o editor de texto.Actualiza el texto del encabezado principal:
- <h1 className="text-3xl font-bold text-gray-900 mb-8"> - Modern Todo App - </h1> + <h1 className="text-3xl font-bold text-gray-900 mb-8"> + My Todo App + </h1>Guarda el archivo y el servidor de desarrollo de Vite recargará la página automáticamente con tus cambios.
Soporte de depuración:
Puedes conectar un depurador a tu aplicación en el puerto 9229. El inspector de Node.js está habilitado con --inspect=0.0.0.0:9230 en el script de desarrollo (dev:server).
Configuración del depurador en VS Code
Crea una configuración de lanzamiento en
.vscode/launch.json:{ "version": "0.2.0", "configurations": [ { "name": "Adjuntar al contenedor Docker", "type": "node", "request": "attach", "port": 9229, "address": "localhost", "localRoot": "${workspaceFolder}", "remoteRoot": "/app", "protocol": "inspector", "restart": true, "sourceMaps": true, "skipFiles": ["<node_internals>/**"] } ] }Inicia tu contenedor de desarrollo:
docker compose up app-dev --buildAdjunta el depurador:
- Abre VS Code.
- En el panel de depuración (Ctrl/Cmd + Shift + D), selecciona Adjuntar al contenedor Docker en el menú desplegable.
- Selecciona el botón verde de reproducción o presiona F5.
Chrome DevTools (alternativa)
También puedes usar Chrome DevTools para la depuración:
Inicia tu contenedor (si aún no se está ejecutando):
docker compose up app-dev --buildAbre Chrome y ve a
chrome://inspect.En la opción Configure (Configurar), agrega:
localhost:9229Cuando aparezca tu objetivo (target) de Node.js, selecciona inspect.
Detalles de la configuración de depuración
La configuración de depuración:
- Puerto del contenedor: 9230 (puerto interno del depurador)
- Puerto del host: 9229 (puerto externo mapeado)
- Script:
tsx watch --inspect=0.0.0.0:9230 src/server/index.ts
El depurador escucha en todas las interfaces (0.0.0.0) dentro del contenedor en el puerto 9230 y es accesible en el puerto 9229 desde tu máquina host.
Solución de problemas de conexión del depurador
Si el depurador no se conecta:
Comprueba si el contenedor se está ejecutando:
docker psComprueba si el puerto está expuesto:
docker port todoapp-devComprueba los logs del contenedor:
docker compose logs app-devDeberías ver un mensaje como:
Debugger listening on ws://0.0.0.0:9230/...
Ahora puedes establecer puntos de interrupción (breakpoints) en tus archivos fuente de TypeScript y depurar tu aplicación Node.js contenerizada.
Para obtener más detalles sobre la depuración en Node.js, consulta la documentación oficial de Node.js.
Resumen
Has configurado tu archivo de Compose con una base de datos PostgreSQL y persistencia de datos. También creaste un Dockerfile multi-etapa y configuraste montajes bind para el desarrollo.
Información relacionada:
- Elemento de nivel superior volumes en Compose
- Elemento de nivel superior services en Compose
- Construcciones multi-etapa
Próximos pasos
En la siguiente sección, aprenderás cómo ejecutar pruebas unitarias utilizando Docker.