Vamos a sumergirnos en el mundo de los contenedores, una tecnología que ha transformado la forma en que los desarrolladores empaquetan, distribuyen y ejecutan sus aplicaciones. Si bien es un concepto que a menudo se asocia con el backend, entender los fundamentos de los contenedores es crucial para cualquier desarrollador, incluido el frontend.
## ¿Qué es un Contenedor?
Un contenedor es una unidad de software empaquetada y estandarizada que puede incluir todo lo necesario para ejecutar una aplicación: el código, el entorno de ejecución, las librerías del sistema y las dependencias.
Piensen en un contenedor como una caja 📦 totalmente aislada que contiene su aplicación. No importa en qué máquina o sistema operativo la pongan, la aplicación dentro de la caja funcionará exactamente igual porque tiene todo lo que necesita consigo. Esto resuelve el clásico problema de **"funciona en mi máquina"**.
> **Nota:** A diferencia de las máquinas virtuales (VMs), los contenedores son mucho más ligeros y rápidos porque comparten el kernel del sistema operativo del host en lugar de virtualizar un sistema operativo completo.
## Docker: El Rey de los Contenedores
Docker es la plataforma más popular para crear, gestionar y ejecutar contenedores. Vamos a ver los componentes principales del ecosistema de Docker.
### Arquitectura de un Host con Docker

