15. Ejercicios prácticos finales

¡Es hora de poner a prueba todo lo que has aprendido! Estos ejercicios están diseñados para repasar los conceptos clave del tutorial, desde la gestión básica de contenedores hasta la orquestación con Docker Compose.

Ejercicio 1: Crear un contenedor con Ubuntu y ejecutar comandos

Objetivo: Familiarizarse con la ejecución interactiva de contenedores.

  1. Inicia un contenedor interactivo a partir de la imagen `ubuntu:20.04` y abre una terminal `bash`.
  2. Dentro del contenedor, actualiza la lista de paquetes.
  3. Instala el paquete `nano`.
  4. Crea un archivo llamado `hola.txt` con el contenido "Hola Docker".
  5. Sal del contenedor y luego elimínalo.
Ver solución
# 1. Iniciar contenedor
docker run -it --name mi-ubuntu-ejercicio ubuntu:20.04 bash

# 2. Actualizar paquetes (dentro del contenedor)
apt-get update

# 3. Instalar nano (dentro del contenedor)
apt-get install -y nano

# 4. Crear archivo (dentro del contenedor)
echo "Hola Docker" > hola.txt

# 5. Salir y eliminar
exit
docker rm mi-ubuntu-ejercicio

Ejercicio 2: Construir una imagen de Python que ejecute un script

Objetivo: Practicar la creación de imágenes con un `Dockerfile`.

Crea un script de Python `saludo.py` que imprima "¡Mi primer script en Docker!". Luego, crea un `Dockerfile` para construir una imagen que ejecute ese script.

Ver solución

saludo.py:

print("¡Mi primer script en Docker!")

Dockerfile:

FROM python:3.9-slim
WORKDIR /app
COPY saludo.py .
CMD ["python", "saludo.py"]

Comandos:

# Construir la imagen
docker build -t mi-script-python .

# Ejecutar la imagen
docker run --rm mi-script-python

Ejercicio 3: Desplegar una aplicación web con Docker Compose

Objetivo: Usar Docker Compose para orquestar un frontend (Nginx) y un backend (Flask).

Configura un `docker-compose.yml` que levante dos servicios:

  • Un servicio `backend` basado en una aplicación Flask simple.
  • Un servicio `frontend` con Nginx que actúe como proxy inverso hacia el backend.
Ver solución

Crea una carpeta `backend` con `app.py` y `Dockerfile` (como en el Tema 6). Luego, una carpeta `nginx` con un archivo `nginx.conf`:

# nginx/nginx.conf
server {
    listen 80;
    location / {
        proxy_pass http://backend:5000; # Apunta al servicio backend
    }
}

docker-compose.yml:

version: '3.8'
services:
  backend:
    build: ./backend
    container_name: flask_backend

  frontend:
    image: nginx:1.25-alpine
    container_name: nginx_frontend
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - backend

Ejecuta `docker compose up` y visita `http://localhost`.

Ejercicio 4: Persistir datos de una base de datos MySQL

Objetivo: Practicar el uso de volúmenes para datos persistentes.

Levanta un contenedor de MySQL 8.0, asegurándote de que los datos se guarden en un volumen nombrado. Luego, detén y elimina el contenedor, y levanta uno nuevo conectado al mismo volumen para verificar que los datos persisten (aunque no haremos cambios en los datos, el contenedor debería iniciarse correctamente con los datos existentes).

Ver solución
# 1. Crear volumen
docker volume create datos_mysql_ejercicio

# 2. Levantar contenedor
docker run -d --name mysql-test -e MYSQL_ROOT_PASSWORD=pass -v datos_mysql_ejercicio:/var/lib/mysql mysql:8.0

# 3. Detener y eliminar
docker stop mysql-test
docker rm mysql-test

# 4. Levantar de nuevo usando el mismo volumen
docker run -d --name mysql-test-2 -e MYSQL_ROOT_PASSWORD=pass -v datos_mysql_ejercicio:/var/lib/mysql mysql:8.0

# Limpieza final
docker stop mysql-test-2 && docker rm mysql-test-2
docker volume rm datos_mysql_ejercicio

Ejercicio 5: Optimizar un Dockerfile con multi-stage builds

Objetivo: Aplicar la técnica de multi-stage builds para reducir el tamaño de una imagen.

Imagina una aplicación simple en C. Crea un `Dockerfile` que compile el código en una etapa y luego copie el ejecutable a una imagen final basada en `scratch` (una imagen vacía).

Ver solución

hello.c:

#include <stdio.h>

int main() {
   printf("Hello from a tiny container!\n");
   return 0;
}

Dockerfile:

# Etapa de build
FROM gcc:latest AS builder
WORKDIR /usr/src/app
COPY hello.c .
RUN gcc -static -o hello hello.c

# Etapa final
FROM scratch
COPY --from=builder /usr/src/app/hello .
CMD ["/hello"]

Construye y ejecuta la imagen. Luego, compara su tamaño (`docker images`) con el de la imagen `gcc`. La diferencia será enorme.