Docker ha revolucionado la forma en que los desarrolladores crean, desplaegan y escalan aplicaciones. Si estás comenzando tu viaje con containerización, esta guía te proporcionará una base sólida en los conceptos fundamentales de Docker.
¿Qué es Docker?
Docker es una plataforma de containerización que permite empacar aplicaciones y todas sus dependencias en unidades llamadas contenedores. Estos contenedores funcionan de forma consistente independientemente del sistema operativo o máquina en la que se ejecuten.
En términos sencillos: Docker resuelve el problema histórico “funciona en mi máquina” que ha frustrado a desarrolladores durante décadas. Con Docker, si una aplicación funciona en tu computadora, funcionará exactamente igual en el servidor de producción.
Concepto 1: Imágenes vs Contenedores
Estos son los dos conceptos más fundamentales de Docker. Es crucial entender su relación.
Docker Images (Imágenes)
Una imagen de Docker es una plantilla de solo lectura que contiene todo lo necesario para ejecutar una aplicación:
- Código fuente
- Runtime (Python, Node.js, Java)
- Dependencias y librerías del sistema
- Variables de entorno
- Instrucciones de configuración
Características principales:
- Inmutable: Una vez creada, la imagen nunca cambia
- Capas (Layers): Construida en capas, cada una representando cambios incrementales
- Reutilizable: Una sola imagen puede crear múltiples contenedores
Analogía: Una imagen es como una receta de pastel. Es un documento con todas las instrucciones necesarias.
Docker Containers (Contenedores)
Un contenedor es una instancia en ejecución de una imagen.
- Es el ambiente aislado donde tu aplicación realmente corre
- Tiene su propio sistema de archivos, variables de entorno, procesos de red
- Posee una capa escribible (writable layer) donde se almacenan cambios durante la ejecución
- Múltiples contenedores pueden ejecutarse desde la misma imagen
Características principales:
- Ligero: Comparte el kernel del sistema operativo host
- Aislado: No interfiere con otros contenedores o el host
- Efímero: Los cambios se pierden cuando el contenedor se detiene (a menos que uses volúmenes)
Analogía: Un contenedor es el pastel hornado usando la receta. Puedes hacer múltiples pasteles idénticos usando la misma receta.
Relación Imagen-Contenedor
Imagen (Blueprint) → docker run → Contenedor (Instancia ejecutándose)
| Aspecto | Imagen | Contenedor |
|---|---|---|
| Tipo | Estática | Dinámica |
| Estado | Inmutable | Mutable |
| Ejecución | No ejecutada | Ejecutándose |
| Capa escribible | No | Sí |
| Instancias | Una | Muchas desde una imagen |
Concepto 2: Docker vs Máquinas Virtuales
Mucha gente confunde Docker con máquinas virtuales (VMs), pero son fundamentalmente diferentes.
Arquitectura
Máquinas Virtuales:
Hardware → Hypervisor (VMware, VirtualBox) → Guest OS → Aplicación
Cada VM incluye su propio sistema operativo completo.
Docker Containers:
Hardware → Host OS → Docker Engine → Contenedor → Aplicación
Los contenedores comparten el kernel del sistema operativo del host.
Comparativa Detallada
| Característica | Docker Containers | Máquinas Virtuales |
|---|---|---|
| Tamaño de imagen | MB (típicamente 100-500MB) | GB (típicamente 2-10GB) |
| Tiempo de arranque | Segundos | Minutos |
| Uso de memoria | Comparte kernel del host | OS completo por VM |
| Aislamiento | Bueno (SO compartido) | Excelente (SO independiente) |
| Portabilidad | Altísima (multiplataforma) | Media (requiere hypervisor compatible) |
| Performance | Muy rápido | Más lento |
| Escalabilidad | Puedes ejecutar 100+ contenedores | Limitado a 10-20 VMs por máquina |
Caso de uso Docker: Desarrollo, testing, microservicios, aplicaciones cloud-native
Caso de uso VMs: Aplicaciones legacy, máximo aislamiento, diferentes SOs necesarios
Concepto 3: Instalación de Docker
La instalación varía según tu sistema operativo.
Windows y macOS
- Descarga Docker Desktop desde docker.com
- Ejecuta el instalador
- Reinicia tu computadora
- Abre terminal/PowerShell y verifica:
docker --version
docker run hello-world
Linux (Ubuntu/Debian)
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io -y
sudo systemctl start docker
sudo systemctl enable docker
Verifica la instalación:
docker --version
docker run hello-world
Si el comando anterior te pide sudo, agrega tu usuario al grupo docker:
sudo usermod -aG docker $USER
# Luego abre una nueva sesión o ejecuta:
newgrp docker
Concepto 4: Comandos Básicos de Docker
Una vez Docker está instalado, necesitas conocer los comandos esenciales para trabajar con imágenes y contenedores.
Comandos de Imágenes
Descargar una imagen:
docker pull ubuntu:latest
Descarga la imagen de Ubuntu desde Docker Hub (repositorio público).
Ver todas las imágenes locales:
docker images
# O más detallado:
docker image ls
Eliminar una imagen:
docker rmi nombre-imagen:tag
Buscar imágenes en Docker Hub:
docker search python
Comandos de Contenedores
Ejecutar un contenedor:
docker run -it ubuntu:latest
| Flag | Significado |
|---|---|
-i | Interactive (mantiene STDIN abierto) |
-t | Asigna pseudo-terminal |
-d | Detached (ejecuta en background) |
-p | Puerto mapping (host:contenedor) |
-v | Volumen (persistencia de datos) |
--name | Nombre personalizado del contenedor |
Ver contenedores en ejecución:
docker ps
# Ver todos (incluyendo detenidos):
docker ps -a
Detener un contenedor:
docker stop nombre-contenedor
# Terminar forzosamente:
docker kill nombre-contenedor
Eliminar un contenedor:
docker rm nombre-contenedor
Ver logs de un contenedor:
docker logs nombre-contenedor
docker logs -f nombre-contenedor # Sigue en tiempo real
Concepto 5: Dockerfile – Crear Imágenes Personalizadas
Un Dockerfile es un archivo de texto que contiene instrucciones para construir una imagen Docker personalizada.
Estructura Básica
Cada Dockerfile comienza con un comando FROM (obligatorio) y sigue una estructura clara:
# Comentario: especifica la imagen base
FROM python:3.9-slim
# Metadata: quién mantiene esta imagen
MAINTAINER Tu Nombre <tu@email.com>
# Establece el directorio de trabajo
WORKDIR /usr/src/app
# Copia los archivos del host al contenedor
COPY requirements.txt .
# Ejecuta comandos durante la construcción
RUN pip install --no-cache-dir -r requirements.txt
# Copia el código de la aplicación
COPY . .
# Expone el puerto que usará la aplicación
EXPOSE 8000
# Comando por defecto al iniciar el contenedor
CMD ["python", "app.py"]
Instrucciones Clave del Dockerfile
| Instrucción | Propósito | Ejemplo |
|---|---|---|
| FROM | Especifica imagen base (obligatorio) | FROM ubuntu:20.04 |
| WORKDIR | Establece directorio de trabajo | WORKDIR /app |
| COPY | Copia archivos del host al contenedor | COPY requirements.txt /app/ |
| RUN | Ejecuta comando durante build | RUN apt-get update && apt-get install -y curl |
| ENV | Establece variables de entorno | ENV PORT=8000 |
| EXPOSE | Documenta qué puerto escucha | EXPOSE 8080 |
| CMD | Comando por defecto al iniciar | CMD ["python", "app.py"] |
| ENTRYPOINT | Configura contenedor como ejecutable | ENTRYPOINT ["node"] |
Ejemplo Práctico: Aplicación Python
Paso 1: Crea la estructura del proyecto
tmi-app/
├── app.py
├── requirements.txt
└── Dockerfile
Paso 2: Crea los archivos de aplicación
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hola desde Docker!'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
# requirements.txt
Flask==2.3.0
Paso 3: Crea el Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
Paso 4: Construye la imagen
docker build -t mi-app:1.0 .
| Opción | Función |
|---|---|
-t | Tag (nombre) de la imagen |
. | Contexto de build (directorio actual) |
Paso 5: Ejecuta el contenedor
docker run -p 5000:5000 mi-app:1.0
Visita http://localhost:5000 en tu navegador.
Concepto 6: Volúmenes – Persistencia de Datos
Por defecto, los datos dentro de un contenedor se pierden cuando este se detiene. Los volúmenes permiten persistencia de datos entre ciclos de vida del contenedor.
Tipos de Volúmenes
1. Named Volumes (Volúmenes nombrados)
Gestionados completamente por Docker:
# Crear volumen
docker volume create mi-volumen
# Usar volumen en contenedor
docker run -v mi-volumen:/data mi-app
# Ver volúmenes
docker volume ls
# Eliminar volumen
docker volume rm mi-volumen
2. Bind Mounts (Montajes vinculados)
Mapean un directorio del host al contenedor:
docker run -v /ruta/host:/ruta/contenedor mi-app
Ejemplo práctico: Base de datos persistente
# Crear volumen para PostgreSQL
docker volume create postgres-data
# Ejecutar PostgreSQL con volumen
docker run -d \
--name mi-db \
-e POSTGRES_PASSWORD=secreto \
-v postgres-data:/var/lib/postgresql/data \
postgres:13
Incluso si eliminas el contenedor, los datos persisten en el volumen.
Concepto 7: Docker Networking
Los contenedores necesitan comunicarse entre sí. Docker proporciona capacidades de networking para conectar contenedores.
Tipos de Redes
1. Bridge (Por defecto)
Contenedores en la misma red bridge pueden comunicarse por nombre:
# Crear red
docker network create mi-red
# Conectar contenedores a la red
docker run --network mi-red --name contenedor1 ubuntu
docker run --network mi-red --name contenedor2 ubuntu
# Dentro del contenedor, ping por nombre:
# ping contenedor1
2. Host
El contenedor comparte la red del host (performance máxima):
docker run --network host mi-app
3. Overlay
Para múltiples hosts en Docker Swarm (avanzado).
Port Mapping
Expone puertos del contenedor al host:
# Sintaxis: -p puerto_host:puerto_contenedor
docker run -p 8080:5000 mi-app
Ahora puedes acceder a la aplicación en http://localhost:8080.
Concepto 8: Docker Compose – Multi-contenedor
Las aplicaciones reales típicamente requieren múltiples servicios (web app, base de datos, caché). Docker Compose permite definir y ejecutar aplicaciones multi-contenedor en un archivo YAML.
Archivo docker-compose.yml Básico
version: '3'
services:
web:
build: .
ports:
- "5000:5000"
environment:
- DATABASE_URL=postgresql://db:5432/myapp
depends_on:
- db
volumes:
- .:/app
db:
image: postgres:13
environment:
- POSTGRES_PASSWORD=secreto
volumes:
- postgres-data:/var/lib/postgresql/data
volumes:
postgres-data:
Explicación
| Clave | Propósito |
|---|---|
| version | Versión de Docker Compose |
| services | Contenedores que forman la app |
| build | Dockerfile a usar (. = actual) |
| ports | Port mapping |
| environment | Variables de entorno |
| depends_on | Orden de inicio de servicios |
| volumes | Persistencia de datos |
| networks | Redes personalizadas (auto creadas) |
Comandos Docker Compose
# Iniciar todos los servicios
docker-compose up
# En background
docker-compose up -d
# Ver logs
docker-compose logs
docker-compose logs -f web # Solo servicio web
# Detener servicios
docker-compose down
# Reconstruir imágenes
docker-compose up --build
Ejemplo Práctico Completo: Flask + Redis
Paso 1: Estructura del proyecto
mi-proyecto/
├── app.py
├── requirements.txt
├── Dockerfile
└── docker-compose.yml
Paso 2: Archivos
# app.py
from flask import Flask, jsonify, request
from redis import Redis
app = Flask(__name__)
redis = Redis(host='redis', port=6379, decode_responses=True)
@app.route('/', methods=['GET', 'POST'])
def home():
if request.method == 'POST':
data = request.json
redis.lpush('items', data['item'])
return jsonify({'status': 'success'})
items = redis.lrange('items', 0, -1)
return jsonify({'items': items})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
# requirements.txt
Flask==2.3.0
redis==4.5.0
# Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
# docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- "5000:5000"
depends_on:
- redis
environment:
- REDIS_HOST=redis
- REDIS_PORT=6379
redis:
image: redis:6-alpine
ports:
- "6379:6379"
Paso 3: Ejecuta
docker-compose up
La aplicación Flask ahora se comunica con Redis a través de la red de Docker Compose.
Conceptos Avanzados Introductorios
Capas de Imagen (Image Layers)
Cada instrucción en un Dockerfile crea una capa. Docker cachea estas capas para acelerar reconstrucciones:
FROM ubuntu:20.04 # Capa 1
RUN apt-get update # Capa 2
COPY app.py /app/ # Capa 3
RUN pip install flask # Capa 4
CMD ["python", "app.py"] # Capa 5
Ventaja: Si cambias solo app.py, Docker no reconstruye las capas 1-2.
Multi-stage Builds (Optimización)
Para reducir tamaño de imagen en producción:
# Etapa 1: Build
FROM node:16 AS builder
WORKDIR /app
COPY . .
RUN npm install && npm run build
# Etapa 2: Runtime (solo lo necesario)
FROM node:16-alpine
WORKDIR /app
COPY --from=builder /app/dist .
CMD ["node", "index.js"]
La imagen final solo contiene el código compilado, no las dependencias de build.
Mejores Prácticas para Principiantes
- Usa imágenes oficiales como base: Alpine para tamaño pequeño, slim para tamaño medio
- Mantén Dockerfiles simples: Una responsabilidad por contenedor
- No ejecutes como root: Crea usuario específico en Dockerfile
- Usa .dockerignore: Excluye archivos innecesarios (como git/)
- Etiqueta imágenes: Usa versionado
mi-app:1.0.0 - Documenta puertos y volúmenes: Para que otros sepan cómo usar tu imagen
Checklist para Comenzar
- ✅ Instala Docker Desktop o Docker Engine
- ✅ Ejecuta
docker run hello-worldpara verificar - ✅ Aprende los comandos básicos (run, ps, logs, stop)
- ✅ Crea tu primer Dockerfile
- ✅ Construye una imagen simple
- ✅ Ejecuta un contenedor desde tu imagen
- ✅ Practica con Docker Compose (2+ servicios)
- ✅ Experimenta con volúmenes y networking
Docker es una tecnología transformadora que simplifica la entrega de software. Los conceptos clave—imágenes, contenedores, Dockerfiles y Docker Compose—proporcionan la base para trabajar con containerización en 2025.
Aunque Docker parece complejo inicialmente, practicando con proyectos simples rápidamente comprenderás cómo revoluciona el desarrollo, testing y despliegue de aplicaciones. La curva de aprendizaje es empinada al principio, pero los beneficios en consistencia, portabilidad y velocidad de desarrollo justifican completamente el esfuerzo.
Comienza con contenedores individuales, avanza a Docker Compose con múltiples servicios, y eventualmente explorarás orquestación con Kubernetes para aplicaciones a escala empresarial.