Docker ha pasado de ser una curiosidad para ingenieros de sistemas a una herramienta indispensable para cualquier persona que trabaje con software. Si alguna vez has oído eso de "en mi máquina funciona" y has sentido escalofríos, Docker es exactamente lo que necesitas.
En esta guía práctica vamos a ver qué es Docker, por qué deberías usarlo y, sobre todo, cómo empezar a usarlo desde cero con ejemplos que puedas probar tú mismo.
¿Qué es Docker y por qué debería importarte?
Docker es una herramienta que permite empaquetar una aplicación con todo lo que necesita para funcionar (librerías, dependencias, configuración) en un objeto llamado contenedor. El contenedor se puede ejecutar en cualquier máquina que tenga Docker instalado, independientemente de lo que haya instalado en ella.
Piénsalo como una caja preconfigurada que contiene tu aplicación y todo su entorno. No importa si la abres en Windows, macOS o Linux: dentro de la caja, la aplicación ve siempre el mismo sistema de archivos, las mismas variables de entorno y las mismas versiones de librerías.
¿Por qué es útil?
- Fin del "en mi máquina funciona": si tu aplicación funciona en un contenedor, funcionará igual en desarrollo, en pruebas y en producción.
- Aislamiento limpio: cada contenedor tiene su propio sistema de archivos, procesos y red. Puedes tener dos aplicaciones que necesiten versiones distintas de Python sin conflictos.
- Ligero: a diferencia de las máquinas virtuales, los contenedores comparten el kernel del sistema anfitrión. No arrancan un sistema operativo completo, solo la aplicación y sus dependencias.
Conceptos básicos: imagen vs contenedor
Antes de meter mano, conviene tener claros dos conceptos:
| Concepto | Qué es |
|---|---|
| Imagen | La receta o plantilla. Un archivo inmutable que contiene todo lo necesario para ejecutar una aplicación. |
| Contenedor | La instancia en ejecución de una imagen. Puedes tener múltiples contenedores de la misma imagen, cada uno con su propio estado. |
La analogía con la programación orientada a objetos funciona bien: una imagen es una clase, un contenedor es una instancia de esa clase.
Paso 1: Instalar Docker
Docker está disponible para Linux, macOS y Windows.
Linux (Debian/Ubuntu):
sudo apt update
sudo apt install docker.io
sudo systemctl start docker
sudo systemctl enable docker
Windows y macOS: descarga Docker Desktop e instálalo. En Windows necesitarás tener WSL2 configurado (Docker Desktop te guía en el proceso).
Para verificar que Docker se ha instalado correctamente:
docker --version
Si ves algo como Docker version 27.x.x, estás listo.
Paso 2: Tu primer contenedor
Abre un terminal y ejecuta:
docker run hello-world
Este comando hace varias cosas:
1. Docker busca la imagen hello-world en tu máquina
2. Como no la encuentra, la descarga de Docker Hub (el repositorio oficial de imágenes)
3. Crea un contenedor a partir de esa imagen y lo ejecuta
4. El contenedor imprime un mensaje y se detiene
Has ejecutado tu primer contenedor. No ha pasado nada espectacular, pero el mecanismo es el mismo que usarás para ejecutar cualquier aplicación.
Paso 3: Ejecutar aplicaciones reales en contenedores
Vamos a hacer algo más útil. Vamos a ejecutar un servidor web Nginx:
docker run -d -p 8080:80 --name mi-nginx nginx
Desglose del comando:
- -d → modo "detached": el contenedor se ejecuta en segundo plano
- -p 8080:80 → mapea el puerto 80 del contenedor al puerto 8080 de tu máquina
- --name mi-nginx → le damos un nombre para referirnos a él después
- nginx → el nombre de la imagen en Docker Hub
Abre tu navegador en http://localhost:8080. Verás la página de bienvenida de Nginx.
Para ver los contenedores en ejecución:
docker ps
Para detener el contenedor:
docker stop mi-nginx
Para eliminarlo:
docker rm mi-nginx
Paso 4: Gestionar imágenes y contenedores
Aquí tienes los comandos esenciales del día a día:
# Listar imágenes descargadas
docker images
# Listar contenedores en ejecución
docker ps
# Listar todos los contenedores (incluyendo los detenidos)
docker ps -a
# Ver logs de un contenedor en tiempo real
docker logs -f mi-nginx
# Ejecutar un comando dentro de un contenedor en ejecución
docker exec -it mi-nginx bash
# Eliminar una imagen
docker rmi nginx
# Eliminar todos los contenedores detenidos
docker container prune
Paso 5: Crear tu propia imagen con un Dockerfile
Hasta ahora hemos usado imágenes creadas por otros. Para contenerizar tu propia aplicación necesitas crear un Dockerfile, que es el archivo de instrucciones para construir tu imagen.
Vamos a crear una imagen para una aplicación simple en Python que muestra la hora actual.
Crea un archivo app.py:
from http.server import HTTPServer, BaseHTTPRequestHandler
from datetime import datetime
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/plain; charset=utf-8')
self.end_headers()
now = datetime.now().strftime('%d/%m/%Y %H:%M:%S')
self.wfile.write(f'🕒 Hora actual: {now}\n'.encode('utf-8'))
HTTPServer(('0.0.0.0', 8080), Handler).serve_forever()
Ahora crea un archivo llamado Dockerfile en el mismo directorio:
# Imagen base ligera de Python
FROM python:3.11-alpine
# Directorio de trabajo dentro del contenedor
WORKDIR /app
# Copiar nuestro código al contenedor
COPY app.py .
# Puerto que expone la aplicación
EXPOSE 8080
# Comando que se ejecuta al iniciar el contenedor
CMD ["python", "app.py"]
Construye la imagen:
docker build -t hora-actual .
Ejecútala:
docker run -d -p 8080:8080 --name mi-hora hora-actual
Visita http://localhost:8080 y verás la hora actual. Acabas de contenerizar tu primera aplicación.
Paso 6: Persistir datos con volúmenes
Los contenedores son efímeros: cuando los eliminas, todos los datos generados dentro se pierden. Para datos que deben persistir (bases de datos, archivos subidos, logs), se usan volúmenes.
Ejecuta una base de datos PostgreSQL con un volumen persistente:
docker run -d \
-p 5432:5432 \
-e POSTGRES_PASSWORD=mi_password \
-v pgdata:/var/lib/postgresql/data \
--name mi-postgres \
postgres:16
El flag -v pgdata:/var/lib/postgresql/data crea un volumen llamado pgdata y lo monta en el directorio de datos de PostgreSQL. Aunque elimines y recrees el contenedor, los datos se conservan.
Para ver y gestionar volúmenes:
docker volume ls
docker volume inspect pgdata
Paso 7: Orquestar múltiples contenedores con Docker Compose
Cuando tu aplicación necesita varios servicios (una base de datos, un backend, un frontend), gestionarlos uno a uno con docker run se vuelve tedioso. Docker Compose te permite definir todos los servicios en un archivo YAML y levantarlos con un solo comando.
Crea un archivo compose.yaml:
services:
web:
build: .
ports:
- "8080:8080"
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: mi_password
POSTGRES_DB: miapp
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Levanta todo con un solo comando:
docker compose up -d
Para ver los logs de todos los servicios a la vez:
docker compose logs -f
Para detener y limpiar:
docker compose down
Docker Compose es probablemente la herramienta que más usarás a partir de ahora. Te permite tener entornos de desarrollo completos con una sola instrucción y compartirlos con tu equipo.
Resumen y próximos pasos
En esta guía has aprendido:
- Qué son los contenedores y por qué resuelven el problema del "en mi máquina funciona"
- La diferencia entre imagen y contenedor
- Comandos esenciales:
docker run,docker ps,docker stop,docker exec - Cómo crear tu propia imagen con un Dockerfile
- Cómo persistir datos con volúmenes
- Cómo orquestar servicios con Docker Compose
¿Qué más puedes explorar?
- Docker Hub tiene miles de imágenes listas para usar: bases de datos, servidores web, lenguajes de programación
- Las redes de Docker te permiten aislar o conectar contenedores entre sí de forma flexible
- Kubernetes es el siguiente escalón si necesitas gestionar decenas o cientos de contenedores en producción
Docker no es difícil: son cuatro conceptos bien entendidos y el resto es práctica. Empieza por contenerizar una aplicación pequeña y ve aumentando la complejidad poco a poco. En un par de semanas te preguntarás cómo trabajabas sin él.