Comentario

Un agente de depuración de Kubernetes: ¿plantillas o scripts?

Zinchenko entrega al LLM plantillas de MetricsQL; mi skill de VM alimenta al agente con un agregado terminado. Disecciono el eje flexibilidad frente a reproducibilidad y por qué read-only por lista negra es más débil.

Mantengo una configuración de Claude Code muy personalizada y, durante el último año, he construido sobre ella dos cosas que tienen relación directa con lo que Arseny Zinchenko describe en su artículo sobre un agente de depuración para Kubernetes: un skill para VictoriaMetrics montado sobre seis scripts de bash, que probé contra el clúster en vivo vm.ethinking.xyz, y un proyecto de rightsizing de EKS de varias semanas (fases A/B, caza de OOM, clases de límites y JAVA_OPTS). Por eso no leí su texto como una reseña tecnológica, sino como la descripción de una bifurcación en la que yo mismo estuve.

Zinchenko ensambla un agente read-only de depuración de pods, lo empaqueta en un plugin de Claude Code con su propio marketplace, conecta los skills oficiales de VictoriaMetrics para métricas, logs y alertas — y prescinde deliberadamente de MCP en favor de los Skills. Formula el objetivo con una franqueza desarmante: tener un agente al que un desarrollador pueda preguntar «why the hell did that Kubernetes Pod crash». Y admite de entrada que, en cuanto a las instrucciones, esto todavía es un PoC. Es el marco correcto, y no vengo a discutirlo — quiero diseccionar un eje de ingeniería que el artículo roza pero no lleva hasta el final.

Mi posición: ambos esquemas son correctos, pero en puntos distintos

La tesis es sencilla. Zinchenko entrega al LLM plantillas de MetricsQL y LogsQL y deja que el agente componga las consultas concretas sobre la marcha. En mi propio skill de VM hice exactamente lo contrario: cada llamada HTTP pasa por scripts de bash, y el agente solo ve salidas preagregadas y razona sobre ellas. Esto no es «mejor» frente a «peor» — son dos extremos de un mismo eje: flexibilidad para la investigación ad-hoc frente a reproducibilidad y auditabilidad.

Su enfoque gana donde la consulta es desconocida de antemano: te sientas a depurar «por qué se reinicia Grafana» (así, por cierto, encontró él la causa de sus propios reinicios), y necesitas la libertad de reformular MetricsQL cinco veces por minuto. Mi enfoque gana donde la ejecución debe ser repetible y explicable a posteriori: el mismo script produce el mismo agregado, el revisor ve exactamente lo que vio el agente, y una semana después puedes reproducir la cifra. Por eso precisamente mi esquema sobrevivió a las fases A/B de rightsizing — allí no puedes dejar que el agente invente una consulta nueva cada vez: necesitabas una metodología que diera números comparables a lo largo de decenas de servicios contra una línea base de OOM=0.

Dónde tiene razón — y no es algo menor

Comparto por entero el argumento central del artículo. Zinchenko da en el clavo: MCP no transporta el conocimiento que de verdad buscas. Su formulación acierta: «MCP only defines how to execute the query - not the query structure itself», y más adelante — «the agent/LLM still decides on the query and the filters». Un query(query: string) tipado valida que pasaste una cadena, pero la propia cadena de MetricsQL la sigue componiendo el modelo. Un skill, en cambio, puede transportar lo que MCP simplemente no puede: qué filtro de stream exacto aplica en nuestro VictoriaLogs, qué etiqueta obligatoria cluster aplica en nuestro VictoriaMetrics, qué correlaciones distinguen CrashLoopBackOff de OOMKilled. Eso es contexto del entorno, no una firma de función.

Sobre kubectl también tiene razón: envolverlo en MCP aporta poco, porque cualquier modelo se sabe la sintaxis de kubectl de memoria y las particularidades del clúster basta con ponerlas en el SKILL.md. Y traza la frontera del caso de uso con honestidad — «we're building a read-only agent that debugs, not deploys», por lo que un skill de despliegue ya hecho no le encaja. Su resumen de la elección — «Bash + curl + our own skill with our context + official VictoriaMetrics skills» — está plenamente justificado para la depuración exploratoria.

Dónde el esquema se queda corto: read-only por lista negra es más débil de lo que parece

Aquí empieza mi objeción principal, y es de orden técnico. La garantía de read-only de Zinchenko se apoya en deny-tools — una lista negra de globs: prohíbe kubectl delete, *curl* -X *, las tuberías hacia sh/bash, las redirecciones >/>>, rm/mv/cp. La lista parece sólida, pero una lista negra por patrones glob es un juego que el defensor siempre pierde. Una escritura a un fichero sin el carácter > se hace con tee. Un POST sin -X se hace con --data en una forma que el patrón no atrapó, o mediante un here-string y sustitución de comandos. xargs y env trasladan un comando prohibido dentro de un envoltorio que la lista negra nunca inspecciona. Cada vector pasado por alto es un agujero, y puedes seguir tapándolos uno a uno indefinidamente.

En mi propia configuración confío en la lógica inversa: una lista blanca (lo explícitamente permitido se autoriza, todo lo demás se deniega por defecto) más una compuerta a nivel de intención para las acciones de producción — cualquier kubectl exec/port-forward, SSH a prod o terraform apply exige una aprobación explícita y nominal en la sesión actual. Una lista blanca no sufre el problema de «olvidé prohibir otro vector más», porque por defecto todo está prohibido.

Pero declaro con franqueza un matiz que el artículo está en su derecho de no enfatizar: trabajamos bajo modelos de amenaza distintos. Su agente es depuración read-only en un clúster de dev, donde el coste de un error es bajo y la lista negra atrapa los resbalones gruesos del modelo. Mi compuerta está hecha para write/prod, donde el coste de un error es un incidente. Así que esto no es un demolición de su diseño — es señalar el techo: deny-tools es bueno como defensa en profundidad y malo como única garantía en cuanto sube el umbral de confianza hacia el entorno.

Qué hacer en la práctica

Reduciría la elección entre los dos esquemas a una sola pregunta: ¿la ejecución es de una sola vez o repetible? Para la depuración investigativa — «por qué se cayó este pod justo ahora» — toma la variante de Zinchenko: plantillas de consulta más la libertad del modelo para combinarlas es más rápido y no exige escribir un script para cada nuevo corte de datos. Para todo lo que necesites reproducir, revisar o defender ante un cliente — envuelve el HTTP en scripts y alimenta al agente con un agregado terminado; entonces la salida es determinista y la auditoría es trivial.

Las fases A/B me convencieron de esto en concreto: cuando el rightsizing se prolonga durante semanas, a lo largo de decenas de servicios, con clases de límites y ajuste de JAVA_OPTS hacia una línea base sin OOM, un agente que compone consultas se convierte en una fuente de números incomparables. El script es esa parte barata, aburrida y fiable sobre la que ya escribí en «The Harness Is the Cheap Part»: lo caro e interesante es el razonamiento del modelo, mientras que la reproducibilidad proviene exactamente de lo que fijaste en el armazón.

Y prescindir de MCP en favor de los Skills para kubectl es una decisión que firmo sin reservas. Donde el modelo ya conoce la sintaxis, un envoltorio tipado añade molestias de instalación y no añade conocimiento. Zinchenko eligió la herramienta correcta; yo solo dibujo la otra mitad del mapa — el punto en el que empiezas a cobrarle a la flexibilidad un impuesto de reproducibilidad.