Saltar al contenido

lección 4

Imágenes y Dockerfiles: la receta de tu entorno

Qué es una imagen, cómo se construye capa a capa, y cómo escribir tu propio Dockerfile con FROM, RUN, COPY y CMD.

55 min

Hasta ahora has usado imágenes que crearon otros: python, node, postgres. Alguien escribió una receta para cada una de ellas, la publicó en Docker Hub, y tú la descargaste con docker run. Pero el verdadero poder de Docker llega cuando TÚ escribes tus propias recetas. En esta lección vas a aprender a crear imágenes personalizadas con un Dockerfile — el archivo de texto que describe tu entorno exacto, paso a paso, ingrediente a ingrediente.

Volvamos a la analogía del restaurante. Hasta ahora has sido un cliente que pide del menú (imágenes pre-hechas). A partir de hoy eres el chef que ESCRIBE el menú. Tu Dockerfile es la receta maestra que cualquier cocinero puede seguir para obtener exactamente el mismo plato. No importa si cocina en Madrid, en Buenos Aires o en un servidor de AWS — la receta produce el mismo resultado.

### ¿Qué es una imagen Docker, realmente?

Una imagen Docker es un paquete inmutable (que no cambia) que contiene TODO lo necesario para ejecutar una aplicación: sistema operativo base, librerías del sistema, tu código, dependencias de tu código, y la configuración. Internamente, una imagen está compuesta de CAPAS apiladas — como un pastel de capas donde cada capa añade algo encima de la anterior.

Una imagen Docker se construye por capas. Las capas se cachean — si no cambian, no se reconstruyen.

La belleza del sistema de capas es el CACHING. Si reconstruyes una imagen y las primeras 3 capas no han cambiado, Docker las reutiliza del cache y solo reconstruye a partir de la capa que sí cambió. Esto hace que las builds sean increíblemente rápidas después de la primera vez.

### Tu primer Dockerfile

Un Dockerfile es simplemente un archivo de texto (sin extensión) llamado exactamente "Dockerfile" que contiene instrucciones secuenciales. Cada instrucción crea una capa nueva en tu imagen. Vamos a construir uno para un script de Python:

1# Primero, crea una carpeta para tu proyecto
2# 🟦 Windows PowerShell:
3mkdir mi-app-docker; cd mi-app-docker
4
5# 🍎 Mac Terminal:
6mkdir mi-app-docker && cd mi-app-docker

Crea el directorio del proyecto

1# Archivo: app.py
2# Un script simple que procesa datos
3import json
4from datetime import datetime
5
6data = {
7 "app": "Mi primera app Docker",
8 "timestamp": datetime.now().isoformat(),
9 "python_version": "3.12",
10 "message": "¡Si ves esto, tu Dockerfile funciona perfecto!"
11}
12
13print(json.dumps(data, indent=2, ensure_ascii=False))

app.py — el script que empaquetaremos en Docker

1# Archivo: Dockerfile (sin extensión, exactamente este nombre)
2FROM python:3.12-slim
3
4WORKDIR /app
5
6COPY app.py .
7
8CMD ["python", "app.py"]

Tu primer Dockerfile — 4 líneas que empaquetan tu app

### Las instrucciones fundamentales del Dockerfile

  • FROM: "Empieza con esta imagen base". Siempre es la primera línea. Define los cimientos de tu imagen.
  • WORKDIR: "Trabaja dentro de este directorio". Crea el directorio si no existe y se posiciona ahí. Es como hacer cd pero dentro de la imagen.
  • COPY: "Copia archivos de tu ordenador al contenedor". COPY <origen-en-tu-PC> <destino-en-la-imagen>.
  • RUN: "Ejecuta este comando DURANTE la construcción de la imagen". Para instalar dependencias, crear directorios, etc.
  • CMD: "Ejecuta este comando CUANDO el contenedor arranque". Es el comando por defecto al hacer docker run.
  • ENV: "Define una variable de entorno". Disponible tanto durante la build como en runtime.
  • EXPOSE: "Documenta que este contenedor escucha en este puerto". Es informativo — no abre el puerto realmente.

### docker build: construir tu imagen

1# Construir la imagen (el punto "." indica el directorio actual)
2docker build -t mi-app:1.0 .
3
4# Desglose:
5# docker build → comando para construir
6# -t mi-app:1.0 → nombre:tag de tu imagen
7# . → directorio donde está el Dockerfile (contexto de build)
8
9# Verás algo como:
10# [1/3] FROM python:3.12-slim
11# [2/3] WORKDIR /app
12# [3/3] COPY app.py .
13# => naming to docker.io/library/mi-app:1.0
14
15# Verificar que la imagen existe
16docker images
17# REPOSITORY TAG IMAGE ID CREATED SIZE
18# mi-app 1.0 abc123def456 5 seconds ago 155MB
19
20# Ejecutar tu imagen personalizada
21docker run mi-app:1.0

