Docker para principiantes: conceptos esenciales

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)
AspectoImagenContenedor
TipoEstáticaDinámica
EstadoInmutableMutable
EjecuciónNo ejecutadaEjecutándose
Capa escribibleNo
InstanciasUnaMuchas 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ísticaDocker ContainersMáquinas Virtuales
Tamaño de imagenMB (típicamente 100-500MB)GB (típicamente 2-10GB)
Tiempo de arranqueSegundosMinutos
Uso de memoriaComparte kernel del hostOS completo por VM
AislamientoBueno (SO compartido)Excelente (SO independiente)
PortabilidadAltísima (multiplataforma)Media (requiere hypervisor compatible)
PerformanceMuy rápidoMás lento
EscalabilidadPuedes ejecutar 100+ contenedoresLimitado 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

  1. Descarga Docker Desktop desde docker.com
  2. Ejecuta el instalador
  3. Reinicia tu computadora
  4. 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
FlagSignificado
-iInteractive (mantiene STDIN abierto)
-tAsigna pseudo-terminal
-dDetached (ejecuta en background)
-pPuerto mapping (host:contenedor)
-vVolumen (persistencia de datos)
--nameNombre 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ónPropósitoEjemplo
FROMEspecifica imagen base (obligatorio)FROM ubuntu:20.04
WORKDIREstablece directorio de trabajoWORKDIR /app
COPYCopia archivos del host al contenedorCOPY requirements.txt /app/
RUNEjecuta comando durante buildRUN apt-get update && apt-get install -y curl
ENVEstablece variables de entornoENV PORT=8000
EXPOSEDocumenta qué puerto escuchaEXPOSE 8080
CMDComando por defecto al iniciarCMD ["python", "app.py"]
ENTRYPOINTConfigura contenedor como ejecutableENTRYPOINT ["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ónFunción
-tTag (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

ClavePropósito
versionVersión de Docker Compose
servicesContenedores que forman la app
buildDockerfile a usar (. = actual)
portsPort mapping
environmentVariables de entorno
depends_onOrden de inicio de servicios
volumesPersistencia de datos
networksRedes 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

  1. Usa imágenes oficiales como base: Alpine para tamaño pequeño, slim para tamaño medio
  2. Mantén Dockerfiles simples: Una responsabilidad por contenedor
  3. No ejecutes como root: Crea usuario específico en Dockerfile
  4. Usa .dockerignore: Excluye archivos innecesarios (como git/)
  5. Etiqueta imágenes: Usa versionado mi-app:1.0.0
  6. 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-world para 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.