Sebastian Gomez
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 generalLa API tendrá tres rutas principales:
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
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 entornomkdir random-quotes
cd random-quotes
npm init -y
npm install express
npm install --save-dev nodemon
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 ExpressCreamos una estructura simple dentro de src/ para separar responsabilidades.
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
}
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 }
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 localesnpm run dev
npm start
curl http://localhost:3000/
curl http://localhost:3000/quotes/random
curl http://localhost:3000/quotes/3
Atajos didácticos:
Contenido recomendado para .dockerignore:
node_modules
npm-debug.log
.npmrc
.DS_Store
10. Paso 5 – Construcción de la imagen DockerFROM 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:
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 Hubdocker login
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
Docker Swarm es el orquestador nativo de Docker. Permite:
docker swarm init --advertise-addr <IP-del-manager>
docker swarm join --token <token> <IP-del-manager>:2377
docker node ls
docker network create --driver overlay random-quotes-net
docker service create \
--name random-quotes-service \
--replicas 3 \
--publish published=3001,target=3000 \
--network random-quotes-net \
tuusuario/random-quotes:latest
docker service ls
docker service ps random-quotes-service
docker service logs random-quotes-service
13. Paso 8 – Validar el despliegue en SwarmCuando 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 habitualesRepositorio de GithubE
├── README.md
└── random-quotes
├── Dockerfile
├── package.json
├── package-lock.json
├── src
│ ├── app.js
│ ├── quotes.js
│ └── server.js
└── .dockerignore
Creador de contenido principalmente acerca de tecnología.