Node.js, Express, Docker y Docker Swarm
Oct 15, 2025

Node.js, Express, Docker y Docker Swarm

Node.js, Express, Docker y Docker Swarm

Esta guía te acompaña paso a paso en la creación de una API de frases motivacionales con Node.js y Express, su empaquetado en Docker y el despliegue en un clúster Docker Swarm. Está pensada para trabajarla en clase, pero también sirve como laboratorio autónomo.

1. Panorama general
  • Producto final: Un servicio HTTP que expone frases en formato JSON, empaquetado en una imagen Docker lista para producción y desplegada en un Swarm con múltiples réplicas.
  • Tecnologías clave: Node.js 20, Express 5, npm, Docker, Docker Hub y Docker Swarm.
  • Duración estimada: 3 bloques de 60 minutos (puedes adaptarlo según el ritmo del grupo).
  • Enfoque pedagógico: primero entendemos los conceptos, luego construimos manualmente, y al final automatizamos con contenedores y orquestación.
2. Objetivos de aprendizaje
  • Comprender por qué Express es un framework minimalista y cómo estructurar un proyecto desde cero.
  • Practicar la creación de endpoints HTTP que devuelvan JSON y gestionen errores simples.
  • Diferenciar dependencias de producción y desarrollo en package.json.
  • Construir una imagen Docker reproducible, aplicando buenas prácticas como .dockerignore y npm ci.
  • Publicar imágenes en Docker Hub y gestionar tags.
  • Desplegar y escalar una aplicación en Docker Swarm, validando que las réplicas atienden peticiones.
3. Lo que vas a construir

La API tendrá tres rutas principales:

Screenshot 2025-10-14 at 8.39.37 PM.jpg

La imagen Docker expondrá el servicio en el puerto 3000 y estará optimizada para ejecución en ARM/AMD gracias a la base node:20-alpine. Finalmente, el servicio se orquestará en Swarm con réplicas distribuidas.

