Skip to content

~/nube/p00 $ cat 05-web-y-compose.md

  • Introducción a la nube pública
  • P00
  • lección 05/06

Imagen propia y Compose

Empaqueta la web estática creada en P00/04 y gestiona su ciclo de vida local con Docker Compose.

De la web local a una aplicación reproducible

Section titled “De la web local a una aplicación reproducible”

En P00/04 creaste la carpeta web/, la publicaste con Nginx y la mejoraste visualmente con Tailwind. Ahora usarás el mismo contenido como carga de prueba: no se evalúa el diseño, sino que pueda empaquetarse, ejecutarse, comprobarse y cerrarse de forma repetible.

Hay tres piezas distintas. Conviene distinguirlas antes de escribir código:

Pieza Responde a Archivo o recurso
Contenido «¿Qué verá el navegador?» web/index.html
Imagen «¿Cómo se prepara el servidor web?» Dockerfile y una imagen como p00-web:local
Aplicación local «¿Cómo se ejecuta y configura este servicio?» compose.yaml y el contenedor creado por Compose

El contenido y los archivos de configuración se guardan en /optativa-nube/p00-puesto-nube; por ello podrán versionarse con Git. Las órdenes Docker se ejecutan siempre desde el anfitrión, nunca dentro de p00-lab.

Supón que cambias el título de web/index.html.

  1. El cambio ya existe en la carpeta compartida y Git puede mostrarlo con git diff desde p00-lab.
  2. La imagen p00-web:local no cambia por sí sola: es una fotografía creada en un momento anterior.
  3. El contenedor en ejecución solo mostrará el cambio si monta la carpeta como volumen o si construyes una imagen nueva y lo recreas.

Esta cadena será importante para diagnosticar la frase «he cambiado el HTML pero el navegador muestra lo antiguo»: antes de editar otra vez, identifica si estás viendo un volumen, una imagen anterior o un contenedor que no se ha recreado.

Punto de partida: ya existe /optativa-nube/p00-puesto-nube/web/index.html de P00/04. Entra en p00-lab y comprueba que el archivo sigue disponible:

Terminal window
docker start -ai p00-lab
su - alumno

Comprueba desde el contenedor que el archivo existe y tiene un encabezado HTML reconocible:

Terminal window
cd /optativa-nube/p00-puesto-nube
ls -l web/index.html
head -n 12 web/index.html

Después revisa los cambios con git status y git diff, pero todavía no hagas commit: incorporarás la página, el Dockerfile y Compose tras comprobar el ciclo completo. El HTML, el Dockerfile y compose.yaml se versionan; una imagen Docker, los contenedores y los registros no.

Sal de p00-lab con dos órdenes: la primera sale de alumno y la segunda vuelve al anfitrión. Desde la terminal del anfitrión, sitúate en la carpeta compartida del proyecto y crea un archivo llamado Dockerfile:

Terminal window
exit
exit
FROM nginx@sha256:adad2ae9204d0fd7a34f40299bc838c3782be1293b10005eac4315ff5a1abf4e
COPY web/ /usr/share/nginx/html/

La primera instrucción parte de una versión fijada de la imagen oficial de Nginx. La segunda copia los archivos de web/ al directorio que Nginx sirve por defecto. La imagen es una fotografía: si después modificas web/index.html, la imagen existente no cambiará hasta que vuelvas a construirla. El digest se revisa de forma controlada antes del curso; no lo sustituyas por una etiqueta mutable durante la práctica.

Antes de construir, crea .dockerignore en la raíz de p00-puesto-nube. Este archivo limita qué ficheros se envían como contexto de construcción; no reemplaza .gitignore, porque cada uno protege un momento distinto del flujo.

.git/
.env
.env.*
*.pem
*.key
*.log
node_modules/

