<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Madurez on lo0 — Blog Técnico</title><link>https://blog.lo0.es/tags/madurez/</link><description>Recent content in Madurez on lo0 — Blog Técnico</description><generator>Hugo -- gohugo.io</generator><language>es</language><lastBuildDate>Sun, 31 May 2026 07:00:00 +0200</lastBuildDate><atom:link href="https://blog.lo0.es/tags/madurez/index.xml" rel="self" type="application/rss+xml"/><item><title>Cinco niveles de madurez de la plataforma debajo del LLM: del servidor con Linux al cluster listo para vLLM</title><link>https://blog.lo0.es/posts/cinco-niveles-madurez-plataforma-llm-on-premise/</link><pubDate>Sun, 31 May 2026 07:00:00 +0200</pubDate><guid>https://blog.lo0.es/posts/cinco-niveles-madurez-plataforma-llm-on-premise/</guid><description>&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>El post de &lt;a href="https://blog.lo0.es/posts/siete-capas-stack-inferencia-llm-on-premise/">las siete capas del stack de inferencia LLM&lt;/a> daba por supuestas muchas piezas: un cluster Kubernetes operativo, GitOps reconciliando, identidades resueltas, GPUs visibles para el scheduler, observabilidad capaz de transportar &lt;code>gen_ai.*&lt;/code>. Antes de que vLLM tenga sentido, &lt;strong>hay que llegar a ese punto de partida&lt;/strong>, y se llega por niveles. Este post define &lt;strong>cinco niveles de madurez&lt;/strong> de la plataforma que vive debajo del LLM, desde un servidor bare metal con Linux instalado (nivel 0) hasta un cluster listo para correr la capa de inferencia (nivel 4) y el handoff al post anterior (nivel 5). Cada nivel &lt;strong>desbloquea una capacidad concreta&lt;/strong> —ejecutar contenedores con reproducibilidad, reconstruir el cluster desde git, autenticar humanos vía OIDC, programar GPUs con MIG y métricas DCGM, demostrar compliance sin intervención manual— y cada uno tiene un &lt;strong>test de validación&lt;/strong> que decide si estás de verdad ahí o solo te lo cuentas. Para cada nivel: qué piezas OSS lo cubren en 2026 (Cilium, RKE2, Flux, cert-manager, Defguard, NVIDIA GPU Operator, KEDA, Trivy, Kyverno…), &lt;strong>el orden de despliegue&lt;/strong> dentro del nivel, las decisiones que cuesta caro saltarse, y los antipatrones que te bajan de nivel cuando creías estar arriba. La tesis: &lt;strong>subir de nivel cuesta poco esfuerzo si lo haces a tiempo, y mucho refactor si pretendes saltártelo&lt;/strong>. La inferencia LLM exige al menos nivel 4; quien intenta servir LLMs desde un nivel 1 o 2 acaba pagando con incidentes nocturnos lo que se ahorró en plataforma.&lt;/p>
&lt;h2 id="estás-aquí-los-cinco-niveles-de-un-vistazo">Estás aquí: los cinco niveles de un vistazo&lt;/h2>
&lt;p>Antes del detalle, la escalera. Cada peldaño añade una capacidad ausente en el anterior. El test del nivel es la pregunta cuya respuesta honesta dice si ya estás en él.&lt;/p>
&lt;div class="diagram" style="max-width:820px;margin:1rem auto;">
&lt;svg viewBox="0 0 820 340" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="cinco niveles de madurez de la plataforma debajo del LLM">
&lt;style>.b{stroke:#333;stroke-width:1.4;rx:6}.l0{fill:#f6e2e2;stroke:#a33}.l1{fill:#f4e3cf;stroke:#a63}.l2{fill:#eef0d0;stroke:#7a3}.l3{fill:#d8eecf;stroke:#373}.l4{fill:#dfe9f5;stroke:#356}.l5{fill:#ead8f5;stroke:#634}.title{font:600 13px sans-serif;fill:#222}.sm{font:11px sans-serif;fill:#222}.tiny{font:600 10px sans-serif;fill:#222}.note{font:italic 10px sans-serif;fill:#555}&lt;/style>
&lt;text x="410" y="20" text-anchor="middle" class="title">Cinco niveles de madurez (más el handoff al stack LLM en el nivel 5)&lt;/text>
&lt;rect x="40" y="38" width="740" height="44" class="b l0"/>&lt;text x="60" y="56" class="tiny">NIVEL 0 · CAÓTICO&lt;/text>&lt;text x="60" y="74" class="sm">Bare metal con Linux · docker / podman ad-hoc · sin orquestador · cambios manuales con SSH&lt;/text>
&lt;rect x="40" y="90" width="740" height="44" class="b l1"/>&lt;text x="60" y="108" class="tiny">NIVEL 1 · REPETIBLE&lt;/text>&lt;text x="60" y="126" class="sm">Cluster k8s instalado (RKE2 / kubeadm) · CNI · CSI · kubectl apply / Helm desde terminal · pods rodando&lt;/text>
&lt;rect x="40" y="142" width="740" height="44" class="b l2"/>&lt;text x="60" y="160" class="tiny">NIVEL 2 · DEFINIDO&lt;/text>&lt;text x="60" y="178" class="sm">GitOps (Flux) · registry interno · observabilidad infra · backups · el cluster se reconstruye desde el repo&lt;/text>
&lt;rect x="40" y="194" width="740" height="44" class="b l3"/>&lt;text x="60" y="212" class="tiny">NIVEL 3 · GESTIONADO&lt;/text>&lt;text x="60" y="230" class="sm">OIDC + RBAC · cert-manager · External Secrets · Kyverno · NetworkPolicy default deny · auditoría&lt;/text>
&lt;rect x="40" y="246" width="740" height="44" class="b l4"/>&lt;text x="60" y="264" class="tiny">NIVEL 4 · OPTIMIZADO PARA GPU&lt;/text>&lt;text x="60" y="282" class="sm">NVIDIA GPU Operator · DCGM Exporter · MIG / time-slicing · KEDA con métricas LLM · OTel listo para gen_ai.*&lt;/text>
&lt;rect x="40" y="298" width="740" height="32" class="b l5"/>&lt;text x="60" y="318" class="sm">&lt;tspan font-weight="700">NIVEL 5 · HANDOFF&lt;/tspan> — el cluster está preparado para que el stack LLM (las 7 capas) tenga sentido&lt;/text>
&lt;/svg>
&lt;/div>
&lt;p>Los niveles &lt;strong>no son intercambiables&lt;/strong>. Un cluster en nivel 2 no puede correr LLMs en producción con garantías: técnicamente carga el pod de vLLM, pero al primer incidente nocturno se descubre que no hay TLS, ni identidades, ni alerting, ni métricas GPU, ni forma de saber quién cambió qué. Subir un nivel después de tener LLMs ya en producción cuesta &lt;strong>órdenes de magnitud&lt;/strong> más que subirlo cuando el cluster aún está vacío.&lt;/p>
&lt;h2 id="la-analogía-del-puesto-callejero-al-restaurante-con-estrella">La analogía: del puesto callejero al restaurante con estrella&lt;/h2>
&lt;p>Imagina la escala de un negocio de hostelería. &lt;strong>Nivel 0&lt;/strong> es el puesto callejero: una plancha, una bombona, un cocinero que improvisa. Puede vender comida — funciona — pero cualquier cosa que se desvíe del día normal (una inspección sanitaria, un cliente alérgico, un pedido de 200 raciones) le tira el negocio. &lt;strong>Nivel 1&lt;/strong> es el bar de tapas: cocina dimensionada, carta corta repetible, varios turnos. El cocinero ya no improvisa cada día; trabaja sobre un menú escrito, aunque las recetas viven en la cabeza del jefe. &lt;strong>Nivel 2&lt;/strong> es el restaurante con menú del día: hay procedimientos escritos, proveedores fijos, control de stock, libro de incidencias. Si el cocinero principal se cae enfermo, el segundo puede sacar el servicio sin estragos. &lt;strong>Nivel 3&lt;/strong> es el restaurante con carta y servicio formal: trazabilidad de cada ingrediente, alérgenos en la carta, certificación sanitaria, contrato con los proveedores, formación obligatoria del personal. &lt;strong>Nivel 4&lt;/strong> es la cocina especializada en un producto complejo (sushi, alta cocina, panadería artesanal): herramientas específicas que el restaurante normal no necesita (horno de leña, cuchillos especiales, cámara de fermentación), procesos calibrados, métricas de calidad. &lt;strong>Nivel 5&lt;/strong> es el restaurante con estrella Michelin: el sistema entero funciona, &lt;strong>el plato es el resultado de la organización, no del talento de una persona&lt;/strong>.&lt;/p>
&lt;p>La analogía aguanta hasta el final, incluido el detalle más interesante: &lt;strong>se puede operar a cualquier nivel&lt;/strong>, pero las promesas que se pueden cumplir son distintas. El puesto callejero no puede prometer una experiencia consistente a 80 comensales con reserva. El cluster en nivel 1 no puede prometer servicio LLM productivo multi-tenant con SLA. En ambos casos el problema no es de &lt;strong>capacidad técnica del último componente&lt;/strong> (la plancha cocina; el pod arranca); es de &lt;strong>capacidad organizativa del sistema entero&lt;/strong>.&lt;/p>
&lt;p>Vamos nivel por nivel.&lt;/p>
&lt;h2 id="nivel-0--caótico-el-servidor-con-linux-y-nada-más">Nivel 0 — Caótico: el servidor con Linux y nada más&lt;/h2>
&lt;p>&lt;strong>La capacidad que da.&lt;/strong> Ejecutar contenedores con &lt;code>docker&lt;/code>/&lt;code>podman&lt;/code>, ejecutar binarios, conectar el servidor a la red. El operador puede entrar por SSH, hacer cosas, y ver resultados.&lt;/p>
&lt;p>&lt;strong>El test del nivel.&lt;/strong> &lt;em>&amp;ldquo;Si reinstalo el servidor desde cero, ¿puedo dejarlo idéntico a como estaba en una tarde, usando sólo notas guardadas?&amp;rdquo;&lt;/em>. Si la respuesta es no (porque los pasos están en la cabeza del que lo montó, en &lt;code>.bash_history&lt;/code>, en un wiki desactualizado), estás en nivel 0.&lt;/p>
&lt;p>&lt;strong>Piezas mínimas que dejar resueltas antes de subir a nivel 1.&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Pieza&lt;/th>
&lt;th>Decisión sugerida en 2026&lt;/th>
&lt;th>Por qué importa al subir&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Distribución Linux&lt;/td>
&lt;td>Debian estable u Ubuntu LTS&lt;/td>
&lt;td>Soporte largo, predecible, kernel reciente disponible&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Kernel&lt;/td>
&lt;td>LTS reciente (≥ 6.6) con BPF y schedulers modernos&lt;/td>
&lt;td>Cilium/eBPF, drivers NVIDIA recientes lo exigen&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Drivers NVIDIA&lt;/td>
&lt;td>Versión que casa con la CUDA del motor LLM que vas a servir&lt;/td>
&lt;td>Mismatch driver/CUDA bloquea vLLM antes de empezar&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Container runtime&lt;/td>
&lt;td>&lt;code>containerd&lt;/code>&lt;/td>
&lt;td>Estándar CNCF, integrado con RKE2/kubeadm&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Filesystem raíz&lt;/td>
&lt;td>XFS o ext4 + LVM thin pools&lt;/td>
&lt;td>Snapshots, ampliación en caliente&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Sincronización horaria&lt;/td>
&lt;td>&lt;code>chrony&lt;/code> con servidores propios&lt;/td>
&lt;td>TLS, logs correlados, certificados cortos lo exigen&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Red de gestión&lt;/td>
&lt;td>VLAN dedicada, ACLs en switch&lt;/td>
&lt;td>Aislar plano de control del tráfico de carga&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Red de cluster&lt;/td>
&lt;td>LACP + jumbo frames + BGP (si vas a Cilium)&lt;/td>
&lt;td>NVLink intra-nodo no salva la red de servicio&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>BMC / IPMI&lt;/td>
&lt;td>Acceso fuera de banda con TLS y MFA&lt;/td>
&lt;td>Recuperación cuando el sistema operativo no arranca&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Antipatrones que te dejan clavado en nivel 0.&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Servidores &lt;strong>mascota&lt;/strong> (con nombre propio, configurados a mano, no reemplazables).&lt;/li>
&lt;li>Cambios aplicados con &lt;code>vi&lt;/code> directo sobre &lt;code>/etc/...&lt;/code> sin commit a un repo.&lt;/li>
&lt;li>Despliegue con &lt;code>docker-compose&lt;/code> sin healthchecks ni reinicio automático.&lt;/li>
&lt;li>Inventario que vive en una hoja Excel que nadie actualiza.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Orden de despliegue dentro del nivel.&lt;/strong> Imagen del sistema desde PXE/cloud-init con configuración inicial (LVM, hostname, red, SSH key, chrony) → bootstrap de bastion/jump host → inventario en Ansible (o equivalente declarativo aunque luego se reemplace) → drivers NVIDIA + container runtime → smoke test (un contenedor CUDA pasa &lt;code>nvidia-smi&lt;/code>). En este punto, el servidor está listo para que entre Kubernetes.&lt;/p>
&lt;h2 id="nivel-1--repetible-cluster-kubernetes-operativo">Nivel 1 — Repetible: cluster Kubernetes operativo&lt;/h2>
&lt;p>&lt;strong>La capacidad que da.&lt;/strong> Programar contenedores con scheduler, abstracción de red entre pods, volúmenes persistentes, lifecycle de cargas, escalado horizontal manual.&lt;/p>
&lt;p>&lt;strong>El test del nivel.&lt;/strong> &lt;em>&amp;quot;¿Puedo perder un nodo y que las cargas se reprogramen sin intervención humana?&amp;quot;&lt;/em>. Si sí, estás en nivel 1. Si no — porque los pods están pinneados a nodos, porque no hay réplicas, porque las PVCs no se reattachean — sigues en 0 con Kubernetes encima.&lt;/p>
&lt;p>&lt;strong>Piezas mínimas del nivel.&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Pieza&lt;/th>
&lt;th>Decisión sugerida en 2026&lt;/th>
&lt;th>Alternativa principal&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Distribución k8s&lt;/td>
&lt;td>&lt;strong>RKE2&lt;/strong> (CIS-hardened por defecto, sin sobrecosto comercial)&lt;/td>
&lt;td>k3s para edge muy pequeño, kubeadm puro para casos custom&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CNI&lt;/td>
&lt;td>&lt;strong>Cilium&lt;/strong> con kube-proxy replacement, BGP, Gateway API&lt;/td>
&lt;td>Calico (sin BGP no compite contra Cilium en 2026)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CSI block + filesystem + object&lt;/td>
&lt;td>&lt;strong>Rook-Ceph&lt;/strong> (RBD + CephFS + RGW S3-compatible)&lt;/td>
&lt;td>OpenEBS Mayastor + Garage para deployments pequeños&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Ingress&lt;/td>
&lt;td>Cilium Gateway API (mejor unificar con CNI)&lt;/td>
&lt;td>NGINX Ingress, Traefik&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cert básico&lt;/td>
&lt;td>Self-signed bootstrap&lt;/td>
&lt;td>(cert-manager entra en nivel 3)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Manejo de cargas&lt;/td>
&lt;td>&lt;code>kubectl apply&lt;/code> + Helm desde terminal&lt;/td>
&lt;td>Sin GitOps todavía&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Container registry&lt;/td>
&lt;td>Cualquier registry interno (o externo de confianza) con TLS&lt;/td>
&lt;td>(registry interno gestionado entra en nivel 2)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Antipatrones que te bajan a nivel 0.&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Servicios desplegados con &lt;code>kubectl apply&lt;/code> desde la terminal de una persona y &lt;strong>sin guardar el YAML en ninguna parte&lt;/strong>.&lt;/li>
&lt;li>Volúmenes persistentes sin política de backup.&lt;/li>
&lt;li>&amp;ldquo;Cluster de un nodo&amp;rdquo; como producción permanente — un solo punto de fallo arquitectónico.&lt;/li>
&lt;li>CNI sin NetworkPolicy disponible o sin BGP cuando la red lo requiere.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Orden de despliegue dentro del nivel.&lt;/strong> RKE2 instalado en al menos tres nodos para control plane HA → Cilium instalado en modo kube-proxy replacement + BGP control plane → Rook-Ceph en al menos tres nodos cubriendo block (RBD) + filesystem (CephFS) + object (RGW S3-compatible) con replicación 3× o Erasure Coding según pool → smoke test (un Deployment con PVC arranca, los pods se reschedulean al cordon de un nodo, los datos persisten).&lt;/p>
&lt;h2 id="nivel-2--definido-el-cluster-se-reconstruye-desde-git">Nivel 2 — Definido: el cluster se reconstruye desde git&lt;/h2>
&lt;p>&lt;strong>La capacidad que da.&lt;/strong> El estado del cluster vive en un repositorio. Cualquier cambio pasa por commit. Cualquier persona puede reconstruir el cluster (o uno equivalente) desde el repo y los backups. La observabilidad básica avisa cuando algo se rompe.&lt;/p>
&lt;p>&lt;strong>El test del nivel.&lt;/strong> &lt;em>&amp;ldquo;Si pierdo el cluster entero, ¿puedo recrearlo en X horas desde el repo + los backups, sin intervención manual fuera del bootstrap?&amp;rdquo;&lt;/em>. Las dos horas son negociables; lo que define el nivel es que &lt;strong>el repo + los backups bastan&lt;/strong>, no que la persona-que-sabe esté disponible.&lt;/p>
&lt;p>&lt;strong>Piezas mínimas del nivel.&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Pieza&lt;/th>
&lt;th>Decisión sugerida en 2026&lt;/th>
&lt;th>Por qué&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Forge&lt;/td>
&lt;td>&lt;strong>Forgejo&lt;/strong> (o Gitea, GitLab CE)&lt;/td>
&lt;td>OSS auto-alojado, fork comunitario de Gitea, gobernanza abierta&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Reconciliador GitOps&lt;/td>
&lt;td>&lt;strong>Flux&lt;/strong>&lt;/td>
&lt;td>CNCF graduado, multi-tenancy nativo, lightweight&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Registry de imágenes&lt;/td>
&lt;td>&lt;strong>Forgejo Container Registry&lt;/strong>&lt;/td>
&lt;td>Junto al código, sin pieza extra&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>TSDB métricas&lt;/td>
&lt;td>&lt;strong>VictoriaMetrics&lt;/strong> + vmagent&lt;/td>
&lt;td>Throughput superior a Prometheus puro, retención larga, compatible PromQL&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Visualización&lt;/td>
&lt;td>&lt;strong>Grafana&lt;/strong>&lt;/td>
&lt;td>Estándar de facto&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Logs&lt;/td>
&lt;td>&lt;strong>Loki&lt;/strong> o &lt;strong>Vector&lt;/strong>&lt;/td>
&lt;td>OSS, integrado con Grafana&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Alerting&lt;/td>
&lt;td>&lt;strong>Alertmanager&lt;/strong> + &lt;strong>Keep&lt;/strong> (orquestador OSS)&lt;/td>
&lt;td>Keep añade enrutamiento multi-canal sin lock-in&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Backups DB&lt;/td>
&lt;td>&lt;strong>Barman Cloud&lt;/strong> (Postgres)&lt;/td>
&lt;td>Estándar para CNPG&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Backups objeto / dataset&lt;/td>
&lt;td>&lt;strong>Ceph RGW multisite&lt;/strong> + snapshots CephFS&lt;/td>
&lt;td>Cross-pool y cross-site&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Antipatrones que te bajan a nivel 1.&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;code>kubectl apply&lt;/code> aplicado en producción &lt;strong>fuera&lt;/strong> del repo (drift no detectado).&lt;/li>
&lt;li>Branches &lt;code>main&lt;/code> con permisos de escritura para humanos sin revisión.&lt;/li>
&lt;li>Repo monolítico sin separación tenant/infra/apps (cambios cruzados no auditables).&lt;/li>
&lt;li>Métricas que no se conservan más de 7 días (sin SLO observable a un mes vista).&lt;/li>
&lt;li>Alerting que dispara para todo (fatiga) o para nada (silencio).&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Orden de despliegue dentro del nivel.&lt;/strong> Forgejo desplegado primero (es prerrequisito de todo lo demás) → Flux instalado y apuntando al repo de manifests → repositorio inicial con Helm releases de Cilium y Rook-Ceph reconciliados por Flux (sustituyendo los &lt;code>kubectl apply&lt;/code> del nivel 1) → VictoriaMetrics + Grafana + Loki vía Helm/Flux → backups Postgres y snapshots Ceph programados → smoke test (tira el cluster, restaura desde repo + backup, los servicios vuelven).&lt;/p>
&lt;h2 id="nivel-3--gestionado-identidades-certificados-secretos-y-políticas">Nivel 3 — Gestionado: identidades, certificados, secretos y políticas&lt;/h2>
&lt;p>&lt;strong>La capacidad que da.&lt;/strong> Cualquier humano que opera el cluster lo hace con identidad propia (no &lt;code>kubeconfig&lt;/code> compartido), con MFA y con permisos limitados. TLS interno automático. Secretos versionados encriptados. Políticas que &lt;strong>rechazan&lt;/strong> configuraciones inseguras antes de que entren al cluster. Auditoría completa de quién hizo qué.&lt;/p>
&lt;p>&lt;strong>El test del nivel.&lt;/strong> &lt;em>&amp;ldquo;Si un atacante consigue el portátil de un administrador, ¿qué puede hacer en producción?&amp;rdquo;&lt;/em>. En nivel 3 la respuesta es &lt;em>&amp;ldquo;poco&amp;rdquo;&lt;/em>: MFA bloquea el segundo factor, las políticas Kyverno bloquean cambios destructivos sin aprobación, las NetworkPolicies impiden lateral movement, los secretos están encriptados con KMS externo, el audit log queda. En nivel 2, &lt;em>&amp;ldquo;todo&amp;rdquo;&lt;/em>.&lt;/p>
&lt;p>&lt;strong>Piezas mínimas del nivel.&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Pieza&lt;/th>
&lt;th>Decisión sugerida en 2026&lt;/th>
&lt;th>Por qué&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>IdP / OIDC&lt;/td>
&lt;td>&lt;strong>Defguard&lt;/strong>&lt;/td>
&lt;td>OSS español, WireGuard + OIDC + 2FA, multi-org&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Federación con cluster&lt;/td>
&lt;td>OIDC en kube-apiserver, OIDC en Forgejo, OIDC en Grafana&lt;/td>
&lt;td>SSO consistente&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PKI interna&lt;/td>
&lt;td>&lt;strong>cert-manager&lt;/strong> + Trust Manager&lt;/td>
&lt;td>Estándar de facto, ACME y CA interna&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ACME externo&lt;/td>
&lt;td>Let&amp;rsquo;s Encrypt para certs de borde&lt;/td>
&lt;td>Sin pago, automatizado&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Secretos en git&lt;/td>
&lt;td>&lt;strong>SOPS&lt;/strong> + age o KMS externo&lt;/td>
&lt;td>Versionable, encriptado en repo&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Sync de secretos&lt;/td>
&lt;td>&lt;strong>External Secrets Operator&lt;/strong>&lt;/td>
&lt;td>Pull desde KMS / Vault al cluster&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Policy as code&lt;/td>
&lt;td>&lt;strong>Kyverno&lt;/strong> (o OPA Gatekeeper)&lt;/td>
&lt;td>Kyverno tiene menos curva de aprendizaje&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>NetworkPolicy&lt;/td>
&lt;td>&lt;strong>Cilium NetworkPolicy&lt;/strong> + L7&lt;/td>
&lt;td>Default deny per namespace&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Runtime security&lt;/td>
&lt;td>&lt;strong>Tetragon&lt;/strong> (Cilium)&lt;/td>
&lt;td>eBPF, complementa NetworkPolicy con detección&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Vulnerability scanning&lt;/td>
&lt;td>&lt;strong>Trivy&lt;/strong> en pipeline CI + admission&lt;/td>
&lt;td>SBOM por imagen, bloqueo de CVE críticas&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Audit log&lt;/td>
&lt;td>kube-apiserver con &lt;code>--audit-policy-file&lt;/code> enviado a Loki&lt;/td>
&lt;td>Trazabilidad regulatoria&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Políticas Kyverno mínimas a tener vivas.&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Deny de imágenes &lt;code>:latest&lt;/code> o sin sha digest.&lt;/li>
&lt;li>Deny de pods sin &lt;code>securityContext.runAsNonRoot=true&lt;/code>.&lt;/li>
&lt;li>Deny de pods sin &lt;code>resources.limits&lt;/code> (CPU + memoria).&lt;/li>
&lt;li>Deny de Services sin label &lt;code>owner=&amp;lt;equipo&amp;gt;&lt;/code>.&lt;/li>
&lt;li>Deny de cambios en namespaces críticos (&lt;code>kube-system&lt;/code>, &lt;code>flux-system&lt;/code>) sin label de aprobación.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Antipatrones que te bajan a nivel 2.&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;code>kubeconfig&lt;/code> compartido entre administradores.&lt;/li>
&lt;li>Secretos en &lt;code>data:&lt;/code> plano del manifest commiteado al repo.&lt;/li>
&lt;li>NetworkPolicy ausente en namespaces nuevos por defecto (allow-all implícito).&lt;/li>
&lt;li>&lt;code>kubectl edit&lt;/code> o &lt;code>kubectl patch&lt;/code> en producción sin pasar por el repo.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Orden de despliegue dentro del nivel.&lt;/strong> Defguard desplegado y enrolado con WireGuard / OIDC → integración OIDC con kube-apiserver, Forgejo y Grafana → cert-manager instalado y emitiendo certificados internos (CA propia para mTLS, Let&amp;rsquo;s Encrypt para borde) → SOPS configurado y External Secrets Operator instalado → migración de secretos plano → encriptado → Kyverno con políticas iniciales y modo &lt;em>audit&lt;/em>, después &lt;em>enforce&lt;/em> → NetworkPolicy default-deny por namespace → Tetragon habilitado → smoke test (intentar saltarse cada política y comprobar que las admisiones rechazan).&lt;/p>
&lt;h2 id="nivel-4--optimizado-para-gpu-el-cluster-ya-sabe-lo-que-es-una-h100">Nivel 4 — Optimizado para GPU: el cluster ya sabe lo que es una H100&lt;/h2>
&lt;p>&lt;strong>La capacidad que da.&lt;/strong> El scheduler de Kubernetes ve las GPUs, las distingue, las puede particionar (MIG) o multiplexar (time-slicing), exponer métricas DCGM, autoescalar con KEDA usando métricas de la propia carga LLM (&lt;code>vllm:num_requests_running&lt;/code>, &lt;code>vllm:gpu_cache_usage_perc&lt;/code>), transportar trazas con semantic conventions GenAI. Todo lo necesario para que el stack de inferencia LLM se apoye en una plataforma que entiende su naturaleza.&lt;/p>
&lt;p>&lt;strong>El test del nivel.&lt;/strong> &lt;em>&amp;ldquo;Si pongo un pod que pide &lt;code>nvidia.com/gpu: 1&lt;/code>, ¿se programa en la GPU correcta, con el slice correcto, con métricas DCGM expuestas, con observabilidad GenAI lista para recibir spans?&amp;rdquo;&lt;/em>. Si sí, estás en nivel 4. Si la respuesta requiere &amp;ldquo;depende de qué nodo y quién lo despliegue&amp;rdquo;, todavía no.&lt;/p>
&lt;p>&lt;strong>Piezas mínimas del nivel.&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Pieza&lt;/th>
&lt;th>Decisión sugerida en 2026&lt;/th>
&lt;th>Por qué&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>GPU device plugin&lt;/td>
&lt;td>&lt;strong>NVIDIA GPU Operator&lt;/strong>&lt;/td>
&lt;td>Despliega drivers, container toolkit, DCGM y MIG manager con un operator&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Particionamiento HW&lt;/td>
&lt;td>&lt;strong>MIG&lt;/strong> (Multi-Instance GPU) en H100 cuando aplique&lt;/td>
&lt;td>Aislamiento hardware real, no time-slicing&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Métricas GPU&lt;/td>
&lt;td>&lt;strong>DCGM Exporter&lt;/strong>&lt;/td>
&lt;td>SM utilization, VRAM, temperatura, throttling, NVLink bandwidth&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Métricas LLM&lt;/td>
&lt;td>&lt;strong>vLLM Prometheus&lt;/strong> endpoint + scrape&lt;/td>
&lt;td>TTFT, TPOT, KV cache, prefix hit rate&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Autoscaling&lt;/td>
&lt;td>&lt;strong>KEDA&lt;/strong> con ScaledObject Prometheus&lt;/td>
&lt;td>Escala por métricas LLM, no por CPU&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Operadores LLM&lt;/td>
&lt;td>&lt;strong>vLLM Production Stack&lt;/strong> / &lt;strong>OME&lt;/strong> (Operator Model Engine)&lt;/td>
&lt;td>Manejo declarativo de modelos / adapters&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Trazas&lt;/td>
&lt;td>&lt;strong>OpenTelemetry Collector&lt;/strong> con receivers OTLP + processors + exporters&lt;/td>
&lt;td>Semantic conventions &lt;code>gen_ai.*&lt;/code> (&lt;a href="https://blog.lo0.es/posts/tracing-llm-otel-genai/">post&lt;/a>)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LeaderWorkerSet&lt;/td>
&lt;td>API LeaderWorkerSet (k8s 1.30+)&lt;/td>
&lt;td>Topología tensor parallel coherente con NVLink&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Topology Manager&lt;/td>
&lt;td>habilitado con &lt;code>single-numa-node&lt;/code>&lt;/td>
&lt;td>Pin de pods GPU a NUMA correcta&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Decisión clave: MIG, time-slicing o pasthrough.&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>MIG&lt;/strong> divide una H100 en 1g.10gb, 2g.20gb, 3g.40gb, 7g.80gb (slices con aislamiento HW real). Útil para servir varios modelos pequeños o reservar capacidad por tenant con garantía. Limitación: hasta 7 instancias por GPU, perfiles predefinidos.&lt;/li>
&lt;li>&lt;strong>Time-slicing&lt;/strong> comparte una GPU entre varios pods sin aislamiento HW. Útil para dev/test, no para producción multi-tenant con SLA.&lt;/li>
&lt;li>&lt;strong>Passthrough&lt;/strong> asigna la GPU entera a un pod. Útil para tensor parallel sobre múltiples GPUs del mismo nodo (LLM grande con TP=4).&lt;/li>
&lt;/ul>
&lt;p>Para una plataforma LLM productiva, la regla práctica: &lt;strong>passthrough para los modelos grandes con TP&lt;/strong>, &lt;strong>MIG para embeddings y modelos pequeños que cohabitan&lt;/strong>, &lt;strong>nunca time-slicing en producción&lt;/strong>.&lt;/p>
&lt;p>&lt;strong>Antipatrones que te bajan a nivel 3.&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Instalar drivers NVIDIA a mano fuera del GPU Operator (rotura silenciosa al actualizar Kubernetes).&lt;/li>
&lt;li>Servir un LLM con &lt;code>requests.gpu: 1&lt;/code> sin haber decidido MIG / passthrough (terminas con GPUs idle por fragmentación o pods que se pisan).&lt;/li>
&lt;li>KEDA autoscalando por CPU (&lt;code>HorizontalPodAutoscaler&lt;/code> clásico) en pods que están casi siempre al 10% de CPU pero al 95% de KV cache.&lt;/li>
&lt;li>OpenTelemetry desplegado pero sin semantic conventions &lt;code>gen_ai.*&lt;/code> (las trazas no son LLM-aware).&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Orden de despliegue dentro del nivel.&lt;/strong> NVIDIA GPU Operator instalado vía Helm/Flux con la versión de driver que case con el motor LLM elegido → DCGM Exporter habilitado y métricas visibles en Grafana (dashboards NVIDIA importados) → MIG manager configurado para los nodos donde tenga sentido (mezcla typical en cluster 4×H100 SXM: dos GPUs con passthrough completo para el LLM general TP=4, dos GPUs particionadas en 2×3g.40gb cada una para LLMs pequeños + embeddings) → OpenTelemetry Collector con processors &lt;code>attributes&lt;/code> para enriquecer spans con etiquetas propias (&lt;code>tenant_id&lt;/code>, &lt;code>priority_tier&lt;/code>) + exporters a Langfuse y a Tempo → KEDA instalado con ScaledObject de ejemplo apuntando a &lt;code>vllm:num_requests_running&lt;/code> → vLLM Production Stack o OME para declarar modelos como CRD → smoke test (un Deployment de vLLM declarado vía CRD arranca, sirve un token, expone métricas, la traza llega a Langfuse, KEDA escala bajo carga sintética).&lt;/p>
&lt;h2 id="nivel-5--handoff-el-cluster-es-plataforma-llm-las-siete-capas-entran-encima">Nivel 5 — Handoff: el cluster es plataforma LLM, las siete capas entran encima&lt;/h2>
&lt;p>Llegado al nivel 4, el cluster cumple el contrato que el &lt;a href="https://blog.lo0.es/posts/siete-capas-stack-inferencia-llm-on-premise/">post de las siete capas&lt;/a> asumía como punto de partida. El nivel 5 no añade infraestructura: añade el &lt;strong>stack LLM&lt;/strong> propiamente dicho. Por completitud, los siete componentes del nivel 5 son:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Gateway&lt;/strong> (Envoy AI Gateway) — entra primero, dirige tráfico a inferencia LLM y embeddings.&lt;/li>
&lt;li>&lt;strong>Inferencia LLM&lt;/strong> (vLLM Production Stack o OME con vLLM) — sobre las GPUs ya descubiertas por el GPU Operator del nivel 4.&lt;/li>
&lt;li>&lt;strong>Embeddings + reranker&lt;/strong> (Infinity, TEI) — pod separado del LLM, ya cubierto en el post anterior.&lt;/li>
&lt;li>&lt;strong>Vector store + datos relacionales&lt;/strong> (Qdrant, PostgreSQL CNPG, Ceph RGW para pesos y adapters, CephFS para datasets) — la mayoría ya existía en nivel 2 como datos; ahora se especializa para RAG.&lt;/li>
&lt;li>&lt;strong>Observabilidad LLM-aware&lt;/strong> (Langfuse) — se enchufa a la cadena OTel del nivel 4.&lt;/li>
&lt;li>&lt;strong>Control plane GitOps&lt;/strong> — el del nivel 2 sigue siendo la única autoridad legítima.&lt;/li>
&lt;li>&lt;strong>Dependency tracking&lt;/strong> (Hubble flows + Otterize) — sobre Cilium que ya existía en nivel 1.&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>El criterio para promocionar de nivel 4 a nivel 5&lt;/strong> no es técnico: es contractual. El cluster ya soporta LLMs; la decisión es cuándo abrir tráfico real de clientes. La promoción exige: golden eval del modelo verde, runbook de incidentes firmado, SLOs negociados, plan de continuidad, mapeo a ENS / NIS2 / 42001 si aplica.&lt;/p>
&lt;h2 id="las-matemáticas-que-importan-cuánto-cuesta-saltarse-un-nivel">Las matemáticas que importan: cuánto cuesta saltarse un nivel&lt;/h2>
&lt;p>Para cuantificar la tesis del post, una estimación con orden de magnitud del &lt;strong>coste de subir cada nivel a tiempo&lt;/strong> versus &lt;strong>subirlo después de tener producción&lt;/strong>. Las cifras son tiempo de ingeniería con un equipo de plataforma pequeño (2-3 personas), asumiendo plantillas y experiencia previa.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Nivel&lt;/th>
&lt;th>Tiempo a montar sobre cluster vacío&lt;/th>
&lt;th>Tiempo a retrofit con producción rodando&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>0 → 1&lt;/td>
&lt;td>1-2 semanas&lt;/td>
&lt;td>1-2 semanas (poco refactor downstream)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1 → 2&lt;/td>
&lt;td>2-3 semanas&lt;/td>
&lt;td>4-8 semanas (migrar todo a git)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2 → 3&lt;/td>
&lt;td>2-4 semanas&lt;/td>
&lt;td>8-16 semanas (rebuild de imágenes, migración de secretos, RBAC retroactivo)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3 → 4&lt;/td>
&lt;td>1-2 semanas&lt;/td>
&lt;td>4-8 semanas (reconfigurar GPU, mover modelos a MIG, instrumentar &lt;code>gen_ai.*&lt;/code>)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>4 → 5&lt;/td>
&lt;td>1-2 semanas&lt;/td>
&lt;td>2-4 semanas&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Total 0 → 5&lt;/strong>&lt;/td>
&lt;td>&lt;strong>~10-15 semanas&lt;/strong>&lt;/td>
&lt;td>&lt;strong>~20-40 semanas si se hace en orden equivocado&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Multiplicador típico observable en la práctica: &lt;strong>2× a 3×&lt;/strong> el coste si se hace en orden equivocado. Y eso asumiendo que &lt;strong>se llega a hacer&lt;/strong> — muchos proyectos no superan el nivel 2 nunca porque &amp;ldquo;lo de la identidad&amp;rdquo; siempre puede esperar a otro sprint. Cuando llega el incidente, ya es tarde para empezar.&lt;/p>
&lt;p>Más allá del tiempo, el coste &lt;strong>operativo&lt;/strong> (incidentes nocturnos, escapes de seguridad, deuda invisible) crece exponencialmente con el desfase entre el nivel real y el nivel necesario. Un cluster en nivel 2 sirviendo LLMs productivos en clientes regulados es una bomba de relojería: técnicamente funciona, organizativamente no.&lt;/p>
&lt;h2 id="diagrama-final-la-escalera-completa-con-piezas">Diagrama final: la escalera completa con piezas&lt;/h2>
&lt;div class="diagram" style="max-width:820px;margin:1rem auto;">
&lt;svg viewBox="0 0 820 520" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="diagrama final de los cinco niveles con sus piezas OSS y el handoff al stack LLM">
&lt;style>.b{stroke:#333;stroke-width:1.4;rx:6}.bg{fill:#fafafa;stroke:#bbb;rx:8}.l0{fill:#f6e2e2;stroke:#a33}.l1{fill:#f4e3cf;stroke:#a63}.l2{fill:#eef0d0;stroke:#7a3}.l3{fill:#d8eecf;stroke:#373}.l4{fill:#dfe9f5;stroke:#356}.l5{fill:#ead8f5;stroke:#634}.lbl{font:600 12px sans-serif;fill:#222}.sm{font:11px sans-serif;fill:#222}.tiny{font:600 10px sans-serif;fill:#222}.note{font:italic 10px sans-serif;fill:#555}.arr{stroke:#666;stroke-width:1.4;fill:none;marker-end:url(#a)}&lt;/style>
&lt;defs>&lt;marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="#666"/>&lt;/marker>&lt;/defs>
&lt;text x="410" y="22" text-anchor="middle" class="lbl">Cinco niveles de madurez · piezas OSS · handoff al stack LLM&lt;/text>
&lt;rect x="40" y="38" width="740" height="76" class="b l0"/>&lt;text x="60" y="56" class="tiny">NIVEL 0 · CAÓTICO · un servidor con Linux&lt;/text>
&lt;text x="60" y="74" class="sm">Debian / Ubuntu LTS · kernel ≥6.6 · containerd · drivers NVIDIA · LVM · chrony · BMC TLS+MFA&lt;/text>
&lt;text x="60" y="92" class="sm">Red: VLAN gestión, LACP, jumbo frames, BGP en switch&lt;/text>
&lt;text x="60" y="108" class="note">Test: ¿puedo reconstruir el servidor desde notas?&lt;/text>
&lt;rect x="40" y="122" width="740" height="76" class="b l1"/>&lt;text x="60" y="140" class="tiny">NIVEL 1 · REPETIBLE · cluster Kubernetes operativo&lt;/text>
&lt;text x="60" y="158" class="sm">RKE2 (CIS-hardened) · Cilium (kube-proxy replacement + BGP) · Rook-Ceph (RBD + CephFS + RGW)&lt;/text>
&lt;text x="60" y="176" class="sm">Gateway API · kubectl/Helm desde terminal · pods rodando con HA&lt;/text>
&lt;text x="60" y="192" class="note">Test: ¿perder un nodo no requiere acción humana?&lt;/text>
&lt;rect x="40" y="206" width="740" height="76" class="b l2"/>&lt;text x="60" y="224" class="tiny">NIVEL 2 · DEFINIDO · el cluster se reconstruye desde git&lt;/text>
&lt;text x="60" y="242" class="sm">Forgejo + Flux · Forgejo Container Registry · VictoriaMetrics + Grafana + Loki&lt;/text>
&lt;text x="60" y="260" class="sm">Backups Barman Cloud + Ceph snapshots/RGW multisite · Alertmanager + Keep&lt;/text>
&lt;text x="60" y="276" class="note">Test: ¿puedo recrear el cluster desde repo + backups?&lt;/text>
&lt;rect x="40" y="290" width="740" height="76" class="b l3"/>&lt;text x="60" y="308" class="tiny">NIVEL 3 · GESTIONADO · identidad, certs, secretos, políticas&lt;/text>
&lt;text x="60" y="326" class="sm">Defguard (OIDC + WireGuard) · cert-manager · SOPS + ESO · Kyverno · Trivy&lt;/text>
&lt;text x="60" y="344" class="sm">NetworkPolicy default deny · Tetragon · audit log&lt;/text>
&lt;text x="60" y="360" class="note">Test: ¿qué puede hacer un atacante con un portátil de admin?&lt;/text>
&lt;rect x="40" y="374" width="740" height="86" class="b l4"/>&lt;text x="60" y="392" class="tiny">NIVEL 4 · OPTIMIZADO PARA GPU · el scheduler entiende H100&lt;/text>
&lt;text x="60" y="410" class="sm">NVIDIA GPU Operator · DCGM Exporter · MIG manager · Topology Manager NUMA&lt;/text>
&lt;text x="60" y="428" class="sm">KEDA con métricas vLLM · OTel Collector con gen_ai.* · LeaderWorkerSet · OME&lt;/text>
&lt;text x="60" y="446" class="sm">Decisión: passthrough TP=4 para LLM grande, MIG para LLMs pequeños + embeddings&lt;/text>
&lt;text x="60" y="460" class="note" fill="#373">Test: ¿un pod con nvidia.com/gpu:1 se programa con métricas y traza listas?&lt;/text>
&lt;rect x="40" y="468" width="740" height="44" class="b l5"/>&lt;text x="60" y="486" class="tiny">NIVEL 5 · HANDOFF&lt;/text>&lt;text x="60" y="504" class="sm">Stack LLM (7 capas del post anterior) entra encima · gateway, vLLM, embeddings, Qdrant, Langfuse...&lt;/text>
&lt;/svg>
&lt;/div>
&lt;p>La escalera no es decorativa: cada nivel &lt;strong>enable&lt;/strong> el siguiente. No se puede tener observabilidad LLM-aware (nivel 4) sin OTel desplegado vía Flux (nivel 2). No se puede tener TLS interno automático (nivel 3) sin un PKI raíz que viva en algún sitio (registro y certificados gestionados desde el nivel 2). No se puede tener KEDA escalando por métricas vLLM (nivel 4) sin Prometheus / VictoriaMetrics scrapeando (nivel 2). Los niveles &lt;strong>no son una jerarquía conceptual&lt;/strong>: son una jerarquía de &lt;strong>dependencias de instalación&lt;/strong>.&lt;/p>
&lt;h2 id="decisiones-de-diseño-típicas-que-rompen-el-progreso">Decisiones de diseño típicas que rompen el progreso&lt;/h2>
&lt;p>Errores que se ven repetidamente y que tiran el cluster atrás de nivel:&lt;/p>
&lt;p>&lt;strong>1. Saltar de nivel 1 a nivel 4 directamente.&lt;/strong> &amp;ldquo;Tenemos prisa por servir el LLM, lo de identidad y GitOps lo hacemos después&amp;rdquo;. Después es siempre dos órdenes de magnitud más caro y siempre llega después del primer incidente.&lt;/p>
&lt;p>&lt;strong>2. Confundir Helm con GitOps.&lt;/strong> Tener Helm charts no es nivel 2. Es nivel 1 con plantillas. Nivel 2 exige que un reconciliador (Flux/ArgoCD) &lt;strong>aplique&lt;/strong> las charts desde un repo, &lt;strong>detecte drift&lt;/strong> y &lt;strong>avise&lt;/strong>.&lt;/p>
&lt;p>&lt;strong>3. cert-manager sin policy de uso.&lt;/strong> Tener certificados auto-renovados pero usar TLS sólo en el ingress, sin mTLS interno entre servicios, deja la promesa de TLS coja y baja el nivel 3 a un cosplay del 3.&lt;/p>
&lt;p>&lt;strong>4. NVIDIA drivers a mano.&lt;/strong> Funciona el día uno y se rompe el día del primer upgrade de kernel. La regla: drivers &lt;strong>siempre vía GPU Operator&lt;/strong>, nunca paquetes del sistema operativo.&lt;/p>
&lt;p>&lt;strong>5. Métricas Prometheus pero retención de 7 días.&lt;/strong> Sin retención larga (≥ 90 días) no hay SLO honesto. VictoriaMetrics con 1 año de retención cuesta poco más que Prometheus con 7 días, y desbloquea cumplimiento y postmortems serios.&lt;/p>
&lt;p>&lt;strong>6. OIDC sólo para kube-apiserver.&lt;/strong> Si Forgejo, Grafana, Defguard y vLLM cada uno tiene su propio sistema de auth, no tienes SSO, tienes islas. Un nivel 3 honesto exige &lt;strong>federación&lt;/strong>.&lt;/p>
&lt;p>&lt;strong>7. Kyverno en modo &lt;em>audit&lt;/em> permanente.&lt;/strong> Las políticas que no rechazan no son políticas, son alertas. En algún momento hay que pasar a &lt;em>enforce&lt;/em>. Mientras tanto, sigues en nivel 2 con cara de 3.&lt;/p>
&lt;p>&lt;strong>8. MIG sin decisión consciente del perfil.&lt;/strong> Configurar MIG con el perfil por defecto sin haber medido el tamaño de los modelos que van a cohabitar deja GPUs fragmentadas con slices que nadie usa. La regla: MIG sólo si has medido y has decidido los perfiles por adelantado.&lt;/p>
&lt;p>Todos comparten una raíz: &lt;strong>declarar el nivel sin pasar el test del nivel&lt;/strong>. Decir &amp;ldquo;ya hicimos GitOps&amp;rdquo; cuando todavía se aplican cosas con &lt;code>kubectl edit&lt;/code> en prod. Decir &amp;ldquo;ya hicimos identidad&amp;rdquo; cuando hay un &lt;code>kubeconfig&lt;/code> admin compartido. Decir &amp;ldquo;estamos listos para LLM&amp;rdquo; cuando no hay DCGM Exporter ni Langfuse enchufado.&lt;/p>
&lt;h2 id="aplicado-a-hardware-on-premise-típico-cluster-4h100-sxm">Aplicado a hardware on-premise típico: cluster 4×H100 SXM&lt;/h2>
&lt;p>Sobre el cluster genérico de referencia (4×H100 SXM 80 GB, NVLink, 640 GB RAM), un setup razonable después de pasar los cinco niveles distribuye así los componentes:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">control plane (3 nodos sin GPU, hostnames cp-01..03)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── kube-apiserver, etcd, controller-manager, scheduler
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── Flux, Forgejo, cert-manager, External Secrets, Kyverno
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── Tetragon (DaemonSet también aquí)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">worker plane (≥ 3 nodos sin GPU, hostnames worker-cpu-01..03)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── Cilium agent (DaemonSet)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── Rook-Ceph OSDs + MONs + MDS (CephFS) + RGW (S3)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── VictoriaMetrics + Grafana + Loki
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── Defguard (StatefulSet)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── Langfuse + OTel Collector
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">worker plane GPU (≥ 2 nodos con 4×H100 SXM, hostnames worker-gpu-01..02)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── NVIDIA GPU Operator (driver + container toolkit)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── DCGM Exporter (DaemonSet)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── MIG manager (configurando el perfil decidido)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── vLLM (Deployment) — LLM general TP=4 ocupa 4 GPUs (passthrough)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── vLLM (Deployment) — LLM código TP=2 ocupa 2 GPUs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── Infinity (embeddings) — 2 réplicas cohabitan en 2 slices MIG
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── KEDA scaler escuchando métricas vLLM
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>La regla operativa: &lt;strong>el plano de control y el plano CPU se separan del plano GPU&lt;/strong>. Un incidente en el plano GPU no debe llevarse por delante el plano de control (que es lo que recupera el cluster). Y el plano CPU concentra todo lo que mueve estado relevante (Forgejo, Rook-Ceph, Postgres CNPG, Langfuse): es el corazón a proteger.&lt;/p>
&lt;p>El hardware GPU se especializa al máximo: pods GPU &lt;strong>solamente&lt;/strong> corren en nodos GPU, y los nodos GPU &lt;strong>no corren&lt;/strong> nada CPU-bound aparte del overhead operativo (Cilium, GPU Operator, DCGM). Esto se enforza con &lt;code>nodeSelector&lt;/code> + taints/tolerations + Kyverno policy que rechaza pods sin requests GPU programándose en nodos GPU.&lt;/p>
&lt;h2 id="lo-que-no-hemos-cubierto-próximos-posts">Lo que no hemos cubierto (próximos posts)&lt;/h2>
&lt;p>Este post recorre el camino vertical hacia arriba. Quedan piezas horizontales y otras transversales que merecen su propio artículo:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Multi-site activo/standby&lt;/strong>: cómo se federan dos clusters con Cilium Cluster Mesh y qué cambia en cada nivel cuando hay dos sites en lugar de uno.&lt;/li>
&lt;li>&lt;strong>Migración entre niveles con tráfico real&lt;/strong>: cómo se retrofitea un cluster que ya está en producción al nivel siguiente sin downtime.&lt;/li>
&lt;li>&lt;strong>La operación día a día&lt;/strong>: runbooks por nivel, qué dashboards mirar cada mañana, qué SLOs definir por componente.&lt;/li>
&lt;li>&lt;strong>El plano de coste&lt;/strong>: cuánto cuesta cada nivel en hardware, energía, horas de ingeniería, licencias OSS opcionales (soporte comercial de Rancher, Cilium Enterprise, etc.) y cuándo cada gasto se justifica.&lt;/li>
&lt;li>&lt;strong>Cumplimiento operacionalizado&lt;/strong>: cómo se mapean los niveles 3 y 4 a controles ENS Alto, NIS2 e ISO/IEC 42001 sin convertir el cluster en un ejercicio de paperwork.&lt;/li>
&lt;/ul>
&lt;h2 id="ver-también">Ver también&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://blog.lo0.es/posts/siete-capas-stack-inferencia-llm-on-premise/">Anatomía de un stack de inferencia LLM on-premise&lt;/a> — lo que se monta &lt;strong>encima&lt;/strong> de un cluster en nivel 4. Este post es su prequel arquitectónico.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/catalogo-herramientas-oss-llmops/">El catálogo OSS para LLMOps en seis etapas&lt;/a> — fichas individuales de muchas de las piezas citadas aquí.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/tracing-llm-otel-genai/">Tracing LLM con OpenTelemetry GenAI&lt;/a> — el OTel del nivel 4 con detalle de las semantic conventions &lt;code>gen_ai.*&lt;/code>.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/pipeline-llmops-seis-etapas/">Pipeline LLMOps de seis etapas&lt;/a> — el marco operacional que vive sobre el cluster nivel 5.&lt;/li>
&lt;/ul>
&lt;h2 id="referencias">Referencias&lt;/h2>
&lt;ul>
&lt;li>RKE2 Documentation — &lt;a href="https://docs.rke2.io/">docs.rke2.io&lt;/a>&lt;/li>
&lt;li>Cilium documentation — &lt;a href="https://docs.cilium.io/">docs.cilium.io&lt;/a>&lt;/li>
&lt;li>Rook-Ceph — &lt;a href="https://rook.io/">rook.io&lt;/a>&lt;/li>
&lt;li>Flux GitOps toolkit — &lt;a href="https://fluxcd.io/">fluxcd.io&lt;/a>&lt;/li>
&lt;li>Forgejo — &lt;a href="https://forgejo.org/">forgejo.org&lt;/a>&lt;/li>
&lt;li>cert-manager — &lt;a href="https://cert-manager.io/">cert-manager.io&lt;/a>&lt;/li>
&lt;li>External Secrets Operator — &lt;a href="https://external-secrets.io/">external-secrets.io&lt;/a>&lt;/li>
&lt;li>Kyverno — &lt;a href="https://kyverno.io/">kyverno.io&lt;/a>&lt;/li>
&lt;li>NVIDIA GPU Operator — &lt;a href="https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/">docs.nvidia.com/datacenter/cloud-native/gpu-operator&lt;/a>&lt;/li>
&lt;li>DCGM Exporter — &lt;a href="https://github.com/NVIDIA/dcgm-exporter">github.com/NVIDIA/dcgm-exporter&lt;/a>&lt;/li>
&lt;li>KEDA — &lt;a href="https://keda.sh/">keda.sh&lt;/a>&lt;/li>
&lt;li>LeaderWorkerSet API — &lt;a href="https://github.com/kubernetes-sigs/lws">github.com/kubernetes-sigs/lws&lt;/a>&lt;/li>
&lt;li>vLLM Production Stack — &lt;a href="https://docs.vllm.ai/">docs.vllm.ai/en/latest/serving/production_stack.html&lt;/a>&lt;/li>
&lt;li>OpenTelemetry Semantic Conventions for GenAI — &lt;a href="https://opentelemetry.io/docs/specs/semconv/gen-ai/">opentelemetry.io/docs/specs/semconv/gen-ai&lt;/a>&lt;/li>
&lt;li>CIS Kubernetes Benchmark&lt;/li>
&lt;li>NIST SP 800-207 — Zero Trust Architecture&lt;/li>
&lt;/ul></description></item></channel></rss>