4. Requisitos previos
  1. Conocimientos previos
  • Fundamentos de JavaScript moderno.
  • Manejo básico de la terminal y Git.
  • Conceptos básicos de contenedores (qué es una imagen, un contenedor, etc.).
  1. Software instalado
  • Node.js 20.x (descargar desde https://nodejs.org).
  • Docker Desktop (macOS/Windows) o Docker Engine 26+ (Linux).
  • Editor de código (VS Code, WebStorm, etc.).
  1. Cuentas y accesos
  • Cuenta en Docker Hub (gratuita) si quieres publicar la imagen.
  • Acceso a una nube o a máquinas virtuales (Amazon EC2, Azure, etc.) para la sección de Swarm.
  1. Verificación rápida

node  --version  # debe devolver v20.x.xnpm  --version  # npm es parte de Node, versión >=10 recomendada

docker  --version  # Docker Engine >= 26

docker  compose  version  # opcional, recomendado

Si algún comando falla, detente y corrige la instalación antes de avanzar.

5. Preparación del entorno
  1. Crea una carpeta de trabajo donde guardarás todo el material del tutorial.
  2. Abre tu terminal y ubícate dentro de esa carpeta.
  3. (Opcional) Inicializa un repositorio Git y haz commit por secciones para registrar tu progreso.
6. Paso 1 – Inicializar el proyecto Node.js
mkdir  random-quotes

cd  random-quotes

npm  init  -y

npm  install  express

npm  install  --save-dev  nodemon

¿Qué sucedió?

  • npm init -y genera un package.json con valores por defecto.
  • express se instala como dependencia de producción.
  • nodemon se agrega como dependencia de desarrollo para recargar automáticamente en modo estudio.

Ajusta package.json

Edita el archivo package.json para que contenga metadatos y scripts útiles:

{
  "name": "random-quotes",
  "version": "1.0.0",
  "description": "Educational Express service that serves random quotes and demonstrates Docker & Docker Swarm deployment.",
  "main": "src/server.js",
  "scripts": {
    "start": "node src/server.js",
    "dev": "nodemon src/server.js",
    "lint": "echo \"No linter configured\" && exit 0",
    "test": "echo \"No tests yet\" && exit 0"
  },
  "keywords": [
    "express",
    "docker",
    "tutorial"
  ],
  "author": "Sebastian Gomez <seagomezar@gmail.com> (http://www.sebasgo.dev/)",
  "license": "ISC",
  "engines": {
    "node": ">=20.0.0"
  },
  "dependencies": {
    "express": "^5.1.0"
  },
  "devDependencies": {
    "nodemon": "^3.1.10"
  }
}

7. Paso 2 – Implementar la API con Express

Creamos una estructura simple dentro de src/ para separar responsabilidades.

7.1 Archivo src/quotes.js

Contiene los datos y una función que selecciona un elemento aleatorio. Es una forma sencilla (sin base de datos) de tener contenido.

const quotes = [{
    id: 1,
    text: 'The best way to predict the future is to invent it.',
    author: 'Alan Kay'},{
    id: 2,
    text: 'Simplicity is the soul of efficiency.',
    author: 'Austin Freeman'},{
    id: 3,
    text: 'Continuous improvement is better than delayed perfection.',
    author: 'Mark Twain'},{
    id: 4,
    text: 'Programs must be written for people to read, and only incidentally for machines to execute.',
    author: 'Harold Abelson'},{
    id: 5,
    text: 'If you automate a mess, you get an automated mess.',
    author: 'Rod Michael'},{
    id: 6,
    text: 'First, solve the problem. Then, write the code.',
    author: 'John Johnson'},{
    id: 7,
    text: 'Any fool can write code that a computer can understand. Good programmers write code that humans can understand.',
    author: 'Martin Fowler'},{
    id: 8,
    text: 'Talk is cheap. Show me the code.',
    author: 'Linus Torvalds'},{
    id: 9,
    text: 'Experience is the name everyone gives to their mistakes.',
    author: 'Oscar Wilde'},{
    id: 10,
    text: 'Success is the ability to go from one failure to another with no loss of enthusiasm.',
    author: 'Winston Churchill'}]function getRandomQuote () {const index = Math.floor(Math.random() * quotes.length)return quotes[index]}

module.exports = {
  quotes,
  getRandomQuote
}

7.2 Archivo src/app.js

Aquí configuramos Express, definimos middlewares y rutas.

const express = require('express')const { quotes, getRandomQuote } = require('./quotes')function createApp () {const app = express()

  app.use(express.json())

  app.get('/', (req, res) => {
    res.json({
      status: 'ok',
      message: 'Welcome to the Random Quotes API',
      endpoints: {
        random: '/quotes/random',
        all: '/quotes',
        byId: '/quotes/:id'}})})

  app.get('/quotes', (req, res) => {
    res.json({
      count: quotes.length,
      data: quotes
    })})

  app.get('/quotes/random', (req, res) => {
    res.json(getRandomQuote())})

  app.get('/quotes/:id', (req, res) => {const id = Number(req.params.id)const quote = quotes.find(entry => entry.id === id)if (!quote) {return res.status(404).json({
        error: `Quote with id ${id} not found`})}return res.json(quote)})return app
}

module.exports = { createApp }

7.3 Archivo src/server.js

Punto de entrada del servicio: crea la app y la expone en un puerto configurable.

const { createApp } = require('./app')const  PORT = Number(process.env.PORT) || 3000const  app = createApp()

app.listen(PORT, () => {
	console.log(`Servicio Random Quotes escuchando en el puerto ${PORT}`)})
8. Paso 3 – Pruebas locales
  1. Ejecución en modo desarrollo (recarga automática):
npm run dev

  1. Ejecución “de producción” (sin nodemon):
npm start

  1. Verifica con el navegador o con curl:
curl http://localhost:3000/

curl http://localhost:3000/quotes/random

curl http://localhost:3000/quotes/3

docker-logs.png

Atajos didácticos:

  • Manda detener el servidor con Ctrl + C.
  • Usa herramientas como Postman o Insomnia para reforzar conceptos de APIs.
  • Intenta una petición a un ID inexistente (/quotes/999) para observar el manejo de errores.
9. Paso 4 – Buenas prácticas previas a Docker
  • Crea un archivo .dockerignore para que la imagen sea más ligera.
  • Añade un .gitignore si aún no lo tienes (evita subir node_modules).
  • Documenta en el README los scripts que definiste para que el equipo los recuerde.

Contenido recomendado para .dockerignore:


node_modules

npm-debug.log

.npmrc

.DS_Store
10. Paso 5 – Construcción de la imagen Docker

10.1 Dockerfile explicado

FROM node:20-alpine AS base

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm ci --omit=dev

COPY . .

ENV NODE_ENV=production

EXPOSE 3000

CMD ["node", "src/server.js"]

Puntos clave:

  • node:20-alpine es liviana y compatible con Linux, ideal para producción.
  • npm ci instala exactamente las versiones definidas en package-lock.json y omite las dependencias de desarrollo.
  • EXPOSE 3000 documenta el puerto, aunque la publicación real se hace con docker run -p.
  • No necesitamos copiar node_modules porque se instalan dentro del contenedor.

10.2 Construir y probar la imagen

docker  build  -t  random-quotes:local  .

docker  images | grep  random-quotes

Ejecuta un contenedor de prueba:

docker  run  --rm  -d  -p  3000:3000  --name  random-quotes-test  random-quotes:local

docker  logs  random-quotes-test  # Verifica que el servidor arrancó

curl  http://localhost:3000/quotes/random

docker  stop  random-quotes-test  # Limpia el contenedor

11. Paso 6 – Publicar en Docker Hub
  1. Crea un repositorio público en Docker Hub (por ejemplo tuusuario/random-quotes).
  2. Inicia sesión desde la terminal:
docker login
  1. Etiqueta la imagen local con tu repositorio:
docker tag random-quotes:local tuusuario/random-quotes:1.0.0

docker push tuusuario/random-quotes:1.0.0

docker tag random-quotes:local tuusuario/random-quotes:latest

docker push tuusuario/random-quotes:latest
  1. Confirma desde la interfaz web de Docker Hub que la imagen se subió correctamente.
12. Paso 7 – Introducción a Docker Swarm

Docker Swarm es el orquestador nativo de Docker. Permite:

  • Crear clústeres con un nodo manager (orquesta) y múltiples nodos worker (ejecutan contenedores).
  • Definir servicios con múltiples réplicas distribuidas.
  • Balancear carga y reiniciar contenedores automáticamente si fallan.

12.1 Preparar el clúster

  1. Asegúrate de tener al menos una máquina (manager). Lo ideal son tres nodos (1 manager, 2 workers) para experimentar.
  2. Instala Docker en cada máquina (consulta la documentación oficial de tu distro).
  3. Inicializa Swarm en el manager:
docker swarm init --advertise-addr <IP-del-manager>
  1. Copia el comando que se imprime para unir workers y ejecútalo en cada máquina worker:
docker swarm join --token <token> <IP-del-manager>:2377
  1. Verifica desde el manager:
docker node ls

12.2 Crear una red overlay (opcional pero recomendado)

docker  network  create  --driver  overlay  random-quotes-net

12.3 Desplegar el servicio

docker  service  create  \

--name random-quotes-service \

--replicas  3  \

--publish published=3001,target=3000 \

--network  random-quotes-net  \

tuusuario/random-quotes:latest

12.4 Supervisar el servicio

docker  service  ls

docker  service  ps  random-quotes-service

docker  service  logs  random-quotes-service

13. Paso 8 – Validar el despliegue en Swarm
  1. Obtén la IP pública del nodo manager (o del balanceador si usas uno).
  2. Abre http://<IP-publica>:3001/quotes/random en tu navegador o con curl.
  3. Actualiza varias veces la página para comprobar que las respuestas varían (las réplicas se reparten las solicitudes).
  4. Si tienes varias máquinas, lanza peticiones desde cada una para simular tráfico distribuido.

Cuando termines:

docker  service  rm  random-quotes-service

docker  network  rm  random-quotes-net  # si la creaste

docker  swarm  leave  --force  # ejecutar en cada nodo, el manager al final
14. Solución de problemas habituales
  • El puerto 3000 está ocupado: Cambia la variable de entorno PORT (PORT=4000 npm start) o detén el proceso que lo usa.
  • npm install falla en Docker: Asegúrate de copiar package-lock.json y de no editarlo manualmente.
  • docker build muy lento: Verifica tu conexión; las imágenes base pueden pesar decenas de MB.
  • El servicio en Swarm no arranca: Revisa docker service logs. Si la imagen no existe, puede que el worker no tenga acceso a Internet o que el nombre esté mal escrito.
  • Workers no se unen al clúster: Abre los puertos 2377 (control), 7946 TCP/UDP (comunicación entre nodos) y 4789 UDP (red overlay) en el firewall.
15. Retos adicionales
  1. Agregar una ruta POST para que la API acepte nuevas frases en memoria.
  2. Persistencia: guardar las frases en un archivo JSON o en una base de datos ligera (por ejemplo, SQLite).
  3. Testing: configurar Jest o Vitest y escribir pruebas para los endpoints.
  4. CI/CD: crear un flujo en GitHub Actions que construya y publique la imagen automáticamente.
  5. Observabilidad: integrar un middleware de logging como morgan o métricas con Prometheus.
16. Lista de verificación final
  • npm run dev funciona y muestra logs en consola.
  • Los endpoints devuelven JSON correctamente y gestionan IDs inválidos.
  • La imagen Docker se construye sin errores y el contenedor responde en el puerto 3000.
  • La imagen está publicada en Docker Hub con al menos un tag semántico.
  • El servicio se despliega en Swarm con varias réplicas y balancea carga.
  • Documentaste todos los comandos importantes en tu README o bitácora.
17. Glosario rápido
  • Node.js: entorno de ejecución JavaScript del lado del servidor basado en V8.
  • Express: framework minimalista para construir APIs y aplicaciones web en Node.
  • npm: gestor de paquetes que instala dependencias desde el registro oficial.
  • Docker Image: plantilla inmutable con todo lo necesario para ejecutar una aplicación.
  • Docker Container: instancia en ejecución de una imagen.
  • Docker Swarm: modo de clustering y orquestación nativo de Docker.
  • Replica: cada copia de un contenedor dentro de un servicio de Swarm.
  • Endpoint: URL expuesta por un servicio que responde a peticiones HTTP.
18. Estructura de referencia del repositorio

Repositorio de GithubE

├── README.md
└── random-quotes
├── Dockerfile
├── package.json
├── package-lock.json
├── src
│ ├── app.js
│ ├── quotes.js
│ └── server.js
└── .dockerignore

Sebastian Gomez

Sebastian Gomez

Creador de contenido principalmente acerca de tecnología.

Leave a Reply

0 Comments

Advertisements

Related Posts

Categorias