Punto de partida: editas la carpeta compartida p00-puesto-nube; el archivo se versionará junto al Dockerfile. Qué cambia: esos patrones no viajarán al contexto de docker build. Cómo comprobarlo: revísalo con git diff y confirma que solo excluye archivos no necesarios para la web. No hagas: no incluyas el directorio web/ ni supongas que .dockerignore arregla un secreto que ya se hubiera incluido en un commit.

Punto de partida: estás en el terminal del anfitrión y existen Dockerfile y web/index.html dentro de p00-puesto-nube. Entra en esa carpeta según tu sistema:

Terminal window
cd "$HOME/optativa-nube/p00-puesto-nube"
Terminal window
Set-Location "$HOME\optativa-nube\p00-puesto-nube"

Después construye una imagen con una etiqueta local y comprueba que existe:

Terminal window
docker build --tag p00-web:local .
docker image ls p00-web

Si creaste optativa-nube en el Escritorio, usa Desktop/optativa-nube en Bash o Desktop\optativa-nube en PowerShell. Estas órdenes se ejecutan en el anfitrión; no uses la ruta interna /optativa-nube ahí.

docker build --tag p00-web:local . usa el Dockerfile de la carpeta actual; --tag da a la imagen un nombre y una etiqueta reconocibles; el punto final indica que esa carpeta es el contexto de construcción. docker image ls p00-web lista las imágenes cuyo nombre coincide. Qué cambia: se crea o actualiza la imagen local; todavía no hay ningún contenedor web en ejecución. Verificación: la lista debe mostrar p00-web con la etiqueta local. Si la construcción falla en COPY web/, revisa que estás en la raíz del proyecto y que existe web/index.html. No hagas: no ejecutes estas órdenes dentro de p00-lab ni copies al Dockerfile archivos distintos de la web estática.

3. Ejecutar, comprobar y cerrar un contenedor

Section titled “3. Ejecutar, comprobar y cerrar un contenedor”

Punto de partida: p00-web:local aparece en docker image ls. Antes de crear el servidor, comprueba que el nombre de práctica está libre:

Terminal window
docker container ls --all --filter name=p00-web

Si aparece un contenedor p00-web, no ejecutes otra creación con el mismo nombre: revisa su estado o ciérralo con la secuencia de cierre de este apartado. Cuando el nombre esté libre, inicia el servidor desde el anfitrión y publica su puerto únicamente en la interfaz local:

Terminal window
docker run --detach --name p00-web \
--publish 127.0.0.1:8080:80 \
p00-web:local

Qué cambia: se crea y arranca el contenedor p00-web; el puerto 80 interno queda accesible solo como 127.0.0.1:8080 en el anfitrión. Ahora comprueba el estado, la publicación y la respuesta. Cada comando responde a una pregunta diferente:

Terminal window
docker ps --filter name=p00-web
docker port p00-web
curl --include --show-error http://localhost:8080/ | head -n 30
docker logs --tail 30 p00-web
Comprobación Resultado esperado
docker ps El contenedor aparece como Up.
docker port Muestra 80/tcp -> 127.0.0.1:8080.
curl Devuelve una línea 200 y el HTML que contiene «Mi web local funciona».
docker logs No muestra un cierre ni errores inesperados.

Abre también http://localhost:8080/ en el navegador del anfitrión. Navegador y curl son dos comprobaciones complementarias: el primero verifica la experiencia visible y el segundo permite leer la respuesta HTTP de forma precisa.

Cómo comprobarlo: los cuatro comandos anteriores deben confirmar estado Up, publicación exclusiva en 127.0.0.1, estado HTTP 200 y registros sin un cierre inesperado. No hagas: no reemplaces la dirección de publicación por 8080:80, no compartas el puerto con la red del aula y no crees un contenedor desde dentro de p00-lab.

Punto de partida para el cierre: la respuesta HTTP ya se ha registrado. Cuando termines esta prueba con docker run, libera el nombre y el puerto:

Terminal window
docker stop p00-web
docker rm p00-web

