Skip to content

Docker fundamentos ​

Docker permite crear, desplegar y ejecutar aplicaciones en contenedores—aíslados del sistema operativo. Cada contenedor tiene su propio sistema de archivos, librerías, y dependencias.

Conceptos básicos ​

Imagen vs Contenedor ​

  • Imagen: plantilla read-only para crear contenedores. Como una clase en OOP.
  • Contenedor: instancia ejecutándose de una imagen. Como un objeto en OOP.

Registry ​

Lugar donde se almacenan imágenes Docker. El más común es Docker Hub (docker.io). Puedes usar otros (Google Container Registry, GitHub Packages, etc.).

Comandos esenciales ​

Imágenes ​

bash
docker images                    # Ver imágenes descargadas
docker pull ubuntu:22.04        # Descargar imagen
docker rmi ubuntu:22.04         # Eliminar imagen

Contenedores ​

bash
docker ps                        # Contenedores ejecutándose
docker ps -a                     # Todos los contenedores (incluidos parados)
docker run -it ubuntu:22.04      # Crear y ejecutar contenedor interactivo
docker start mi-contenedor        # Iniciar contenedor parado
docker stop mi-contenedor         # Parar contenedor
docker rm mi-contenedor           # Eliminar contenedor

flags comunes ​

bash
docker run -d                    # Modo detached (en segundo plano)
docker run -p 8080:80             # Mapear puerto host:contenedor
docker run -v /data:/data         # Montar volumen host:contenedor
docker run --name mi-app          # Nombrar el contenedor

Dockerfile ​

Archivo de instrucciones para construir una imagen.

Ejemplo: Videogames API (Java 21) ​

dockerfile
# Usar imagen oficial de Java con Alpine (ligero)
FROM eclipse-temurin:21-jdk-alpine

# Directorio de trabajo
WORKDIR /app

# Copiar el JAR compilado
COPY target/*.jar app.jar

# Exponer puerto
EXPOSE 8080

# Comando inicial
ENTRYPOINT ["java", "-jar", "app.jar"]

Ejemplo: FastAPI (Python) ​

dockerfile
FROM python:3.12-slim

WORKDIR /app

# Copiar requirements primero (mejor caché)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copiar código
COPY . .

EXPOSE 8000

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Buenas prácticas ​

Orden de capas importa: Docker reconstruye desde la capa que cambió. Copiar requirements.txt antes que el código permite caché:

dockerfile
COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .   # Este cambio no invalida el caché de pip

Usar imágenes oficiales pequeñas: alpine vs ubuntu vs debian.

Docker Compose ​

Para aplicaciones con múltiples contenedores (ej: app + base de datos).

yaml
# docker-compose.yml
version: '3.8'

services:
  api:
    build: .
    ports:
      - "8080:8080"
    environment:
      - DATABASE_URL=postgresql://db:5432/mydb
    depends_on:
      - db

  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=mydb
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=secret
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
bash
docker-compose up -d    # Crear e iniciar
docker-compose down   # Parar y eliminar
docker-compose logs   # Ver logs
docker-compose exec db psql -U user -d mydb  # Conectar a DB

Concepto de capas ​

Cada instrucción en Dockerfile crea una capa (layer):

dockerfile
FROM ubuntu:22.04          # Capa 1: imagen base
RUN apt-get update          # Capa 2
RUN apt-get install -y git # Capa 3
COPY . /app                 # Capa 4

Docker reutiliza capas entre imágenes si no cambiaron. Por eso copiar requirements.txt antes que el código fuente aprovecha el caché.

Desarrollo local con Docker ​

Mi flujo para proyectos Python/Flask ​

bash
# 1. Crear Dockerfile en backend/
# 2. docker-compose.yml
# 3. Levantar
docker-compose up --build

# 4. Ver logs
docker-compose logs -f backend

# 5. Debug: entrar al contenedor
docker-compose exec backend bash

Volúmenes para desarrollo ​

yaml
services:
  backend:
    build: .
    volumes:
      - ./backend:/app       # Sincroniza cambios en tiempo real
    command: uvicorn main:app --reload --host 0.0.0.0 --port 8000

Con --reload, Flask recarga cuando cambias el código.

Ejemplo real: Videogames API en Railway ​

Railway puede desplegar desde un Dockerfile:

dockerfile
FROM eclipse-temurin:21-jdk-alpine
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Railway detecta el Dockerfile, construye la imagen, y despliega el contenedor.

Comandos de limpieza ​

bash
docker system prune          # Eliminar todo lo no usado
docker image prune -a        # Eliminar imágenes sin usar
docker container prune      # Eliminar contenedores parados

Cuándo usar Docker ​

Sí:

  • Desplegar en producción ( Railway, Render, Fly.io)
  • Entorno de desarrollo consistente (todos usan la misma imagen)
  • Microservicios (base de datos separada de la app)
  • CI/CD (mismo entorno en pipeline que en producción)

No:

  • Proyecto simple de una sola página → overkill
  • Cuando el hosting no lo soporta (GitHub Pages no tiene Docker)

Docker parece complicado al principio, pero su modelo es simple: imágenes capas, contenedores instancias, compose multi-contenedor. Una vez que lo internalizas, desplegar deja de dar miedo.

Más guías y proyectos en mmoreno.dev.