Build + Run: de Dockerfile a contenedor ejecutándose

Consejo de senior: SIEMPRE versiona tus imágenes con tags numéricos (mi-app:1.0, mi-app:1.1, mi-app:2.0). Nunca uses solo "latest". Cuando algo se rompe en producción y necesitas volver a la versión anterior, agradecer tener tags es lo primero que harás.

### Un Dockerfile más realista: con dependencias

Los scripts del mundo real tienen dependencias (librerías externas). Veamos cómo incluirlas:

1# Archivo: requirements.txt
2requests==2.31.0
3pandas==2.1.4

requirements.txt — las dependencias de tu proyecto

1# Archivo: Dockerfile (versión con dependencias)
2FROM python:3.12-slim
3
4WORKDIR /app
5
6# Primero copiamos SOLO requirements.txt (truco de cache)
7COPY requirements.txt .
8RUN pip install --no-cache-dir -r requirements.txt
9
10# Luego copiamos el código
11COPY . .
12
13CMD ["python", "app.py"]

Dockerfile con dependencias — nota el orden de las instrucciones

¿Por qué copiamos requirements.txt ANTES que el resto del código? Por el CACHE de capas. Si cambias tu app.py pero no tocas requirements.txt, Docker reutiliza la capa de pip install del cache y solo recopia tu código. Esto ahorra MINUTOS en cada build. Si copiaras todo junto (COPY . .), cualquier cambio en cualquier archivo invalidaría el cache del pip install.

El orden de las instrucciones en un Dockerfile IMPORTA MUCHO para el rendimiento. Regla de oro: pon las cosas que cambian MENOS frecuentemente arriba (instalar dependencias del sistema, pip install) y las que cambian MÁS frecuentemente abajo (copiar tu código). Así maximizas el cache.

### .dockerignore: lo que NO quieres copiar

Igual que .gitignore le dice a Git qué archivos ignorar, .dockerignore le dice a Docker qué NO copiar al contexto de build. Esto hace la build más rápida y tu imagen más pequeña:

1# Archivo: .dockerignore
2__pycache__
3*.pyc
4.git
5.venv
6.env
7node_modules
8*.md
9.DS_Store

.dockerignore — mantén tu imagen limpia y ligera

Consejo de senior: si tu build es lenta, revisa el .dockerignore. He visto builds que tardaban 5 minutos porque copiaban la carpeta .git (con todo el historial), node_modules, o datasets de prueba de varios GB. El .dockerignore es la primera línea de defensa contra builds lentas.

### Multi-stage builds: imágenes limpias de producción

A veces necesitas herramientas pesadas para CONSTRUIR tu aplicación (compiladores, cabeceras de desarrollo, gcc) pero no las necesitas para EJECUTARLA. Un multi-stage build usa dos etapas: una para construir, otra para ejecutar. Solo la segunda acaba en la imagen final.

1# Etapa 1: construir (con herramientas pesadas)
2FROM python:3.12-slim AS builder
3WORKDIR /build
4COPY requirements.txt .
5RUN pip install --no-cache-dir --target=/build/deps -r requirements.txt
6
7# Etapa 2: ejecutar (sin herramientas de build)
8FROM python:3.12-slim
9WORKDIR /app
10COPY --from=builder /build/deps /usr/local/lib/python3.12/site-packages/
11COPY . .
12CMD ["python", "main.py"]

Multi-stage: la primera etapa instala, la segunda solo copia lo instalado.

Usarás multi-stage en el proyecto de esta sección. La idea siempre es la misma: construir en una etapa y copiar solo lo necesario a la etapa final. Si tu imagen pesa más de 500 MB, pregúntate si no estás arrastrando herramientas de build.

### Resumen del flujo completo

El flujo de trabajo con Dockerfiles es siempre el mismo: (1) Escribes un Dockerfile describiendo tu entorno. (2) Ejecutas docker build para crear una imagen. (3) Ejecutas docker run para crear un contenedor a partir de esa imagen. (4) Si cambias algo, vuelves a hacer build (el cache hace que sea rápido). Es un ciclo que repetirás cientos de veces en tu carrera.

## ejercicios

[01]

Diagnostica un Dockerfile con tres fallos

Este Dockerfile construye, pero tiene tres problemas. Reescríbelo arreglándolos. Pistas: la base, el orden y el CMD.

Cargando editor...
[02]

Optimiza el orden de capas

Este Dockerfile funciona pero es lento: cada cambio en el código reinstala curl y pandas. Reordena las instrucciones.

Cargando editor...
[03]

Build y etiquetado de imágenes

Construye una vez y pon tres tags. Un tag es una etiqueta, no una copia: los tres apuntan a la misma imagen.

Cargando editor...

Regístrate para guardar tu progreso.

## comentarios

Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.

Inicia sesión para comentar y responder.

cargando comentarios...