Runbook · P2.1 — Stand-up de Lakekeeper (la cara abierta del contrato)
⚠️ DESACTUALIZADO (predata 2026-07-09). El stand-up YA está HECHO: Lakekeeper vivo, prod en
type=rest. Estado real:docs/INFRA.md. Correcciones: SqlCatalog RETIRADO (P2.5, el rollback a=sqlya no aplica); el vendingvendedestá ROTO sobre R2 → ruta = remote-signing (no vended-credentials). Queda como registro histórico.
Iteración P2.1 de
warehouse-open-catalog.md
(H1 · Abrir el contrato). Levanta Lakekeeper como el catálogo técnico Iceberg REST
sobre R2 — la cara abierta de la singularidad. Alcance completo: componentes, fases,
configcloudflare-r2, auth, coexistencia, verificación, riesgos, done-criteria.Qué desbloquea: que cualquier motor (Karma, Trino, DuckDB, Spark) resuelva tablas del
Warehouse por el estándar + credenciales R2 vendidas — sin el hop ml-runner. Es donde
la singularidad empieza a servir como interfaz abierta (semilla de Doberman).
0 · Lo que YA está listo (no repetir)
- Namespaces canónicos (
main.default/main.test) — D3 ya migró las 84 tablas; el register de P2.2 apuntará a esos namespaces. - Seam flip-ready (
services/ml-runner/app/lakehouse/writer.py):build_catalogtype='rest'+catalog_config_from_envleenLAKEHOUSE_CATALOG_TYPE=rest+LAKEHOUSE_REST_URI/_WAREHOUSE/_TOKEN, y la ruta de vending gateada porLAKEHOUSE_REST_VENDING(default off). - Karma
RestResolver(crates/karma-parquet/src/rest.rs) — consume Lakekeeper vía iceberg-rust. Listo; su swap enkarma-serveres Punto 4.
Por tanto P2.1 es puramente infra + config (operador). El código no bloquea.
1 · Componentes
| Componente | Qué | Nota |
|---|---|---|
| Lakekeeper (servicio) | el catálogo Iceberg REST — imagen oficial quay.io/lakekeeper/catalog | stateless; escala horizontal; expone /catalog (Iceberg REST) + API de management |
| Postgres de Lakekeeper | su metastore propio (≥ 15) | NO es el SqlCatalog actual — Lakekeeper trae su store; se migra por register API |
| R2 (storage) | el warehouse Parquet+metadata ya existente | Lakekeeper apunta ahí con un storage profile cloudflare-r2 |
| Auth | token/OAuth2 para que ml-runner + Karma se autentiquen | Lakekeeper soporta bootstrap-token / OIDC |
Decisión de deploy: Railway (como ml-runner/karma-server) o el host que prefieras; necesita el servicio + una instancia Postgres. Ambos pueden vivir en Railway.
2 · Fases del stand-up
F1 · Desplegar Lakekeeper + su Postgres
- Provisionar un Postgres para Lakekeeper (Railway plugin o instancia dedicada).
- Desplegar la imagen de Lakekeeper apuntando su
PG_DATABASE_URLa ese Postgres. - Correr la migración de esquema de Lakekeeper (
lakekeeper migrate) + arrancar (lakekeeper serve). - Gate: el endpoint de Lakekeeper responde (health / OpenAPI).
F2 · Configurar el warehouse + storage profile cloudflare-r2
Crear un warehouse en Lakekeeper con storage profile R2. Claves del perfil
cloudflare-r2 (fija internamente flavor=s3-compat, sts-enabled=true):
account-id(Cloudflare)access-key-id/secret-access-key(las claves S3 de R2 ya en uso)token— Admin API token de R2 (el que Lakekeeper usa para generar las temp-creds)endpoint— el endpoint R2 (https://<account>.r2.cloudflarestorage.com)warehouse/ bucket =lakehouse(mismo bucket del tridente)sts-token-validity-seconds(opcional; default 3600)
Reversibilidad: el SqlCatalog actual se queda read-only durante la transición; no
se toca el warehouse en R2 (datos + metadata.json intactos).
F3 · Bootstrap + auth
- Bootstrap el proyecto/warehouse inicial de Lakekeeper (su management API).
- Definir el modo de auth (bootstrap-token simple para empezar, o OIDC/OAuth2).
- Emitir el token que consumirán ml-runner (
LAKEHOUSE_CATALOG_TOKEN) y Karma.
F4 · Verificación (antes de tocar prod)
- Health: Lakekeeper responde;
GET /v1/config/list namespacescon el token. - Smoke de una tabla: registrar UNA tabla ya existente (una de las 84, su
metadata.jsonen R2) vía register API, y hacerloadTable→ debe devolver la metadata. - Smoke de vending (si activas
cloudflare-r2):loadTablecon headerX-Iceberg-Access-Delegation: vended-credentials→ la respuesta traestorage-credentialsscoped al prefijo de la tabla (…/data/y…/metadata/, no solo metadata — el gotcha conocido).
Gate P2.1: una tabla real resuelve por Lakekeeper (metadata) y, con vending on, se lee de R2 con las creds vendidas.
3 · Env wiring (ml-runner) — el flip
Una vez Lakekeeper verificado, el ml-runner apunta ahí (sin redeploy de código — todo por env):
LAKEHOUSE_CATALOG_TYPE = rest
LAKEHOUSE_REST_URI = https://<lakekeeper>/catalog
LAKEHOUSE_REST_WAREHOUSE = lakehouse # el nombre del warehouse en Lakekeeper
LAKEHOUSE_CATALOG_TOKEN = <token de Lakekeeper>
# (más tarde, tras el smoke de vending)
LAKEHOUSE_REST_VENDING = 1 # activa las creds vendidas de R2
El flip es reversible (volver a sql). LAKEHOUSE_REST_VENDING se deja en 0
(metadata-only) hasta pasar el smoke de vending, luego 1.
4 · Estrategia de coexistencia (transición segura)
- SqlCatalog read-only mientras se registra en Lakekeeper — dos catálogos, una copia de datos en R2.
- Flip por env (✅ HECHO en prod):
LAKEHOUSE_CATALOG_TYPE=rest. ⚠️ P2.5: el rollback a=sqlya NO es válido — la fuente de verdad de metadata está en Lakekeeper; volver a SqlCatalog leería punteros rancios. Rollback real = revert/redeploy a un snapshot previo, no re-apuntar al SqlCatalog. - Vending gateado:
LAKEHOUSE_REST_VENDING=0primero (Lakekeeper gobierna metadata, FileIO sigue client-side PyArrow — el modo probado), luego1(creds vendidas) tras el smoke. - Escrituras: mantener el commit por el catálogo actual hasta estabilizar la lectura; mover el commit a Lakekeeper es P2.4.
5 · Riesgos / gotchas
- Metastore propio de Lakekeeper: no envuelve el SqlCatalog → hay que registrar las
84 (P2.2, register API con las ubicaciones
metadata.jsonactuales). Punteros, no datos. - Scope del vending: las creds deben cubrir el prefijo de la tabla (data + metadata), no solo metadata — Lakekeeper scopea a la tabla; verificar en el smoke.
- R2 endpoint / path-style: R2 no es AWS; el perfil
cloudflare-r2lo maneja, pero confirmar elendpointy que las temp-creds funcionan (el Admin token es la clave). - Auth: empezar con bootstrap-token simple; OIDC es endurecimiento posterior.
- Compatibilidad
metadata.json: las tablas se escribieron con PyIceberg SqlCatalog; el register apunta almetadata.jsonactual — validar que Lakekeeper lo carga tal cual (smoke F4).
6 · Done-criteria de P2.1
- Lakekeeper desplegado + su Postgres migrado +
servesano. - Warehouse con storage profile
cloudflare-r2configurado contra R2. - Una tabla real (de las 84) registrada y resuelta por
loadTable(metadata). - Smoke de vending OK (creds R2 scoped al prefijo de tabla) — o diferido si se arranca metadata-only.
- Token emitido; el ml-runner puede apuntar
LAKEHOUSE_CATALOG_TYPE=rest(sin flipear prod aún).
7 · Qué sigue (el resto de H1)
- P2.2 — registrar las 84 tablas vía register API (namespaces
main.default/main.test). - P2.3 — flip
LAKEHOUSE_CATALOG_TYPE=rest+LAKEHOUSE_REST_VENDING=1(canary por reader-flag), y cablear KarmaRestResolver. - P2.4 — mover el commit de escritura a Lakekeeper.
- P2.5 — retirar el SqlCatalog + el proxy de lectura del ml-runner.
Nota: los detalles operativos exactos de Lakekeeper (nombres de env vars de su imagen, comandos de bootstrap, forma del register API) se confirman contra la doc oficial de Lakekeeper al ejecutar F1–F3; este runbook fija el alcance y las decisiones, que es lo que no cambia.