View in English
Node.js, Express, Docker y Docker Swarm
Oct 15, 2025
Updated: Jun 25, 2026

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 24, 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.

Una nota sobre orquestación: Docker Swarm sigue incluido en Docker y funciona perfectamente para aprender, pero hoy está en modo de mantenimiento. En producción, el estándar de orquestación es Kubernetes. Usamos Swarm aquí porque es la forma más sencilla de entender los conceptos de réplicas, balanceo y servicios sin la complejidad de Kubernetes. Una vez que domines estas ideas, dar el salto a Kubernetes será mucho más natural.

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:

  • GET /quotes devuelve todas las frases.
  • GET /quotes/random devuelve una frase aleatoria.
  • GET /quotes/:id devuelve la frase con el identificador indicado.

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

4. Requisitos previos

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.).

Software instalado

  • Node.js 24.x o el LTS activo vigente (descargar desde https://nodejs.org).
  • Docker Desktop (macOS/Windows) o Docker Engine 26+ (Linux).
  • Editor de código (VS Code, WebStorm, etc.).

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.

Verificación rápida

node --version    # debe devolver v24.x.x (o el LTS que tengas instalado)
npm --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": ">=22.0.0"
  },
  "dependencies": {
    "express": "^5.1.0"
  },
  "devDependencies": {
    "nodemon": "^3.1.10"
  }
}

Nota pedagógica: definir los scripts desde el inicio te ayuda a ejecutar tareas repetitivas sin memorizar comandos largos.

7. Paso 2, implementar la API con Express

Tabla de endpoints de la API de quotes (GET /, /quotes, /quotes/random, /quotes/:id)

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) || 3000
const app = createApp()

app.listen(PORT, () => {
  console.log(`Servicio Random Quotes escuchando en el puerto ${PORT}`)
})

8. Paso 3, pruebas locales

Ejecución en modo desarrollo (recarga automática):

npm run dev

Ejecución de producción (sin nodemon):

npm start

Verifica con el navegador o con curl:

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

Atajos didácticos:

  • Detén 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

  1. Crea un archivo .dockerignore para que la imagen sea más ligera.
  2. Añade un .gitignore si aún no lo tienes (evita subir node_modules).
  3. 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:24-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:24-alpine es liviana y compatible con Linux, ideal para producción. Si necesitas otra línea LTS, puedes usar node:22-alpine.
  • npm ci instala exactamente las versiones definidas en package-lock.json y, con --omit=dev, omite las dependencias de desarrollo (este flag reemplaza al antiguo --only=production, ya obsoleto).
  • 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
Terminal ejecutando el contenedor random-quotes con docker run, logs y una petición curl

Importante: si estás en macOS/Windows con Docker Desktop, el puerto se publica en tu máquina host automáticamente. En Linux, asegúrate de no tener el puerto 3000 ocupado.

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 y publícala:
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

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.

Recuerda lo que dijimos al inicio: Swarm está en modo de mantenimiento y en producción real lo habitual es Kubernetes. Aun así, sigue siendo la forma más clara de aprender estos conceptos.

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>

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

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

Consejo: si actualizas la imagen, usa docker service update --image tuusuario/random-quotes:1.0.1 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

  • Agregar una ruta POST para que la API acepte nuevas frases en memoria.
  • Persistencia: guardar las frases en un archivo JSON o en una base de datos ligera (por ejemplo, SQLite).
  • Testing: configurar Jest o Vitest y escribir pruebas para los endpoints.
  • CI/CD: crear un flujo en GitHub Actions que construya y publique la imagen automáticamente.
  • 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 GitHub:

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

Eso es todo, espero que esta guía te sea de utilidad y la puedas aplicar a algún proyecto que tengas en mente, o que simplemente te haya ayudado a entender cómo encajan Node.js, Express, Docker y Docker Swarm. Déjame un comentario si te sirvió, si quieres añadir alguna opinión o si tienes alguna duda. Y recuerda que si te gustó, también puedes compartirlo usando los links a las redes sociales aquí abajo. Buena suerte.

Sebastian Gomez

Sebastian Gomez

Creador de contenido principalmente acerca de tecnología.

Leave a Reply

0 Comments

Advertisements

Related Posts

Categorias