Virtualización con contenedores y arquitecturas dinámicas
Al finalizar este trabajo práctico de recuperatorio, el/la estudiante deberá ser capaz de:
docker-compose interconectando un backend con un proxy inverso (Nginx) o base de datos..env, healthchecks y comunicación entre capas.
docker --version) y Docker Compose (docker compose version).docker run hello-world.
Capturar la salida y explicar brevemente en el informe los 4 pasos que
ejecuta la API de Docker internamente (pull, create, start, output).docker run -d -p 8085:80 --name servidor-test -e PORT=80 nginx:alpine
docker inspect servidor-test:
PORT=80).docker stats servidor-test durante 10 segundos.recuperatorio-docker/
├── Dockerfile
├── .dockerignore
├── .env
└── app/
├── package.json
└── server.js
server.js (Node.js/Express o
equivalente) que responda en el puerto definido por la variable de entorno
PORT (por defecto 3000). Debe incluir:
/: retorna un JSON con el nombre del
alumno, legajo y el mensaje "API del Recuperatorio activa"./health: retorna un JSON
{ "status": "OK", "timestamp": "..." }.node:18-alpine o superior).WORKDIR /app).package*.json)
e instalar (npm install --production).EXPOSE y el comando predeterminado
CMD.# Construir la imagen: docker build -t <apellido>-api:v1 . # Ejecutar pasando una variable de entorno en la línea de comandos: docker run -d -p 3000:3000 --env PORT=3000 --name mi-api <apellido>-api:v1
docker run -d -p 3001:3000 -v $(pwd)/app:/app --name mi-api-dev <apellido>-api:v1
Modificar la respuesta del JSON en server.js y verificar la actualización.
docker-compose.yml que orqueste dos servicios interconectados:
api: basado en la imagen creada en la Parte B;
configurado con variables de entorno leídas desde un archivo
.env; debe incluir una instrucción
healthcheck que consulte periódicamente
http://localhost:3000/health.proxy: servidor Nginx actuando como
Reverse Proxy. Redirige el tráfico del puerto host 8080 al contenedor
api utilizando el nombre del servicio como hostname interno
(http://api:3000).docker compose up -d.docker network inspect sobre la red creada
automáticamente por Compose y explicar cómo se resuelven los nombres de dominio
internos (DNS integrado de Docker).node_modules, .git,
.env y archivos .log. Explicar por qué
es crítico evitar copiar node_modules en la construcción de la imagen.node:latest).
Incluir captura de docker images.docker stop $(docker ps -aq) docker rm $(docker ps -aq) docker rmi $(docker images -q) docker system prune -f
docker login.<usuario-dockerhub>/<apellido>-api:v1 y subirla a Docker Hub:docker tag <apellido>-api:v1 <usuario-dockerhub>/<apellido>-api:v1 docker push <usuario-dockerhub>/<apellido>-api:v1
docker pull <usuario-dockerhub>/<apellido>-api:v1 docker run -d -p 3000:3000 -e PORT=3000 --name test-evaluacion <usuario-dockerhub>/<apellido>-api:v1
docker rmi) y volver a descargarla
desde el registro público para confirmar su correcto funcionamiento fuera del entorno
de desarrollo.A continuación se muestra el Dockerfile íntegramente resuelto para la
Parte B, cumpliendo todos los puntos exigidos por la consigna: imagen base liviana,
optimización por capas de caché, instalación de dependencias, EXPOSE
y CMD.
# ============================================================================= # Dockerfile · TP Recuperatorio - API Backend Node.js/Express # Imagen optimizada para producción, basada en Alpine Linux. # ============================================================================= # 1) IMAGEN BASE LIVIANA: node:18-alpine usa Alpine Linux (~5MB de base) en # lugar de Debian completo, reduciendo drásticamente el tamaño final # de la imagen (comparar luego con node:latest en la Parte D). FROM node:18-alpine # 2) ENTORNO: valor por defecto del puerto (puede sobreescribirse con # -e PORT=... en docker run o con el archivo .env en docker-compose). ENV PORT=3000 \ NODE_ENV=production # 3) DIRECTORIO DE TRABAJO: todos los comandos siguientes se ejecutan # relativos a /app dentro del contenedor. WORKDIR /app # 4) CACHÉ DE CAPAS (clave): copiamos SOLO los manifiestos de dependencias # en primer lugar. Docker cachea esta capa: si package*.json no cambia, # no se vuelve a ejecutar npm install al reconstruir, aunque cambie el # código fuente. Esto acelera enormemente las reconstrucciones durante # el desarrollo. COPY app/package*.json ./ # 5) INSTALACIÓN DE DEPENDENCIAS: --production omite las devDependencies # (tests, linters, etc.), dejando la imagen final más liviana. # npm ci garantiza builds reproducibles a partir del package-lock.json. RUN npm install --production # 6) CÓDIGO FUENTE: recién ahora copiamos el resto de la aplicación. COPY app/ . # 7) PUERTO: documenta que el contenedor escucha en el 3000. EXPOSE 3000 # 8) ARRANQUE: proceso principal del contenedor (PID 1). CMD ["node", "server.js"]
| Instrucción | Qué hace y por qué |
|---|---|
FROM node:18-alpine |
Base oficial de Node.js sobre Alpine: imagen final de ~50-100 MB en lugar de
~1 GB de node:latest (comparativa pedida en la Parte D). |
ENV PORT=3000 NODE_ENV=production |
Define las variables de entorno por defecto; server.js debe
leer process.env.PORT || 3000. |
WORKDIR /app |
Directorio de trabajo aislado dentro del contenedor. |
COPY app/package*.json ./ |
Copia primero solo los manifiestos: aprovecha la caché de capas de Docker.
Mientras las dependencias no cambien, npm install se salta en
cada rebuild. |
RUN npm install --production |
Instala solo las dependencias de producción, sin devDependencies. |
COPY app/ . |
Copia el código fuente. Recordá excluir node_modules con
.dockerignore (Parte D) para no pisar los módulos
instalados dentro de la imagen. |
EXPOSE 3000 |
Documenta el puerto de escucha de la API. |
CMD ["node", "server.js"] |
Comando por defecto al iniciar el contenedor. Formato exec (JSON array): Node recibe las señales del sistema directamente (apagado limpio). |
docker build -t <apellido>-api:v1 . docker run -d -p 3000:3000 --env PORT=3000 --name mi-api <apellido>-api:v1 curl http://localhost:3000/ curl http://localhost:3000/health
Dockerfile,
docker-compose.yml, server.js,
nginx.conf y .dockerignore.docker ps, docker stats, curl/navegador).package.json antes del resto del código.Comprimido (.zip) o enlace al repositorio Git
sin la carpeta node_modules.
Destinatario: coordi.tecnicatura@uccuyo.edu.ar
Asunto: [SO] RECUPERATORIO TP Contenedores — Apellido, Nombre
| Criterio | Puntos |
|---|---|
Parte A: inspección, métricas de consumo (docker stats) e IP interna | 10 pts |
| Parte B: Dockerfile bien estructurado, manejo de variables de entorno y bind mount | 20 pts |
Parte C: docker-compose.yml funcional con 2 servicios (api + proxy), DNS interno y healthcheck | 25 pts |
| Parte D: buenas prácticas (.dockerignore, caché de capas, optimización y limpieza) | 15 pts |
| Parte E: publicación exitosa en Docker Hub y verificación del comando pull + run | 20 pts |
| Informe: claridad técnica, capturas explicativas y conclusiones | 10 pts |
| Total | 100 pts |