Docker para desarrolladores: qué es y cómo empezar
Docker es una herramienta que ha revolucionado la manera en que los desarrolladores construyen, distribuyen y ejecutan aplicaciones. En este post te explicaré qué es Docker y sus conceptos clave para que puedas empezar a usarlo en tu flujo de trabajo.
¿Qué es Docker?
Docker es una plataforma de código abierto que permite automatizar el despliegue, escalado y gestión de aplicaciones mediante contenedores. Permite empaquetar aplicaciones con todas sus dependencias en unidades estandarizadas llamadas contenedores.
La diferencia con una máquina virtual es que los contenedores comparten el kernel del sistema operativo anfitrión, por lo que son mucho más ligeros y arrancan en segundos. Si te interesa ver cómo el kernel expone esa información de procesos, en Explorando /proc lo cuento con detalle.
Conceptos clave
1. Contenedores
Los contenedores son paquetes ligeros, autónomos y ejecutables que incluyen todo lo necesario para ejecutar un software: el código, el runtime, las herramientas del sistema, las librerías y la configuración.
2. Imágenes
Las imágenes de Docker son los planos de los contenedores. Son plantillas de solo lectura que contienen un conjunto de instrucciones para crear un contenedor. Las imágenes se almacenan en registros como Docker Hub.
3. Dockerfile
Un Dockerfile es un archivo de texto que contiene todos los comandos necesarios para construir una imagen. Automatiza el proceso de creación de imágenes de Docker.
La relación entre las tres piezas es un flujo de una sola dirección:
flowchart LR
A[Dockerfile] -->|docker build| B[Imagen]
B -->|docker push| R[(Registro<br/>Docker Hub)]
R -->|docker pull| B
B -->|docker run| C1[Contenedor 1]
B -->|docker run| C2[Contenedor 2]
De una misma imagen puedes levantar tantos contenedores como quieras, y cada uno arranca desde el mismo estado.
Ventajas de usar Docker
- Consistencia entre entornos de desarrollo, pruebas y producción.
- Mejor colaboración entre equipos de desarrollo y operaciones.
- Despliegue y escalado más rápidos.
- Uso eficiente de los recursos del sistema.
Comandos Docker esenciales
Una vez instalado Docker, estos son los comandos que más vas a usar:
# Descargar una imagen de Docker Hub
docker pull python:3.13-slim
# Listar imágenes descargadas
docker images
# Ejecutar un contenedor
docker run -p 5000:5000 mi-app
# Listar contenedores en ejecución
docker ps
# Listar todos los contenedores (incluyendo detenidos)
docker ps -a
# Detener un contenedor
docker stop <id-del-contenedor>
# Eliminar un contenedor
docker rm <id-del-contenedor>
# Ver los logs de un contenedor
docker logs <id-del-contenedor>
# Abrir una shell dentro de un contenedor en ejecución
docker exec -it <id-del-contenedor> sh
Ejemplo: dockerizando una aplicación web con Python
Veamos un ejemplo básico de cómo containerizar una aplicación Flask.
1. La aplicación
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return '¡Hola desde Docker!'
if __name__ == '__main__':
app.run(host='0.0.0.0')
Y un requirements.txt con las dependencias:
flask
2. El Dockerfile
FROM python:3.13-slim
WORKDIR /app
# Primero las dependencias: esta capa se cachea mientras no cambien
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Después el código, que cambia mucho más a menudo
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
El orden importa: Docker cachea cada instrucción como una capa. Si copias el código antes de instalar las dependencias, cada cambio en app.py vuelve a ejecutar el pip install completo.
3. Construir y ejecutar
docker build -t mi-flask-app .
docker run -p 5000:5000 mi-flask-app
Este ejemplo demuestra lo fácil que es containerizar una aplicación con Docker, garantizando que funcionará igual en cualquier entorno.
Errores comunes al empezar
- "La app arranca, pero no puedo entrar desde el navegador". Casi siempre es una de dos: la app escucha en
127.0.0.1dentro del contenedor (por eso el ejemplo usahost='0.0.0.0'), o te olvidaste del-p 5000:5000. - "Mis cambios no se ven". El contenedor usa la imagen que construiste, no tu carpeta. Tienes que volver a hacer
docker build, o montar el código como volumen en desarrollo (-v $(pwd):/app). - "Perdí los datos de la base de datos". Todo lo que se escribe dentro de un contenedor desaparece cuando lo borras. Para datos que deben sobrevivir, usa volúmenes.
- Imágenes enormes. Usa variantes
-slimo-alpinede la imagen base y añade un.dockerignore(con.git,node_modules,.venv…) para no copiar basura a la imagen. permission deniedal usardockeren Linux. Tu usuario no está en el grupodocker. Añádelo consudo usermod -aG docker $USERy vuelve a iniciar sesión.
Siguiente paso: Docker Compose
Cuando tu aplicación necesita varios servicios (por ejemplo, un servidor web + una base de datos), usar docker run para cada uno se vuelve tedioso. Docker Compose permite definir y levantar todos los servicios con un solo comando:
# compose.yaml
services:
web:
build: .
ports:
- "5000:5000"
depends_on:
- db
db:
image: mysql:8.4
environment:
MYSQL_ROOT_PASSWORD: secreto
volumes:
- db-data:/var/lib/mysql
volumes:
db-data:
docker compose up
Con este archivo, levantas toda la infraestructura de tu app en un solo comando, y el volumen db-data hace que los datos de MySQL sobrevivan aunque borres el contenedor.
Conclusión
Docker simplifica enormemente el trabajo de los desarrolladores al eliminar el clásico "en mi máquina funciona". Si también trabajas con Node.js, puedes aplicar los mismos conceptos para dockerizar tu servidor Express o para aislar tu base de datos MySQL en un contenedor.
Y si vas más allá de las apps web, los contenedores también son la primera línea de defensa cuando ejecutas agentes de IA en tu máquina: en el caso del agente de OpenAI que hackeó a Hugging Face repaso cómo configurarlos para que un agente no pueda salir de su caja. Para proyectos Python, también te puede interesar uv, que acelera mucho la instalación de dependencias dentro de la imagen.