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:
| Ruta | Comportamiento |
|---|---|
/health | Informa si la aplicación está activa y si alcanza el servicio de caché. |
/recipes | Devuelve recetas y utiliza Redis como caché. |
/favorites | Lee favoritos guardados en un directorio de datos. |
POST /favorites | Añade un favorito al archivo de datos. |
TasteMatch lee estas variables:
| Variable | Necesidad |
|---|---|
PORT | Opcional; valor predeterminado 3000. |
APP_MODE | Opcional; valor predeterminado development. |
CACHE_HOST | Obligatoria; hostname de Redis. |
CACHE_PORT | Opcional; valor predeterminado 6379. |
DATA_DIR | Obligatoria; 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;ENVoEXPOSEsolo 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:
- un build inicial;
- otro sin cambios;
- 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:
- crear el almacenamiento;
- montar el destino elegido;
- iniciar TasteMatch;
- añadir un favorito;
- comprobarlo;
- eliminar el contenedor de la aplicación;
- crear otro contenedor con el mismo almacenamiento;
- 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:
- Dockerfile comentado en el README, no necesariamente con comentarios dentro del archivo;
.dockerignorey justificación de patrones;- archivo de configuración de ejemplo sin secretos;
- comandos mínimos para reproducir build y ejecución;
- diagrama de arquitectura;
- tabla de puertos internos y publicados;
- nombre y miembros de la red;
- decisión de persistencia y prueba de recreación;
- evidencias de funcionamiento;
- dos informes de diagnóstico;
- procedimiento de limpieza;
- 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:
| Área | Comprobación verificable |
|---|---|
| Build | La imagen se construye desde un checkout limpio. |
| Ejecución | TasteMatch permanece running y sus logs muestran el arranque. |
| Configuración | Dos ejecuciones reciben valores distintos sin reconstruir la imagen. |
| Variable obligatoria | La ausencia produce un fallo comprensible. |
| Persistencia | Un favorito sobrevive al reemplazo del contenedor. |
| Red | TasteMatch alcanza Redis por nombre. |
| Aislamiento | localhost no se usa para el otro contenedor. |
| Puertos | El host llega a TasteMatch y Redis queda sin publicar. |
| Diagnóstico | Cada 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:
- ¿Qué parte pertenece a la imagen y cuál a la configuración de ejecución?
- ¿Qué cambio de código obligó a reconstruir y qué cambio de configuración no?
- ¿Por qué tus datos sobreviven al contenedor?
- ¿Por qué TasteMatch llega a Redis por nombre y no mediante
localhost? - ¿Qué puerto publicaste y por qué no publicaste Redis?
- ¿Qué evidencia fue más útil al diagnosticar cada incidencia?
- ¿Qué recurso eliminarías primero y qué comprobarías antes de borrar el almacenamiento?
- ¿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.