Un host con Docker tiene dos partes principales:
- **Docker Daemon (o Engine):** Es el proceso que se ejecuta en segundo plano y gestiona todas las operaciones de los contenedores. Se encarga de construir imágenes, ejecutar contenedores y manejar la red y el almacenamiento.
- **Docker CLI (Cliente):** Es la herramienta de línea de comandos que usamos para interactuar con el Docker Daemon. Cuando escribes un comando como `docker run`, el cliente se comunica con el daemon para ejecutar la acción.
### Imágenes vs. Contenedores
Es crucial entender la diferencia entre estos dos conceptos:
- **Imagen:** Una imagen es una plantilla inmutable que contiene las instrucciones para crear un contenedor. Las imágenes se construyen a partir de un Dockerfile.
- **Contenedor:** Un contenedor es una instancia en ejecución de una imagen. Se pueden crear múltiples contenedores a partir de una misma imagen.
## Conceptos Clave en Docker
### Persistencia de Datos
Por defecto, los contenedores son efímeros. Esto significa que cuando se detiene y se elimina un contenedor, todos los datos que estaban dentro de él se pierden. Para que los datos persistan, usamos **volúmenes**.
Un volumen es un mecanismo de almacenamiento que existe fuera del ciclo de vida del contenedor. Es un directorio en el sistema de archivos del host que se "monta" o se enlaza a un directorio dentro del contenedor. De esta manera, aunque el contenedor sea eliminado, los datos en el volumen permanecen.
### Exposición de Puertos
Si su aplicación se ejecuta dentro de un contenedor, ¿cómo se conecta un usuario a ella? La respuesta es **exponiendo puertos**.
La exposición de puertos crea un enlace entre un puerto del host (tu máquina) y un puerto del contenedor. Por ejemplo, si tu aplicación frontend se ejecuta en el puerto 3000 dentro del contenedor, puedes mapear el puerto 8080 de tu máquina al puerto 3000 del contenedor (`-p 8080:3000`). Así, al acceder a `localhost:8080` desde tu navegador, en realidad estás accediendo a la aplicación dentro del contenedor.
## Comandos Básicos de Docker CLI
Ahora, vamos a ver los comandos que más usarán en su día a día.
| Comando | Descripción | Ejemplo de Uso |
|---------|-------------|----------------|
| `docker pull` | Descarga una imagen de un registro (como Docker Hub). | `docker pull node:18-alpine` |
| `docker build` | Construye una imagen a partir de un Dockerfile. | `docker build -t mi-app-frontend .` |
| `docker run` | Crea y ejecuta un contenedor a partir de una imagen. | `docker run -d -p 8080:3000 mi-app-frontend` |
| `docker ps` | Lista los contenedores en ejecución. Usa `-a` para ver todos (ejecutando y detenidos). | `docker ps -a` |
| `docker stop` | Detiene un contenedor en ejecución. | `docker stop <ID_o_nombre_del_contenedor>` |
| `docker start` | Inicia un contenedor que está detenido. | `docker start <ID_o_nombre_del_contenedor>` |
| `docker exec` | Ejecuta un comando dentro de un contenedor en ejecución. | `docker exec -it <ID_o_nombre_del_contenedor> /bin/bash` |
## Docker Compose: Orquestación de Aplicaciones
Cuando una aplicación se vuelve más compleja, con un frontend, un backend y una base de datos, ejecutar cada contenedor individualmente se vuelve tedioso. Aquí es donde entra **Docker Compose**.
Docker Compose es una herramienta para definir y ejecutar aplicaciones multi-contenedor usando un solo archivo `docker-compose.yml`.
### Ejemplo de docker-compose.yml
```yaml
version: '3.8'
services:
frontend:
build: .
ports:
- "8080:3000"
volumes:
- ./src:/usr/src/app/src
container_name: mi_app_frontend
command: npm start
backend:
image: mi-backend-imagen:1.0
ports:
- "5000:5000"
```
#### Explicación de las propiedades:
- **`version`:** Especifica la versión del formato del archivo Compose.
- **`services`:** Define los servicios (contenedores) de la aplicación.
- **`frontend` y `backend`:** Son los nombres de los servicios que vamos a crear.
- **`build: .`:** Le dice a Docker que construya la imagen para el servicio frontend usando el Dockerfile que está en el directorio actual.
- **`ports`:** Mapea los puertos del host a los puertos del contenedor.
- **`volumes`:** Monta un volumen. En este caso, el código fuente local (`./src`) se sincroniza con el código dentro del contenedor, lo que permite ver los cambios en tiempo real sin tener que reconstruir la imagen. ¡Ideal para desarrollo!
- **`container_name`:** Asigna un nombre específico al contenedor.
### Comandos Útiles de Docker Compose
| Comando | Descripción | Ejemplo de Uso |
|---------|-------------|----------------|
| `docker compose up` | Construye, crea y levanta todos los servicios definidos en el archivo docker-compose.yml. | `docker compose up -d` Modo que ejecuta los contenedores en segundo plano.|
| `docker compose down` | Detiene y elimina los contenedores, redes y volúmenes creados por up. | `docker compose down` |
| `docker compose ps` | Lista los servicios de la aplicación. | `docker compose ps` |
| `docker compose logs` | Muestra los logs de los servicios. | `docker compose logs frontend` |
## ¿Por qué deberíamos usar Docker?
- **Entorno de desarrollo consistente:** Todos en el equipo trabajan con el mismo entorno, eliminando problemas de dependencias.
- **Aislamiento:** Permite probar diferentes versiones de Node.js o de librerías sin afectar el sistema global.
### 2) Instalar una distribución Linux (recomendado Ubuntu)
- Opción rápida (Windows 11 y 10 recientes):
```powershell
wsl --install -d Ubuntu
```
- Alternativa: instala "Ubuntu" desde Microsoft Store y luego inicialízala (crea usuario y contraseña Linux).
### 3) Instalar Docker Desktop para Windows
- Descarga e instala desde: [Docker Desktop para Windows](https://www.docker.com/products/docker-desktop/).
- Durante la instalación, deja activada la opción **Use WSL 2 instead of Hyper-V**.
- Abre Docker Desktop y en Settings > Resources > WSL Integration, habilita la integración con tu distro (p. ej., Ubuntu).
### 4) Verificar instalación
En una terminal (PowerShell o Ubuntu en WSL):
```powershell
docker --version
docker compose version
docker run hello-world
```
Si el comando `hello-world` imprime un mensaje de bienvenida, Docker está funcionando correctamente.
> Nota: Si ves errores de permisos desde WSL, asegúrate de que la integración WSL esté habilitada en Docker Desktop y reinicia tanto Docker Desktop como la distro WSL (`wsl --shutdown`).
## Instalación de Docker en Ubuntu
Instalación usando el repositorio oficial de Docker (recomendado para Ubuntu 20.04/22.04/24.04).