pre-paso: instalar docker
Antes de levantar tu primera base de datos en contenedor, necesitas tener Docker instalado en tu máquina. Aquí un resumen rápido según tu sistema operativo:
windows / macos
- Descarga Docker Desktop.
- Instala con el asistente y reinicia si lo pide.
- Abre una terminal y valida:
docker --versionDeberías ver algo como Docker version 26.x
linux (ubuntu/debian)
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg lsb-release
# añadir repo oficial
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io -y
docker --versionInstala el engine oficial desde el repositorio de Docker.
extra tip (linux)
sudo usermod -aG docker $USER
# luego cerrar sesión y volver a entrarTe permite ejecutar docker sin sudo en cada comando.
1) docker — tu sandbox de datos
levanta un Postgres local. rompes algo → borras el contenedor y listo. cero miedo.
# 1) Levantar Postgres en Docker (persistente)
docker run -d --name pg-local \
-e POSTGRES_USER=admin \
-e POSTGRES_PASSWORD=admin \
-e POSTGRES_DB=demo \
-p 5432:5432 \
-v pgdata:/var/lib/postgresql/data \
postgres:16
# 2) (opcional) UI simple para revisar tablas
docker run -d --name adminer -p 8080:8080 adminervolumen persistente (pgdata) para no perder datos.
# docker-compose.yml
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: admin
POSTGRES_DB: demo
ports: ["5432:5432"]
volumes: ["pgdata:/var/lib/postgresql/data"]
adminer:
image: adminer
ports: ["8080:8080"]
volumes:
pgdata: {}`docker compose up -d` y queda andando + Adminer en :8080.
-- init.sql
CREATE TABLE IF NOT EXISTS orders (
order_id SERIAL PRIMARY KEY,
user_id INT,
total NUMERIC,
status TEXT,
order_ts TIMESTAMP DEFAULT NOW()
);útil para tus primeros inserts desde Python.
2) conectar fuentes: csv, api y base de datos
- CSV: lee con pandas y sube a Postgres.
- API: prueba con Rick & Morty; es perfecta para practicar requests.
- BD: usa
psycopg2osqlalchemy.
# csv_to_pg.py
import pandas as pd
from sqlalchemy import create_engine
csv_path = "orders.csv" # crea un CSV de prueba con columnas: user_id,total,status
df = pd.read_csv(csv_path)
engine = create_engine("postgresql+psycopg2://admin:admin@localhost:5432/demo")
# inserta/append en la tabla 'orders'
df.to_sql("orders", engine, if_exists="append", index=False)
print("Cargado CSV → Postgres (tabla orders)")# rick_and_morty.py
import requests, pandas as pd
base = "https://rickandmortyapi.com/api"
r = requests.get(f"{base}/character", timeout=30)
r.raise_for_status()
data = r.json()
chars = pd.json_normalize(data["results"])[["id","name","status","species","gender","origin.name"]]
print(chars.head()) # pruébalo, luego puedes cargarlo a Postgres igual que el CSV# insert_pg.py
import psycopg2
conn = psycopg2.connect("dbname=demo user=admin password=admin host=localhost port=5432")
cur = conn.cursor()
cur.execute("INSERT INTO orders (user_id,total,status) VALUES (%s,%s,%s)", (123, 45.6, "PAID"))
conn.commit()
cur.close(); conn.close()
print("Fila insertada")3) nube (con cuidado) o simula local
4) airflow local: automatiza tu etl
no necesitas Composer todavía. instala Airflow local y crea un DAG con PythonOperator que lea una API y cargue en tu Postgres dockerizado.
# (opción rápida) entorno local
python -m venv .venv && source .venv/bin/activate
pip install "apache-airflow==2.9.*" --constraint \
https://raw.githubusercontent.com/apache/airflow/constraints-2.9.3/constraints-3.11.txt
export AIRFLOW_HOME=$(pwd)/airflow_home
airflow db init
airflow users create -u admin -p admin -r Admin -e a@a.com -f A -l Admin
airflow webserver -p 8081 # en otra terminal: airflow schedulerabre 2 terminales: webserver y scheduler.
# ${AIRFLOW_HOME}/dags/local_etl.py
from datetime import datetime
from airflow import DAG
from airflow.operators.python import PythonOperator
import psycopg2, requests
DB_DSN = "dbname=demo user=admin password=admin host=host.docker.internal port=5432" # si Airflow en Docker
# si Airflow sin Docker y Postgres local nativo usa host=localhost
def extract_characters(**_):
url = "https://rickandmortyapi.com/api/character"
data = requests.get(url, timeout=30).json()["results"]
return [(c["id"], c["name"], c["status"]) for c in data[:10]]
def load_to_pg(**context):
rows = context["ti"].xcom_pull(task_ids="extract")
conn = psycopg2.connect(DB_DSN)
cur = conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS characters (id INT PRIMARY KEY, name TEXT, status TEXT)")
cur.executemany("INSERT INTO characters (id,name,status) VALUES (%s,%s,%s) ON CONFLICT (id) DO NOTHING", rows)
conn.commit(); cur.close(); conn.close()
with DAG(
"local_etl",
start_date=datetime(2025,8,1),
schedule="@daily",
catchup=False,
) as dag:
extract = PythonOperator(task_id="extract", python_callable=extract_characters)
load = PythonOperator(task_id="load", python_callable=load_to_pg, provide_context=True)
extract >> loadsi corres Airflow en Docker, usa host.docker.internal para hablar con Postgres del host.
5) reportes sin looker: python basta
lee desde Postgres y genera un PNG + HTML. suficiente para un “entregable” rápido.
# report.py — top estados de characters
import pandas as pd
import matplotlib.pyplot as plt
from sqlalchemy import create_engine
engine = create_engine("postgresql+psycopg2://admin:admin@localhost:5432/demo")
df = pd.read_sql("SELECT status, COUNT(*) c FROM characters GROUP BY 1 ORDER BY c DESC", engine)
print(df)
# gráfico rápido
ax = df.plot(kind="bar", x="status", y="c", title="Personajes por estado")
plt.tight_layout()
plt.savefig("characters_by_status.png")
# HTML rápido
html = df.to_html(index=False)
open("report.html","w").write(f"<h1>Personajes por estado</h1>{html}")
print("Generados: characters_by_status.png y report.html")produce characters_by_status.png y report.html
recap de la ruta
- Docker: Postgres local.
- Fuentes: API / CSV / BD → Python.
- Nube: con créditos; si no, simula local.
- Airflow local: DAG con PythonOperator + psycopg2.
- Reportes: gráficos/HTML rápidos en Python.