Primer commit

This commit is contained in:
mchiecher 2025-09-11 07:17:37 -03:00
commit 54e872d46c
9 changed files with 293 additions and 0 deletions

117
README.md Normal file
View File

@ -0,0 +1,117 @@
# Introducción a la Contenerización 🐳
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
![Arquitectura de Docker](dockerarchitecture.webp)
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 rm` | Elimina un contenedor detenido. | `docker rm <ID_o_nombre_del_contenedor>` |
| `docker rmi` | Elimina una imagen. | `docker rmi <nombre_de_la_imagen>` |
| `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.
- **Despliegue simplificado:** El mismo contenedor que se usa para desarrollo se puede usar en producción, lo que reduce las incompatibilidades.

31
backend/build/Dockerfile Normal file
View File

@ -0,0 +1,31 @@
# ===========================================
# DOCKERFILE PARA APLICACIÓN BACKEND PYTHON
# ===========================================
# Usamos una imagen base ligera de Python 3.10 basada en Alpine Linux
# Alpine es una distribución Linux muy pequeña (~5MB) que reduce el tamaño de la imagen
# python:3.10-alpine incluye Python 3.10 y las herramientas básicas necesarias
FROM python:3.10-alpine
# Establecemos el directorio de trabajo dentro del contenedor
# Todas las operaciones posteriores (COPY, RUN, CMD) se ejecutarán desde este directorio
# /usr/src/app es una convención común para aplicaciones en contenedores
WORKDIR /usr/src/app
# Copiamos el archivo de la aplicación Python desde el host al contenedor
# El primer parámetro (app.py) es el archivo fuente en el host
# El segundo parámetro (.) es el destino en el contenedor (directorio actual = /usr/src/app)
# Esto copia app.py al directorio de trabajo del contenedor
COPY app.py .
# Exponemos el puerto 8000 que usará el servidor web
# Esta instrucción es solo documentativa - no abre el puerto automáticamente
# El puerto se abre realmente cuando se ejecuta el contenedor con -p o en docker-compose
# Es una buena práctica documentar qué puertos usa la aplicación
EXPOSE 8000
# Comando por defecto para iniciar la aplicación cuando se ejecute el contenedor
# Se ejecuta automáticamente cuando se inicia el contenedor (a menos que se sobrescriba)
# Formato de array: ["comando", "argumento1", "argumento2"]
# python app.py ejecutará el archivo app.py con el intérprete de Python
CMD ["python", "app.py"]

22
backend/build/app.py Normal file
View File

@ -0,0 +1,22 @@
import http.server
import socketserver
# Puerto en el que se ejecutará el servidor
PORT = 8000
# Creamos un manejador de solicitudes HTTP
class MyHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
# Establecemos el código de respuesta y el tipo de contenido
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
# Enviamos la respuesta HTML
self.wfile.write(bytes("<h1>Hola desde el Backend</h1><p>Esta es una aplicacion de ejemplo con el servidor web de Python.</p>", "utf-8"))
# Configuramos el servidor
with socketserver.TCPServer(("", PORT), MyHandler) as httpd:
# Ahora el mensaje se imprime solo una vez, cuando el servidor se levanta correctamente
print(f"Servidor backend corriendo en el puerto {PORT}")
httpd.serve_forever()

39
docker-compose.yml Normal file
View File

@ -0,0 +1,39 @@
# Versión del formato de Docker Compose
# La versión 3.8 es compatible con Docker Engine 19.03.0+
# Actualmente especificar la versión es opcional.
version: '3.8'
# Sección que define todos los servicios (contenedores) de la aplicación
services:
# Servicio para la aplicación frontend de Node.js
frontend:
# Configuración de construcción de la imagen Docker
build:
# Directorio donde se encuentra el Dockerfile para construir la imagen
# En este caso, busca el Dockerfile en la carpeta ./frontend/build
context: ./frontend/build
# Mapeo de puertos entre el host y el contenedor
# Formato: "puerto_host:puerto_contenedor"
# El puerto 3000 del host se mapea al puerto 3000 del contenedor
ports:
- "3000:3000"
# Las líneas siguientes (comentadas) definen volúmenes (bind mounts):
# montan archivos/carpetas del host dentro del contenedor
#volumes:
# - ./frontend/data/index.js:/usr/src/app/index.js
# Servicio para la aplicación backend (Python/Flask)
backend:
# Configuración de construcción de la imagen Docker
build:
# Directorio donde se encuentra el Dockerfile para construir la imagen
# En este caso, busca el Dockerfile en la carpeta ./backend/build
context: ./backend/build
# Mapeo de puertos entre el host y el contenedor
# El puerto 8000 del host se mapea al puerto 8000 del contenedor
ports:
- "8000:8000"
# Comando personalizado para ejecutar cuando se inicie el contenedor
# python -u: ejecuta Python con salida sin buffer (unbuffered)
# app.py: el archivo principal de la aplicación Python
command: python -u app.py