docker run es útil para comprender las opciones de un único contenedor, pero empieza a ser frágil cuando hay varias opciones o servicios. Docker Compose guarda la configuración declarativa de una aplicación en compose.yaml. Así el puerto, el montaje y el proceso de arranque se revisan como un archivo, se versionan con Git y se repiten con órdenes cortas.

Compose no es «Docker dentro de Docker»: es una herramienta que se ejecuta en el anfitrión y crea contenedores Docker a partir del archivo de configuración.

Red interna: comunicación no es lo mismo que publicar un puerto

Section titled “Red interna: comunicación no es lo mismo que publicar un puerto”

Cada contenedor tiene una interfaz de red. En una red bridge definida por el usuario, los contenedores conectados pueden encontrarse por el nombre del contenedor o del servicio; esa comunicación ocurre dentro de Docker. ports, en cambio, crea una puerta desde el anfitrión hacia un puerto del contenedor.

En esta primera web hay un único servicio, así que no necesitamos añadir un backend ni abrir más puertos. Compose creará una red propia para el proyecto y conectará web a ella. Más adelante, si hubiera otro servicio aprobado, podría usar el nombre web dentro de esa red; desde el navegador seguiría siendo necesario un puerto publicado si se quisiera acceder desde el anfitrión.

Navegador del anfitrión
│ http://127.0.0.1:8080
▼
Puerto publicado (solo anfitrión)
│ 80/tcp
▼
Contenedor web ───── red interna de Compose ───── futuros servicios autorizados

Flujo local de Compose: el navegador accede a 127.0.0.1; Compose administra el contenedor web, la red interna y un montaje de solo lectura, con cierre mediante docker compose down.

No hagas: no uses network_mode: host, no cambies a una red compartida ni publiques puertos adicionales para una web estática. La red interna prepara el modelo mental para servicios posteriores; no es una invitación a crear una arquitectura compleja en P00.

En la raíz de p00-puesto-nube, crea este archivo desde el anfitrión o desde el editor sobre la carpeta compartida:

name: p00-puesto-nube
services:
web:
build:
context: .
dockerfile: Dockerfile
image: p00-web:local
ports:
- "127.0.0.1:8080:80"
volumes:
- ./web:/usr/share/nginx/html:ro
environment:
P00_ENTORNO: local
Clave Significado en esta práctica
services.web Declara un servicio llamado web.
build.context: . Construye usando la carpeta actual como contexto.
image Asigna un nombre claro a la imagen resultante.
ports Publica el puerto del contenedor solo en el anfitrión local.
volumes Hace visible web/ en Nginx durante la práctica.
:ro El contenedor puede leer el contenido, pero no modificarlo.
environment Demuestra una variable no sensible; no es un lugar para secretos.

El volumen tiene un efecto didáctico importante: al editar web/index.html, Nginx ve el cambio sin reconstruir la imagen. Eso es útil durante el desarrollo, pero no cambia la imagen p00-web:local; para una entrega reproducible deberás reconstruirla y comprobar que funciona también sin ese montaje.

Punto de partida: compose.yaml está en la raíz del proyecto y aún no has creado servicios con Compose. Qué cambiará: al iniciar, Compose creará el servicio web y una red interna del proyecto. Cómo comprobarlo: usa primero docker compose config; no hace falta declarar una red manual para este único servicio. No hagas: no añadas servicios, imágenes o dependencias de aplicaciones externas; los materiales Node/Mongo de referencia están fuera del alcance de P00.

Punto de partida: estás en la raíz del proyecto en el anfitrión y no queda activo el contenedor creado con docker run. Si quedó alguno, detenlo y elimínalo antes para liberar el puerto 8080. Sigue siempre esta secuencia:

Terminal window
docker compose config
docker compose up --detach --build
docker compose ps
docker compose logs --tail 30
curl --include --show-error http://localhost:8080/ | head -n 30
  1. docker compose config valida y muestra la configuración que Compose interpretará. Corrige un error de YAML aquí, antes de crear recursos.
  2. docker compose up --detach --build construye la imagen si hace falta, crea el servicio y la red interna y los deja ejecutándose en segundo plano.
  3. docker compose ps indica el nombre, estado y puertos del servicio.
  4. docker compose logs --tail 30 ofrece las últimas líneas para diagnosticar sin volcar registros completos.
  5. curl cierra el ciclo: comprueba que el servicio publicado responde HTTP.

Qué cambia: aparecen el contenedor administrado por Compose y una red interna; la imagen puede reconstruirse si el Dockerfile o su contexto han cambiado. docker compose ps debe mostrar el servicio web activo y el puerto publicado. No necesitas conocer ni usar la dirección IP interna de la red: para la web local la dirección estable es http://localhost:8080/.

Modifica una frase de web/index.html y guarda el archivo. Como existe el volumen de solo lectura, actualiza el navegador y repite curl: el cambio debe aparecer sin reconstrucción. Documenta en la bitácora que este comportamiento procede del volumen, no de que Docker haya modificado la imagen.

Punto de partida para el cierre: has terminado la comprobación y has guardado un resultado resumido. Cuando termines, detén y elimina los recursos que Compose creó:

Terminal window
docker compose down
docker compose ps

El segundo comando debe indicar que no quedan servicios del proyecto en ejecución. Tras down, Compose elimina el contenedor y su red propia; la imagen local puede mantenerse para una prueba posterior. No hagas: no uses docker system prune, docker network prune ni limpieza global: podrían afectar a recursos que no pertenecen a P00.

6. De una incidencia a una corrección razonada

Section titled “6. De una incidencia a una corrección razonada”
Síntoma Primera comprobación Corrección probable
docker compose config falla Lee línea y sangría indicadas. Corrige YAML; no añadas tabuladores ni claves inventadas.
El servicio no queda Up docker compose ps y docker compose logs --tail 30. Revisa el Dockerfile o el error mostrado por Nginx.
El navegador no conecta docker compose ps, después curl. Revisa que el puerto sea 127.0.0.1:8080:80 y que no esté ocupado.
Aparece 404 Comprueba web/index.html y el destino del volumen. Corrige la ruta ./web:/usr/share/nginx/html:ro.
El HTML no cambia Confirma que editas la carpeta compartida y que el volumen está presente. Guarda el archivo correcto; no reconstruyas sin entender por qué.
Aparece un secreto en un archivo Detén la entrega y retíralo antes de hacer commit. Usa valores no sensibles y revisa .gitignore.

Una corrección se considera completa cuando se verifica con la misma prueba que reveló el problema. Por ejemplo, tras corregir un puerto, repite docker compose ps y curl; no basta con que Compose deje de mostrar un error.

Con tu editor, crea evidencias/web-local.md con la URL usada, una respuesta HTTP resumida, el resultado de docker compose ps y la confirmación de cierre. No copies registros completos. Después vuelve a p00-lab para revisar exactamente los archivos que guardarás:

Terminal window
docker start -ai p00-lab
su - alumno
cd /optativa-nube/p00-puesto-nube
git status
git diff
git add Dockerfile .dockerignore compose.yaml web/index.html evidencias/web-local.md
git diff --staged
git commit -m "Publica la web de comprobación con Compose"
exit
exit

No añadas imágenes, contenedores, registros extensos, .env, claves privadas ni credenciales. El producto contiene la configuración, una comprobación HTTP resumida y el cierre realizado. El primer exit sale de alumno y el segundo vuelve al anfitrión antes de ejecutar el cierre.

Termina la sesión deteniendo el servicio Compose y el contenedor de trabajo:

Terminal window
docker compose down

La web HTML y Tailwind de P00/04 creó el contenido y comprobó el puerto; este bloque añade una imagen propia y una configuración reproducible con Compose.