3 · Docker: imágenes y contenedores
'Funciona en mi máquina' es la excusa más vieja del software. Docker la elimina empaquetando tu código, su versión de Python y cada dependencia en una imagen portable que corre de forma idéntica en cualquier lugar.
Una IMAGEN de Docker es un plano congelado y de solo lectura de tu app + todo su entorno (librerías del SO, el Python exacto, cada dependencia fijada); un CONTENEDOR es una instancia en ejecución de esa imagen. Construyes la imagen una vez y corre de forma idéntica en cualquier máquina, acabando con 'funciona en mi máquina' para siempre.
Sin esto:
Sin contenedores, tu modelo depende de lo que resulte estar instalado en el servidor destino — un Python distinto, una librería del sistema faltante, un scikit-learn que no coincide — y los despliegues se rompen de formas que no puedes reproducir localmente.
En la lección anterior fijaste dependencias para que una reconstrucción use las mismas librerías de Python. Pero tu app también depende de cosas fuera de Python: el sistema operativo, las librerías del sistema, la versión del propio intérprete de Python. Fijar requirements.txt no captura eso. Docker sí — empaqueta todo el entorno.
Imagen vs contenedor — la distinción clave. Una imagen es un plano congelado y de solo lectura: una instantánea por capas de un sistema de archivos que contiene tu código, el userland del SO, el Python exacto y cada dependencia instalada. Es como una clase en POO, o un artefacto .joblib — inerte, solo una plantilla guardada. Un contenedor es una instancia en ejecución de una imagen — como un objeto creado a partir de una clase. Puedes arrancar muchos contenedores desde una sola imagen; cada uno es un proceso aislado y vivo. El lema: construye una imagen una vez, corre muchos contenedores desde ella.
El Dockerfile es la receta que construye una imagen. Es un archivo de texto con instrucciones, cada una describe un paso:
FROM python:3.11-slim— parte de una imagen base oficial (un Debian mínimo con Python 3.11 ya instalado).slimsignifica reducida — más pequeña y rápida de enviar.WORKDIR /app— fija el directorio de trabajo dentro de la imagen; los comandos siguientes corren aquí.COPY requirements.txt .— copia tus requirements fijados dentro de la imagen.RUN pip install -r requirements.txt— instala las dependencias en tiempo de build, horneándolas dentro de la imagen.COPY . .— copia el resto de tu código.CMD ["python", "app.py"]— el comando por defecto a ejecutar cuando arranca un contenedor.
Ejecutas docker build una vez para convertir el Dockerfile en una imagen, y luego docker run para lanzar un contenedor desde ella. Como la imagen lleva todo su entorno, ese contenedor se comporta de forma idéntica en tu laptop, en la máquina de un compañero, en un runner de CI o en un servidor en la nube — el largamente prometido fin de "funciona en mi máquina".
Un primer Dockerfile para una app de ML en Python: imagen base, directorio de trabajo, copiar + instalar dependencias fijadas, copiar el código, comando por defecto.
`docker build` convierte el Dockerfile en una imagen; `docker run` lanza un contenedor desde ella. `docker images` lista imágenes, `docker ps` lista contenedores en ejecución.
¿Cuál es la diferencia entre una imagen de Docker y un contenedor de Docker?
- Una imagen de Docker es un plano de solo lectura de tu app + su entorno completo; un contenedor es una instancia en ejecución de ella — una imagen genera muchos contenedores.
- Un Dockerfile es la receta de build: `FROM` una imagen base, `WORKDIR`, `COPY` + `RUN pip install` las dependencias, `COPY` el código, luego `CMD` el comando por defecto.
- `docker build -t nombre:tag .` produce la imagen; `docker run` lanza un contenedor — de forma idéntica en cualquier máquina, porque todo el entorno está horneado dentro.
Casi todo servicio de ML desplegado se envía como una imagen de Docker — es la unidad universal que aceptan las plataformas en la nube (AWS, Azure, GCP), así que el resto de este track se construye sobre el contenedor que defines aquí.
Si lo quitas: Configurarías a mano cada servidor destino y rezarías para que su Python y librerías del sistema coincidan con los tuyos — los despliegues frágiles e irreproducibles que los contenedores se inventaron para eliminar.