Saltar al contenido principal

Proyecto final de fundamentos

Has utilizado imágenes existentes, creado las tuyas y trabajado por separado con construcción, persistencia, redes, configuración y diagnóstico. Ahora recibirás una aplicación funcional fuera de Docker y tendrás que contenerizarla tomando tus propias decisiones.

No encontrarás un Dockerfile terminado, un .dockerignore resuelto ni una secuencia completa de comandos. El objetivo es poder explicar el sistema que construyas.

El encargo: contenerizar TasteMatch​

Descarga el starter de TasteMatch (ZIP), descomprímelo y abre README-starter.md para comprobar la aplicación fuera de Docker. También encontrarás sus archivos en static/downloads/docker-fundamentos/tastematch-final/ dentro del repositorio.

Recibes este proyecto inicial:

tastematch-final/
├── package.json
├── package-lock.json
├── src/
│ └── server.js
├── data/
│ └── favorites.json
├── test.env
└── README-starter.md

npm ci genera node_modules/ localmente; no se incluye en la descarga. debug.log es un ejemplo de archivo local que podrías generar durante las pruebas, no un requisito del starter.

La aplicación ya funciona fuera de Docker con Node.js 22. Ofrece:

RutaComportamiento
/healthInforma si la aplicación está activa y si alcanza el servicio de caché.
/recipesDevuelve recetas y utiliza Redis como caché.
/favoritesLee favoritos guardados en un directorio de datos.
POST /favoritesAñade un favorito al archivo de datos.

TasteMatch lee estas variables:

VariableNecesidad
PORTOpcional; valor predeterminado 3000.
APP_MODEOpcional; valor predeterminado development.
CACHE_HOSTObligatoria; hostname de Redis.
CACHE_PORTOpcional; valor predeterminado 6379.
DATA_DIRObligatoria; directorio donde conserva favoritos.

No hay credenciales reales. Redis puede ejecutarse con la imagen redis:7-alpine para esta práctica local. No necesitas aprender Redis: la aplicación ya sabe utilizarlo.

Arquitectura objetivo:

navegador del host
│ puerto publicado
▼
TasteMatch app ── red Docker ──→ Redis
│
▼
almacenamiento persistente de favoritos

Fase 0 — Analizar antes de tocar Docker​

Entrega una tabla con estas decisiones:

  • runtime y versión;
  • archivos necesarios para instalar dependencias;
  • archivos necesarios para ejecutar;
  • comando de preparación;
  • comando de inicio;
  • puerto interno;
  • configuración opcional y obligatoria;
  • datos que deben persistir;
  • segundo servicio y puerto;
  • comunicación necesaria;
  • archivos que no deben participar en el build.

Predice también qué se perdería si eliminas el contenedor de TasteMatch sin almacenamiento separado.

No continúes hasta poder dibujar el recorrido desde los archivos del host hasta ambos contenedores.

Fase 1 — Crear la primera imagen​

Escribe un Dockerfile pequeño y razonado. Puedes utilizar las instrucciones estudiadas:

  • FROM;
  • WORKDIR;
  • COPY;
  • RUN;
  • CMD;
  • ENV o EXPOSE solo si expresan una decisión útil.

No es obligatorio utilizar ENTRYPOINT. Cada línea debe responder a una necesidad de la fase 0.

Construye una primera versión con un nombre y tag claros. Comprueba la imagen con docker image ls y explica por qué construirla todavía no inicia la aplicación.

Validación de la fase​

  • el build termina;
  • la imagen tiene nombre y tag;
  • puedes explicar cada instrucción;
  • no hay secretos dentro del Dockerfile;
  • el comando de inicio coincide con package.json.

Fase 2 — Razonar sobre contexto y build​

Revisa todos los archivos iniciales. Crea .dockerignore justificando cada patrón. Entre otros, decide qué hacer con:

  • node_modules/;
  • debug.log;
  • archivos de configuración local;
  • historial de Git, si existe;
  • datos de desarrollo.

No excluyas automáticamente algo que el build necesite.

Organiza el Dockerfile para que los archivos de dependencias y el código puedan cambiar de forma independiente cuando tenga sentido. Realiza:

  1. un build inicial;
  2. otro sin cambios;
  3. otro después de modificar solo un mensaje de src/server.js.

Compara qué trabajo pudo reutilizarse. No tienes que conseguir un Dockerfile «óptimo», sino justificar su orden y comprobar que la aplicación continúa incluida.

Fase 3 — Proporcionar configuración​

Prepara un archivo de ejemplo sin secretos, por ejemplo docker.example.env. Decide valores para APP_MODE, CACHE_HOST, CACHE_PORT y DATA_DIR.

Ejecuta TasteMatch proporcionando configuración en runtime mediante -e o --env-file. No construyas imágenes diferentes para desarrollo y pruebas si solo cambian estos valores.

Demuestra:

  • qué valores predeterminados conserva la aplicación;
  • qué valores proporcionaste;
  • qué sucede si falta una variable obligatoria;
  • dónde aparece la evidencia del fallo.

No presentes el archivo como almacén seguro y no incluyas contraseñas, tokens o claves.

Fase 4 — Persistir favoritos​

DATA_DIR señala el lugar donde TasteMatch escribe favorites.json. Decide entre volumen y bind mount.

Tu demostración debe incluir:

  1. crear el almacenamiento;
  2. montar el destino elegido;
  3. iniciar TasteMatch;
  4. añadir un favorito;
  5. comprobarlo;
  6. eliminar el contenedor de la aplicación;
  7. crear otro contenedor con el mismo almacenamiento;
  8. demostrar que el favorito permanece.

Explica por qué elegiste volumen o bind mount y qué habría ocurrido si el archivo solo estuviera en la zona escribible del primer contenedor.

