Pipeline · 4 de 10
Infraestructura core
Con el baseline validado, recién acá se justifica invertir en infraestructura real: Postgres, Redis, MLflow y el feature server de Feast, todos en Docker Compose sobre un servidor propio (no cloud gestionado).
Docker Compose, servidor propio
Un solo docker-compose.yml orquesta los 4 servicios (y más adelante, la
API y el dashboard) sobre un servidor propio. No hay justificación de
escala para un orquestador gestionado en un proyecto de este tamaño —
Compose es reproducible, versionado en git, y cualquiera puede levantar
el stack completo con un comando.
Postgres
postgres:16-alpine, una sola instancia con dos bases de datos: aqp_housing
(feature_metadata, features, prediction_logs — todo lo propio del
proyecto) y mlflow (el backend store de tracking). Separar en dos DBs
evita mezclar tablas internas de MLflow con las del proyecto, sin pagar
el costo de un segundo contenedor.
Una instancia, dos bases de datos
Las 3 tablas de aqp_housing se crean acá (schema vacío, vía
infra/postgres/init/*.sql, montado en docker-entrypoint-initdb.d) pero
se pueblan en pasos distintos del pipeline — separar esquema de datos deja
recargar cualquiera de las tres sin reiniciar el volumen de Postgres:
-- 02_create_feature_metadata.sql — poblada en este mismo paso
CREATE TABLE feature_metadata (
name TEXT PRIMARY KEY, description TEXT NOT NULL, dtype TEXT NOT NULL,
source_columns TEXT NOT NULL, transformation TEXT NOT NULL,
feast_feature_view TEXT NOT NULL, owner TEXT NOT NULL,
created_at DATE NOT NULL, version INTEGER NOT NULL
);
-- 03_create_features.sql — poblada en el paso 5 (Feature store)
CREATE TABLE features (
id TEXT PRIMARY KEY, created_on TIMESTAMP NOT NULL,
district TEXT NOT NULL, surface DOUBLE PRECISION NOT NULL,
property_type TEXT NOT NULL, operation_type TEXT NOT NULL,
district_avg_price_per_m2 DOUBLE PRECISION NOT NULL,
price_usd DOUBLE PRECISION NOT NULL
);
-- 04_create_prediction_logs.sql — poblada en el paso 7 (API de inferencia)
CREATE TABLE prediction_logs (
id SERIAL PRIMARY KEY, requested_at TIMESTAMP NOT NULL DEFAULT now(),
operation_type TEXT NOT NULL, district TEXT NOT NULL,
surface DOUBLE PRECISION NOT NULL, property_type TEXT NOT NULL,
predicted_price_usd DOUBLE PRECISION NOT NULL,
model_version TEXT NOT NULL, latency_ms DOUBLE PRECISION NOT NULL
);
feature_metadata se carga acá mismo con
ml/data_prep/load_feature_metadata.py — un upsert (ON CONFLICT (name) DO UPDATE), no un insert puro, así que re-correrlo tras editar el CSV no
duplica filas. Las otras dos tablas siguen vacías hasta sus pasos
correspondientes.
Redis
redis:7-alpine con --appendonly yes (persistencia AOF). No es un cache
descartable — es el online store de Feast, así que un restart del
contenedor sin AOF perdería los valores ya materializados.
MLflow
ghcr.io/mlflow/mlflow:v3.12.0 extendido con psycopg2-binary (la
imagen oficial no trae driver de Postgres). Tracking + Model Registry en
un solo servidor.
Un bug real: artifacts de MLflow
--default-artifact-root /mlruns hace que el cliente de MLflow
intente escribir artifacts directo en su propio filesystem — asumiendo
storage compartido con el servidor. Probado con el cliente real corriendo
en el host: falló con OSError: Read-only file system (la raíz de macOS
es read-only). El fix es --artifacts-destination /mlruns en vez de
--default-artifact-root, manteniendo --serve-artifacts — así el
cliente sube/baja artifacts vía HTTP al servidor, y es el servidor quien
escribe a su propio disco.
Feast, todavía sin feature views
feature_repo/feature_store.yaml se define acá — registry, offline store
apuntando a Postgres, online store apuntando a Redis — pero sin feature
views todavía (eso es el próximo paso).
Introspeccionando el paquete feast instalado (no adivinado de memoria)
aparecieron dos gotchas reales: el offline store de Postgres de Feast usa
psycopg v3 internamente (no psycopg2, el driver que sí usan MLflow y
el resto del proyecto), y su default de sslmode es require — el
Postgres local no tiene SSL configurado, así que hace falta
sslmode: disable explícito o falla con server does not support SSL.
Artefactos de este paso
| Artefacto | Tipo | Qué es |
|---|---|---|
docker-compose.yml | Config | Orquesta los 7 servicios del proyecto (acá se agregan los primeros 4). |
.env.example | Config | Credenciales, puertos, y variables como MLFLOW_ARTIFACTS_DESTINATION. |
infra/postgres/init/*.sql | Schema | 4 scripts: crear la DB mlflow, y las tablas feature_metadata/features/prediction_logs (vacías). |
infra/mlflow/Dockerfile | Infra | Extiende la imagen oficial de MLflow con psycopg2-binary. |
feature_repo/feature_store.yaml | Config | Registry + offline store (Postgres) + online store (Redis) — sin feature views todavía. |
ml/data_prep/load_feature_metadata.py | Script | Carga ml/feature_metadata.csv a la tabla real, vía upsert. |