Tu clúster es un grafo: detección de drift en Kubernetes con Drasi y Cypher

🕒 Publicado en Zendoric: 1 de septiembre de 2026 · 00:48
✨ Generado con IA · cómo se hace
El artículo no trata de IA generativa sino de ingeniería de plataformas: documenta un experimento práctico con Drasi, un motor que permite tratar el estado en vivo de un clúster de Kubernetes (concretamente AKS) como un grafo consultable con Cypher, el lenguaje de consulta popularizado por bases de datos de grafos.
El artículo no trata de IA generativa sino de ingeniería de plataformas: documenta un experimento práctico con Drasi, un motor que permite tratar el estado en vivo de un clúster de Kubernetes (concretamente AKS) como un grafo consultable con Cypher, el lenguaje de consulta popularizado por bases de datos de grafos. El punto de partida es honesto sobre los límites de la herramienta: en cualquier clúster con GitOps ya existen mecanismos de detección de drift —Gatekeeper o Kyverno bloqueando configuraciones inválidas en el momento de la admisión, Flux o Argo comparando el estado del clúster contra git, y Prometheus disparando alertas sobre métricas sostenidas durante un tiempo determinado—. El autor no presenta Drasi como sustituto de ninguno de ellos, sino como una pieza que cubre un hueco específico: preguntas relacionales que cruzan varios tipos de recursos a la vez y que ninguna de esas herramientas puede responder por separado, del tipo '¿sigue este Deployment infrarreplicado cinco minutos después?' o '¿está este Pod ejecutando una imagen no aprobada?'.
La mecánica de Drasi consiste en consultas Cypher "standing" (continuas) sobre el grafo de recursos, con funciones temporales como trueFor (una condición debe mantenerse verdadera durante una ventana de tiempo) y trueLater (autoprogramación de comprobaciones futuras). La ventaja frente a un sistema de sondeo periódico es que las reacciones se disparan en las transiciones reales de estado, no en cada tick de comprobación, y el conjunto de resultados se mantiene actualizado por sí mismo. Drasi puede desplegarse dentro del propio clúster o apuntar a uno externo mediante un Secret de kubeconfig, y el autor recomienda limitar los permisos RBAC de esa credencial a list/watch solo sobre los tipos de recursos que las reglas necesiten leer, como si fuera una cuenta de servicio de un panel de solo lectura.
El núcleo del artículo es metodológico: el autor escribió primero seis reglas basándose únicamente en la documentación oficial, y después las puso a prueba contra un clúster real con violaciones provocadas deliberadamente. El resultado fue revelador —solo dos reglas sobrevivieron sin cambios, dos necesitaron reescritura y dos resultaron directamente inviables, no por errores de sintaxis sino por una limitación estructural de la plataforma—. Esa brecha entre lo que parece correcto sobre el papel y lo que realmente funciona al ejecutarse es, según el propio autor, el resultado más útil del ejercicio.
La limitación estructural es clave para entender el resto del artículo: la fuente de Kubernetes de Drasi observa una lista fija de doce tipos de recursos —Pod, Deployment, ReplicaSet, StatefulSet, DaemonSet, Job, Service, ServiceAccount, Node, Ingress, PersistentVolume y PersistentVolumeClaim— y ningún permiso adicional de RBAC amplía esa lista. Namespace, NetworkPolicy, PodDisruptionBudget y Secret simplemente no son observables hoy, así que cualquier regla que dependa de ellos queda descartada de raíz, y la única solución es escoger otro recurso sobre el que apoyarse, no intentar arreglarlo con una consulta más ingeniosa.
Entre las reglas que sí funcionaron, la de despliegues infrarreplicados de forma sostenida compara spec.replicas con status.readyReplicas envuelta en un trueFor de cinco minutos, precisamente para que las fluctuaciones normales de un rollout no disparen falsos positivos. Fue necesario añadir coalesce(status.readyReplicas, 0) porque ese campo aparece como nulo mientras el despliegue está en curso, y sin ese ajuste la consulta original fallaba directamente. El autor la validó provocando una violación real con un nodeSelector imposible de programar, acortando la ventana a cinco minutos solo para poder observar la transición sin esperas largas.
La regla de imágenes fuera del registro aprobado reveló otro límite del parser: STARTS WITH, CONTAINS, ENDS WITH y las expresiones regulares con =~ no existen en el subconjunto de Cypher soportado, así que hubo que reescribirla usando left(imagen, N) <> 'prefijo'. Ya sobre el clúster real, la consulta detectó correctamente una imagen de docker.io/library/nginx etiquetada deliberadamente de forma incorrecta, pero también sacó a la luz dos casos límite reales que un clúster de pruebas simplificado no habría mostrado: los sidecars de Dapr se reportan como docker.io/daprio/daprd, por lo que hay que añadir ese prefijo a la lista de permitidos o cada pod con ese sidecar generaría una alerta falsa; y los componentes de Calico se reportan únicamente como un digest sha256 sin ningún prefijo de registro, lo que deja a cualquier regla basada en prefijos completamente ciega ante imágenes ancladas por digest, una limitación real que el autor prefiere señalar en lugar de ocultar.
Las tres reglas descartadas —namespaces sin NetworkPolicy, expiración de certificados TLS en Secrets, y agotamiento de PodDisruptionBudget— comparten el mismo problema de fondo: los tipos de recurso implicados (Namespace, NetworkPolicy, Secret, PodDisruptionBudget) no están en la lista de observación de la fuente. A eso se suma que la sintaxis EXISTS { MATCH ... }, pensada para expresar la ausencia de una relación (por ejemplo, 'este namespace nunca tuvo una NetworkPolicy asociada'), tampoco está soportada por el parser actual. El autor destaca que la idea de usar trueLater para autoprogramar comprobaciones de expiración de certificados sin necesidad de un cron job escaneando secretos sigue siendo conceptualmente válida y pedagógicamente interesante, aunque hoy no se pueda demostrar contra Secrets de Kubernetes; apunta que el CRD Certificate de cert-manager podría ser una vía alternativa, aunque aclara que no la ha probado.
El artículo también documenta tres reglas nuevas no contempladas en el plan original. La de presión sostenida de nodo (memoria o disco) usa el mismo patrón de trueFor sobre las condiciones del Node, y quedó validada solo en sintaxis: el autor decidió no forzar una violación real porque el nodo de prueba compartía el plano de control del propio Drasi, y agotar su memoria deliberadamente ponía en riesgo todo el entorno. La de contenedores en CrashLoopBackOff necesitó un middleware de tipo "unwind" para extraer los containerStatuses de cada Pod como nodos Container independientes, con la advertencia práctica de que ese bloque de middleware debe anidarse bajo la clave sources: del manifiesto y no bajo spec:, un error que falla de forma silenciosa y sin mensaje claro si se coloca mal. Esta regla sí se verificó con una violación real, quedando vacía en el clúster sano y disparándose en el instante en que se forzó el estado de fallo. La tercera, despliegues sin ReplicaSet asociado, sustituye la subconsulta EXISTS {} (no soportada) por un OPTIONAL MATCH combinado con count(), y se mantuvo vacía porque todos los despliegues del clúster de pruebas tenían su ReplicaSet correspondiente.
Una de las ideas más prácticas del artículo es la filosofía de un 'rulebook sano': en un clúster conforme, las reglas que funcionan deberían estar vacías la inmensa mayoría del tiempo, y solo mostrar una fila en el instante exacto en que una violación real cruza su umbral, desapareciendo de nuevo en cuanto el problema se resuelve, sin retraso de sondeo. Si una regla dispara constantemente contra un clúster sano, la recomendación es revisar primero la consulta —normalmente falta un coalesce o la ventana de debounce es demasiado corta para el comportamiento normal de un rollout—. Y si una regla nunca dispara pase lo que pase, la recomendación inversa es igual de importante: no asumir que el clúster está bien, sino comprobar primero si el tipo de recurso en cuestión está siquiera en la lista de observación de la fuente antes de confiar en un resultado vacío.
El autor resume además, a modo de referencia práctica no documentada oficialmente y deducida de los propios mensajes de error del motor de consultas, la superficie real del parser de Cypher soportado: operadores de comparación (=, <>, !=, <, <=, >, >=), IN, IS y aritmética básica sí funcionan, igual que left(), coalesce(), OPTIONAL MATCH con funciones de agregación y el middleware de unwind; en cambio, STARTS WITH, CONTAINS, ENDS WITH, las expresiones regulares con =~ y las subconsultas existenciales EXISTS { MATCH } no están soportadas. También menciona, como curiosidad útil para quien quiera reproducir las pruebas, que listar las imágenes reales de los pods del propio Drasi reveló los nombres internos de sus componentes (query-container-query-host, query-container-view-svc, query-container-publish-api, source-query-api, source-change-dispatcher, source-change-router), lo que explica por qué las sondas de registro con nombres más simples como query-host o view-svc siempre devuelven error 404.
En cuanto a notas operativas de un despliegue real, el artículo señala que el kubeconfig debe almacenarse como Secret de Kubernetes, evitando configuraciones basadas en exec en contenedores de runtime; recomienda vigilar eventos ResourceVersionTooOld en el namespace de la fuente, que indican una caché de watch desincronizada capaz de perder cambios sin generar ningún aviso evidente; advierte que modificar la fuente de Kubernetes obliga a borrarla y volver a crearla, junto con todas las consultas que dependen de ella, a diferencia de las fuentes de SQL Server que se pueden reaplicar limpiamente por nombre; y aclara que el registro del proveedor por defecto ya ocurre de forma automática tras ejecutar drasi init, por lo que las guías más antiguas que indicaban aplicar manualmente esos manifiestos están desactualizadas para la versión actual de la CLI.
El artículo cierra reforzando dónde no debe usarse esta técnica: la aplicación de políticas en el momento de la admisión sigue siendo responsabilidad de Gatekeeper o Kyverno —Drasi observa, no bloquea—, y la detección de drift entre git y el clúster sigue perteneciendo a Flux o Argo. También recomienda mantener una instancia de Drasi por clúster en lugar de apuntar una única instancia externa contra varios clústeres a la vez, una lección que el autor dice haber asimilado solo a medias por experiencia ajena. La conclusión general es modesta y coherente con el tono del texto: cinco reglas que funcionan de verdad contra un clúster real, con la sintaxis exacta que realmente se interpreta, valen más como punto de partida que seis reglas que solo parecían correctas sobre el papel.
🔗 Relacionadas en Zendoric
- Ingeniería de plataformas para la empresa agéntica: cómo gestionar aplicaciones, recursos y agentes de IA · 2026-07-24
- La memoria de los agentes de IA deja de ser una demo: ya es ingeniería de bases de datos con fallos reales · 2026-06-30
- MCP, la 'fontanería' que conecta la IA con tus apps, corrige un fallo de diseño para escalar a millones de usuarios · 2026-07-21