Fase 5 — Añadir Redis y una red​

Crea manualmente una red para el proyecto. Inicia Redis y TasteMatch en ella.

La aplicación debe recibir:

CACHE_HOST=<nombre del contenedor Redis>
CACHE_PORT=6379

No uses localhost para llegar desde TasteMatch a Redis. Explica:

localhost dentro de TasteMatch → TasteMatch
nombre de Redis en la red → contenedor Redis

Comprueba con /health, logs e inspección de la red que existe comunicación. No dependas de una IP interna escrita manualmente.

No uses Docker Compose: este proyecto demuestra que comprendes y puedes gestionar cada pieza por separado.

Fase 6 — Acceso desde el host​

Publica únicamente el puerto de TasteMatch que necesita el navegador. Redis debe permanecer interno si ningún cliente del host lo necesita.

Dibuja y justifica:

host :PUERTO ──→ TasteMatch :PORT
│
red interna
│
▼
Redis :6379

Comprueba /health, /recipes y /favorites. Distingue siempre el puerto del host del puerto interno.

Fase 7 — Diagnosticar dos incidencias​

Provoca y resuelve al menos dos incidencias. Utiliza dos causas diferentes de esta lista:

  • omitir CACHE_HOST;
  • configurar CACHE_HOST=localhost;
  • publicar hacia un puerto interno incorrecto;
  • excluir accidentalmente src/;
  • montar almacenamiento en una ruta distinta de DATA_DIR;
  • utilizar un destino de solo lectura cuando la aplicación debe escribir;
  • definir un comando principal que termina inmediatamente.

Para cada una entrega:

síntoma
→ evidencia
→ hipótesis
→ causa confirmada
→ un cambio
→ nueva comprobación

La evidencia debe incluir las herramientas adecuadas: estado, logs, inspect, publicaciones, montajes o red. No borres todo ni cambies varias piezas a la vez.

Fase 8 — Limpiar conscientemente​

Haz inventario antes de eliminar:

contenedores
imágenes
redes
volúmenes o rutas del host

Detén y elimina únicamente los contenedores del proyecto. Elimina la red cuando deje de estar en uso. Decide si conservas la imagen.

Antes de eliminar almacenamiento persistente, muestra qué datos contiene y explica qué se perdería. No utilices prune.

Entregables​

Entrega:

  1. Dockerfile comentado en el README, no necesariamente con comentarios dentro del archivo;
  2. .dockerignore y justificación de patrones;
  3. archivo de configuración de ejemplo sin secretos;
  4. comandos mínimos para reproducir build y ejecución;
  5. diagrama de arquitectura;
  6. tabla de puertos internos y publicados;
  7. nombre y miembros de la red;
  8. decisión de persistencia y prueba de recreación;
  9. evidencias de funcionamiento;
  10. dos informes de diagnóstico;
  11. procedimiento de limpieza;
  12. README breve del proyecto.

README del proyecto​

Debe permitir a otra persona reproducir tu trabajo sin convertirse en una transcripción de la terminal. Incluye secciones para:

Construcción​

Contexto, tag y decisiones del Dockerfile.

Ejecución​

Contenedores necesarios y orden lógico, explicando dependencias sin usar Compose.

Configuración​

Variables, valores predeterminados y ejemplo no sensible.

Persistencia​

Origen, destino, mecanismo elegido y prueba de supervivencia.

Red​

Nombre, miembros, hostnames y puertos internos.

Acceso​

URL del host y publicación utilizada.

Limpieza​

Recursos exactos y decisión sobre datos persistentes.

Casos de prueba​

Registra al menos estos casos:

ÁreaComprobación verificable
BuildLa imagen se construye desde un checkout limpio.
EjecuciónTasteMatch permanece running y sus logs muestran el arranque.
ConfiguraciónDos ejecuciones reciben valores distintos sin reconstruir la imagen.
Variable obligatoriaLa ausencia produce un fallo comprensible.
PersistenciaUn favorito sobrevive al reemplazo del contenedor.
RedTasteMatch alcanza Redis por nombre.
Aislamientolocalhost no se usa para el otro contenedor.
PuertosEl host llega a TasteMatch y Redis queda sin publicar.
DiagnósticoCada incidencia conserva evidencia antes de corregirse.

Restricciones​

  • utiliza únicamente conceptos de Docker 0 y Docker 1;
  • no uses Compose, orquestadores ni pipelines;
  • no añadas secretos reales;
  • no uses chmod 777, --no-cache, borrado forzado o reinicio de Docker como soluciones automáticas;
  • no dependas de IPs internas;
  • no elimines recursos ajenos al proyecto;
  • el lenguaje de programación no debe convertirse en la dificultad.

Reflexión final​

Responde con ejemplos de tu proyecto:

  1. ¿Qué parte pertenece a la imagen y cuál a la configuración de ejecución?
  2. ¿Qué cambio de código obligó a reconstruir y qué cambio de configuración no?
  3. ¿Por qué tus datos sobreviven al contenedor?
  4. ¿Por qué TasteMatch llega a Redis por nombre y no mediante localhost?
  5. ¿Qué puerto publicaste y por qué no publicaste Redis?
  6. ¿Qué evidencia fue más útil al diagnosticar cada incidencia?
  7. ¿Qué recurso eliminarías primero y qué comprobarías antes de borrar el almacenamiento?
  8. ¿Qué empieza a resultar incómodo al tener que gestionar manualmente contenedores, red, almacenamiento, variables y puertos?

Esa última incomodidad abre el siguiente recorrido: describir y gestionar conjuntamente una aplicación con varias piezas. Ese será el papel de Docker Compose en Docker 2; aquí no necesitamos utilizarlo para demostrar los fundamentos.