629 lines
28 KiB
Markdown
629 lines
28 KiB
Markdown
|
|
# Talk Structure: Why I Needed Rust
|
|||
|
|
|
|||
|
|
> Orientado a: `abstract_en.md`
|
|||
|
|
> Fuente: `2026-02-17-notas_voz.md`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Sistema de Medidores *(elemento visual persistente)*
|
|||
|
|
|
|||
|
|
Tres barras que aparecen en esquina de cada slide de transición.
|
|||
|
|
Escala 0–5. Se actualizan en cada etapa clave.
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
🛡 Confianza ●●●●● = máxima certeza sobre el sistema
|
|||
|
|
😴 Descanso ●●●●● = puedes dormir tranquilo
|
|||
|
|
🔥 Estrés ●●●●● = alarma permanente, pesadillas
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Estado inicial (apertura)**
|
|||
|
|
```
|
|||
|
|
🛡 ●●●○○ 😴 ●●●○○ 🔥 ●○○○○
|
|||
|
|
```
|
|||
|
|
*Se presenta como "¿Reconoces estos números? Sigamos..."*
|
|||
|
|
|
|||
|
|
**Progresión a lo largo de la charla:**
|
|||
|
|
|
|||
|
|
| Momento | 🛡 Confianza | 😴 Descanso | 🔥 Estrés |
|
|||
|
|
|---|:---:|:---:|:---:|
|
|||
|
|
| Etapa 1 — Local | ●●●●○ | ●●●●○ | ●○○○○ |
|
|||
|
|
| Etapa 2 — Redes | ●●●○○ | ●●●○○ | ●●○○○ |
|
|||
|
|
| Etapa 3 — Cloud/CI-CD | ●●○○○ | ●○○○○ | ●●●●○ |
|
|||
|
|
| YAML hell / peak pain | ●○○○○ | ○○○○○ | ●●●●● |
|
|||
|
|
| Rust — compilador | ●●●○○ | ●●○○○ | ●●●○○ |
|
|||
|
|
| Rust — tipos + traits | ●●●●○ | ●●●●○ | ●●○○○ |
|
|||
|
|
| Cierre | ●●●●● | ●●●●● | ●○○○○ |
|
|||
|
|
|
|||
|
|
> El título de la charla ES el último estado del medidor de sueño.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 0. Hook de apertura
|
|||
|
|
|
|||
|
|
**Pregunta de apertura al público** *(manos arriba)*:
|
|||
|
|
> "¿Quién ha sido despertado a las 3am por un fallo de infraestructura?"
|
|||
|
|
|
|||
|
|
*Pausa. Mirar la sala. Asentir.*
|
|||
|
|
|
|||
|
|
> "Esto es para vosotros."
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**Imagen concreta**: definir un host, un nodo, un puerto.
|
|||
|
|
- Eso es todo. Queremos que algo corra en algún sitio y que algo pueda hablar con él.
|
|||
|
|
- ¿Cómo de difícil puede ser?
|
|||
|
|
|
|||
|
|
**38 años de infraestructura. 1987–2025.**
|
|||
|
|
- Perl (90s) → Python (2000s) → Bash → Chef → Ansible → Terraform → Go → ???
|
|||
|
|
- Cada vez pensé que tenía la respuesta.
|
|||
|
|
- Cada vez la realidad me demostró que no.
|
|||
|
|
|
|||
|
|
**Promesa**: vamos a ver por qué eso, durante décadas, nos ha dado pesadillas. Y por qué Rust cambió eso.
|
|||
|
|
|
|||
|
|
*→ Mostrar medidores en interrogante o en estado inicial neutro*
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. Evolución de los desafíos en infraestructura (2013-2025)
|
|||
|
|
|
|||
|
|
*Abstract: "The evolution of infrastructure challenges (2013-2025)"*
|
|||
|
|
|
|||
|
|
### Etapa 1 — Local (finales 80s / primeros 90s)
|
|||
|
|
- Terminales tontos, desarrollo local
|
|||
|
|
- Ciclos de despliegue largos, baja urgencia
|
|||
|
|
- Un solo estado, fácil de observar y controlar
|
|||
|
|
- IaC: scripts procedurales, lógica oculta en comandos o en la aplicación
|
|||
|
|
- **La era Perl**: podíamos hacer cualquier cosa. También podíamos romper cualquier cosa.
|
|||
|
|
- Metaprogramming bello y aterrador. Sin red de seguridad. Fallos silenciosos a las 3am.
|
|||
|
|
- *Lección: poder sin seguridad es un desastre.*
|
|||
|
|
|
|||
|
|
> *Medidores fin Etapa 1:* `🛡 ●●●●○ 😴 ●●●●○ 🔥 ●○○○○`
|
|||
|
|
> *"Tienes un servidor. Sabes lo que tiene. Puedes dormir."*
|
|||
|
|
|
|||
|
|
### Etapa 2 — Conectividad en red / Internet
|
|||
|
|
- Los sistemas están cada vez más lejos — acceso remoto, redes distantes
|
|||
|
|
- Seguridad empieza a importar; procesos críticos, coste de caída sube
|
|||
|
|
- Más agentes participando (dev, ops, seguridad) — hay que armonizar
|
|||
|
|
- Armonizar: instalación de paquetes, configuración de recursos, actualizaciones en paralelo
|
|||
|
|
- IaC: procesos automatizables y reproducibles, primeros intentos declarativos, config-driven model
|
|||
|
|
- **La era Python**: desarrollo rápido, gran comunidad. Pero nada te impedía estar equivocado.
|
|||
|
|
- Type hints llegaron tarde, y opcionales. Errores en runtime > errores en compilación.
|
|||
|
|
- *Lección: pragmatismo sin garantías es frágil.*
|
|||
|
|
|
|||
|
|
> *Medidores fin Etapa 2:* `🛡 ●●●○○ 😴 ●●●○○ 🔥 ●●○○○`
|
|||
|
|
> *"Más piezas. Más personas. Empieza a ponerse interesante."*
|
|||
|
|
|
|||
|
|
### Etapa 3 — Contenedores / Cloud / CI-CD
|
|||
|
|
- Monolítico → distribuido, 24×7×365, alta disponibilidad
|
|||
|
|
- Cloud, híbrido, multi-cloud, on-prem — todo a la vez
|
|||
|
|
- Rollback y rollforward: como transacciones de BD, pero en infraestructura
|
|||
|
|
- Escalado horizontal **y** vertical, también **desescalado** — en ambas direcciones
|
|||
|
|
- CI/CD continuo, ciclos cortos — nuevas funcionalidades, nuevos despliegues, permanente
|
|||
|
|
- IaC: Helm, Ansible, Terraform — más herramientas, más complejidad
|
|||
|
|
- Múltiples versiones de "lo que creemos que es válido" — la fuente de verdad se fragmenta
|
|||
|
|
- **La era Cloud/IaC**: Ansible, Terraform, Chef, Puppet. ¿Qué cambió? La sintaxis.
|
|||
|
|
- Seguimos peleando con type safety. Seguimos descubriendo errores en producción.
|
|||
|
|
- *Lección: más herramientas no resuelven problemas de paradigma.*
|
|||
|
|
- **Resultado**: estados de alarma como norma, no como excepción
|
|||
|
|
|
|||
|
|
> *Medidores fin Etapa 3:* `🛡 ●●○○○ 😴 ●○○○○ 🔥 ●●●●○`
|
|||
|
|
|
|||
|
|
> *"¿Hemos aumentado la productividad? Sí.<br> ¿Hemos aumentado el estrés? Sí.<br> ¿Hemos aumentado las posibilidades de tener problemas? También.<br> ¿Tenemos más control y seguridad? No."*
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────┐
|
|||
|
|
│ SLIDE STANDALONE — The Breaking Point │
|
|||
|
|
│ │
|
|||
|
|
│ Podía automatizar infraestructura. │
|
|||
|
|
│ │
|
|||
|
|
│ No podía hacerla fiable. │
|
|||
|
|
│ │
|
|||
|
|
│ No podía prevenir los errores. │
|
|||
|
|
│ │
|
|||
|
|
│ No podía dormir. │
|
|||
|
|
└─────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
> *Medidores:* `🛡 ●○○○○ 😴 ○○○○○ 🔥 ●●●●●`
|
|||
|
|
> *Tono: sin ironía. Personal. Pausa larga. Dejar que aterrice.*
|
|||
|
|
> *Lo que no se puede medir: el miedo.*
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Por qué el IaC tradicional falla a escala
|
|||
|
|
|
|||
|
|
*Abstract: "Why traditional approaches to IaC fall short at scale"*
|
|||
|
|
|
|||
|
|
### La analogía del restaurante *(hilo conductor de toda la charla)*
|
|||
|
|
|
|||
|
|
Un restaurante tiene tres actores: **comensal** (mesa), **camarero** (servicio), **cocina**.
|
|||
|
|
|
|||
|
|
| Restaurante | Infraestructura |
|
|||
|
|
|---|---|
|
|||
|
|
| Comensal declara lo que quiere | Config declarativa (YAML, HCL, Helm) |
|
|||
|
|
| Camarero valida y transmite | Orchestrator (K8s, Ansible, Terraform) |
|
|||
|
|
| Cocina ejecuta y entrega | Runtime / provisioning |
|
|||
|
|
| El plato llega — o no | Deployment exitoso — o no |
|
|||
|
|
|
|||
|
|
**Lo que hace que funcione** — o no:
|
|||
|
|
- El comensal declara, no implementa: no dice *cómo* cocinar, dice *qué* quiere
|
|||
|
|
- El camarero debe saber qué es posible y validar *antes* de ir a cocina
|
|||
|
|
- "Quiero X" → camarero va a cocina → "X no hay, ¿por qué está en la carta?" → vuelve a mesa
|
|||
|
|
- Equivalente: configuré un host con el puerto 8443 → ese puerto no está permitido → hay que reconfigurar desde cero
|
|||
|
|
|
|||
|
|
**La verdad que muta — y el problema de estado**
|
|||
|
|
- La "verdad" no es estática. Muta en cada paso de la cadena:
|
|||
|
|
- Comensal: su petición oral
|
|||
|
|
- Camarero: la nota que toma
|
|||
|
|
- Cocina: las marcas sobre lo que ya está hecho o no
|
|||
|
|
- Caja: el ticket de pago
|
|||
|
|
- Cada actor ve una *parte* de la verdad, relevante para él en ese momento
|
|||
|
|
- Como en un sistema neuronal: hay eventos que no activan ciertas neuronas porque no son relevantes para ellas
|
|||
|
|
- **El camarero conoce al cliente habitual** ("siempre sin sal") — la cocina, no. ¿Está ese contexto explícito en el pedido, o asumido?
|
|||
|
|
- Si el camarero cambia, o si interviene otra estación de cocina → el estado implícito se pierde
|
|||
|
|
|
|||
|
|
**El coste del fallo depende de dónde ocurre**
|
|||
|
|
- Fallo en mesa al pedir (pedido imposible): barato — se corrige antes de llegar a cocina
|
|||
|
|
- Fallo en cocina al elaborar (ingrediente no disponible): medio — hay que volver a mesa y renegociar
|
|||
|
|
- Fallo al entregar (plato se cae): caro — hay que rehacer desde cero
|
|||
|
|
- Fallo cuando llega el plato y no es lo que se pidió: muy caro — experiencia destruida + tiempo perdido
|
|||
|
|
- **Fail early = fail cheap. Fail in production = nightmare.**
|
|||
|
|
|
|||
|
|
**La renegociación: "no tengo champiñón"**
|
|||
|
|
- Un actor en la cadena descubre que no puede cumplir parte del pedido
|
|||
|
|
- Debe volver atrás y validar con el comensal: "¿te pongo verduras?"
|
|||
|
|
- Esto requiere que el cambio sea explícito, trazado, y re-autorizado — no silencioso
|
|||
|
|
- Equivalente en infra: configuration drift, estado divergente sin notificación
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### El ciclo de la config: cómo llegamos hasta aquí
|
|||
|
|
|
|||
|
|
1. **Hardcoded** — dependencias, rutas, puertos dentro del binario. Control total, cero flexibilidad
|
|||
|
|
2. **Config externa** (JSON) — funciona bien entre máquinas, ilegible para humanos a escala
|
|||
|
|
3. **YAML / TOML** — más legible, pero sintaxis frágil: indentación, tipos implícitos, errores silenciosos
|
|||
|
|
4. **YAML + Serde** — cargamos la config con Serde, que valida la *estructura*:
|
|||
|
|
- ¿Existe el campo? ¿Es del tipo correcto?
|
|||
|
|
- ¿Aceptamos "elefante" como animal de compañía? Depende del tipo. Pero si el tipo es `String`... sí.
|
|||
|
|
- **Serde valida forma. No valida significado.**
|
|||
|
|
5. **Helm / Jinja templates** — escribimos YAML a partir de variables (en YAML o JSON)
|
|||
|
|
- El template genera el YAML final que consume la aplicación
|
|||
|
|
- ¿Valida el contenido del YAML generado? **No. En absoluto.**
|
|||
|
|
- Como usar un LLM con un markdown de referencia: el formato está, pero ¿el contenido es correcto? Eso no lo garantiza nadie.
|
|||
|
|
6. **Resultado**: CI/CD continuo + config sin validación semántica = **esperanza continua**
|
|||
|
|
|
|||
|
|
> *"Lo que hacemos es escribir lo que queremos, como una carta a los Reyes Magos. Y cruzamos los dedos."*
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────┐
|
|||
|
|
│ SLIDE STANDALONE — Esperanza continua │
|
|||
|
|
│ │
|
|||
|
|
│ CI/CD continuo. │
|
|||
|
|
│ Sin validación semántica. │
|
|||
|
|
│ │
|
|||
|
|
│ Esperanza continua. │
|
|||
|
|
│ │
|
|||
|
|
│ (cruzamos los dedos en producción) │
|
|||
|
|
└─────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
> *Medidores:* `🛡 ●○○○○ 😴 ○○○○○ 🔥 ●●●●●`
|
|||
|
|
> *Tono: irónico, de reconocimiento. La audiencia asiente.*
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Las tres preguntas sin respuesta
|
|||
|
|
|
|||
|
|
**Pregunta 1 — ¿Por qué esperamos a que se rompa?**
|
|||
|
|
- "In my machine it works" — en producción, no lo sé
|
|||
|
|
- Fail late = coste máximo
|
|||
|
|
- Queremos: fail fast, fail cheap — cuanto antes, menor el coste
|
|||
|
|
|
|||
|
|
**Pregunta 2 — ¿Tenemos claro lo que queremos?**
|
|||
|
|
- ¿La declaración es suficiente y consistente con lo que es posible?
|
|||
|
|
- ¿Respeta los límites del sistema? ¿Cuáles son esos límites — estáticos o dinámicos?
|
|||
|
|
- ¿Cuál es la fuente de verdad? ¿Y cuándo y cómo muta?
|
|||
|
|
|
|||
|
|
**Pregunta 3 — ¿Podemos garantizar determinismo?**
|
|||
|
|
- ¿Tenemos herramientas que usen nuestras definiciones para operar automáticamente en el ciclo de vida?
|
|||
|
|
- ¿Podemos tener certeza de que obtendremos lo que formulamos — no de forma aleatoria?
|
|||
|
|
- No "en mi máquina funciona". Siempre. En cualquier máquina. En cualquier momento.
|
|||
|
|
|
|||
|
|
> *En realidad no estamos inventando nada. Todo ya existe. El problema es si lo estamos gestionando adecuadamente.*
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────┐
|
|||
|
|
│ SLIDE STANDALONE — El paradigma │
|
|||
|
|
│ │
|
|||
|
|
│ Las herramientas no eran el problema. │
|
|||
|
|
│ │
|
|||
|
|
│ Los lenguajes no eran el problema. │
|
|||
|
|
│ │
|
|||
|
|
│ El paradigma era el problema. │
|
|||
|
|
└─────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
> *Tono: claridad repentina. El momento en que todo encaja.*
|
|||
|
|
> *Necesitaba algo que forzara claridad — no que habilitara el caos.*
|
|||
|
|
> *Que previniera errores — antes de producción.*
|
|||
|
|
> *Que hiciera las asunciones explícitas — no ocultas.*
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────┐
|
|||
|
|
│ SLIDE STANDALONE — Pesadilla continua │
|
|||
|
|
│ │
|
|||
|
|
│ Sistemas que no sabemos cómo controlar. │
|
|||
|
|
│ Esperamos que funcionen. │
|
|||
|
|
│ Cuando no funcionan, los arreglamos. │
|
|||
|
|
│ │
|
|||
|
|
│ Pesadilla continua. │
|
|||
|
|
│ │
|
|||
|
|
│ (el estado de alarma es lo normal) │
|
|||
|
|
└─────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
> *Medidores:* `🛡 ●○○○○ 😴 ○○○○○ 🔥 ●●●●●`
|
|||
|
|
> *Tono: oscuro, sin ironía. El suelo emocional de la charla. Pausa aquí.*
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Type safety y memory safety como fiabilidad en producción
|
|||
|
|
|
|||
|
|
*Abstract: "Type safety and memory safety as production reliability"*
|
|||
|
|
|
|||
|
|
### El puente: de YAML+Serde a tipos Rust
|
|||
|
|
|
|||
|
|
Serde carga config estructuralmente válida. Pero "elefante" como valor de `animal_companía: String` compila.
|
|||
|
|
La respuesta de Rust: **no uses `String`. Usa un tipo.**
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
enum Animal { Perro, Gato, Conejo } // "elefante" no compila
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Eso es lo que cambia. No el formato de la config. El modelo de qué puede contener.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Lo que Rust introduce — respuesta a las tres preguntas
|
|||
|
|
|
|||
|
|
**Tipado estático estricto** *(responde: "¿tenemos claro lo que queremos?")*
|
|||
|
|
- Un número no es un string. Un puerto no es cualquier entero.
|
|||
|
|
- Los tipos son la declaración de intenciones — sin ambigüedad, sin equívocos
|
|||
|
|
- Las conversiones (cast) son explícitas, no implícitas
|
|||
|
|
- Tipos primarios ricos + structs, enums, tuplas, newtypes
|
|||
|
|
|
|||
|
|
**Inmutabilidad por defecto** *(responde: "¿qué puede cambiar?")*
|
|||
|
|
- "Quiero tomate. Puedes inventarte lo que quieras, pero sin tomate no es el plato que pedí."
|
|||
|
|
- `let` es inmutable. `let mut` es explícito y deliberado.
|
|||
|
|
- Los invariantes son invariantes — no silenciosamente mutados en runtime
|
|||
|
|
|
|||
|
|
**`Option<T>`, no nulos** *(responde: "¿qué es opcional y qué es obligatorio?")*
|
|||
|
|
- No `null`, no "asume que está si no se especifica"
|
|||
|
|
- `Option<T>`: o está (`Some`) o no está (`None`) — y ambos casos se manejan explícitamente
|
|||
|
|
- Restaurante: "champiñón opcional. Si no está, lo sé. Lo decido. Lo gestiono."
|
|||
|
|
- El camarero no puede ignorar que no hay champiñón — el compilador no le deja
|
|||
|
|
|
|||
|
|
**Enums como dominio cerrado** *(responde: "¿cuáles son los valores válidos?")*
|
|||
|
|
- Un cloud provider es `enum Provider { AWS, GCP, Azure, OnPrem }` — no un `String` libre
|
|||
|
|
- Un puerto es un `u16` con rango validado — no cualquier entero
|
|||
|
|
- Las restricciones están en el tipo, no en comentarios, no en documentación, no en la memoria del equipo
|
|||
|
|
|
|||
|
|
**Traits como contratos entre actores** *(responde: "¿pueden los actores de la cadena cambiar?")*
|
|||
|
|
- Composición en vez de herencia rígida
|
|||
|
|
- Cada "actor" de la cadena (AWS provider, K8s, on-prem) implementa el mismo trait
|
|||
|
|
- La cadena funciona con cualquier implementación que cumpla el contrato
|
|||
|
|
- Como el restaurante: el camarero puede cambiar — si conoce el protocolo, el proceso es el mismo
|
|||
|
|
|
|||
|
|
**El compilador como el camarero que valida** *(responde: "¿cuándo detectamos el fallo?")*
|
|||
|
|
- Valida **antes** de construir el binario — no en runtime, no en producción
|
|||
|
|
- No cuando lleva horas ejecutándose. No cuando se llama a una función que nadie tocaba hace meses.
|
|||
|
|
- Fail early. Fail cheap. En compilación, no en el 2am del domingo.
|
|||
|
|
- Binarios predecibles: comportamiento de memoria, recursos y flujos deterministas
|
|||
|
|
|
|||
|
|
> *"El compilador es el camarero que valida el pedido antes de que llegue a cocina. Antes de que el cliente espere. Antes de que el ingrediente no esté."*
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### El impacto humano — cuando el sistema es fiable
|
|||
|
|
|
|||
|
|
Esto no es solo técnico. Cuando la infraestructura es predecible:
|
|||
|
|
|
|||
|
|
- El sueño vuelve
|
|||
|
|
- La confianza vuelve
|
|||
|
|
- El equipo confía en la automatización
|
|||
|
|
- El estrés baja
|
|||
|
|
- Puedes descansar de verdad
|
|||
|
|
|
|||
|
|
> *"Lo que no se puede medir: el miedo. Lo que sí se mide: el MTTR. Antes > 30min. Ahora < 5min."*
|
|||
|
|
|
|||
|
|
> *Medidores — compilador activo:* `🛡 ●●●○○ 😴 ●●○○○ 🔥 ●●●○○`
|
|||
|
|
> *Medidores — tipos + traits + options:* `🛡 ●●●●○ 😴 ●●●●○ 🔥 ●●○○○`
|
|||
|
|
> *Medidores — Rust completo en producción:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────┐
|
|||
|
|
│ SLIDE STANDALONE — Certeza continua │
|
|||
|
|
│ │
|
|||
|
|
│ El compilador valida antes del binario. │
|
|||
|
|
│ Los tipos definen lo que es posible. │
|
|||
|
|
│ El contrato se cumple — o no compila. │
|
|||
|
|
│ │
|
|||
|
|
│ Certeza continua. │
|
|||
|
|
│ │
|
|||
|
|
│ (para continuar durmiendo bien) │
|
|||
|
|
└─────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
> *Medidores:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
|
|||
|
|
> *Tono: resolución. El arco cierra. El título de la charla, demostrado.*
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. Orquestación segura multi-cloud y on-prem
|
|||
|
|
|
|||
|
|
*Abstract: "Building safe orchestration across multi-cloud and on-prem environments"*
|
|||
|
|
|
|||
|
|
### De la teoría al sistema real
|
|||
|
|
|
|||
|
|
Todo lo anterior no es hipotético. Es la arquitectura de **Provisioning** — un sistema de orquestación de infraestructura en producción, escrito en Rust, que gestiona despliegues en AWS, UpCloud y on-prem desde una sola base de código.
|
|||
|
|
|
|||
|
|
La pregunta es: ¿cómo se traduce "tipos y traits" a un sistema que maneja multi-cloud real?
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Nickel como source of truth tipado (ADR-003)
|
|||
|
|
|
|||
|
|
YAML fue rechazado explícitamente. TOML fue rechazado. La razón: sin type safety.
|
|||
|
|
|
|||
|
|
Nickel — un lenguaje de configuración funcional con sistema de tipos — reemplaza YAML:
|
|||
|
|
|
|||
|
|
```nickel
|
|||
|
|
{
|
|||
|
|
infrastructure = {
|
|||
|
|
compute | {
|
|||
|
|
region | String,
|
|||
|
|
count | Number & (> 0),
|
|||
|
|
auto_scaling | {
|
|||
|
|
min | Number & (> 0),
|
|||
|
|
max | Number & (>= min), -- el compilador verifica esto
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Validación en tiempo de compilación de la config — no en runtime, no en producción
|
|||
|
|
- Merging jerárquico tipado: defaults → workspace → profile → environment → runtime
|
|||
|
|
- Resultado (ADR-003): **zero configuration type errors en producción**
|
|||
|
|
- Alternativas rechazadas explícitamente: TOML (no type safety), KCL, YAML+validación híbrida
|
|||
|
|
|
|||
|
|
> *"Serde valida forma. Nickel valida significado. El compilador valida antes del despliegue."*
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Traits como contratos de provider (el restaurante, resuelto)
|
|||
|
|
|
|||
|
|
El problema del restaurante: la cocina puede cambiar. AWS no es UpCloud no es bare metal.
|
|||
|
|
La solución Rust: todos implementan el mismo trait.
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
// El trait es el menú. Los providers son las cocinas.
|
|||
|
|
#[async_trait]
|
|||
|
|
pub trait TaskStorage: Send + Sync {
|
|||
|
|
async fn create_task(&self, task: WorkflowTask) -> StorageResult<WorkflowTask>;
|
|||
|
|
async fn update_task(&self, id: &str, status: TaskStatus) -> StorageResult<()>;
|
|||
|
|
async fn list_tasks(&self, filter: TaskFilter) -> StorageResult<Vec<WorkflowTask>>;
|
|||
|
|
async fn audit_operation(&self, op: AuditEntry) -> StorageResult<()>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Implementaciones: FilesystemStorage, SurrealDbStorage, MemoryStorage
|
|||
|
|
// El orchestrator no sabe cuál usa. El compilador garantiza que todas cumplen.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
// Dominios cerrados — no strings libres
|
|||
|
|
enum RollbackStrategy { ConfigDriven, Conservative, Aggressive, Custom { operations: Vec<String> } }
|
|||
|
|
enum DependencyType { Hard, Soft, Optional }
|
|||
|
|
enum TaskStatus { Pending, Running, Completed, Failed, Cancelled }
|
|||
|
|
enum ProviderType { UpCloud, AWS, Local }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Cuando se añade un nuevo provider: implementa el trait o no compila. No hay forma de olvidarse de un caso.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Orquestación con grafo de dependencias
|
|||
|
|
|
|||
|
|
El orchestrator construye un DAG (grafo acíclico dirigido) de tareas con detección de ciclos en tiempo de compilación del workflow.
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
pub struct WorkflowConfig {
|
|||
|
|
pub max_parallel_tasks: usize,
|
|||
|
|
pub task_timeout_seconds: u64,
|
|||
|
|
pub fail_fast: bool, // falla rápido, falla barato
|
|||
|
|
pub checkpoint_interval_seconds: u64,
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Instalación de Kubernetes como ejemplo — el orchestrator resuelve:
|
|||
|
|
```
|
|||
|
|
containerd (Hard) → etcd (Hard) → kubernetes → cilium (requires kubernetes) → rook-ceph
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
No se puede instalar kubernetes si containerd falla. El tipo `DependencyType::Hard` lo garantiza.
|
|||
|
|
El compilador detecta dependencias circulares. No el operador de guardia a las 3am.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### El estado explícito elimina el drift
|
|||
|
|
|
|||
|
|
El problema de configuration drift: el sistema real diverge silenciosamente del estado deseado.
|
|||
|
|
|
|||
|
|
Rust obliga a que toda mutación sea explícita (`let mut`). El sistema de estado del orchestrator hace lo mismo a nivel de infraestructura:
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
pub struct WorkflowExecutionState {
|
|||
|
|
pub status: WorkflowStatus,
|
|||
|
|
pub task_states: HashMap<String, TaskExecutionState>,
|
|||
|
|
pub checkpoints: Vec<WorkflowCheckpoint>, // qué se hizo y cuándo
|
|||
|
|
pub statistics: WorkflowStatistics,
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Checkpoints cada 5 minutos (configurable)
|
|||
|
|
- Hasta 50 checkpoints retenidos
|
|||
|
|
- Cada estado de provider capturado en `HashMap<String, ProviderState>`
|
|||
|
|
- Sin estado implícito. Sin "el camarero recuerda que el cliente no quiere sal". Está en el pedido.
|
|||
|
|
|
|||
|
|
> *Medidores — sección 4:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. Aplicaciones reales
|
|||
|
|
|
|||
|
|
*Abstract: "Real applications: Kubernetes, blockchain validators, disaster recovery"*
|
|||
|
|
|
|||
|
|
### Kubernetes — despliegue con tipos
|
|||
|
|
|
|||
|
|
El orchestrator se despliega en Kubernetes. 3 réplicas, rolling update, anti-affinity entre nodos.
|
|||
|
|
Pero lo relevante no es que *corre* en Kubernetes — es cómo se *gestiona* Kubernetes como target.
|
|||
|
|
|
|||
|
|
El orchestrator provisiona los componentes del cluster como workflow tipado:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
containerd → etcd → kubernetes control plane
|
|||
|
|
→ kubelet (workers)
|
|||
|
|
→ CoreDNS
|
|||
|
|
→ Cilium (CNI, requires kubernetes)
|
|||
|
|
→ Rook-Ceph (storage, requires kubernetes + Cilium)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Cada componente es un tipo. Cada dependencia es un `DependencyType`. El compiler detecta si intentas instalar Cilium sin Kubernetes. No el CI a las 2am.
|
|||
|
|
|
|||
|
|
Los health probes del orchestrator en K8s — `/health` cada 10 segundos — no son decoración. Son parte del contrato: el sistema sabe cuándo está sano. Lo sabe antes de que falle el cliente.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Blockchain validators — disponibilidad como tipo
|
|||
|
|
|
|||
|
|
Los validators de blockchain (Polkadot, Ethereum) tienen un requisito brutal: uptime o slashing. Un validator que falla pierde fondos. No hay margen para "en mi máquina funciona".
|
|||
|
|
|
|||
|
|
Lo que Rust + este sistema aporta:
|
|||
|
|
|
|||
|
|
**Secretos con post-quantum cryptography (ADR-006)**
|
|||
|
|
- CRYSTALS-Kyber (KEM) + Falcon (signatures) + AES-256-GCM (hybrid)
|
|||
|
|
- Las claves del validator no pueden ser comprometidas por computación cuántica futura
|
|||
|
|
- La rotación de claves es automática, trazada, con audit log
|
|||
|
|
|
|||
|
|
**SLOs con error budgets reales (ADR-009)**
|
|||
|
|
```
|
|||
|
|
Tier 1 — Critical: 99.99% uptime = 52.6 minutos de downtime/año
|
|||
|
|
```
|
|||
|
|
Esto no es un número de marketing. Es un Prometheus rule que dispara alertas y bloquea deploys cuando el burn rate supera el presupuesto.
|
|||
|
|
|
|||
|
|
**Configuración determinista**
|
|||
|
|
- Los parámetros del validator son tipos, no strings: no puede entrar un valor de `bond_amount` que no sea un `u128` validado
|
|||
|
|
- La config del validator es reproducible bit-a-bit en cualquier nodo — mismo Nickel schema, mismo resultado
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Disaster recovery — rollback como tipo, no como procedimiento
|
|||
|
|
|
|||
|
|
El problema clásico de DR: el runbook existe, pero no se ejecutó correctamente, o el estado en DR no coincide con el de producción.
|
|||
|
|
|
|||
|
|
**Checkpoints como snapshots de estado completo**
|
|||
|
|
```rust
|
|||
|
|
pub struct Checkpoint {
|
|||
|
|
pub workflow_state: Option<WorkflowExecutionState>,
|
|||
|
|
pub resources: Vec<ResourceSnapshot>,
|
|||
|
|
pub provider_states: HashMap<String, ProviderState>,
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
No es "vuelve al commit anterior". Es el estado completo del sistema: qué estaba corriendo, en qué provider, con qué configuración, en qué momento.
|
|||
|
|
|
|||
|
|
**Rollback como estrategia tipada**
|
|||
|
|
```rust
|
|||
|
|
enum RollbackStrategy {
|
|||
|
|
ConfigDriven, // lo que diga la config
|
|||
|
|
Conservative, // preserva todo salvo lo marcado
|
|||
|
|
Aggressive, // revierte todo
|
|||
|
|
Custom { operations: Vec<String> }, // playbook explícito
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
No hay forma de hacer rollback sin elegir una estrategia. El compilador no te deja ignorar el caso.
|
|||
|
|
|
|||
|
|
**Self-healing automatizado (ADR-010)**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────┐
|
|||
|
|
│ SLIDE STANDALONE — MTTR │
|
|||
|
|
│ │
|
|||
|
|
│ Sin tipos. Sin compilador. Sin estado. │
|
|||
|
|
│ │
|
|||
|
|
│ MTTR > 30 minutos. │
|
|||
|
|
│ │
|
|||
|
|
│ (a las ??, apagar el fuego) Alarma pesadilla│
|
|||
|
|
│ ──────────────────────────────── │
|
|||
|
|
│ │
|
|||
|
|
│ Rust. Tipos. Estado explícito. │
|
|||
|
|
│ Automated response. │
|
|||
|
|
│ │
|
|||
|
|
│ MTTR < 5 minutos. │
|
|||
|
|
│ │
|
|||
|
|
│ (a las 3am, sin ti) Descanso en paz mental │
|
|||
|
|
└─────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
> *Medidores:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
|
|||
|
|
> *Tono: cifras reales, no promesas. Pausa después de "sin ti".*
|
|||
|
|
|
|||
|
|
El `RemediationEngine` ejecuta playbooks tipados: `ScaleService`, `FailoverService`, `RestartService`, `ClearCache`. Si el remedio falla 3 veces, escala a humano. No itera indefinidamente.
|
|||
|
|
|
|||
|
|
**Multi-backend backup tipado**
|
|||
|
|
- restic, borg, tar, rsync — todos como variantes de un enum
|
|||
|
|
- El backup de producción y el restore en DR usan el mismo tipo, el mismo schema
|
|||
|
|
- "Funciona en prod pero no en DR" no puede ocurrir si el estado es el mismo tipo
|
|||
|
|
|
|||
|
|
> *"No estamos cruzando los dedos. Tenemos el estado. Tenemos el tipo. Tenemos el rollback. Dormimos."*
|
|||
|
|
|
|||
|
|
> *Medidores — sección 5:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Cierre
|
|||
|
|
|
|||
|
|
### Para los veterans de infraestructura
|
|||
|
|
|
|||
|
|
Si has vivido lo que yo he vivido:
|
|||
|
|
- Rust resuelve los problemas que *ya conoces* — no problemas hipotéticos
|
|||
|
|
- No es hype. Llevo décadas viendo tecnologías venir y irse.
|
|||
|
|
- Dale una oportunidad real. Tu sueño te lo agradecerá.
|
|||
|
|
|
|||
|
|
### Para los engineers más jóvenes
|
|||
|
|
|
|||
|
|
Aprende de alguien que lo intentó todo:
|
|||
|
|
- No malgastes décadas en infraestructura frágil
|
|||
|
|
- Empieza con type safety desde el principio
|
|||
|
|
- Constrúyelo fiable desde el día uno
|
|||
|
|
|
|||
|
|
### La perspectiva
|
|||
|
|
|
|||
|
|
> *"A mi edad, tengo perspectiva. He visto tecnologías venir y irse. Rust no es hype. Resuelve problemas reales que he tenido durante décadas. Tener más años no es una limitación. Es una ventaja."*
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
- No hemos inventado nada nuevo. Los problemas siempre han existido.
|
|||
|
|
- Rust los reúne y los resuelve: tipos, traits, compiler, memory safety.
|
|||
|
|
- El resultado: infraestructura predecible. Deployments sin pesadillas.
|
|||
|
|
- **Duermes bien.**
|
|||
|
|
|
|||
|
|
> *Medidores — cierre:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
|
|||
|
|
>
|
|||
|
|
> *Slide final: los tres medidores en verde. Título de la charla. Fin.*
|