BIN
dockerarchitecture.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

43
frontend/build/Dockerfile Normal file
View File

@ -0,0 +1,43 @@
# ===========================================
# DOCKERFILE PARA APLICACIÓN FRONTEND NODE.JS
# ===========================================
# Usamos una imagen base ligera de Node.js 18 basada en Alpine Linux
# Alpine es una distribución Linux muy pequeña (~5MB) que reduce el tamaño de la imagen
# node:18-alpine incluye Node.js 18 y npm, las herramientas necesarias para aplicaciones JavaScript
FROM node:18-alpine
# Establecemos el directorio de trabajo dentro del contenedor
# Todas las operaciones posteriores (COPY, RUN, CMD) se ejecutarán desde este directorio
# /usr/src/app es una convención común para aplicaciones en contenedores
WORKDIR /usr/src/app
# Copiamos SOLO los archivos package.json y package-lock.json (si existe)
# El patrón package*.json copia ambos archivos: package.json y package-lock.json
# Esto se hace ANTES de copiar el resto del código para aprovechar la caché de Docker
# Si solo cambia el código fuente (no las dependencias), Docker reutiliza la capa de npm install
COPY package*.json ./
# Instalamos las dependencias de Node.js definidas en package.json
# npm install lee package.json y descarga todas las dependencias listadas
# Se ejecuta en esta capa para que Docker pueda cachear el resultado
# Si las dependencias no cambian, esta capa se reutiliza en builds posteriores
RUN npm install
# Copiamos TODO el resto del código de la aplicación al contenedor
# El primer punto (.) representa el directorio actual del host (donde está el Dockerfile)
# El segundo punto (.) representa el directorio de trabajo del contenedor (/usr/src/app)
# Esto incluye index.js, archivos estáticos, etc. (excluyendo lo que esté en .dockerignore)
COPY . .
# Exponemos el puerto 3000 que usará la aplicación web
# Esta instrucción es solo documentativa - no abre el puerto automáticamente
# El puerto se abre realmente cuando se ejecuta el contenedor con -p o en docker-compose
# Es una buena práctica documentar qué puertos usa la aplicación
EXPOSE 3000
# Comando por defecto para iniciar la aplicación cuando se ejecute el contenedor
# Se ejecuta automáticamente cuando se inicia el contenedor (a menos que se sobrescriba)
# Formato de array: ["comando", "argumento1", "argumento2"]
# node index.js ejecutará el archivo index.js con el runtime de Node.js
CMD ["node", "index.js"]

14
frontend/build/index.js Normal file
View File

@ -0,0 +1,14 @@
// Importamos el módulo express
const express = require('express');
const app = express();
const port = 3000;
// Definimos la ruta raíz que enviará una respuesta
app.get('/', (req, res) => {
res.send('<h1>¡Hola desde el Frontend!</h1><p>Esta es una aplicacion de ejemplo en Node.js.</p>');
});
// El servidor escucha en el puerto definido
app.listen(port, () => {
console.log(`Aplicacion frontend corriendo en http://localhost:${port}`);
});

View File

@ -0,0 +1,13 @@
{
"name": "frontend-app",
"version": "1.0.0",
"description": "Una aplicacion de ejemplo en Node.js para Docker",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.18.2"
}
}

14
frontend/data/index.js Normal file
View File

@ -0,0 +1,14 @@
// Importamos el módulo express
const express = require('express');
const app = express();
const port = 3000;
// Definimos la ruta raíz que enviará una respuesta
app.get('/', (req, res) => {
res.send('<h1>¡Hola desde el Frontend con los archivos residentes en el host!</h1><p>Esta es una aplicacion de ejemplo en Node.js.</p>');
});
// El servidor escucha en el puerto definido
app.listen(port, () => {
console.log(`Aplicacion frontend corriendo en http://localhost:${port}`);
});