Testing de Helm Charts en Producción: Capas, Herramientas y Pipeline CI Mínimo

Testing de Helm Charts en Producción: Capas, Herramientas y Pipeline CI Mínimo

Cuando un Helm chart falla en producción, el impacto es inmediato y visible. Una ServiceAccount mal configurada, una errata en una clave de ConfigMap, o un condicional sin probar en los templates pueden desencadenar incidentes que se propagan en cascada por todo el pipeline de despliegue. La paradoja es que la mayoría de los equipos invierten considerablemente en el testing del código de aplicación, mientras tratan los Helm charts como “simples ficheros de configuración”.

El testing de charts es fundamental para despliegues Helm de calidad en producción. Para una cobertura completa del testing junto con el resto de mejores prácticas Helm, consulta nuestra guía completa de Helm.

Los Helm charts son código de infraestructura. Definen cómo se ejecutan, escalan e integran tus aplicaciones dentro del clúster. Tratarlos con menos rigor que la lógica de aplicación es un riesgo que la mayoría de entornos productivos no pueden permitirse.


El Coste Real de los Charts sin Testear

A finales de 2024, una empresa SaaS de tamaño medio sufrió una interrupción de 4 horas porque una actualización de chart introdujo un cambio incompatible en los permisos RBAC. El chart se había probado localmente con helm install --dry-run, pero la validación de dry-run no interactúa con la capa RBAC del API server. El despliegue tuvo éxito a nivel sintáctico, pero falló operacionalmente.

El incidente reveló tres carencias en su flujo de trabajo:

  1. Sin validación de esquema contra la versión de Kubernetes objetivo
  2. Sin integration tests en un clúster real
  3. Sin aplicación de políticas para las líneas base de seguridad

Estas carencias son habituales. Según una encuesta de la CNCF sobre prácticas GitOps de 2024, menos del 40% de las organizaciones testean Helm charts de forma sistemática antes del despliegue en producción.

El problema no es la falta de herramientas; es comprender qué capa aborda cada herramienta.


Capas de Testing: Qué Valida Cada Nivel

El testing de Helm charts no es una operación única. Requiere validación en múltiples capas, cada una detectando distintas clases de errores.

Capa 1: Validación de Sintaxis y Estructura

Qué detecta: YAML malformado, estructura de chart inválida, campos obligatorios ausentes

Herramientas:

Ejemplo de fallo detectado:

# Una indentación incorrecta rompe el chart
resources:
  limits:
      cpu: "500m"
    memory: "512Mi"  # Indentación incorrecta

Limitación: No valida si los manifiestos renderizados son objetos Kubernetes válidos.


Capa 2: Validación de Esquema

Qué detecta: Manifiestos que serían rechazados por el API de Kubernetes

Herramienta principal: kubeconform

Kubeconform es el sucesor activamente mantenido del obsoleto kubeval. Valida contra los esquemas OpenAPI para versiones específicas de Kubernetes y admite CRDs personalizadas.

Perfil del proyecto:

  • Mantenimiento: Activo, impulsado por la comunidad
  • Puntos fuertes: Soporte de CRDs, validación multi-versión, ejecución rápida
  • Por qué importa: helm lint valida la estructura del chart, pero no si los manifiestos renderizados son conformes con los esquemas de Kubernetes

Ejemplo de fallo detectado:

apiVersion: apps/v1
kind: Deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: app
        image: nginx:latest
# Campo obligatorio ausente: spec.selector

Ejemplo de configuración:

helm template my-chart . | kubeconform \
  -kubernetes-version 1.30.0 \
  -schema-location default \
  -schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' \
  -summary

Integración en CI:

#!/bin/bash
set -e

KUBE_VERSION="1.30.0"

echo "Renderizando chart..."
helm template my-release ./charts/my-chart > manifests.yaml

echo "Validando contra Kubernetes $KUBE_VERSION..."
kubeconform \
  -kubernetes-version "$KUBE_VERSION" \
  -schema-location default \
  -summary \
  -output json \
  manifests.yaml | jq -e '.summary.invalid == 0'

Alternativa: kubectl --dry-run=server (requiere acceso al clúster, valida contra el API server real)


Capa 3: Unit Testing

Qué detecta: Errores lógicos en templates, condicionales incorrectos, interpolación errónea de valores

Los unit tests validan que, dada una serie de valores de entrada, el chart produce los manifiestos esperados. Es donde se verifica la lógica de los templates antes de llegar al clúster.

Herramienta principal: helm-unittest

helm-unittest es el framework de unit testing más ampliamente adoptado para Helm charts.

Perfil del proyecto:

  • GitHub: Más de 3.300 estrellas, ~100 contribuidores
  • Mantenimiento: Activo (releases cada 2-3 meses)
  • Maintainer principal: Quentin Machu (originalmente @QubitProducts, ahora independiente)
  • Respaldo comercial: Ninguno
  • Bus Factor: Medio-Alto (sin respaldo institucional, pero con comunidad activa)

Puntos fuertes:

  • Ejecución rápida (no requiere clúster)
  • Sintaxis de test familiar (similar a Jest/Mocha)
  • Soporte de snapshot testing
  • Buena documentación

Limitaciones:

  • No valida el comportamiento en tiempo de ejecución
  • No puede testear interacciones con admission controllers
  • Sin validación contra el API real de Kubernetes

Ejemplo de test:

# tests/deployment_test.yaml
suite: test deployment
templates:
  - deployment.yaml
tests:
  - it: should set resource limits when provided
    set:
      resources.limits.cpu: "1000m"
      resources.limits.memory: "1Gi"
    asserts:
      - equal:
          path: spec.template.spec.containers[0].resources.limits.cpu
          value: "1000m"
      - equal:
          path: spec.template.spec.containers[0].resources.limits.memory
          value: "1Gi"

  - it: should not create HPA when autoscaling disabled
    set:
      autoscaling.enabled: false
    template: hpa.yaml
    asserts:
      - hasDocuments:
          count: 0

Alternativa: Terratest (módulo Helm)

Terratest es un framework de testing en Go de Gruntwork con soporte de primera clase para Helm. A diferencia de helm-unittest, Terratest despliega charts en clústeres reales y permite aserciones programáticas en Go.

Ejemplo de test con Terratest:

func TestHelmChartDeployment(t *testing.T) {
    kubectlOptions := k8s.NewKubectlOptions("", "", "default")
    options := &helm.Options{
        KubectlOptions: kubectlOptions,
        SetValues: map[string]string{
            "replicaCount": "3",
        },
    }

    defer helm.Delete(t, options, "my-release", true)
    helm.Install(t, options, "../charts/my-chart", "my-release")

    k8s.WaitUntilNumPodsCreated(t, kubectlOptions, metav1.ListOptions{
        LabelSelector: "app=my-app",
    }, 3, 30, 10*time.Second)
}

Cuándo usar Terratest frente a helm-unittest:

  • Usa helm-unittest para validación rápida centrada en templates en CI
  • Usa Terratest cuando necesites integration testing completo con la flexibilidad de Go

Capa 4: Integration Testing

Qué detecta: Fallos en tiempo de ejecución, conflictos de recursos, comportamiento real de Kubernetes

Los integration tests despliegan el chart en un clúster real (o efímero) y verifican que funciona de extremo a extremo.

Herramienta principal: chart-testing (ct)

chart-testing es el proyecto oficial de Helm para testear charts en clústeres activos.

Perfil del proyecto:

  • Propietario: Proyecto oficial de Helm (CNCF)
  • Maintainers: Equipo de Helm (contribuidores de Microsoft, IBM, Google)
  • Gobernanza: Respaldado por la CNCF con roadmap público
  • LTS: Alineado con el ciclo de releases de Helm
  • Bus Factor: Bajo (el respaldo institucional de la CNCF ofrece garantías sólidas a largo plazo)

Puntos fuertes:

  • Estándar de facto para charts Helm públicos
  • Testing de upgrade integrado (valida las migraciones)
  • Detecta qué charts han cambiado en un PR (eficiente para monorepos)
  • Integración con GitHub Actions mediante acción oficial

Limitaciones:

  • Requiere un clúster Kubernetes activo
  • Configuración inicial más compleja que el unit testing
  • No incluye security scanning

Qué valida ct:

  • El chart se instala correctamente
  • Los upgrades funcionan sin romper el estado
  • El linting supera las comprobaciones
  • Se respetan las restricciones de versión

Ejemplo de configuración de ct:

# ct.yaml
target-branch: main
chart-dirs:
  - charts
chart-repos:
  - bitnami=https://charts.bitnami.com/bitnami
helm-extra-args: --timeout 600s
check-version-increment: true

Workflow típico en GitHub Actions:

name: Lint and Test Charts

on: pull_request

jobs:
  lint-test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Set up Helm
        uses: azure/setup-helm@v3

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Set up chart-testing
        uses: helm/chart-testing-action@v2

      - name: Run chart-testing (lint)
        run: ct lint --config ct.yaml

      - name: Create kind cluster
        uses: helm/kind-action@v1

      - name: Run chart-testing (install)
        run: ct install --config ct.yaml

Cuándo ct resulta imprescindible:

  • Repositorios de charts públicos (esperado por la comunidad)
  • Charts con rutas de upgrade complejas
  • Repositorios multi-chart con necesidades de optimización de CI

Capa 5: Validación de Seguridad y Políticas

Qué detecta: Configuraciones de seguridad incorrectas, violaciones de políticas, problemas de cumplimiento normativo

Esta capa impide desplegar charts que superan los tests funcionales pero violan las líneas base de seguridad organizativas o contienen vulnerabilidades.

Aplicación de Políticas: Conftest (Open Policy Agent)

Conftest es la interfaz CLI de Open Policy Agent para validación de políticas-como-código.

Perfil del proyecto:

  • Proyecto padre: Open Policy Agent (Proyecto Graduado de la CNCF)
  • Gobernanza: Sólido respaldo de la CNCF, soporte multi-vendor
  • Adopción en producción: Netflix, Pinterest, Goldman Sachs
  • Bus Factor: Bajo (proyecto CNCF graduado con respaldo multi-vendor)

Puntos fuertes:

  • Políticas escritas en Rego (reutilizables y componibles)
  • Funciona con cualquier entrada YAML/JSON (no específico de Helm)
  • Puede aplicar estándares organizativos de forma programática
  • Integración con admission controllers (Gatekeeper)

Ejemplo de política en Rego:

package main

import future.keywords.in

deny[msg] {
  input.kind == "Deployment"
  container := input.spec.template.spec.containers[_]
  not container.resources.limits.memory
  msg := sprintf("El contenedor '%s' debe definir límites de memoria", [container.name])
}

deny[msg] {
  input.kind == "Deployment"
  container := input.spec.template.spec.containers[_]
  not container.resources.limits.cpu
  msg := sprintf("El contenedor '%s' debe definir límites de CPU", [container.name])
}

Ejecutar la validación:

helm template my-chart . | conftest test -p policy/ -

Alternativa: Kyverno

Kyverno ofrece aplicación de políticas usando manifiestos nativos de Kubernetes en lugar de Rego. Las políticas se escriben en YAML y pueden validar, mutar o generar recursos.

Ejemplo de política Kyverno:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-resource-limits
spec:
  validationFailureAction: Enforce
  rules:
  - name: check-container-limits
    match:
      resources:
        kinds:
        - Pod
    validate:
      message: "Todos los contenedores deben tener límites de CPU y memoria"
      pattern:
        spec:
          containers:
          - resources:
              limits:
                memory: "?*"
                cpu: "?*"

Conftest vs Kyverno:

  • Conftest: Políticas ejecutadas en CI, flexible para cualquier YAML
  • Kyverno: Aplicación en tiempo de ejecución dentro del clúster, nativo de Kubernetes

Ambos pueden coexistir: Conftest en CI para retroalimentación temprana, Kyverno en el clúster para aplicación en tiempo de ejecución.

Análisis de Vulnerabilidades: Trivy

Trivy de Aqua Security ofrece análisis de seguridad integral para Helm charts.

Perfil del proyecto:

  • Maintainer: Aqua Security (respaldo comercial con núcleo open source)
  • Alcance: Análisis de vulnerabilidades + detección de configuraciones incorrectas
  • Integración Helm: Comando oficial trivy helm
  • Bus Factor: Bajo (respaldo comercial + fuerte adopción open source)

Qué analiza Trivy en Helm charts:

  • Vulnerabilidades en las imágenes de contenedores referenciadas
  • Configuraciones incorrectas (similar a Conftest pero con reglas predefinidas)
  • Secretos comprometidos accidentalmente en los templates

Ejemplo de análisis:

trivy helm ./charts/my-chart --severity HIGH,CRITICAL --exit-code 1

Ejemplo de salida:

myapp/templates/deployment.yaml (helm)
====================================

Tests: 12 (SUCCESSES: 10, FAILURES: 2)
Failures: 2 (HIGH: 1, CRITICAL: 1)

HIGH: Container 'app' of Deployment 'myapp' should set 'securityContext.runAsNonRoot' to true
════════════════════════════════════════════════════════════════════════════════
Ensure containers run as non-root users

See https://kubernetes.io/docs/concepts/security/pod-security-standards/
────────────────────────────────────────────────────────────────────────────────
 myapp/templates/deployment.yaml:42

Otras Herramientas de Seguridad

Polaris (Fairwinds)

Polaris puntúa los charts en función de las mejores prácticas de seguridad y fiabilidad. A diferencia de las herramientas de aplicación, proporciona una puntuación de salud y recomendaciones accionables.

Caso de uso: Dashboard para la calidad de charts en toda una plataforma.

Checkov (Bridgecrew/Palo Alto)

Similar a Trivy pero con un enfoque IaC más amplio (Terraform, CloudFormation, Kubernetes, Helm). Políticas predefinidas para marcos de cumplimiento normativo (CIS, PCI-DSS).

Cuándo usar Checkov:
– Entorno multi-IaC (no solo Helm)
– Requisitos de validación orientados al cumplimiento normativo


Criterios de Selección para Entornos Enterprise

Bus Factor y Viabilidad a Largo Plazo

Para infraestructura productiva, la sostenibilidad de las herramientas importa tanto como las funcionalidades. Los canales de soporte comunitario como el Slack de Helm en la CNCF (#helm-users, #helm-dev) y el CNCF TAG Security ofrecen información valiosa sobre qué proyectos tienen comunidades de maintainers activas.

Preguntas que debes hacerte:

  • ¿Está el proyecto respaldado por una fundación (CNCF, Linux Foundation)?
  • ¿Contribuyen varias empresas?
  • ¿Lo usan en producción organizaciones reconocibles?
  • ¿Hay un roadmap público?

Clasificación de riesgos:

HerramientaGobernanzaBus FactorNotas
chart-testingCNCFBajoProyecto oficial de Helm
Conftest/OPACNCF GraduadoBajoRespaldo multi-vendor
TrivyAqua SecurityBajoRespaldo comercial + OSS
kubeconformComunidadMedioActivo, pero un solo maintainer
helm-unittestComunidadMedio-AltoSin respaldo institucional
PolarisFairwindsMedioOSS patrocinado por empresa

Compatibilidad con Versiones de Kubernetes

Las herramientas deben soportar explícitamente las versiones de Kubernetes que ejecutas en producción.

Señales de alerta:

  • Sin matriz de compatibilidad documentada
  • Dependencias codificadas en versiones antiguas de K8s
  • Sin testing contra múltiples versiones de K8s en su propio CI

Ejemplo de comprobación de compatibilidad:

# ¿Soporta la herramienta tu versión de K8s?
kubeconform --help | grep -A5 "kubernetes-version"

Para herramientas como ct, verifica siempre que testean contra una matriz de versiones de Kubernetes en su propio CI.

Opciones de Soporte Comercial

Cuándo importa el soporte comercial:

  • Requisitos de cumplimiento normativo (SOC2, HIPAA, ENS, etc.)
  • Experiencia interna limitada
  • Operaciones con SLA

Opciones disponibles:

  • Trivy: Aqua Security ofrece Trivy Enterprise
  • OPA/Conftest: Styra ofrece OPA Enterprise
  • Terratest: Gruntwork ofrece consultoría y módulos premium

La mayoría de equipos no necesitan soporte comercial específicamente para el testing de charts, pero resulta valioso en industrias reguladas donde las auditorías exigen SLAs de proveedor.

Integración de Escáneres de Seguridad

Para pipelines enterprise, las herramientas de testing de charts deben integrarse limpiamente con:

  • Plataformas SIEM/SOAR
  • Sistemas de notificación de CI/CD
  • Dashboards de seguridad (p.ej., Grafana, Datadog)

Funcionalidades necesarias:

  • Formatos de salida estructurados (JSON, SARIF)
  • Códigos de salida para fallo en CI
  • Soporte de políticas personalizadas
  • Webhook o API para streaming de eventos

Ejemplo: Integrar Trivy con un SIEM

# .github/workflows/security.yaml
- name: Run Trivy scan
  run: trivy helm ./charts --format json --output trivy-results.json

- name: Send to SIEM
  run: |
    curl -X POST https://siem.empresa.com/api/events \
      -H "Content-Type: application/json" \
      -d @trivy-results.json

Arquitectura del Pipeline CI/CD de Testing

Un pipeline de Helm chart de calidad para producción combina múltiples capas:

┌─────────────────────────────────────────────────────────┐
│  PRE-COMMIT (Developer)                                  │
│  helm lint + yamllint                                    │
└──────────────────────┬──────────────────────────────────┘
                       │
┌──────────────────────▼──────────────────────────────────┐
│  CI PIPELINE (PR)                                        │
│  ┌─────────────────┐   ┌─────────────────────────────┐  │
│  │  kubeconform    │   │  helm-unittest               │  │
│  │  (schema)       │   │  (unit tests)                │  │
│  └────────┬────────┘   └──────────────┬──────────────┘  │
│           └──────────────┬────────────┘                  │
│                          │                               │
│              ┌───────────▼──────────┐                   │
│              │  trivy helm          │                   │
│              │  (security scan)     │                   │
│              └───────────┬──────────┘                   │
└──────────────────────────┼──────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────┐
│  PRE-PRODUCTION (Staging)                                │
│  chart-testing (ct) - clúster efímero                   │
└─────────────────────────────────────────────────────────┘

Principios de eficiencia del pipeline CI Helm:

  • Fail fast: Los errores de sintaxis y esquema nunca deben llegar a los integration tests
  • Ejecución paralela donde sea posible (unit tests + security scans)
  • Cachear imágenes de clústeres efímeros para reducir el tiempo de configuración
  • Omitir charts sin cambios (detección de cambios integrada en ct)

Matriz de Decisión: Cuándo Usar Cada Herramienta

Escenario 1: Equipo Pequeño / Startup en Fase Inicial

Requisitos: Mínima sobrecarga, iteración rápida, seguridad razonable

Stack recomendado:

Linting:      helm lint + yamllint
Validación:   kubeconform
Seguridad:    trivy helm

Opcional: helm-unittest (si la lógica de templates se vuelve compleja)

Razonamiento: Línea base sin dependencias externas que detecta el 80% de los problemas sin complejidad operativa.


Escenario 2: Enterprise con Requisitos de Cumplimiento Normativo

Requisitos: Auditable, validación completa, soporte comercial disponible

Stack recomendado:

Linting:       helm lint + yamllint
Validación:    kubeconform
Unit Tests:    helm-unittest
Seguridad:     Trivy Enterprise + Conftest (políticas personalizadas)
Integration:   chart-testing (ct)
Runtime:       Kyverno (admission control)

Opcional: Terratest para escenarios de upgrade complejos

Razonamiento: Defensa en múltiples capas con aplicación tanto en pre-despliegue como en tiempo de ejecución. Soporte comercial disponible para componentes de seguridad.


Escenario 3: Plataforma Interna Multi-Tenant

Requisitos: Evitar que charts defectuosos afecten a otros tenants, aplicar estándares a escala

Stack recomendado:

Pipeline CI:
  • helm lint → kubeconform → helm-unittest → ct
  • Conftest (aplicar resource quotas, namespaces, network policies)
  • Trivy (bloquear vulnerabilidades críticas)

Runtime:
  • Kyverno o Gatekeeper (aplicar políticas en admission)
  • ResourceQuotas por namespace
  • NetworkPolicies por defecto

Herramientas adicionales: Dashboard Polaris para puntuación de calidad de charts, admission webhooks personalizados para reglas específicas de plataforma.

Razonamiento: Los entornos multi-tenant no pueden tolerar validación “blanda”. La aplicación en tiempo de ejecución es obligatoria.


Escenario 4: Charts Open Source Públicos

Requisitos: Confianza de la comunidad, testing transparente, amplia compatibilidad

Stack recomendado:

Imprescindible:
  • chart-testing (estándar esperado)
  • CI público (GitHub Actions con logs completos)
  • Test contra múltiples versiones de K8s

Recomendable:
  • helm-unittest con alta cobertura
  • Generación automatizada de changelog
  • Values de ejemplo para escenarios comunes

Razonamiento: Los charts públicos se juzgan por la transparencia del testing. La ausencia de ct es una señal de alarma para los usuarios potenciales.


El Stack de Testing Mínimo Viable

Para cualquier entorno que despliegue Helm charts en producción, esta es la línea base del pipeline CI Helm:

Capa 1: Pre-Commit (Máquina del Desarrollador)

helm lint charts/my-chart
yamllint charts/my-chart

Capa 2: Pipeline CI (Automático en PR)

# Validación rápida
helm template my-chart ./charts/my-chart | kubeconform \
  -kubernetes-version 1.30.0 \
  -summary

# Línea base de seguridad
trivy helm ./charts/my-chart --exit-code 1 --severity CRITICAL,HIGH

Capa 3: Pre-Producción (Entorno de Staging)

# Integration test con clúster real
ct install --config ct.yaml --charts charts/my-chart

Inversión de tiempo:

  • Configuración inicial: 4-8 horas
  • Sobrecarga por PR: 3-5 minutos
  • Mantenimiento: ~1 hora/mes

Cálculo de ROI:

Coste medio de un incidente de producción causado por un chart sin testear:
– Detección: 15 minutos
– Triaje: 30 minutos
– Rollback: 20 minutos
– Post-mortem: 1 hora
– Total: ~2,5 horas de tiempo de ingeniería

Si el testing de charts evita un solo incidente por trimestre, se amortiza en el primer mes.


Anti-Patrones Habituales que Debes Evitar

Anti-Patrón 1: Usar únicamente --dry-run

helm install --dry-run valida la sintaxis, pero omite:

  • Lógica de admission controllers
  • Validación RBAC
  • Creación real de recursos

Mejor enfoque: Combinar dry-run con kubeconform y al menos un integration test.


Anti-Patrón 2: Testear solo en clústeres similares a producción

“Testeamos en staging, que es idéntico a producción.”

El problema: Los clústeres de staging raramente coinciden exactamente con producción (número de nodos, storage classes, network policies). Los integration tests deben ejecutarse en entornos aislados y efímeros.


Anti-Patrón 3: Security scanning sin aplicación

Ejecutar trivy helm sin que los hallazgos críticos provoquen el fallo del build es teatro de seguridad.

Mejor enfoque: Establecer --exit-code 1 y aplicarlo en CI.


Anti-Patrón 4: Ignorar las rutas de upgrade

La mayoría de los fallos de charts ocurren durante los upgrades, no en las instalaciones iniciales. Chart-testing aborda esto con ct install --upgrade.


Preguntas Frecuentes sobre Testing de Helm Charts

¿Qué es el testing de Helm charts y por qué es importante en producción?

El testing de Helm charts garantiza que los manifiestos de Kubernetes generados a partir de los templates Helm sean sintácticamente correctos, conformes con el esquema, seguros y funcionen correctamente al desplegarse. En producción, los charts sin testear pueden provocar interrupciones del servicio, incidentes de seguridad o upgrades fallidos, aunque el propio código de aplicación sea estable.

¿Es helm lint suficiente para validar un Helm chart?

No. helm lint solo valida la estructura del chart y las mejores prácticas básicas. No valida los manifiestos renderizados contra los esquemas de la API de Kubernetes, no prueba la lógica de los templates ni verifica el comportamiento en tiempo de ejecución. El testing orientado a producción requiere capas adicionales: validación de esquema, unit tests e integration tests.

¿Cuál es la diferencia entre los unit tests y los integration tests de Helm?

Los unit tests (p.ej., con helm-unittest) validan la lógica de los templates afirmando la salida esperada para unos valores de entrada dados, sin desplegar nada. Los integration tests (p.ej., con chart-testing o Terratest) despliegan los charts en un clúster Kubernetes real y validan el comportamiento en tiempo de ejecución, los upgrades y las interacciones con el API server.

¿Qué herramientas se recomiendan para validar Helm charts contra los esquemas de Kubernetes?

La herramienta más recomendada es kubeconform, que valida los manifiestos renderizados contra los esquemas OpenAPI de Kubernetes para versiones específicas y admite CRDs. Una alternativa es kubectl --dry-run=server, que valida contra un API server activo.

¿Cómo puede el testing de Helm charts evitar interrupciones en producción?

El testing detecta los modos de fallo más habituales antes del despliegue: selectores ausentes en Deployments, permisos RBAC inválidos, condicionales incorrectos o versiones de API incompatibles. Muchas interrupciones de producción tienen su origen en errores de configuración y lógica de charts, no en bugs de la aplicación.

¿Cuál es el papel del security scanning en el testing de Helm charts?

El security scanning detecta configuraciones incorrectas, violaciones de políticas y vulnerabilidades que los tests funcionales pueden pasar por alto. Herramientas como Trivy y Conftest (OPA) ayudan a aplicar líneas base de seguridad, prevenir valores por defecto inseguros y bloquear despliegues que vulneren los requisitos organizativos o normativos.

¿Es chart-testing (ct) necesario para charts Helm privados?

Aunque no es estrictamente obligatorio, chart-testing es muy recomendable para cualquier chart desplegado en producción. Se considera el estándar de facto para los integration tests, especialmente para charts con upgrades, múltiples dependencias o entornos de clúster compartidos.

¿Cuál es el pipeline CI Helm mínimo viable?

Como mínimo, un pipeline listo para producción debe incluir:
– helm lint para validación estructural
– kubeconform para validación de esquema
– trivy helm para security scanning

Los integration tests se pueden añadir a medida que los charts ganen en complejidad o criticidad.


Conclusión: El Testing es Madurez de Infraestructura

La brecha entre los equipos que testean Helm charts y los que no lo hacen no se debe a la disponibilidad de herramientas, sino a tratar el código de infraestructura con la misma disciplina que el código de aplicación.

El coste del testing se mide en minutos por PR. El coste de no testear se mide en horas de incidentes de producción, confianza erosionada en la automatización y equipos que vuelven a los despliegues manuales porque “Helm es demasiado arriesgado”.

El stack de testing que elijas importa menos que el hecho de tener uno. Empieza con el stack mínimo viable (lint + schema + seguridad), ejecútalo de forma consistente y amplíalo a medida que tus charts se vuelvan más complejos.

Al implementar un pipeline CI de testing de Helm charts estructurado, detectas el 95% de los problemas antes de que lleguen a producción. El 5% restante son casos límite que requieren observabilidad en producción, no más capas de testing.

El testing de Helm charts no consiste en alcanzar la perfección: consiste en eliminar los fallos prevenibles que socavan la confianza en tu pipeline de despliegue.

FreeLens vs OpenLens vs Lens (2026): ¿Cuál es el mejor IDE para Kubernetes?

FreeLens vs OpenLens vs Lens (2026): ¿Cuál es el mejor IDE para Kubernetes?

Si llevas un tiempo gestionando clústeres Kubernetes, probablemente ya sabes que las decisiones de herramientas no se quedan como “solo herramientas” durante mucho tiempo.

Lectura relacionada: Clientes de escritorio para Kubernetes en 2026, probados en un clúster real.

Lo que empieza como una comodidad para el equipo de desarrollo puede convertirse rápidamente en:

  • una conversación sobre licencias con el departamento legal,
  • un problema de adquisición,
  • o un estándar de plataforma con el que te quedas atascado durante años.

El ecosistema de IDEs para Kubernetes es un ejemplo de manual de este fenómeno.

Muchos equipos adoptaron Lens porque realmente mejoraba las operaciones del día a día. Luego cambió la licencia. Luego aparecieron restricciones. Luego empezaron los forks.

Hoy, la pregunta real no es “¿Cuál tiene mejor interfaz?”, sino:

  • ¿Cuál se mantiene activamente?
  • ¿Cuál es seguro de usar en una empresa?
  • ¿Por qué existe un fork de un fork?
  • ¿Siguen siendo técnicamente compatibles?
  • ¿Cuánto cuesta realmente cambiar?

Vamos a responder todo esto desde una perspectiva de ingeniería de plataforma y operaciones en producción.


¿Qué es FreeLens?

FreeLens es un IDE de escritorio para Kubernetes, gratuito y de código abierto bajo licencia MIT. Es un fork comunitario de OpenLens —que a su vez era el núcleo abierto de Lens Desktop— creado en 2024 para mantener viva una versión realmente libre de la herramienta después de que Mirantis restringiera los binarios de Lens y el repositorio de OpenLens quedara sin mantenimiento.

Te da exactamente lo que hizo popular a Lens: lista de clústeres, vista en vivo de tus workloads, logs, terminal integrada y edición de recursos, todo desde una aplicación de escritorio que lee tu kubeconfig existente.

La versión actual es FreeLens v1.10.3 (julio de 2026), el proyecto ronda las 5.300 estrellas en GitHub y publica instaladores para macOS, Windows y Linux en amd64 y arm64. A diferencia de Lens Desktop, no hay cuenta, ni registro, ni licencia comercial que negociar: lo instalas y funciona.

Cómo instalar FreeLens

FreeLens está empaquetado para los gestores habituales, así que en la mayoría de casos es un único comando:

# macOS (Homebrew)
brew install --cask freelens

# Windows (WinGet)
winget install Freelensapp.Freelens

# Linux (Snap)
snap install freelens --classic

# Linux (Flatpak)
flatpak install flathub app.freelens.Freelens

En Debian y Ubuntu puedes añadir el repositorio APT del proyecto y recibir las actualizaciones en tu ciclo normal de apt upgrade:

curl -L https://raw.githubusercontent.com/freelensapp/freelens/refs/heads/main/freelens/build/apt/freelens.asc \
  | sudo tee /etc/apt/keyrings/freelens.asc
curl -L https://raw.githubusercontent.com/freelensapp/freelens/refs/heads/main/freelens/build/apt/freelens.sources \
  | sudo tee /etc/apt/sources.list.d/freelens.sources
sudo apt update
sudo apt install freelens

Si prefieres no usar gestor de paquetes, la página de releases de GitHub tiene instaladores directos para cada plataforma: .dmg y .pkg para macOS, .exe, .msi y un .exe portable para Windows, y .AppImage, .deb y .rpm para Linux. Todos vienen con checksums SHA256 y SBOM, un detalle que tu equipo de seguridad agradecerá más de lo que esperas.

Una vez instalado, FreeLens detecta ~/.kube/config automáticamente. Si gestionas varios clústeres, todo lo que ya haces con contextos sigue funcionando: no hay un paso aparte de registro de clúster.

Extensiones de FreeLens

Las extensiones son la razón principal por la que mucha gente se quedó en la familia Lens en lugar de pasarse a un dashboard web, y es el área donde FreeLens más ha avanzado. El proyecto mantiene su propio ecosistema bajo la organización freelensapp en GitHub, y buena parte de las extensiones escritas para OpenLens ya están portadas. Las que merece la pena conocer:

  • freelens-fluxcd-extension — con diferencia la más adoptada (~123 estrellas). Muestra los recursos de FluxCD (Kustomizations, HelmReleases, sources) como objetos de primer nivel en la interfaz. Si haces GitOps con Flux, instala esta la primera.
  • freelens-ai-extension — consulta asistida por IA del clúster desde el propio IDE.
  • freelens-gateway-api-extension — vistas para objetos de Gateway API (Gateways, HTTPRoutes), que la interfaz base sigue tratando como CRDs genéricos.
  • freelens-karpenter-extension — inspeccionar NodePools y nodos aprovisionados por Karpenter sin bajar a la CLI. Encaja con Cluster Autoscaler vs Karpenter.
  • freelens-kamaji-extension y freelens-sveltos-extension — para control planes alojados (Kamaji) y distribución de add-ons multi-clúster (Sveltos).
  • freelens-example-extension — el andamiaje del que partir si quieres escribir la tuya. La API de extensiones se hereda de Lens, así que el código antiguo de OpenLens suele portarse con cambios menores.

Un aviso: freelens-node-pod-menu está deprecada — sus acciones de menú contextual para nodos y pods se integraron en la aplicación base, así que no la necesitas en versiones actuales. Si sigues un tutorial antiguo que te dice que la instales, sáltate ese paso.

¿Qué es OpenLens?

OpenLens era el núcleo de código abierto de Lens Desktop: el repositorio con el código real de la aplicación, sin los extras propietarios que Mirantis añadía a los binarios distribuidos. Durante un par de años fue la respuesta estándar a «cómo uso Lens sin aceptar los términos comerciales»: compilabas OpenLens tú mismo, o usabas alguna de las builds comunitarias, y tenías la experiencia Lens sin el requisito de cuenta.

Esa etapa se acabó. OpenLens ya no se mantiene y las builds comunitarias dejaron de seguir el upstream. Si lo usas hoy, estás ejecutando Electron y dependencias sin parchear, que es la parte que convierte una preferencia de herramienta en una conversación de seguridad. El camino natural de migración es FreeLens: es la continuación directa del mismo código, así que tu kubeconfig, tus contextos y la mayoría de tus extensiones se conservan. El detalle de qué se rompe y qué no está en la sección de migración más abajo.

La historia de los forks: cómo hemos llegado hasta aquí

Entender el árbol genealógico importa, porque explica por qué FreeLens existe en absoluto.

Lens: el producto original

Lens comenzó como un IDE Kubernetes open-core con una comunidad muy activa. Con el tiempo, evolucionó hacia un producto comercial con:

  • licencia propietaria,
  • funcionalidades de pago para empresas,
  • y restricciones en el uso gratuito en entornos corporativos.

Este cambio era perfectamente legítimo desde el punto de vista de negocio, pero rompió el contrato implícito que muchos equipos asumían cuando lo estandarizaron como herramienta interna.

OpenLens: el primer fork

OpenLens nació para preservar:

  • licencias open-source,
  • uso comercial sin restricciones,
  • compatibilidad con las extensiones de Lens.

Durante un tiempo, OpenLens fue la alternativa evidente para los equipos que querían mantenerse en código abierto sin sacrificar funcionalidad.

FreeLens: el fork del fork

FreeLens apareció más adelante, y aquí es donde mucha gente frunce el ceño.

¿Por qué hacer un fork de OpenLens?

Porque el desarrollo de OpenLens empezó a ralentizarse:

  • la cadencia de releases se volvió irregular,
  • los cambios del upstream de Kubernetes llegaban tarde,
  • la gobernanza y la continuidad a largo plazo quedaban en el aire.

FreeLens existe porque algunos contribuidores no estaban dispuestos a apostar sus operaciones de producción diarias en un proyecto con un momentum incierto.

No era ideología. Era gestión del riesgo operacional.


¿Siguen mantenidos los tres proyectos?

Respuesta corta: sí, pero no igual.

Lens

  • Desarrollo activo
  • Respaldado por un proveedor comercial (Mirantis)
  • Adopción rápida de nuevas funcionalidades de Kubernetes

Contrapartidas:

  • Restricciones de licencia
  • Funcionalidades de pago
  • Requiere revisión legal en la mayoría de las empresas

OpenLens

  • Aún mantenido
  • Base de contribuidores más pequeña
  • Velocidad de releases más lenta

Funciona, pero ya no se siente como un estándar seguro a largo plazo para los equipos de plataforma.

FreeLens

  • Mantenimiento activo
  • Enfoque explícito en apertura a largo plazo
  • Prioriza compatibilidad y estabilidad con la API de Kubernetes

Ahora mismo, FreeLens muestra el equilibrio más sano entre mantenimiento e independencia.


Compatibilidad técnica: ¿puedes cambiar sin dolor?

Aquí viene la buena noticia: sí, en su mayor parte.

Acceso al clúster y configuración

Los tres herramientas:

  • usan ficheros kubeconfig estándar,
  • soportan múltiples contextos y clústeres,
  • trabajan con RBAC, CRDs y namespaces de la misma forma.

No se requiere ningún cambio en el lado del clúster.

Extensiones y plugins

  • La mayoría de extensiones de Lens funcionan en OpenLens.
  • La mayoría de extensiones de OpenLens funcionan en FreeLens.
  • Las extensiones propietarias exclusivas de Lens son la principal excepción.

En el uso real:

  • Aproximadamente el 90 % de los flujos de trabajo habituales son idénticos
  • Las diferencias aparecen solo en casos límite o en funcionalidades de pago

Diferencias de UX

Hay algunas diferencias de interfaz:

  • branding,
  • estructura de menús,
  • bloqueo de funcionalidades por plan en Lens.

Nada que requiera reentrenamiento del equipo ni actualización de documentación.


Consideraciones legales y de licencia (aquí suele romperse todo)

Este es, con frecuencia, el factor decisivo en entornos empresariales.

Lens

  • Requiere comprobaciones de cumplimiento de licencia
  • El uso gratuito puede violar las políticas internas de muchas empresas
  • Se requieren planes de pago para una adopción más amplia

Si operas en un entorno regulado o sujeto a auditorías, esto solo ya puede ser un bloqueante definitivo.

OpenLens

  • Licencia open-source
  • Generalmente seguro para uso corporativo
  • Ligera incertidumbre por la reducción de actividad del proyecto

FreeLens

  • Explícitamente open-source
  • Sin restricciones de uso
  • Intención clara de mantenerse gratuito para uso comercial

Si Legal pregunta: “¿Podemos estandarizar esto en toda la empresa?”
FreeLens es la respuesta más fácil de defender.


Comparativa rápida: FreeLens vs OpenLens vs Lens

CriterioFreeLensOpenLensLens
LicenciaOpen-sourceOpen-sourcePropietaria / comercial
Uso corporativoSin restriccionesSin restriccionesRequiere revisión legal
Mantenimiento activoSí — v1.10.3 (jul 2026)No — abandonado, sin parches de seguridadSí (Mirantis)
Velocidad de releasesRegularLentaAlta
Compatibilidad kubeconfigTotalTotalTotal
Soporte de extensiones~90 % compatibilidad~90 % compatibilidadExtenso (algunas de pago)
Coste de cambioMuy bajoMuy bajoBajo (desde open-source)
Soporte empresarialNoNoSí (de pago)
Riesgo a largo plazoBajoMedioMedio-alto (licencia)

¿Cuál deberías usar en una empresa?

Una recomendación pragmática:

Usa Lens si:

  • quieres soporte respaldado por un proveedor,
  • estás dispuesto a pagar,
  • ya has estandarizado las herramientas de Mirantis.

Usa OpenLens si:

  • ya lo estás usando,
  • cubre tus necesidades actuales,
  • aceptas un ritmo de actualizaciones más lento.

Usa FreeLens si:

  • quieres cero riesgo de licencia,
  • quieres un estándar open-source,
  • te importa el mantenimiento a largo plazo,
  • necesitas algo que puedas estandarizar con seguridad.

Para la mayoría de los equipos de plataforma y DevOps, FreeLens es actualmente la opción de menor riesgo.


Coste del cambio: ¿cuánto cuesta realmente migrar?

Sorprendentemente poco.

Una migración típica implica:

  • instalar el nuevo binario,
  • reutilizar los kubeconfigs existentes,
  • reinstalar extensiones si es necesario.

Lo que no necesitas hacer:

  • cambios en el clúster,
  • modificaciones en CI/CD,
  • refactorización de plataforma.

Tiempo de inactividad: ninguno
Rollback: trivial

Este es uno de esos casos poco frecuentes en los que cambiar pronto es barato. Si estás usando Lens con restricciones de licencia, o OpenLens y te preocupa su ritmo de mantenimiento, no hay motivo para esperar.


¿Es un “fork de un fork” una señal de alerta?

Normalmente, sí.

En este caso, no.

FreeLens existe porque:

  • el mantenimiento importaba más que la marca,
  • la apertura importaba más que la monetización,
  • la predictibilidad importaba más que las promesas del roadmap.

Irónicamente, esto está muy alineado con cómo evolucionó el propio Kubernetes.

Los proyectos open-source saludables hacen fork cuando la gobernanza falla o cuando los objetivos del proyecto divergen del uso real. Eso es exactamente lo que ocurrió aquí. No es un drama de GitHub — es el proceso funcionando tal como está diseñado.


FreeLens en la práctica: lo que encontrarás al usarlo

Si nunca has instalado FreeLens, aquí va un resumen de lo que puedes esperar.

Instalación: disponible como binario para macOS, Linux y Windows. Sin dependencias raras ni configuración compleja.

Primera ejecución: detecta automáticamente tus contextos de ~/.kube/config. Si tienes múltiples clústeres, aparecen todos. No necesitas reconfigurar nada.

Interfaz: muy similar a OpenLens y Lens. La curva de aprendizaje para alguien que venga de cualquiera de los otros dos es prácticamente nula.

Extensiones: la mayoría de extensiones que instalabas en OpenLens funcionan sin cambios. Las extensiones propietarias de pago de Lens son la única excepción relevante.

Actualizaciones: FreeLens publica releases de forma regular y sigue el upstream de Kubernetes con más agilidad que OpenLens en su etapa actual.

Para los equipos de plataforma que ya tienen flujos de trabajo establecidos, la transición es casi transparente.


Herramientas de gestión Kubernetes en 2026: el contexto más amplio

Vale la pena mencionar que estas tres herramientas no son las únicas opciones para la gestión de Kubernetes. Existen alternativas como k9s (CLI), Headlamp (web-based, open-source) o Rancher Desktop, cada una con su propio enfoque.

¿Por qué sigue siendo relevante la comparativa FreeLens vs OpenLens vs Lens?

Porque estas tres herramientas comparten el mismo árbol de código, la misma base de usuarios y el mismo modelo de uso. Para los equipos que ya están en este ecosistema, la migración entre ellas es real y barata. Para los que vienen de fuera, la discusión es diferente.

Si estás construyendo tu stack de gestión Kubernetes desde cero en 2026, FreeLens es un punto de partida sólido. Si ya usas Lens o OpenLens, la pregunta es cuándo moverse, no si moverse.


Preguntas frecuentes

¿Qué es FreeLens?

FreeLens es un IDE de escritorio para Kubernetes, gratuito y de código abierto bajo licencia MIT. Es un fork comunitario de OpenLens, que a su vez era el núcleo abierto de Lens Desktop. Ofrece lista de clústeres, vista en vivo de workloads, logs, terminal y edición de recursos leyendo tu kubeconfig, sin requerir cuenta ni licencia comercial. La versión actual es la v1.10.3 (julio de 2026).

¿Cómo se instala FreeLens?

Con una sola orden en la mayoría de sistemas: brew install --cask freelens en macOS, winget install Freelensapp.Freelens en Windows, o snap install freelens --classic en Linux. También hay Flatpak, repositorio APT para Debian y Ubuntu, e instaladores directos (.dmg, .pkg, .exe, .msi, .AppImage, .deb, .rpm) en la página de releases de GitHub, para amd64 y arm64.

¿Sigue estando mantenido OpenLens?

No. OpenLens ya no se mantiene y las builds comunitarias dejaron de seguir el upstream, así que ejecutarlo hoy significa usar Electron y dependencias sin parchear. La migración natural es FreeLens, que continúa el mismo código bajo licencia MIT: se conservan el kubeconfig, los contextos y la mayoría de las extensiones.

¿Es FreeLens gratis para uso comercial?

Sí. FreeLens se distribuye bajo licencia MIT, que permite el uso comercial sin restricciones y sin necesidad de cuenta ni suscripción. Es la diferencia práctica frente a Lens Desktop, cuyo uso en empresa requiere licencia comercial y revisión legal.

¿Qué extensiones tiene FreeLens?

El proyecto mantiene su propio ecosistema en la organización freelensapp de GitHub, y muchas extensiones de OpenLens ya están portadas. Las más útiles son la de FluxCD (la más adoptada), Gateway API, Karpenter, Kamaji, Sveltos y una extensión de IA. Ojo: freelens-node-pod-menu está deprecada porque sus funciones se integraron en la aplicación base.

Veredicto: respuesta clara, aburrida y segura para producción

Si separamos el ruido de GitHub del análisis:

  • Lens optimiza para ingresos y funcionalidades empresariales.
  • OpenLens preservó la apertura, pero ha perdido momentum.
  • FreeLens optimiza para sostenibilidad y libertad.

Desde la perspectiva de la ingeniería de plataforma:

“FreeLens es el IDE Kubernetes por defecto más seguro hoy en día para la mayoría de organizaciones.”

Bajo coste de cambio, alta compatibilidad, sin sorpresas legales.

Y en entornos de producción, lo aburrido y predecible casi siempre gana.


Publicado originalmente en inglés el 16 de enero de 2026. Adaptación al español de Alexandre Vazquez.

Alternativas al Kubernetes Dashboard en 2026: Headlamp, Portainer, Rancher y más

Alternativas al Kubernetes Dashboard en 2026: Headlamp, Portainer, Rancher y más

Alternativas al Kubernetes Dashboard en 2026: Headlamp, FreeLens y las mejores interfaces web

El Kubernetes Dashboard ha sido archivado oficialmente y ya no recibe mantenimiento. Si todavía dependes de él para gestionar tus clústeres, es el momento de buscar una alternativa seria. El ecosistema Kubernetes ha madurado muchísimo: los entornos modernos exigen gestión multi-clúster, integración con GitOps y capacidades de RBAC avanzadas que el dashboard original jamás fue capaz de ofrecer.

Lectura relacionada: Clientes de escritorio para Kubernetes más allá de Lens: 6 probados.

En este artículo repasamos las mejores alternativas disponibles en 2026, tanto interfaces web como herramientas de escritorio y CLI, para que puedas elegir la que mejor encaja con tu equipo y tu infraestructura.


Por qué el Kubernetes Dashboard quedó obsoleto

El dashboard original de Kubernetes cumplió bien su función durante años, pero tenía limitaciones estructurales difíciles de superar:

  • Foco en un único clúster: en un mundo donde lo habitual es gestionar varios clústeres simultáneamente (desarrollo, staging, producción, multi-cloud), esta restricción se convirtió en un cuello de botella real.
  • RBAC limitado: las capacidades de control de acceso basado en roles eran insuficientes para entornos corporativos que necesitan permisos granulares por equipo o proyecto.
  • Sin integración GitOps: la gestión declarativa de configuración a través de herramientas como Argo CD o Flux se ha convertido en estándar, y el dashboard no ofrecía ningún punto de integración.
  • Observabilidad mínima: más allá de listar recursos, el dashboard apenas proporcionaba métricas ni visibilidad real del estado del clúster.
  • Preocupaciones de seguridad: la arquitectura de seguridad del dashboard requería una configuración muy cuidadosa para no exponer vectores de ataque innecesarios, algo que muchos equipos pasaban por alto.

El resultado fue inevitable: el proyecto fue archivado y el grupo SIG UI de Kubernetes recomendó migrar a alternativas más modernas.


Las mejores alternativas al Kubernetes Dashboard en 2026

1. Headlamp: el sucesor oficial

Headlamp es la herramienta recomendada oficialmente por el grupo Kubernetes SIG UI como sucesora del dashboard deprecado. Es un proyecto CNCF con respaldo comunitario sólido, lo que le da una posición privilegiada en este listado.

Características principales:

  • Interfaz web moderna con diseño intuitivo, muy similar al dashboard original en cuanto a curva de aprendizaje
  • Sistema de plugins extensible: puedes añadir funcionalidad sin tocar el núcleo
  • Opciones de despliegue flexibles: dentro del clúster (in-cluster) o como aplicación de escritorio
  • Autenticación mediante kubeconfig y soporte para OpenID Connect (OIDC) para SSO empresarial
  • Operaciones de lectura y escritura completamente gobernadas por RBAC

Instalación mediante Helm:

helm repo add headlamp https://kubernetes-sigs.github.io/headlamp/
helm install my-headlamp headlamp/headlamp --namespace kube-system

# Como addon de Minikube
minikube addons enable headlamp
minikube service headlamp -n headlamp

Ideal para: equipos que migran desde el dashboard original y buscan una experiencia comparable con soporte oficial y arquitectura moderna. También es el punto de partida lógico si no tienes requisitos especiales de multi-clúster o CI/CD integrado.

Pros:
– Aval oficial del proyecto Kubernetes
– Curva de aprendizaje baja para quienes ya conocen el dashboard
– Arquitectura de plugins permite crecer con las necesidades del equipo
– Activamente mantenido y con comunidad creciente

Contras:
– El ecosistema de plugins todavía es menor comparado con herramientas más maduras
– No incluye funcionalidades de CI/CD ni GitOps de forma nativa


2. Portainer: gestión multi-clúster para empresas

Portainer empezó como una solución para gestionar contenedores Docker, pero ha evolucionado hasta convertirse en una plataforma completa de gestión Kubernetes. Si necesitas una interfaz web Kubernetes con capacidades empresariales y gestión centralizada de múltiples clústeres, Portainer es una opción muy sólida.

Características principales:

  • Gestión de múltiples clústeres desde una única interfaz unificada
  • RBAC empresarial con controles de acceso granulares por equipo, proyecto o namespace
  • Despliegue y escalado visual de workloads sin necesidad de tocar YAML directamente
  • Soporte para integración GitOps
  • Auditoría completa de operaciones
  • Compatible tanto con Kubernetes como con Docker y Docker Swarm

Ideal para: organizaciones que gestionan múltiples clústeres en entornos heterogéneos y necesitan RBAC centralizado, control empresarial y una sola herramienta para gobernar toda su infraestructura de contenedores.

Pros:
– Solución unificada para Docker y Kubernetes
– RBAC muy granular y completo
– Versión Community Edition gratuita con funcionalidades básicas
– Interfaz muy pulida y fácil de usar

Contras:
– Las funcionalidades más avanzadas requieren la versión Business (de pago)
– Puede resultar excesivo para equipos pequeños con un único clúster


3. Skooner (antes K8Dash): ligero y rápido

Skooner es la alternativa para quienes valoran la simplicidad por encima de todo. Mantiene la filosofía del dashboard original —una interfaz web limpia para visualizar y gestionar recursos del clúster— pero con mejoras de rendimiento y actualizaciones en tiempo real.

Características principales:

  • Actualizaciones de recursos en tiempo real sin necesidad de refrescar la página
  • Interfaz minimalista y limpia, sin funcionalidades superfluas
  • Instalación sencilla con configuración mínima
  • Visualización de métricas básicas en tiempo real
  • Autenticación OIDC integrada

Ideal para: equipos que necesitan una vista directa y sin complicaciones de su clúster, sin los overheads de plataformas más completas. Perfecto también como herramienta de monitorización rápida o para entornos de desarrollo.

Pros:
– Instalación y configuración muy rápidas
– Rendimiento excelente incluso con clústeres grandes
– Curva de aprendizaje prácticamente inexistente

Contras:
– Funcionalidades limitadas respecto a alternativas más completas
– Comunidad más pequeña y ritmo de desarrollo más pausado


4. Devtron: plataforma DevOps completa

Devtron va mucho más allá de ser una simple interfaz web para Kubernetes. Es una plataforma de entrega de aplicaciones completa que incluye visualización del clúster, pipelines CI/CD integrados, escaneo de seguridad y gestión de despliegues. Si tu equipo está construyendo una plataforma interna para desarrolladores (IDP), Devtron merece una evaluación seria.

Características principales:

  • Despliegue de aplicaciones multi-clúster
  • Pipelines CI/CD integrados nativamente en la plataforma
  • Escaneo de seguridad y funcionalidades de cumplimiento normativo
  • Vistas centradas en aplicaciones en lugar de en recursos individuales
  • Soporte para siete proveedores SSO diferentes
  • Tienda de Helm charts para despliegues gestionados

Ideal para: equipos de plataforma que construyen entornos de desarrollo interno (IDP) y necesitan pipelines de despliegue completos junto con la visibilidad del clúster en una sola herramienta.

Pros:
– Plataforma todo-en-uno: elimina la necesidad de herramientas separadas para CI/CD
– Vista centrada en aplicaciones muy útil para equipos de desarrollo
– Funcionalidades de seguridad integradas

Contras:
– Curva de aprendizaje considerable
– Infraestructura propia más pesada que un simple dashboard
– Puede ser excesivo si solo necesitas visualizar recursos


5. KubeSphere: plataforma de contenedores full-stack

KubeSphere se define a sí mismo como «un sistema operativo distribuido para aplicaciones cloud-native, usando Kubernetes como núcleo». No es simplemente una interfaz web Kubernetes —es una plataforma completa con arquitectura multi-tenant, observabilidad integrada, service mesh y mucho más.

Características principales:

  • Arquitectura multi-tenant con gestión de equipos y proyectos
  • Flujos de trabajo DevOps integrados
  • Integración con service mesh (Istio)
  • Federación multi-clúster
  • Observabilidad y monitorización integradas (Prometheus, Grafana, etc.)
  • Arquitectura plug-and-play para añadir o quitar módulos

Ideal para: organizaciones que quieren construir una plataforma de contenedores completa con todo incluido de serie, sin necesitar integrar múltiples herramientas por separado.

Pros:
– Solución verdaderamente integral
– Observabilidad y monitorización integradas sin configuración adicional
– Comunidad activa y buena documentación

Contras:
– Consumo de recursos considerable
– La complejidad de la plataforma puede ser un obstáculo para equipos pequeños
– Curva de aprendizaje significativa para sacar partido a todas sus capacidades


6. Rancher: la plataforma empresarial consolidada

Rancher, de SUSE, es una de las plataformas de gestión Kubernetes más maduras disponibles. Lleva años en producción en entornos empresariales de gran escala y ha demostrado su fiabilidad en escenarios multi-cloud complejos.

Características principales:

  • Gestión de cualquier distribución Kubernetes: EKS, GKE, AKS, RKE, K3s, on-premises…
  • Autenticación centralizada e RBAC unificado para todos los clústeres
  • Monitorización integrada con Prometheus y Grafana
  • Catálogo de aplicaciones con Helm charts
  • Gestión de políticas y escaneo de seguridad

Ideal para: empresas que gestionan entornos Kubernetes heterogéneos a gran escala, con múltiples proveedores cloud y necesidades de cumplimiento normativo.

Pros:
– Soporte empresarial de SUSE para entornos críticos
– Muy maduro y probado en producción a gran escala
– Gestión unificada de clústeres en cualquier proveedor
– Funcionalidades de seguridad y compliance muy completas

Contras:
– Despliegue y configuración inicial complejos
– Puede ser excesivo para entornos pequeños o medianos
– Rancher Management Server requiere sus propios recursos y mantenimiento


7. Octant: exploración del clúster orientada al desarrollador

Octant, desarrollado originalmente por VMware, pone el foco en el desarrollador más que en el administrador de clústeres. Es ideal para equipos de desarrollo que necesitan visibilidad de lo que está ejecutándose en el clúster sin necesitar acceso completo de administración.

Características principales:

  • Modelo de extensibilidad basado en plugins
  • Visualización de relaciones entre recursos del clúster
  • Port forwarding directamente desde la interfaz
  • Streaming de logs en tiempo real
  • Inspección de recursos con contexto completo

Ideal para: desarrolladores de aplicaciones que necesitan visibilidad del clúster sin responsabilidades de administración, y para equipos que quieren herramientas orientadas al ciclo de vida de la aplicación.

Pros:
– Orientado al flujo de trabajo del desarrollador
– Port forwarding y streaming de logs muy útiles en el día a día
– Ligero y fácil de instalar localmente

Contras:
– El desarrollo ha ralentizado desde la adquisición de VMware por Broadcom
– Funcionalidades de administración limitadas respecto a otras alternativas


Alternativas de escritorio y CLI

No todo tiene que ser una interfaz web. Para muchos equipos, las herramientas de escritorio o línea de comandos ofrecen ventajas claras: acceso offline, mejor rendimiento y una integración más estrecha con el flujo de trabajo local.

FreeLens, OpenLens y Lens

El ecosistema Lens es probablemente la alternativa de escritorio más popular para gestionar clústeres Kubernetes. Lens IDE fue durante años la referencia absoluta, pero cambios en su política de licencias llevaron a la aparición de forks como OpenLens y FreeLens.

  • Lens IDE: el original, ahora bajo Mirantis con una versión gratuita (Lens Personal) y planes de pago.
  • OpenLens: fork open source del código base de Lens, sin telemetría ni restricciones de licencia.
  • FreeLens: alternativa mantenida por la comunidad, más ligera y con foco en la funcionalidad esencial.

Las tres ofrecen una experiencia de escritorio muy pulida con gestión multi-clúster, visualización de recursos, streaming de logs y terminal integrada.

K9s: la interfaz de terminal definitiva

K9s es una interfaz de usuario para terminales (TUI) que permite gestionar clústeres Kubernetes sin salir de la línea de comandos. Si pasas el día en la terminal, K9s puede ser más eficiente que cualquier interfaz web.

Sus atajos de teclado, la navegación rápida entre recursos y la vista en tiempo real del estado del clúster la convierten en una herramienta imprescindible en el toolkit de cualquier SRE o administrador de Kubernetes.


Tabla comparativa

HerramientaTipoMulti-clústerRBACGitOpsCI/CDOpen Source
HeadlampWebParcialSíNoNoSí
PortainerWebSíAvanzadoSíNoCE gratuita
SkoonerWebNoBásicoNoNoSí
DevtronWebSíSíSíSíSí
KubeSphereWebSíAvanzadoSíSíSí
RancherWebSíAvanzadoSíNo nativoCE gratuita
OctantWeb/LocalNoDelegadoNoNoSí
FreeLensEscritorioSíDelegadoNoNoSí
K9sCLI/TUISíDelegadoNoNoSí

Cómo elegir la alternativa adecuada

La elección correcta depende fundamentalmente del tamaño de tu equipo, la complejidad de tu infraestructura y el caso de uso principal.

Elige Headlamp si:

  • Buscas el sucesor oficial con respaldo comunitario y del proyecto Kubernetes
  • Quieres una experiencia similar al dashboard original pero actualizada
  • La extensibilidad mediante plugins es importante para el futuro
  • Priorizas proyectos open source bajo el paraguas CNCF

Elige Portainer si:

  • Gestionas múltiples clústeres Kubernetes desde un único panel
  • El RBAC empresarial es un requisito crítico
  • Tu equipo también gestiona entornos Docker y quieres una herramienta unificada
  • Necesitas audit logging completo para cumplimiento normativo

Elige Skooner si:

  • La simplicidad y el rendimiento son prioritarios
  • Solo necesitas visualización y gestión básica de recursos
  • Quieres una instalación rápida sin configuración compleja

Elige Devtron o KubeSphere si:

  • Estás construyendo una plataforma interna para desarrolladores (IDP)
  • Necesitas pipelines CI/CD integrados junto con la gestión del clúster
  • Las vistas centradas en aplicaciones son más útiles para tu equipo que las vistas por recurso

Elige Rancher si:

  • Gestionas Kubernetes a escala empresarial con múltiples proveedores cloud
  • Necesitas soporte comercial y SLAs garantizados
  • La gestión de políticas y el compliance son requisitos no negociables

Considera FreeLens o K9s si:

  • Trabajas principalmente desde un entorno de desarrollo local
  • Necesitas acceso al estado del clúster sin conexión
  • Prefieres la eficiencia de una aplicación de escritorio o una TUI sobre una interfaz web

Consideraciones para la migración

Migrar desde el Kubernetes Dashboard no es simplemente instalar una nueva herramienta. Hay varios aspectos que debes evaluar antes de hacer el cambio:

  1. Compatibilidad de autenticación: verifica que la nueva herramienta es compatible con tu proveedor de identidad actual (LDAP, Active Directory, OIDC, etc.) antes de comprometerte con una opción.

  2. Traducción de configuración RBAC: los ClusterRole y RoleBinding que tenías configurados para el dashboard deberán adaptarse a la nueva herramienta. Documenta los permisos actuales antes de migrar.

  3. Automatizaciones existentes: si tienes scripts o pipelines que apuntan a URLs del dashboard para healthchecks u otras operaciones, necesitarán actualizarse.

  4. Formación del equipo: cada herramienta tiene su propia curva de aprendizaje. Incluye tiempo de formación en tu planificación, especialmente para herramientas más completas como Rancher o KubeSphere.

  5. Reconfiguración de Ingress: si tenías el dashboard expuesto externamente a través de un Ingress, necesitarás reconfigurarlo para la nueva herramienta, revisando también los certificados TLS y las reglas de autenticación.

  6. Validación en entorno no productivo: siempre prueba la nueva herramienta en un clúster de desarrollo o staging antes de desplegarla en producción. Comprueba especialmente los permisos RBAC y los flujos de autenticación.


Preguntas frecuentes

¿Está el Kubernetes Dashboard oficialmente deprecado?

Sí. El proyecto ha sido archivado oficialmente y ya no recibe actualizaciones de seguridad, correcciones de errores ni nuevas funcionalidades. Usarlo en entornos de producción supone un riesgo de seguridad real.

¿Cuál es el sustituto oficial?

Headlamp es el sucesor recomendado oficialmente por el grupo Kubernetes SIG UI.

¿Headlamp está listo para producción?

Sí. Headlamp soporta autenticación OIDC, RBAC granular y está siendo adoptado progresivamente en entornos de producción. Con la configuración adecuada de acceso, es una opción válida para producción.

¿Existe alguna alternativa ligera como el dashboard original?

Skooner es la opción más próxima en filosofía al dashboard original: interfaz web sencilla, instalación mínima y gestión básica de recursos.

¿Es obligatorio usar una interfaz web?

No. Muchos equipos prefieren herramientas de escritorio como FreeLens u OpenLens, o directamente la TUI de K9s, que ofrecen mejor rendimiento y acceso offline. La elección depende del flujo de trabajo de cada equipo.

¿Es seguro exponer un dashboard Kubernetes públicamente en Internet?

Requiere precauciones muy serias: autenticación robusta (OIDC o equivalente), RBAC estricto con permisos de mínimo privilegio, restricciones de red, VPN o listas de IPs permitidas, y terminación TLS. La exposición sin estas medidas ha sido el origen de múltiples brechas de seguridad documentadas.

¿Puede un dashboard sustituir a kubectl?

No. Los dashboards son herramientas complementarias que facilitan la visualización y las operaciones habituales. Para operaciones avanzadas, automatización y flujos GitOps, kubectl y las herramientas CLI siguen siendo imprescindibles.

¿Qué dashboard es mejor para desarrolladores?

Octant y Devtron están diseñados con el desarrollador en mente, ofreciendo vistas centradas en aplicaciones y funcionalidades como port forwarding y streaming de logs que son especialmente útiles en el ciclo de desarrollo.

¿Qué herramienta es mejor para gestión multi-clúster?

Portainer, Rancher y KubeSphere están diseñadas específicamente para gestionar múltiples clústeres desde una interfaz centralizada. Para entornos enterprise a gran escala, Rancher suele ser la opción más completa y madura.


Conclusión

El archivo del Kubernetes Dashboard marca el final de una era, pero también es una señal clara de la madurez del ecosistema. Las alternativas disponibles en 2026 no son simples sustitutos —son plataformas mucho más potentes que abordan las necesidades reales de los equipos de plataforma modernos.

Si solo puedes recordar tres cosas de este artículo:

  1. Headlamp es el sucesor oficial recomendado por el proyecto Kubernetes. Si buscas una migración sencilla, empieza por aquí.
  2. Portainer o Rancher son las opciones a evaluar si gestionas múltiples clústeres en entornos empresariales.
  3. FreeLens y K9s merecen un lugar en el toolkit de cualquier administrador Kubernetes, independientemente de qué interfaz web elijas para el equipo.

La clave está en evaluar tus necesidades reales: tamaño del equipo, número de clústeres, requisitos de compliance y flujos de trabajo existentes. No existe una respuesta única válida para todos los casos.


¿Usas alguna de estas herramientas en tu infraestructura? ¿Te quedas con alguna opción no mencionada aquí? Déjalo en los comentarios.

Versiones Gateway API Kubernetes: Guía Completa de Compatibilidad 2026

Versiones Gateway API Kubernetes: Guía Completa de Compatibilidad 2026

Versiones Kubernetes Gateway API: Guía Completa de Compatibilidad y Actualización

Publicado: 2 de febrero de 2026 · Por Alexandre Vazquez


La Kubernetes Gateway API ha madurado a un ritmo notable: de sus orígenes experimentales ha pasado a convertirse en el estándar de facto para la gestión del tráfico de ingress y service mesh. Sin embargo, con varias versiones publicadas y distintos niveles de madurez en cada una de ellas, entender qué versión utilizar, cómo se relaciona con tu clúster de Kubernetes y cuándo actualizar puede resultar complejo.

Esta guía de referencia cubre en profundidad las versiones de Gateway API disponibles, su relación con las versiones de Kubernetes, los niveles de soporte de los principales proveedores y la filosofía de actualización que te permitirá tomar decisiones fundamentadas para tu infraestructura.


Entendiendo el modelo de versionado de Gateway API

El modelo de versionado de Gateway API es deliberadamente distinto al de las APIs nativas de Kubernetes. Mientras que los recursos integrados en Kubernetes están ligados a una versión concreta del clúster, los CRDs de Gateway API se instalan de forma independiente siempre que el clúster cumpla los requisitos mínimos.

Esta independencia tiene implicaciones prácticas importantes: puedes actualizar Gateway API sin necesidad de actualizar Kubernetes y, a la inversa, actualizar Kubernetes sin que eso fuerce un cambio en tus CRDs de Gateway API. Son dos ciclos de vida desacoplados que hay que gestionar de forma coordinada, pero no sincronizada obligatoriamente.

Requisitos mínimos de versión de Kubernetes

A partir de Gateway API v1.1 y versiones posteriores, el requisito mínimo es Kubernetes 1.26 o superior. El proyecto se compromete a mantener soporte para un mínimo de las últimas 5 versiones menores de Kubernetes, lo que ofrece una ventana razonable para planificar las actualizaciones del clúster.

Esto significa que si ejecutas Kubernetes 1.26, 1.27, 1.28, 1.29 o 1.30, puedes instalar la última versión de Gateway API sin preocuparte por incompatibilidades. Para la mayoría de los equipos que mantienen sus clústeres relativamente actualizados, esta restricción rara vez supone un problema.


Canales de publicación: Standard y Experimental

Gateway API utiliza dos canales de publicación diferenciados para equilibrar estabilidad e innovación. Comprender cuándo usar cada uno es fundamental antes de instalar o actualizar los CRDs.

Canal Standard

El canal Standard contiene únicamente recursos y campos a nivel GA (Generally Available, v1) y Beta (v1beta1). Al instalar desde el canal Standard obtienes:

  • Garantías de estabilidad: no se introducen cambios disruptivos una vez que un recurso alcanza Beta o GA
  • Compatibilidad hacia atrás: es seguro actualizar entre versiones menores sin modificar tus configuraciones existentes
  • Preparación para producción: funcionalidades ampliamente probadas con múltiples implementaciones
  • Cobertura de conformance: las pruebas de conformance completas garantizan portabilidad entre implementaciones

Los recursos incluidos en el canal Standard son GatewayClass, Gateway, HTTPRoute y ReferenceGrant a nivel v1, además de recursos estabilizados como GRPCRoute.

Canal Experimental

El canal Experimental incluye todo lo del canal Standard más recursos Alpha y campos experimentales. Este canal está orientado a:

  • Pruebas tempranas de funcionalidades: acceder a nuevas capacidades antes de que se estabilicen
  • Funcionalidad de vanguardia: probar las últimas novedades de Gateway API
  • Sin garantías de estabilidad: pueden producirse cambios disruptivos entre versiones
  • Retroalimentación al proyecto: contribuir a dar forma a la API mediante la prueba de características experimentales

Las funcionalidades pueden graduarse del canal Experimental al Standard, o bien ser descartadas, en función de la experiencia de implementación y la retroalimentación de la comunidad.

Recomendación: salvo que tengas una necesidad concreta de una característica experimental, utiliza siempre el canal Standard en entornos de producción.


Historial de versiones y funcionalidades de Gateway API

v1.0 (octubre de 2023)

La versión 1.0 marcó un hito importante al graduar los recursos principales al nivel GA. Este lanzamiento incluyó:

  • Gateway, GatewayClass y HTTPRoute a nivel v1 (estables)
  • Garantías de compatibilidad hacia atrás para los recursos v1
  • Estado de preparación para producción en la gestión del tráfico de ingress
  • Múltiples implementaciones conformes de distintos fabricantes

Con v1.0 se estableció que Gateway API estaba lista para cargas de trabajo productivas, dejando de ser un experimento para convertirse en una apuesta seria del ecosistema Kubernetes.

v1.1 (mayo de 2024)

La versión 1.1 amplió significativamente la API con soporte para service mesh:

  • GRPCRoute: soporte nativo para el enrutamiento de tráfico gRPC
  • Capacidades de service mesh: gestión del tráfico este-oeste junto al norte-sur
  • Múltiples implementaciones: tanto Istio como otros service mesh alcanzaron conformance
  • Funcionalidades mejoradas: criterios de coincidencia adicionales y capacidades de enrutamiento avanzadas

Esta versión cerró la brecha entre los controladores de ingress tradicionales y las implementaciones completas de service mesh, consolidando Gateway API como una solución unificada para ambos casos de uso.

v1.2 y v1.3

Estas versiones intermedias introdujeron ciclos de publicación más estructurados y funcionalidades adicionales:

  • Refinamiento de las pruebas de conformance
  • BackendTLSPolicy (experimental en v1.3)
  • Mejoras en observabilidad y depuración
  • Enrutamiento entre namespaces mejorado

v1.4 (octubre de 2025)

La última versión GA disponible en el momento de escribir esta guía, v1.4.0, incorporó:

  • Refinamiento continuado de la API
  • Funcionalidades experimentales adicionales para la comunidad
  • Perfiles de conformance mejorados
  • Documentación y guías de migración más completas

Matriz de compatibilidad: Gateway API vs. Kubernetes

La siguiente tabla muestra la relación entre versiones de Gateway API y versiones de Kubernetes:

Versión Gateway APIKubernetes mínimoKubernetes recomendadoFecha de publicación
v1.0.x1.251.26+Octubre 2023
v1.1.x1.261.27+Mayo 2024
v1.2.x1.261.28+2024
v1.3.x1.261.29+2024
v1.4.x1.261.30+Octubre 2025

La conclusión clave de esta tabla es que Gateway API v1.1 y versiones posteriores requieren Kubernetes 1.26 como mínimo. Esto significa que en cualquier clúster medianamente actualizado puedes ejecutar la última versión de Gateway API sin restricciones de compatibilidad con Kubernetes en sí.


Niveles de soporte de los proveedores

Los distintos proveedores (implementaciones de Gateway API) soportan versiones y conjuntos de funcionalidades diferentes. Entender el nivel de soporte de tu proveedor es tan importante como conocer la versión de Gateway API que quieres instalar.

Niveles de conformance

Gateway API define tres niveles de conformance para las funcionalidades:

  1. Core: funcionalidades que toda implementación debe soportar para afirmar conformance. Son portables entre todas las implementaciones.
  2. Extended: funcionalidades opcionales estandarizadas. Las implementaciones indican soporte Extended de forma separada al Core.
  3. Implementation-specific: funcionalidades propias del fabricante sin requisitos de conformance.

Esta jerarquía te permite razonar sobre portabilidad: si usas únicamente funcionalidades Core, puedes cambiar de proveedor sin reescribir tus recursos de Gateway API.

Soporte de los principales proveedores

Istio

Istio alcanzó el soporte GA de Gateway API en su versión 1.22 (mayo de 2024). Istio ofrece:

  • Soporte completo del canal Standard (recursos v1)
  • Gestión del tráfico de service mesh (este-oeste) mediante GAMMA
  • Control del tráfico de ingress (norte-sur)
  • Soporte experimental de BackendTLSPolicy a partir de Istio 1.26

Istio es la opción preferida para organizaciones que necesitan tanto ingress como service mesh en una única solución, dado su nivel de madurez y su amplia adopción.

Envoy Gateway

Envoy Gateway sigue de cerca las publicaciones de Gateway API. La versión 1.4.0 incluye:

  • Soporte de Gateway API v1.3.0
  • Matriz de compatibilidad con versiones de Envoy Proxy
  • Enfoque en casos de uso de ingress
  • Fuerte adopción de funcionalidades experimentales

Es importante consultar la matriz de compatibilidad de Envoy Gateway para asegurarse de que la versión de Envoy Proxy instalada es coherente con la versión de Gateway API y de Kubernetes en uso.

Cilium

Cilium integra Gateway API de forma profunda con su implementación CNI:

  • Arquitectura de proxy Envoy por nodo
  • Aplicación de políticas de red para el tráfico gestionado por Gateway
  • Soporte tanto de ingress como de service mesh
  • Procesamiento de paquetes basado en eBPF

La arquitectura singular de Cilium lo convierte en una opción muy potente para organizaciones que ya utilizan Cilium para la red del clúster, al poder unificar en un mismo componente la red, la seguridad y el enrutamiento del tráfico de aplicaciones.

Contour

Contour v1.31.0 implementa Gateway API v1.2.1, con soporte de:

  • Todos los recursos v1 del canal Standard
  • La mayoría de los recursos v1alpha2 (TLSRoute, TCPRoute, GRPCRoute)
  • Soporte de BackendTLSPolicy

Cómo verificar la conformance de tu proveedor

Antes de decidir qué versión de Gateway API instalar, sigue estos pasos para validar el soporte de tu proveedor:

  1. Visita la página oficial de implementaciones: el proyecto Gateway API mantiene una lista actualizada de implementaciones con sus niveles de conformance.
  2. Consulta la documentación del proveedor: la mayoría de los proveedores publican matrices de compatibilidad que relacionan versiones de Gateway API, Kubernetes y proxy.
  3. Revisa los informes de conformance: los proveedores envían resultados de pruebas de conformance que detallan exactamente qué funcionalidades Core y Extended soportan.
  4. Prueba en entornos no productivos: antes de actualizar producción, valida tus casos de uso específicos en un entorno de staging.

Filosofía de actualización: cuándo y cómo actualizar

Una de las preguntas más frecuentes sobre Gateway API es: ¿necesito ejecutar siempre la última versión? La respuesta depende de tus necesidades concretas y tu tolerancia al riesgo.

Permanecer en versiones antiguas

No es necesario ejecutar siempre la última versión de Gateway API. Es perfectamente válido:

  • Mantenerse en una versión estable más antigua si cubre tus necesidades
  • Actualizar solo cuando necesites funcionalidades específicas de una versión posterior
  • Esperar a que tu proveedor soporte oficialmente las versiones más recientes
  • Priorizar la estabilidad frente a tener las últimas novedades

Las garantías de compatibilidad hacia atrás del canal Standard aseguran que, cuando finalmente actualices, tus configuraciones existentes seguirán funcionando sin modificaciones.

Cuándo considerar la actualización

Conviene plantearse una actualización cuando:

  1. Necesitas una funcionalidad específica: un nuevo matcher de HTTPRoute, soporte de GRPCRoute u otra capacidad disponible únicamente en versiones más recientes.
  2. Tu proveedor lo recomienda: los proveedores suelen optimizar su soporte para versiones concretas de Gateway API; seguir sus recomendaciones es una práctica sensata.
  3. Consideraciones de seguridad: aunque es infrecuente, pueden surgir vulnerabilidades que justifiquen una actualización urgente.
  4. Actualización del clúster Kubernetes: al actualizar Kubernetes, verifica que tu versión de Gateway API es compatible con la nueva versión del clúster.

Prácticas seguras de actualización

1. Utiliza siempre el canal Standard

El uso de los CRDs del canal Standard simplifica y hace más seguras las actualizaciones. Las funcionalidades experimentales pueden introducir cambios disruptivos entre versiones, mientras que las del canal Standard mantienen compatibilidad.

2. Actualiza una versión menor a la vez

Aunque en general es seguro saltarse versiones, la ruta de actualización más probada es la incremental. Pasar de v1.2 a v1.3 y después a v1.4 es más seguro que saltar directamente de v1.2 a v1.4.

3. Prueba siempre antes de actualizar en producción

Valida las actualizaciones en entornos no productivos antes de aplicarlas en producción:

# Instalar una versión específica de Gateway API en el clúster de pruebas
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/standard-install.yaml

4. Revisa las notas de publicación

Cada versión de Gateway API publica notas detalladas que incluyen:

  • Nuevas funcionalidades y capacidades
  • Graduación de características experimentales al canal Standard
  • Avisos de deprecación
  • Consideraciones específicas de la actualización

No omitas este paso: las notas de publicación son la fuente más fiable para anticipar cualquier impacto en tus configuraciones existentes.

5. Verifica la compatibilidad del proveedor primero

Antes de actualizar los CRDs de Gateway API, confirma que tu controlador de Gateway soporta la versión objetivo. Instalar Gateway API v1.4 no aporta ningún beneficio si tu controlador solo soporta v1.2; en el peor de los casos, puede generar comportamientos inesperados.

6. Nunca sobreescribas CRDs de un canal diferente

Las implementaciones no deben sobreescribir CRDs de Gateway API que pertenezcan a un canal diferente. Lleva un registro de si estás usando una instalación del canal Standard o Experimental y mantén la coherencia.

Gestión de CRDs: comandos útiles

La gestión de los CRDs de Gateway API requiere atención al detalle. Estos comandos te ayudarán a verificar el estado actual de tu instalación:

# Comprobar la versión instalada de Gateway API
kubectl get crd gateways.gateway.networking.k8s.io -o yaml | \
  grep 'gateway.networking.k8s.io/bundle-version'

# Verificar qué canal está instalado
kubectl get crd gateways.gateway.networking.k8s.io -o yaml | \
  grep 'gateway.networking.k8s.io/channel'

Ejecuta estos comandos antes de cualquier actualización para tener una línea de base clara de lo que tienes instalado.


Mantenerse informado sobre nuevas versiones

Gateway API sigue un ciclo de publicación estructurado con canales de comunicación claros. Aquí tienes las fuentes más relevantes para estar al día:

Fuentes para seguir las publicaciones

  1. Página de releases en GitHub: sigue el repositorio kubernetes-sigs/gateway-api para recibir notificaciones de nuevas versiones en el momento en que se publican.
  2. Blog oficial de Kubernetes: las publicaciones mayores de Gateway API se anuncian en el blog oficial de Kubernetes con explicaciones detalladas de las novedades.
  3. Listas de correo y Slack: únete a los canales de la comunidad Gateway API para debates y anuncios anticipados.
  4. Anuncios del proveedor: los proveedores de Gateway anuncian el soporte de nuevas versiones de Gateway API a través de sus propios canales; suscríbete a los de tu proveedor específico.

Cadencia de publicación

Gateway API sigue una cadencia de publicación trimestral para las versiones menores, con versiones de parche según sea necesario para correcciones de errores y problemas de seguridad. Esta cadencia predecible facilita la planificación de actualizaciones en los equipos de infraestructura.


Marco de decisión práctico

Este marco te ayudará a decidir qué versión de Gateway API ejecutar en función de tu situación concreta.

Para nuevos despliegues

  • Cargas de trabajo de producción: usa la última versión GA soportada por tu proveedor, instalada desde el canal Standard.
  • Entornos orientados a la innovación: considera el canal Experimental si necesitas funcionalidades de vanguardia y puedes asumir posibles cambios disruptivos.
  • Enfoque conservador: usa v1.1 o posterior con el canal Standard; es un punto de partida seguro y funcional para la mayoría de los casos de uso.

Para despliegues existentes

  • Si todo funciona correctamente: mantente en tu versión actual hasta que necesites nuevas funcionalidades. La estabilidad tiene valor.
  • Si el proveedor recomienda actualizar: sigue las indicaciones del proveedor, especialmente si hay implicaciones de seguridad.
  • Si tienes planificada una actualización de Kubernetes: verifica la compatibilidad con antelación; puede que necesites actualizar Gateway API antes o simultáneamente al clúster.

Actualizaciones motivadas por funcionalidades

NecesidadVersión mínima requerida
Soporte de service mesh (GAMMA)v1.1
GRPCRoute establev1.1
BackendTLSPolicyv1.3+ (canal Experimental) + soporte del proveedor
Perfiles de conformance refinadosv1.4

Preguntas frecuentes sobre compatibilidad de Gateway API

¿Cuál es la diferencia entre Kubernetes Ingress y Gateway API?

Kubernetes Ingress es una API heredada centrada principalmente en el tráfico HTTP(S) con capacidades de extensión limitadas. Gateway API es su sucesor, y ofrece un modelo más expresivo y orientado a roles que soporta múltiples protocolos, enrutamiento avanzado, mejor separación de responsabilidades y un comportamiento coherente entre implementaciones. Si empiezas un nuevo proyecto hoy, Gateway API es la elección correcta.

¿Qué versión de Gateway API debería usar en producción hoy?

Para la mayoría de los entornos de producción, lo recomendable es usar la última versión GA (v1.x) soportada por tu proveedor de Gateway, instalada desde el canal Standard. Esto garantiza estabilidad, compatibilidad hacia atrás y las garantías de conformance, sin renunciar a las mejoras continuas del proyecto.

¿Puedo actualizar Gateway API sin actualizar mi clúster Kubernetes?

Sí. Los CRDs de Gateway API se instalan de forma independiente a Kubernetes. Mientras tu clúster cumpla el requisito mínimo de versión de Kubernetes (1.26+ para versiones recientes), puedes actualizar Gateway API sin tocar el clúster.

¿Qué ocurre si mi proveedor no soporta la última versión de Gateway API?

Si tu proveedor va por detrás de las últimas versiones, debes mantenerte en la última versión soportada oficialmente por ese proveedor. Instalar CRDs de Gateway API más nuevos de lo que soporta tu controlador puede dar lugar a funcionalidades no disponibles o a comportamientos indefinidos. La compatibilidad con el proveedor siempre debe tener prioridad sobre ejecutar la versión más reciente de la API.

¿Es seguro actualizar los CRDs de Gateway API sin tiempo de inactividad?

En la mayoría de los casos, sí, cuando se utiliza el canal Standard. Gateway API ofrece garantías sólidas de compatibilidad hacia atrás para los recursos GA y Beta. No obstante, siempre deberías probar las actualizaciones en entornos no productivos y verificar que tu proveedor soporta la versión objetivo antes de aplicarlas en producción.

¿Qué son los perfiles de conformance en Gateway API?

Los perfiles de conformance son conjuntos predefinidos de pruebas que agrupan funcionalidades por caso de uso (por ejemplo, ingress HTTP, service mesh, etc.). Permiten a los proveedores declarar su nivel de soporte de forma granular y a los usuarios comparar implementaciones de forma objetiva. Se introdujeron en versiones recientes de Gateway API y siguen evolucionando.


Conclusión

Kubernetes Gateway API representa el futuro de la gestión del tráfico en Kubernetes: una API estandarizada, extensible y orientada a roles tanto para ingress como para service mesh. Entender el modelo de versionado, los requisitos de compatibilidad y la filosofía de actualización te permite tomar decisiones fundamentadas que equilibran innovación y estabilidad.

Los puntos clave de esta guía:

  • Los CRDs de Gateway API se instalan de forma independiente a Kubernetes; solo requieren la versión 1.26 o superior para versiones recientes.
  • El canal Standard ofrece estabilidad y compatibilidad; el canal Experimental da acceso anticipado a nuevas funcionalidades con el coste de posibles cambios disruptivos.
  • No es necesario ejecutar siempre la última versión: actualiza cuando necesites funcionalidades específicas o cuando tu proveedor lo recomiende.
  • Verifica siempre la compatibilidad del proveedor antes de actualizar los CRDs de Gateway API.
  • Sigue prácticas seguras de actualización: prueba primero, actualiza de forma incremental, revisa las notas de publicación.

Siguiendo estas pautas, podrás desplegar y mantener Gateway API en tu infraestructura Kubernetes con confianza, tomando decisiones de actualización que se ajusten a las necesidades y la tolerancia al riesgo de tu organización.


Referencias

Cómo limpiar ConfigMaps y Secrets huérfanos en Kubernetes

Cómo limpiar ConfigMaps y Secrets huérfanos en Kubernetes

Kubernetes Housekeeping: Cómo Limpiar ConfigMaps y Secrets Huérfanos

El mantenimiento de un cluster Kubernetes en producción va mucho más allá de desplegar aplicaciones y escalar pods. Con el tiempo, uno de los problemas más silenciosos que acumula cualquier cluster en uso real es el de los recursos huérfanos: ConfigMaps y Secrets que en algún momento sirvieron para algo, pero que hoy llevan semanas o meses sin ser referenciados por ningún workload activo.

Kubernetes no elimina automáticamente ConfigMaps ni Secrets cuando se borra el Deployment, StatefulSet o Job que los utilizaba. El resultado es predecible: acumulación progresiva de basura en etcd, superficie de ataque innecesaria con credenciales obsoletas todavía accesibles, y un ruido operativo que complica cualquier diagnóstico.

Este artículo cubre las causas habituales de este problema, métodos de detección —tanto manuales como automatizados—, estrategias de prevención y, sobre todo, cómo ejecutar la limpieza de forma segura en entornos reales.


Por qué se acumulan ConfigMaps y Secrets huérfanos

Antes de limpiar, conviene entender cómo se llega a esta situación. No es un caso de negligencia puntual: es el resultado natural de varios patrones de trabajo habituales.

Actualizaciones de aplicaciones sin limpieza del historial

Cada vez que se actualiza la configuración de una aplicación —cambio de variables de entorno, rotación de credenciales, nueva URL de base de datos— es habitual crear un nuevo ConfigMap o Secret con nombre versionado. El problema: el anterior no se borra.

Despliegues eliminados que dejan su configuración atrás

Cuando un equipo hace kubectl delete deployment myapp, el Deployment desaparece, pero los ConfigMaps y Secrets asociados permanecen. Kubernetes no establece esa relación de ciclo de vida salvo que se configure explícitamente.

Rollouts fallidos

Un rollout que falla a mitad puede dejar una versión de ConfigMap preparada para una versión de la aplicación que nunca llegó a arrancar. Esa configuración queda flotando en el namespace indefinidamente.

Flujos de trabajo en desarrollo

Los entornos de desarrollo y staging son especialmente propensos: pruebas rápidas, experimentos, recursos creados a mano sin etiquetas ni propietario declarado. Muy pocos equipos tienen disciplina para limpiar detrás de ellos.

Pipelines CI/CD con nombres únicos por hash

Herramientas como Helm y Kustomize pueden generar ConfigMaps con sufijos de hash basados en el contenido (myapp-config-7d4f9c). Cada actualización genera un nuevo recurso. Sin un mecanismo de pruning activo, los anteriores se acumulan indefinidamente.


El impacto real de no hacer housekeeping

No es solo una cuestión de orden. Los recursos huérfanos tienen consecuencias concretas:

Seguridad degradada. Un Secret huérfano puede contener credenciales antiguas, claves de API o certificados que ya no deberían ser accesibles para ningún proceso. Siguen siendo válidos en muchos casos, y siguen ocupando espacio en etcd —que no está cifrado por defecto en todos los clusters.

Consumo de etcd. Cada ConfigMap y Secret ocupa espacio en la base de datos de estado del cluster. etcd tiene límites de tamaño (el valor por defecto es 2 GB), y alcanzarlo en producción es una emergencia operativa real. Los recursos huérfanos aceleran ese límite sin aportar ningún valor.

Ruido operativo. Un namespace con cien ConfigMaps activos y doscientos huérfanos es un namespace difícil de operar. Localizar la configuración correcta, depurar un problema de arranque, revisar qué credenciales están en uso… todo se complica.

Coste de cognitive load para el equipo. No es trivial: los equipos que trabajan en namespaces desordenados cometen más errores, tardan más en diagnosticar incidencias y tienen más resistencia a hacer cambios.


Detección: encontrar los recursos huérfanos

Detección manual con kubectl y jq

El enfoque básico consiste en cruzar la lista de ConfigMaps y Secrets de un namespace contra las referencias activas en los Pods en ejecución.

Script para ConfigMaps:

#!/bin/bash
# detect-orphaned-configmaps.sh
# Identifica ConfigMaps no referenciados por ningún Pod activo

NAMESPACE=${1:-default}

echo "Comprobando ConfigMaps huérfanos en namespace: $NAMESPACE"
echo "---"

# Obtener todos los ConfigMaps del namespace
CONFIGMAPS=$(kubectl get configmaps -n $NAMESPACE -o jsonpath='{.items[*].metadata.name}')

for cm in $CONFIGMAPS; do
    # Omitir kube-root-ca.crt — gestionado por el sistema
    if [[ "$cm" == "kube-root-ca.crt" ]]; then
        continue
    fi

    # Comprobar si algún Pod referencia este ConfigMap
    REFERENCED=$(kubectl get pods -n $NAMESPACE -o json | \
        jq -r --arg cm "$cm" '.items[] |
        select(
            (.spec.volumes[]?.configMap.name == $cm) or
            (.spec.containers[].env[]?.valueFrom.configMapKeyRef.name == $cm) or
            (.spec.containers[].envFrom[]?.configMapRef.name == $cm)
        ) | .metadata.name' | head -1)

    if [[ -z "$REFERENCED" ]]; then
        echo "Huérfano: $cm"
    fi
done

Script para Secrets:

#!/bin/bash
# detect-orphaned-secrets.sh

NAMESPACE=${1:-default}

echo "Comprobando Secrets huérfanos en namespace: $NAMESPACE"
echo "---"

SECRETS=$(kubectl get secrets -n $NAMESPACE -o jsonpath='{.items[*].metadata.name}')

for secret in $SECRETS; do
    # Omitir tokens de service account y Secrets del sistema
    SECRET_TYPE=$(kubectl get secret $secret -n $NAMESPACE -o jsonpath='{.type}')
    if [[ "$SECRET_TYPE" == "kubernetes.io/service-account-token" ]]; then
        continue
    fi

    # Comprobar si algún Pod referencia este Secret
    REFERENCED=$(kubectl get pods -n $NAMESPACE -o json | \
        jq -r --arg secret "$secret" '.items[] |
        select(
            (.spec.volumes[]?.secret.secretName == $secret) or
            (.spec.containers[].env[]?.valueFrom.secretKeyRef.name == $secret) or
            (.spec.containers[].envFrom[]?.secretRef.name == $secret) or
            (.spec.imagePullSecrets[]?.name == $secret)
        ) | .metadata.name' | head -1)

    if [[ -z "$REFERENCED" ]]; then
        echo "Huérfano: $secret"
    fi
done

Importante: Estos scripts comprueban únicamente Pods en ejecución. Un ConfigMap puede estar referenciado en un Deployment cuyo Pod está temporalmente caído, en un CronJob que no ha disparado recientemente, o en un template de Job. Antes de borrar cualquier recurso, es imprescindible cruzar también contra Deployments, StatefulSets, DaemonSets, CronJobs y Jobs.

Herramientas automatizadas

Para clusters con muchos namespaces o workflows de auditoría recurrente, los scripts manuales no escalan. Hay herramientas específicas para esta tarea.

Kor

Kor es una herramienta de código abierto diseñada específicamente para detectar recursos sin usar en Kubernetes. Tiene soporte nativo para ConfigMaps, Secrets, ServiceAccounts, Services y varios recursos más.

# Instalación en macOS
brew install kor

# Escanear ConfigMaps y Secrets sin usar en un namespace
kor all --namespace production --output json

# Solo ConfigMaps
kor configmap --namespace production

# Solo Secrets, excluyendo namespaces de sistema
kor secret --namespace production --exclude-namespaces kube-system,kube-public

La salida en formato JSON es especialmente útil para integrar el análisis en pipelines de CI/CD o dashboards de monitorización.

Popeye

Popeye genera informes completos de salud del cluster, incluyendo recursos huérfanos, configuraciones incorrectas, y buenas prácticas no seguidas.

# Instalación
brew install derailed/popeye/popeye

# Escaneo completo con salida en JSON
popeye --output json --save

# Escaneo centrado en un namespace
popeye --namespace production

Popeye es especialmente valioso para una primera auditoría de un cluster heredado, porque da una visión global del estado de salud en múltiples dimensiones, no solo recursos huérfanos.


Prevención: evitar que se acumulen en primer lugar

Detectar y limpiar es necesario, pero el objetivo real es que los recursos tengan un ciclo de vida gestionado desde el principio. Hay varias estrategias complementarias.

Owner References: limpieza automática en cascada

El mecanismo más potente de Kubernetes para gestión de ciclo de vida es ownerReferences. Si un ConfigMap tiene como propietario un Deployment, cuando ese Deployment se borra, el garbage collector de Kubernetes elimina automáticamente el ConfigMap.

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: production
  ownerReferences:
    - apiVersion: apps/v1
      kind: Deployment
      name: myapp
      uid: d9607e19-f88f-11e6-a518-42010a800195
      controller: true
      blockOwnerDeletion: true
data:
  app.properties: |
    database.url=postgres://db:5432

Las herramientas de gestión de paquetes como Helm y Kustomize gestionan esto automáticamente para los recursos que crean. Si estás aplicando manifiestos directamente con kubectl apply, es responsabilidad del equipo añadir estas referencias.

Etiquetado consistente: visibilidad y control

Las etiquetas son la base de cualquier operación de gestión masiva en Kubernetes. Sin etiquetas consistentes, no hay forma de hacer consultas selectivas ni de atribuir un recurso a su propietario.

apiVersion: v1
kind: ConfigMap
metadata:
  name: api-gateway-config-v2
  labels:
    app: api-gateway
    component: configuration
    version: v2
    managed-by: argocd
    owner: platform-team
data:
  config.yaml: |
    # configuración aquí

Con etiquetas bien definidas, las operaciones de limpieza se simplifican enormemente:

# Todos los ConfigMaps de una aplicación
kubectl get configmaps -l app=api-gateway

# Eliminar versiones antiguas
kubectl delete configmaps -l app=api-gateway,version=v1

Conviene definir un estándar de etiquetado a nivel de organización y hacerlo cumplir con políticas de admisión (ver sección de OPA Gatekeeper más adelante).

GitOps con pruning habilitado

Las herramientas GitOps como ArgoCD mantienen el estado del cluster sincronizado con el repositorio Git. Cuando se habilita el pruning, cualquier recurso que desaparezca del repositorio también se elimina del cluster automáticamente.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
spec:
  syncPolicy:
    automated:
      prune: true      # Eliminar recursos que no estén en Git
      selfHeal: true

Esta es probablemente la estrategia de prevención más eficaz para equipos que trabajan con GitOps: el cluster siempre refleja exactamente lo que está en Git, sin residuos.

Kustomize configMapGenerator con pruning

Kustomize tiene un generador nativo de ConfigMaps que añade un sufijo de hash basado en el contenido. Esto garantiza que cualquier cambio en la configuración genere un nuevo ConfigMap (y que los Pods hagan rolling update automáticamente). Combinado con --prune, las versiones antiguas se eliminan al aplicar la nueva.

kubectl apply --prune -k ./overlays/production \
  -l app=myapp

Este patrón es especialmente útil en pipelines CI/CD donde la configuración cambia frecuentemente.

Resource Quotas como mecanismo de presión

Establecer cuotas de recursos por namespace crea una presión natural para que los equipos mantengan el orden:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: config-quota
  namespace: production
spec:
  hard:
    configmaps: "50"
    secrets: "50"

Cuando un equipo alcanza el límite, se ve obligado a revisar qué hay en el namespace y eliminar lo que ya no sirve. No es la solución más elegante, pero funciona como red de seguridad.


Ejecución de la limpieza: de lo manual a lo automatizado

Limpieza manual puntual

Para una primera limpieza de un cluster heredado, el proceso recomendado es:

# 1. Generar listado de candidatos a eliminar
./detect-orphaned-configmaps.sh production > orphaned-cms.txt
cat orphaned-cms.txt

# 2. Revisar el listado manualmente antes de actuar

# 3. Eliminar los confirmados
for cm in $(cat orphaned-cms.txt | grep "Huérfano:" | awk '{print $2}'); do
    kubectl delete configmap $cm -n production
done

La revisión manual es imprescindible en la primera ejecución. Los scripts de detección basados en Pods activos pueden tener falsos positivos para recursos referenciados por workloads caídos temporalmente.

Backup antes de cualquier borrado

Antes de eliminar nada en producción, siempre:

# Backup de todos los ConfigMaps del namespace
kubectl get configmap -n production -o yaml > cm-backup-$(date +%Y%m%d).yaml

# Solo eliminar ConfigMaps con más de 30 días de antigüedad
kubectl get configmap -n production -o json | \
  jq -r --arg date "$(date -d '30 days ago' -u +%Y-%m-%dT%H:%M:%SZ)" \
  '.items[] | select(.metadata.creationTimestamp < $date) | .metadata.name' | \
  while read cm; do
    echo "Candidato a eliminar: $cm (creado: $(kubectl get cm $cm -n production -o jsonpath='{.metadata.creationTimestamp}'))"
    # Descomentar para eliminar realmente:
    # kubectl delete configmap $cm -n production
  done

El filtro por antigüedad es una capa de seguridad adicional: un ConfigMap reciente tiene más probabilidades de estar en uso aunque no aparezca referenciado en ningún Pod activo en ese momento.

CronJob para limpieza periódica automatizada

Una vez que el proceso manual está validado, automatizarlo con un CronJob es el paso natural:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: configmap-cleanup
  namespace: kube-system
spec:
  schedule: "0 2 * * 0"  # Domingos a las 2:00 AM
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: cleanup-sa
          containers:
          - name: cleanup
            image: bitnami/kubectl:latest
            command:
            - /bin/bash
            - -c
            - |
              echo "Iniciando limpieza de ConfigMaps..."

              for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
                echo "Revisando namespace: $ns"

                # ConfigMaps referenciados por workloads activos
                REFERENCED_CMS=$(kubectl get deploy,sts,ds -n $ns -o json | \
                  jq -r '.items[].spec.template.spec |
                  [.volumes[]?.configMap.name,
                   .containers[].env[]?.valueFrom.configMapKeyRef.name,
                   .containers[].envFrom[]?.configMapRef.name] |
                  .[] | select(. != null)' | sort -u)

                ALL_CMS=$(kubectl get cm -n $ns -o jsonpath='{.items[*].metadata.name}')

                for cm in $ALL_CMS; do
                  if [[ "$cm" == "kube-root-ca.crt" ]]; then
                    continue
                  fi

                  if ! echo "$REFERENCED_CMS" | grep -q "^$cm$"; then
                    echo "Eliminando ConfigMap huérfano: $cm en namespace: $ns"
                    kubectl delete cm $cm -n $ns
                  fi
                done
              done
          restartPolicy: OnFailure

El CronJob necesita los permisos adecuados para listar y borrar recursos en todos los namespaces:

# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
  name: cleanup-sa
  namespace: kube-system
---
# ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cleanup-role
rules:
- apiGroups: [""]
  resources: ["configmaps", "secrets", "namespaces"]
  verbs: ["get", "list", "delete"]
- apiGroups: ["apps"]
  resources: ["deployments", "statefulsets", "daemonsets"]
  verbs: ["get", "list"]
---
# ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: cleanup-binding
subjects:
- kind: ServiceAccount
  name: cleanup-sa
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: cleanup-role
  apiGroup: rbac.authorization.k8s.io

Integración en CI/CD

Para pipelines que despliegan con versionado explícito (etiquetas Git, versiones semánticas), la limpieza se puede integrar directamente como paso post-deploy:

# Ejemplo en GitLab CI
cleanup_old_configs:
  stage: post-deploy
  image: bitnami/kubectl:latest
  script:
    - |
      # Eliminar ConfigMaps de versiones antiguas tras despliegue exitoso
      kubectl delete configmap -n production \
        -l app=myapp,version!=${CI_COMMIT_TAG}

    - |
      # Conservar solo las últimas 3 versiones por timestamp
      kubectl get configmap -n production \
        -l app=myapp \
        --sort-by=.metadata.creationTimestamp \
        -o name | head -n -3 | xargs -r kubectl delete -n production
  only:
    - tags
  when: on_success

Este enfoque es muy limpio: cada despliegue exitoso elimina las versiones anteriores, manteniendo un historial mínimo pero suficiente para un rollback rápido.


Patrones avanzados para clusters en producción

OPA Gatekeeper: forzar etiquetado desde admisión

La forma más efectiva de garantizar que todos los ConfigMaps tengan etiquetas de ciclo de vida es hacerlo obligatorio en el momento de su creación, a nivel de admission webhook. OPA Gatekeeper permite definir estas políticas como código:

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: configmaprequiredlabels
spec:
  crd:
    spec:
      names:
        kind: ConfigMapRequiredLabels
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package configmaprequiredlabels

        violation[{"msg": msg}] {
          input.review.kind.kind == "ConfigMap"
          not input.review.object.metadata.labels["app"]
          msg := "Los ConfigMaps deben tener la etiqueta 'app' para gestión de ciclo de vida"
        }

        violation[{"msg": msg}] {
          input.review.kind.kind == "ConfigMap"
          not input.review.object.metadata.labels["owner"]
          msg := "Los ConfigMaps deben tener la etiqueta 'owner' para gestión de ciclo de vida"
        }

Con esta política activa, ningún ConfigMap sin las etiquetas app y owner puede crearse en el cluster. Esto resuelve el problema en origen.

Monitorización con Prometheus

Para clusters grandes, la visibilidad continua del estado de recursos huérfanos es fundamental. Un exporter personalizado puede exponer métricas para Prometheus:

apiVersion: v1
kind: ConfigMap
metadata:
  name: orphan-detection-exporter
data:
  script.sh: |
    #!/bin/bash
    # Exponer métricas para scraping de Prometheus
    while true; do
      echo "# HELP k8s_orphaned_configmaps Número de ConfigMaps huérfanos"
      echo "# TYPE k8s_orphaned_configmaps gauge"

      for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
        count=$(./detect-orphaned-configmaps.sh $ns | grep -c "Huérfano:")
        echo "k8s_orphaned_configmaps{namespace=\"$ns\"} $count"
      done

      sleep 300  # Actualizar cada 5 minutos
    done

Con las métricas disponibles en Prometheus, se puede configurar una alerta que notifique cuando el número de ConfigMaps huérfanos supera un umbral:

groups:
- name: kubernetes-housekeeping
  rules:
  - alert: ConfigMapsHuerfanosExcesivos
    expr: k8s_orphaned_configmaps > 20
    for: 24h
    labels:
      severity: warning
    annotations:
      summary: "Alto número de ConfigMaps huérfanos en {{ $labels.namespace }}"
      description: "El namespace {{ $labels.namespace }} tiene {{ $value }} ConfigMaps huérfanos"

Políticas de limpieza en flota multi-cluster

En entornos con múltiples clusters, gestionar las políticas de limpieza de forma centralizada es un requisito. Crossplane permite definir estas políticas como recursos declarativos que se aplican a toda la flota:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: cluster-cleanup-policy
spec:
  compositeTypeRef:
    apiVersion: platform.example.com/v1
    kind: ClusterCleanupPolicy
  resources:
    - name: cleanup-cronjob
      base:
        apiVersion: kubernetes.crossplane.io/v1alpha1
        kind: Object
        spec:
          forProvider:
            manifest:
              apiVersion: batch/v1
              kind: CronJob

Este patrón es especialmente valioso cuando el equipo de plataforma necesita garantizar que todos los clusters de la organización siguen las mismas políticas de mantenimiento, sin depender de que cada equipo recuerde configurarlo manualmente.


Plan de implementación

Acciones inmediatas (hoy)

  1. Auditar el estado actual. Ejecutar los scripts de detección en todos los namespaces relevantes. Guardar el resultado.
  2. Hacer backup. Exportar todos los ConfigMaps y Secrets a YAML antes de tocar nada.
  3. Eliminar los obvios. Recursos con más de 90 días de antigüedad sin ninguna referencia en ningún workload son candidatos seguros. Eliminar con revisión manual previa.

Corto plazo (1-4 semanas)

  1. Definir estándar de etiquetado. Acordar con los equipos qué etiquetas son obligatorias (app, owner, version, managed-by).
  2. Añadir owner references a los manifiestos existentes que no las tengan.
  3. Desplegar CronJobs de detección. Empezar solo con detección (sin borrado), para generar visibilidad.
  4. Integrar limpieza en CI/CD para los pipelines con versionado explícito.

Medio plazo (1-3 meses)

  1. Adoptar GitOps con pruning habilitado en ArgoCD o Flux.
  2. Implementar OPA Gatekeeper para forzar etiquetado en admisión.
  3. Configurar alertas en Prometheus sobre métricas de recursos huérfanos.
  4. Establecer quotas por namespace como mecanismo de presión secundario.

Ongoing

  • Revisar semanalmente los informes de Popeye o Kor en los namespaces clave.
  • Incluir revisión de recursos huérfanos en la planificación de sprints del equipo de plataforma.
  • Incorporar la gestión de ciclo de vida de ConfigMaps y Secrets en el onboarding de nuevos desarrolladores.

Conclusión

Los ConfigMaps y Secrets huérfanos son uno de esos problemas de Kubernetes que parecen menores hasta que no lo son: un cluster con años de historial puede tener miles de recursos obsoletos, consumiendo etcd, exponiendo credenciales que deberían estar rotadas, y generando confusión operativa en cada incidencia.

La buena noticia es que el problema tiene solución clara. No se trata de una operación de limpieza masiva única —eso solo resuelve el síntoma— sino de un enfoque por capas que combina:

  • Prevención estructural mediante owner references, etiquetado consistente y GitOps con pruning.
  • Detección continua con herramientas como Kor, Popeye o un exporter Prometheus personalizado.
  • Limpieza controlada con scripts auditados, backups previos, y criterios de antigüedad antes de borrar.

Un cluster bien mantenido no es el que nunca acumula residuos —es imposible evitarlo en entornos dinámicos— sino el que tiene mecanismos para detectarlos, contenerlos y eliminarlos de forma sistemática y segura.

La gestión del ciclo de vida de los recursos de configuración es una práctica de ingeniería de plataforma, no una tarea de limpieza puntual. Cuanto antes se integre en los procesos del equipo, menos deuda técnica operativa se acumula.

Gateway API Kubernetes 2026: Evaluación crítica del soporte por proveedor

Gateway API Kubernetes 2026: Evaluación crítica del soporte por proveedor

El Gateway API de Kubernetes ha dejado de ser una promesa para convertirse en el estándar presente para la gestión de tráfico. Con las APIs estables de Ingress NGINX ya deprecadas —señal inequívoca del cambio de paradigma—, los equipos de plataforma deben decidir qué proveedor de Gateway API adoptar. La página oficial de implementaciones lista numerosas opciones, pero la realidad muestra soporte fragmentado, estabilidad variable y lagunas significativas que afectan especialmente a estrategias multi-cluster.

Esta evaluación va más allá de los checklists de marketing para analizar el soporte práctico de Gateway API en los principales proveedores cloud, ingress controllers y service meshes. Se examina qué versiones son realmente production-ready, los problemas de interoperabilidad que nadie menciona en los keynotes, y qué deberías valorar antes de estandarizar tu infraestructura.


El espectro de madurez del Gateway API: de Experimental a Standard

No todos los recursos del Gateway API tienen el mismo nivel de madurez. El modelo de versionado único de esta API —con funcionalidades que progresan por los tracks Experimental, Standard y Extended— implica que el soporte por proveedor es inherentemente desigual. Una implementación puede tener soporte completo para los recursos estables Gateway y HTTPRoute mientras ofrece respaldo solo parcial o experimental para GRPCRoute o TCPRoute.

Esto genera un dilema fundamental: diseñar para el mínimo común denominador o aceptar restricciones específicas de cada proveedor. La decisión depende de mapear con precisión los requisitos de gestión de tráfico —HTTP, terminación TLS, gRPC, balanceo TCP/UDP— contra lo que cada proveedor ofrece en forma estable.

Soporte de la API core: los cimientos

La mayoría de los proveedores soportan ya las versiones v1 (GA) de los recursos fundamentales:

  • GatewayClass y Gateway: Soporte prácticamente universal para v1. Son los recursos del plano de control para provisionar y configurar load balancers.
  • HTTPRoute: Soporte universal para v1. Es el caballo de batalla del enrutamiento HTTP/HTTPS y el recurso más estable del ecosistema.

El soporte para otros tipos de rutas revela la fragmentación real:

  • GRPCRoute: Con frecuencia en beta o experimental. Crítico para arquitecturas de microservicios modernas, pero aún sin fiabilidad universal.
  • TCPRoute y UDPRoute: Soporte irregular. Algunos proveedores los implementan como beta; otros los ignoran directamente, forzando a usar anotaciones propias o recursos custom.
  • TLSRoute: Habitualmente vinculado a integraciones específicas de gestión de certificados, como cert-manager.

Esta heterogeneidad no es un bug —es consecuencia lógica de una especificación que avanza por capas—, pero tiene implicaciones prácticas enormes cuando planificas una migración desde Ingress o cuando diseñas una plataforma multi-tenant.


Análisis en profundidad por proveedor: la realidad de las implementaciones

AWS Elastic Kubernetes Service (EKS)

AWS dispone de un controlador oficial de Gateway API para EKS. El soporte es pragmático pero actualmente limitado en alcance.

Recursos soportados: GatewayClass, Gateway, HTTPRoute y GRPCRoute. GRPCRoute usa v1beta1, lo que indica que no ha alcanzado estabilidad GA. Este detalle importa: adoptar recursos en beta en producción es una decisión de riesgo que conviene documentar y monitorizar.

Infraestructura subyacente: El controlador mapea directamente a AWS Application Load Balancer (ALB) y Network Load Balancer (NLB). Es a la vez una fortaleza —aprovechas servicios AWS gestionados con todos sus SLAs— y una restricción, ya que heredas los límites de ALB/NLB en cuanto a funcionalidades y configuración.

Laguna crítica: No hay soporte para TCPRoute ni UDPRoute. Las cargas de trabajo que requieren balanceo TCP/UDP puro deben recurrir al tipo LoadBalancer de Kubernetes Service, o a un ingress controller adicional junto al controlador de Gateway API. El resultado es un modelo de gestión fragmentado: parte del tráfico bajo Gateway API, parte bajo primitivas legacy. Para equipos que buscan coherencia operativa, esto es un obstáculo real.

Observabilidad: La integración con CloudWatch y AWS X-Ray existe, pero la correlación entre recursos del Gateway API y trazas/métricas granulares requiere configuración adicional. No es plug-and-play.

Google Kubernetes Engine (GKE)

GKE ha integrado el soporte del Gateway API directamente en su oferta de Kubernetes gestionado, con foco en la infraestructura de load balancing global de Google Cloud.

El controlador GKE Gateway soporta recursos v1 y provisionará Google Cloud Global External Load Balancers. La integración con la gestión de certificados de Google y con Cloud CDN es una ventaja diferencial real —si ya vives en el ecosistema GCP. Para funcionalidades de enrutamiento avanzado, puede que necesites backend configs específicos de GCP, lo que introduce acoplamiento con la plataforma.

Punto fuerte: La capacidad de usar load balancers globales con anycast significa que un único Gateway puede servir tráfico optimizado por latencia a nivel mundial. Para aplicaciones con usuarios distribuidos geográficamente, esto tiene valor operativo considerable.

Punto débil: La portabilidad sufre. Las configuraciones que aprovechan integraciones específicas de GCP son difícilmente trasladables a otro proveedor o a on-premises sin reescribir la capa de política.

Azure Kubernetes Service (AKS)

AKS proporciona el Application Gateway Ingress Controller (AGIC) con soporte para Gateway API, mapeando sobre Azure Application Gateway. El soporte para tipos de ruta más recientes, como GRPCRoute, ha ido históricamente por detrás de otros proveedores.

La experiencia en AKS con Gateway API tiene una característica particular: la integración con Azure AD Workload Identity y el modelo de permisos de Azure puede simplificar ciertos flujos de autenticación, pero también introduce complejidad en el modelo de delegación del Gateway. GatewayClass, cuando apunta a recursos de Azure Application Gateway, hereda las limitaciones de configuración del WAF de Azure —positivo si ya tienes WAF habilitado, problemático si necesitas políticas que Application Gateway no soporta nativamente.

NGINX Ingress Controller

Con las APIs estables de Ingress deprecadas en favor de Gateway API, la implementación de Gateway API de NGINX es ya el camino principal hacia adelante para este controlador. Y aquí es donde se diferencia fundamentalmente de los proveedores cloud:

Al no estar limitado por el producto de load balancing propietario de ningún proveedor cloud, NGINX tiene soporte generalmente excelente para el rango completo de recursos tanto experimentales como estándar. Esto lo convierte en una opción sólida para despliegues híbridos o multi-cloud que requieren paridad de funcionalidades entre entornos.

Para plataformas on-premises o híbridas, NGINX es actualmente el candidato más maduro. La consistencia entre un clúster en AWS, otro en Azure y tus propios nodos on-prem es algo que los controladores cloud nativos simplemente no pueden ofrecer.

La contrapartida: asumes tú la carga operativa del controlador. No hay un equipo de SREs de cloud gestionándolo; eres tú quien gestiona las actualizaciones, los incidentes y la capacidad.

Kong Ingress Controller

Kong ha sido un adoptador temprano y comprensivo del Gateway API, con frecuencia implementando funcionalidades con rapidez. Aprovecha el extenso ecosistema de plugins de Kong Gateway, lo que puede ser un gran atractivo —pero también introduce vendor lock-in.

El modelo de extensión de Kong mediante plugins es potente: rate limiting, autenticación JWT, transformaciones de request/response, logging a sistemas externos. Todo esto es accesible desde el Gateway API mediante KongPlugin CRDs. El problema es que esas KongPlugin CRDs son específicas de Kong; si en el futuro necesitas migrar a otro controlador, esa capa de política no viene contigo.

Para organizaciones que ya tienen inversión en Kong Gateway y quieren unificar la gestión bajo Gateway API, es una elección natural. Para los demás, la dependencia del ecosistema Kong es un factor de riesgo a ponderar.


Lagunas críticas para arquitectos enterprise

Más allá de verificar qué recursos están soportados, existen lagunas más profundas que impactan los despliegues en producción, especialmente en entornos complejos.

1. Soporte multi-cluster y entornos híbridos

La especificación del Gateway API incluye conceptos como ReferenceGrant para enrutamiento cross-namespace y, en el horizonte, cross-cluster. En la práctica, muy pocos proveedores tienen una historia multi-cluster robusta y production-ready. La mayoría de las implementaciones asumen un único clúster como unidad de operación.

Para arquitecturas que abarcan múltiples clústeres —aislamiento por equipo, distribución geográfica, dominios de fallo—, la realidad actual obliga a:

  • Gestionar recursos Gateway separados por clúster.
  • Usar un load balancer global externo (DNS/GSLB) para distribuir tráfico entre los gateways de cada clúster.
  • Mantener configuraciones de política potencialmente divergentes entre clústeres.

Esto niega parte de la promesa de la API de proporcionar configuración unificada y abstraída. El mapa de ruta existe —el KEP de multi-cluster Gateway está en progreso—, pero en 2026 todavía no es algo en lo que puedas apoyarte en producción sin trabajo de integración significativo.

2. Policy Attachment y consistencia de extensiones

El Gateway API está diseñado para extensión mediante policy attachment: rate limiting, reglas WAF, autenticación. No existe ningún estándar sobre cómo se implementan estas políticas. Un proveedor puede usar un CRD RateLimitPolicy custom; otro confía en anotaciones; un tercero usa un motor de políticas separado.

El resultado es deriva de configuración masiva y vendor lock-in, rompiendo el objetivo de portabilidad que es uno de los argumentos centrales a favor del Gateway API. Si tienes diez equipos desplegando aplicaciones en tres proveedores distintos, mantener coherencia en las políticas de seguridad se convierte en un problema de gobernanza que el Gateway API en su estado actual no resuelve por sí solo.

La solución pragmática: define una capa de abstracción interna —plantillas Helm, módulos Terraform, o una plataforma interna— que traduzca políticas de negocio a configuración específica de proveedor. Más trabajo, pero te da control sobre la deriva.

3. Interfaces de observabilidad y depuración

Aunque la API define campos status, la riqueza de datos operacionales varía enormemente: logs de error detallados, métricas granulares vinculadas a recursos de la API, integración de trazas distribuidas. Algunos proveedores exponen integración profunda con su stack de monitorización; otros ofrecen visibilidad mínima.

Esta asimetría tiene consecuencias reales para los equipos SRE. En un incidente de producción, la diferencia entre un proveedor que expone métricas por HTTPRoute —latencia, tasa de error, distribución de códigos de respuesta— y uno que solo expone métricas agregadas del load balancer puede significar horas de diferencia en el tiempo de resolución.

Antes de comprometerte con un proveedor, valida específicamente:

  • ¿Hay métricas por recurso HTTPRoute o solo por gateway?
  • ¿Los logs incluyen el nombre del HTTPRoute que procesó la request?
  • ¿Hay integración nativa con OpenTelemetry o necesitas sidecars adicionales?
  • ¿Las alertas se pueden definir contra condiciones del Gateway API o solo contra métricas de infraestructura?

Framework de evaluación: preguntas para tu equipo

Antes de seleccionar un proveedor, trabaja este checklist técnico. No es exhaustivo, pero cubre los vectores de decisión que más frecuentemente se ignoran:

1. Requisitos de ruta
¿Necesitamos soporte estable solo para HTTP, o también para gRPC, TCP, UDP? ¿Es aceptable soporte en beta para rutas no-HTTP? ¿Con qué frecuencia añadiremos nuevos tipos de workload?

2. Modelo de infraestructura
¿Queremos un load balancer gestionado por el cloud (más sencillo, menos control) o un controlador basado en clúster (más portable, mayor carga operativa)? ¿Quién en el equipo va a operar y actualizar este controlador?

3. Futuro multi-cluster
¿Nuestra arquitectura es hoy de un único clúster pero probablemente crecerá? ¿El proveedor tiene un roadmap creíble para Gateway API multi-cluster? ¿Cómo de dolorosa sería la migración si necesitamos cambiar en 18 meses?

4. Necesidades de política
¿Qué políticas avanzadas son necesarias: autenticación, WAF, rate limiting? ¿Cómo las implementa el proveedor? ¿Podemos convivir con CRDs de política específicas del proveedor, o eso rompe nuestra estrategia de portabilidad?

5. Observabilidad y depuración
¿Qué logging, métricas y trazas se exponen para los recursos del Gateway API? ¿Se integran con nuestra plataforma de observabilidad existente (Grafana, Datadog, Dynatrace)?

6. Ruta de actualización
¿Cuál es el historial del proveedor en adoptar nuevas versiones del Gateway API? ¿Cuánto tardó en soportar v1 de HTTPRoute tras su GA? ¿Hay breaking changes entre versiones menores?


Recomendaciones estratégicas

Basadas en el panorama actual, estas son las rutas pragmáticas según contexto:

Para despliegues en un único cloud: empieza con el controlador nativo de tu proveedor cloud (AWS, GKE, AKS). Es el camino de menor resistencia y ofrece la mejor integración con el resto de servicios cloud (IAM, certificados, monitorización). Sé consciente de sus limitaciones específicas respecto a tipos de ruta no soportados y documenta los workarounds desde el principio.

Para híbrido, multi-cloud o on-premises: estandariza sobre un controlador portable basado en clúster, como Ingress-NGINX o Kong. La consistencia entre entornos te ahorrará complejidad operativa significativa, aunque implique renunciar a ciertas integraciones cloud-native. En un entorno donde tienes tres clouds y dos datacenters propios, la coherencia de configuración vale más que la integración óptima con cualquier proveedor individual.

Para proyectos greenfield: diseña tus aplicaciones y configuraciones solo contra los recursos estables v1 (Gateway, HTTPRoute). Trata cualquier uso de recursos beta/experimental como riesgo conocido que puede requerir refactoring posterior. Esta disciplina es especialmente importante si anticipas que el proyecto durará más de dos años —el panorama del Gateway API seguirá evolucionando.

Siempre ten un plan de salida: aísla los YAMLs de configuración del Gateway API de las políticas y anotaciones específicas del proveedor. Usa namespaces o directorios separados para la configuración portable vs. la específica. Esta modularidad hará que cualquier migración futura sea considerablemente menos dolorosa. La historia del Ingress API nos enseñó que las abstracciones de Kubernetes evolucionan —planifica para ello.


Conclusión

La evolución del Gateway API es un avance neto para el ecosistema Kubernetes, ofreciendo un modelo de expresividad muy superior al Ingress original. Sin embargo, en 2026 el panorama de proveedores sigue madurando. El soporte es amplio pero no profundo, y las lagunas críticas en gestión multi-cluster y portabilidad de políticas persisten.

El arquitecto que tenga éxito será el que elija proveedor basándose en cómo sus restricciones y capacidades específicas se alinean con los patrones de tráfico inmediatos de su organización y la estrategia de plataforma a largo plazo. La era de la configuración Gateway API universal —write-once-run-anywhere— no ha llegado todavía. Pero con una selección informada y deliberada de proveedor, y con la disciplina de diseñar contra los recursos estables del core, puedes construir una base sólida para cuando esa promesa se materialice.

La migración desde Ingress no es un evento puntual sino un proceso que se extiende durante meses o años en organizaciones grandes. Cuanto antes elijas deliberadamente en lugar de dejarte llevar por el camino de menor resistencia, más control tendrás sobre los trade-offs. Y en infraestructura de plataforma, el control sobre los trade-offs es exactamente lo que distingue una arquitectura robusta de una que genera deuda técnica silenciosa.


¿Estás evaluando Gateway API para tu plataforma Kubernetes? ¿Qué proveedor has elegido y por qué? Cuéntamelo en los comentarios o en LinkedIn.

Prometheus Alertmanager vs Grafana Alerting (2026): Arquitectura, diferencias y cuándo usar cada uno

Prometheus Alertmanager vs Grafana Alerting (2026): Arquitectura, diferencias y cuándo usar cada uno

En entornos de observabilidad en producción es habitual acabar con dos sistemas de alertas corriendo en paralelo: Prometheus Alertmanager gestionando las alertas basadas en métricas, y Grafana Alerting cubriendo el resto de necesidades de notificación. El resultado es el problema clásico de consolidación de alertas: páginas duplicadas al equipo de guardia, reglas de silencio dispersas entre dos plataformas, y ningún sistema con autoridad clara sobre el ciclo de vida de las alertas.

La elección entre ambas herramientas depende de tu ecosistema de datasources, la madurez de tu flujo GitOps y el enfoque de gestión de guardias. Los dos son productos maduros en producción, pero abordan el problema desde ángulos fundamentalmente distintos.

Visión general de la arquitectura

Prometheus Alertmanager: el componente independiente

Alertmanager opera como un componente dedicado y desacoplado. No evalúa reglas de alerta — esa responsabilidad recae en sistemas externos (Prometheus, Thanos Ruler, Cortex, Mimir Ruler), que evalúan expresiones PromQL y envían las alertas activas a la API de Alertmanager mediante HTTP POST.

Modelo de configuración: un único fichero YAML (alertmanager.yml) que contiene el árbol de routing, las definiciones de receivers, las reglas de inhibition y las plantillas de silencio. Sin base de datos, sin estado gestionado por interfaz — configuración puramente declarativa, controlable por versiones, que facilita la reproducibilidad total y los flujos GitOps.

Modelo de alta disponibilidad: múltiples instancias de Alertmanager forman un clúster basado en gossip usando el protocolo mesh, compartiendo el estado de silencios y notificaciones entre nodos. El failover ocurre sin notificaciones duplicadas ni pérdidas.

Grafana Alerting: la plataforma integrada

Grafana Alerting embebe el ciclo de vida completo de las alertas — evaluación de reglas, gestión de estado, routing y notificación — dentro del propio proceso de Grafana. Internamente utiliza un fork del Alertmanager de Prometheus para el routing y la notificación, aunque este detalle de implementación resulta invisible para el usuario.

Capacidad diferencial: evalúa reglas de alerta directamente, consultando cualquier datasource compatible — Prometheus, Loki, Elasticsearch, CloudWatch, PostgreSQL y más de 100 plugins de datasource. Las reglas, contact points, políticas de notificación y mute timings se almacenan en la base de datos de Grafana o se provisionan mediante YAML o API.

Alta disponibilidad: en entornos self-hosted se utiliza base de datos compartida y peer-discovery entre instancias de Grafana. Grafana Cloud proporciona HA totalmente gestionada.


Comparativa de características

CaracterísticaPrometheus AlertmanagerGrafana Alerting
DatasourcesSolo compatibles con PrometheusCualquier datasource de Grafana
Evaluación de reglasExterna (Prometheus/Ruler)Integrada
Árbol de routingYAML jerárquicoPolíticas de notificación con label matchers
Agrupacióngroup_by, group_wait, group_intervalControles equivalentes mediante políticas
InhibitionReglas nativas de inhibitionSoportado desde v10.3, menos flexible
SilenciosBasados en labels, limitados en el tiempoMute timings + silencios ad hoc
Canales de notificaciónEmail, Slack, PagerDuty, OpsGenie, webhook, etc.Los anteriores más Teams, Discord, Google Chat, LINE y más
PlantillasGo templatesGo templates + variables de Grafana
Multi-tenancyNo integradoNativo mediante organizaciones + RBAC
Alta disponibilidadClúster basado en gossipRespaldado por base de datos con peer discovery
Modelo de configuraciónFichero YAML únicoUI + API + YAML de provisionamiento
Compatibilidad GitOpsExcelenteRequiere ficheros de provisionamiento o Terraform
Servicio gestionadoGrafana Cloud (Mimir), Amazon Managed PrometheusGrafana Cloud

Puntos fuertes de Alertmanager

Configuración declarativa, nativa para GitOps

Toda la configuración reside en un único fichero YAML sin estado oculto en ninguna base de datos. Los cambios pasan por pull requests, siguen el mismo proceso de revisión de código que el resto de la infraestructura, y se despliegan mediante pipelines CI/CD exactamente igual que cualquier otro componente de infraestructura como código.

# alertmanager.yml — todo en un único fichero
global:
  resolve_timeout: 5m
  slack_api_url: "https://hooks.slack.com/services/T00/B00/XXX"

route:
  receiver: platform-team
  group_by: [alertname, cluster, namespace]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    - match:
        severity: critical
      receiver: pagerduty-oncall
      group_wait: 10s
    - match_re:
        team: "^(payments|checkout)$"
      receiver: payments-slack
      continue: true

receivers:
  - name: platform-team
    slack_configs:
      - channel: "#platform-alerts"
  - name: pagerduty-oncall
    pagerduty_configs:
      - service_key: ""
  - name: payments-slack
    slack_configs:
      - channel: "#payments-oncall"

inhibit_rules:
  - source_match:
      severity: critical
    target_match:
      severity: warning
    equal: [alertname, cluster]

Cada cambio es completamente auditable. Los rollbacks se reducen a un git revert. La herramienta amtool permite validar la configuración en los pipelines de CI antes de desplegar.

Ligero y con una única responsabilidad

Alertmanager hace una sola cosa: enrutar y entregar notificaciones. Sin dashboards, sin motor de consultas, sin plugins de datasource. Este diseño de responsabilidad única garantiza un consumo de recursos mínimo — una instancia modesta gestiona miles de alertas activas con pocos cientos de megabytes de memoria. El tiempo de arranque se mide en milisegundos; la carga operativa es prácticamente inexistente.

Inhibition y routing maduros

Las reglas de inhibition son elementos de primera clase en la configuración de Alertmanager. Suprimen warnings secundarios cuando ya hay alertas críticas activas, evitando las tormentas de alertas que tanto daño hacen a los equipos de guardia. El árbol de routing jerárquico con flags continue permite entregas matizadas: enviar simultáneamente al canal del equipo Y escalar a PagerDuty con agrupaciones distintas en cada nivel.

Alta disponibilidad probada en producción

El clúster HA basado en gossip lleva años demostrando su estabilidad. Tres réplicas detrás de un load balancer (o con service discovery de Kubernetes) proporcionan notificaciones fiables sin almacenamiento compartido. El protocolo gestiona la deduplicación automáticamente, sin configuración adicional.


Puntos fuertes de Grafana Alerting

Reglas de alerta multi-datasource

Este es el diferenciador más potente de Grafana Alerting. Las reglas pueden consultar Loki para detectar picos de errores en logs, CloudWatch para la utilización de recursos AWS, Elasticsearch para errores de aplicación, o PostgreSQL para métricas de negocio — todo desde un único sistema de alertas centralizado.

# Ejemplo de provisionamiento de regla de alerta en Grafana
# Alerta basada en tasa de errores en Loki
apiVersion: 1
groups:
  - orgId: 1
    name: application-errors
    folder: Production
    interval: 1m
    rules:
      - uid: loki-error-spike
        title: "Alta tasa de errores en el servicio de pagos"
        condition: C
        data:
          - refId: A
            datasourceUid: loki-prod
            model:
              expr: 'sum(rate({app="payment-service"} |= "ERROR" [5m]))'
          - refId: B
            datasourceUid: "__expr__"
            model:
              type: reduce
              expression: A
              reducer: last
          - refId: C
            datasourceUid: "__expr__"
            model:
              type: threshold
              expression: B
              conditions:
                - evaluator:
                    type: gt
                    params: [10]
        for: 5m
        labels:
          severity: warning
          team: payments

Alertmanager no puede hacer esto — recibe únicamente alertas pre-evaluadas y no tiene concepto de datasource.

Interfaz unificada para la gestión de alertas

Una sola interfaz gestiona la creación de reglas de alerta, su visualización, la configuración de políticas de notificación, los contact points y la gestión de silencios. Para equipos que prefieren la configuración visual frente a editar YAML, esto reduce significativamente la barrera de entrada. Los ingenieros pueden ver el estado de las alertas activas, el historial de evaluación y los caminos de routing de notificaciones directamente en el navegador, sin necesidad de conocer la sintaxis de configuración.

Multi-tenancy nativo y RBAC

El modelo de organizaciones de Grafana y el control de acceso basado en roles se extienden de forma natural al sistema de alertas. Distintos equipos gestionan sus propias reglas de alerta, contact points y políticas de notificación dentro del ámbito de su organización o carpeta, con aislamiento completo y sin visibilidad cruzada entre equipos. Conseguir esto con Alertmanager autónomo requeriría instancias separadas por tenant o el Alertmanager multi-tenant de Mimir.

Mute timings y planificación más rica

Mientras que Alertmanager soporta silencios (supresiones ad hoc, limitadas en el tiempo), Grafana Alerting añade los mute timings: ventanas temporales recurrentes que suprimen notificaciones. Esto permite mantenimientos programados, alertas solo en horario laboral, o la supresión de alertas no críticas los fines de semana. Con Alertmanager, gestionar estos escenarios requiere herramientas externas o la creación manual de silencios recurrentes.

Grafana Cloud como opción gestionada

Para equipos que quieren evitar la gestión de infraestructura de alertas self-hosted, Grafana Cloud proporciona Grafana Alerting completamente gestionado con HA, persistencia de estado y entrega de notificaciones. El stack de Grafana Cloud incluye también Mimir Alertmanager gestionado, que permite alertas nativas de Prometheus mientras se aprovecha la infraestructura administrada.


Cuándo usar Prometheus Alertmanager

Alertmanager es la opción óptima cuando:

  • Tu stack de métricas es nativo de Prometheus. Todas las reglas de alerta usan expresiones PromQL evaluadas por Prometheus, Thanos Ruler o Mimir Ruler. No existe ningún valor añadido en enrutar las notificaciones a través de Grafana.
  • GitOps es innegociable. Cada cambio de infraestructura requiere revisión mediante pull request y configuración completamente declarativa. El modelo de fichero único de Alertmanager simplifica enormemente la gestión frente al estado respaldado por base de datos de Grafana. Herramientas como amtool permiten validar la configuración en los pipelines de CI.
  • Necesitas routing granular con inhibition. Árboles de routing complejos con múltiples niveles de agrupación, reglas de inhibition y flags continue se expresan de forma más natural en el formato YAML de Alertmanager. Esta lógica lleva años estable y documentada.
  • Ejecutas microservicios con routing por equipo. Cuando cada equipo es propietario de su subárbol de routing con lógica compleja, el modelo jerárquico de Alertmanager escala mejor que la configuración guiada por interfaz gráfica. Los equipos gestionan su sección de configuración mediante CODEOWNERS en Git.
  • Quieres mínima carga operativa. Alertmanager es un binario único con requisitos de recursos mínimos — sin backups de base de datos, migraciones ni actualizaciones de frameworks de interfaz.

Cuándo usar Grafana Alerting

Grafana Alerting es la opción óptima cuando:

  • Alertas sobre más que métricas Prometheus. Reglas de alerta basadas en logs de Loki, queries de Elasticsearch, métricas de CloudWatch o consultas a bases de datos requieren Grafana Alerting. La alternativa — ejecutar herramientas de alertas separadas por datasource — es peor en todos los sentidos.
  • Tu equipo prefiere configuración visual. No todos los ingenieros quieren editar árboles de routing en YAML. Si tu organización valora las interfaces visuales para la gestión de alertas y contact points, la interfaz de Grafana proporciona ventajas de productividad significativas.
  • Usas Grafana Cloud. Si ya estás en Grafana Cloud, usar el sistema de alertas integrado es el camino de menor resistencia: HA, entrega gestionada de notificaciones y experiencia unificada sin infraestructura adicional.
  • El multi-tenancy es un requisito. Varios equipos necesitan configuraciones de alertas aisladas con RBAC. El modelo nativo de organizaciones y carpetas de Grafana es significativamente más sencillo que ejecutar instancias Alertmanager por tenant.
  • Necesitas mute timings para ventanas de mantenimiento recurrentes. Equipos que suprimen alertas regularmente durante ventanas programadas (despliegues, procesamiento por lotes, supresión de no-críticos en fin de semana) encontrarán los mute timings de Grafana mucho más ergonómicos que gestionar silencios recurrentes manualmente.

Ejecutar ambos a la vez: el patrón híbrido

Muchos entornos de producción ejecutan deliberadamente ambos sistemas con límites de responsabilidad claramente definidos.

Arquitectura híbrida habitual

  • Prometheus Alertmanager gestiona todas las alertas basadas en métricas. Reglas PromQL evaluadas por Prometheus o rulers de almacenamiento a largo plazo (Thanos, Mimir). Alertmanager es propietario del routing, la agrupación y la notificación.
  • Grafana Alerting gestiona las alertas no-Prometheus: alertas basadas en logs de Loki, métricas de negocio desde datasources SQL, reglas de correlación multi-datasource.

Los límites de propiedad deben estar documentados y ser explícitos:

# Límites de propiedad para alertas híbridas

# Prometheus Alertmanager gestiona:
#   - Todas las reglas de alerta basadas en PromQL
#   - Alertas de infraestructura (node, kubelet, etcd, CoreDNS)
#   - Alertas SLO/SLI de aplicaciones basadas en métricas

# Grafana Alerting gestiona:
#   - Reglas de alerta basadas en logs (Loki, Elasticsearch)
#   - Alertas de métricas de negocio (datasources SQL)
#   - Reglas de correlación multi-datasource
#   - Alertas para equipos que prefieren gestión por interfaz

# Compartido:
#   - Contact points / receivers usan los mismos canales Slack y servicios PagerDuty
#   - Las rotaciones de guardia se gestionan externamente (PagerDuty, Grafana OnCall)

Ambos sistemas entregan a los mismos canales de notificación. La disciplina crítica: asegurarse de que los silencios y las ventanas de mantenimiento se aplican en los dos sistemas cuando sea necesario. Este es el principal coste operativo del enfoque híbrido — si silencias en uno, no olvides silenciar en el otro.

Grafana como visualizador de Alertmanager

Incluso usando Alertmanager en exclusiva para el routing, Grafana puede actuar como visualizador de solo lectura. Grafana soporta nativamente datasources externos de Alertmanager, lo que permite ver las alertas activas, los silencios vigentes y los grupos de alertas dentro de la interfaz de Grafana — visibilidad operativa completa sin mover la lógica de alertas.

# Provisionamiento de datasource de Grafana para Alertmanager externo
apiVersion: 1
datasources:
  - name: Alertmanager
    type: alertmanager
    url: http://alertmanager.monitoring.svc:9093
    access: proxy
    jsonData:
      implementation: prometheus

Este patrón es especialmente útil en equipos donde los SREs gestionan Alertmanager mediante GitOps, pero los desarrolladores prefieren consumir el estado de las alertas desde la interfaz de Grafana sin necesidad de acceso directo a Alertmanager.


Consideraciones de migración

Migrar de Alertmanager a Grafana Alerting

  • Conversión de reglas: las reglas de alerta y recording basadas en PromQL en los ficheros de reglas de Prometheus deben recrearse como reglas de alerta de Grafana. Grafana proporciona herramientas de migración para importar reglas en formato Prometheus, aunque las expresiones complejas requieren ajuste manual.
  • Traducción del árbol de routing: el árbol de routing jerárquico de Alertmanager se mapea a las políticas de notificación de Grafana, pero la semántica difiere. El comportamiento del flag continue y las rutas por defecto pueden comportarse de forma distinta — prueba el routing exhaustivamente antes de dar por buena la migración.
  • Migración de silencios e inhibition: los silencios activos son efímeros y no requieren migración. Las reglas de inhibition deben recrearse en el formato de Grafana. Las ventanas de mantenimiento recurrentes se convierten en mute timings.
  • Ejecuta ambos en paralelo primero: la estrategia de migración más segura consiste en ejecutar ambos sistemas durante dos a cuatro semanas, enviando notificaciones desde los dos, y cortando cuando haya confianza suficiente en Grafana. Acepta el ruido temporal de alertas duplicadas — es mucho menos costoso que perder páginas críticas durante la migración.

Migrar de Grafana Alerting a Alertmanager

  • Limitación de datasources: solo migran las alertas con datasources compatibles con Prometheus. Las alertas que consultan Loki, Elasticsearch o datasources SQL no tienen equivalente en Alertmanager — requieren soluciones alternativas o simplemente quedan fuera del alcance de la migración.
  • Exportación de reglas: exporta las reglas de alerta de Grafana y conviértelas a ficheros de reglas en formato Prometheus. La API de Grafana (GET /api/v1/provisioning/alert-rules) proporciona salida estructurada apta para transformación mediante scripts.
  • Mapeo de contact points: mapea los contact points de Grafana a receivers de Alertmanager — formatos distintos, conceptos equivalentes.
  • Pérdida de estado: Alertmanager no hereda el historial de evaluación de Grafana. Empiezas desde cero. Planifica un periodo breve en el que algunas alertas pueden re-dispararse mientras Prometheus evalúa reglas que antes gestionaba Grafana.

Marco de decisión

Árbol de decisión rápido:

Punto de partida:
│
├── ¿Alertas sobre datasources no-Prometheus (Loki, ES, SQL, CloudWatch)?
│   ├── SÍ → Grafana Alerting (al menos para esos datasources)
│   └── NO ↓
│
├── ¿GitOps / configuración declarativa es un requisito innegociable?
│   ├── SÍ → Alertmanager
│   └── NO ↓
│
├── ¿Necesitas multi-tenancy con RBAC?
│   ├── SÍ → Grafana Alerting (o Mimir Alertmanager)
│   └── NO ↓
│
├── ¿Estás en Grafana Cloud?
│   ├── SÍ → Grafana Alerting (camino de menor resistencia)
│   └── NO ↓
│
└── Por defecto → Alertmanager (más simple, más ligero, más probado)

La respuesta honesta de muchos equipos es “ambos”: Alertmanager para las métricas nativas de Prometheus, Grafana Alerting para todo lo demás. Es una arquitectura perfectamente válida siempre que los límites de propiedad estén documentados y el equipo de guardia sepa exactamente dónde buscar y, sobre todo, dónde silenciar.


Diferencias clave que a menudo se pasan por alto

Más allá de las características que aparecen en cualquier comparativa, hay diferencias operativas que solo se descubren trabajando con ambas herramientas en producción.

El estado de evaluación es radicalmente distinto

Con Alertmanager, el estado de evaluación de las reglas vive en Prometheus (o en Thanos/Mimir Ruler). Alertmanager solo ve alertas que ya han disparado — nunca las reglas en sí ni su historial de evaluación. Grafana Alerting, en cambio, expone el historial completo de evaluación de cada regla: cuántas veces ha disparado, cuándo ha entrado en estado pending, cuándo ha resuelto. Esta visibilidad es enormemente útil para depurar reglas que disparan de forma intermitente o con latencia.

El debugging del routing tiene complejidades distintas

Alertmanager proporciona amtool routing test para probar cómo se enrutaría una alerta hipotética. Es una herramienta poderosa para validar cambios de configuración antes de desplegarlos. Grafana Alerting ofrece una vista similar en la interfaz, pero la falta de herramienta CLI equivalente puede ser un obstáculo para equipos con workflows automatizados de validación.

La gestión de silencio en producción es distinta

Con Alertmanager, crear un silencio en producción durante un incidente requiere acceso a la interfaz web o a la API de Alertmanager. En entornos con acceso restringido, esto puede ser un cuello de botella. Grafana Alerting expone la gestión de silencios en la misma interfaz que los dashboards, lo que suele estar más accesible para desarrolladores que no tienen acceso directo a la infraestructura de monitorización.

Los templates de notificación tienen alcances distintos

Ambas herramientas usan Go templates, pero el contexto de datos disponible difiere. Alertmanager expone el conjunto completo de labels y annotations de la alerta, pero nada más. Grafana Alerting permite incluir imágenes de paneles en las notificaciones — un captura de pantalla del panel relevante adjunta a la alerta en Slack o PagerDuty. Esta funcionalidad, aunque secundaria, puede tener un impacto real en la velocidad de diagnosis durante incidentes.


Alertas en Kubernetes: el caso específico

Para monitoring de Kubernetes específicamente, el kube-prometheus-stack (que incluye Prometheus, Alertmanager y un conjunto comprehensivo de reglas de alerta pre-construidas) sigue siendo el estándar de la industria. Estas reglas usan PromQL y están diseñadas para Alertmanager.

Desplegar kube-prometheus-stack hace de Alertmanager la elección directa para alertas basadas en métricas. Las reglas del stack cubren nodos, kubelet, etcd, CoreDNS, recursos de Kubernetes y métricas de workloads. Recrear estas reglas en Grafana Alerting manualmente es trabajo sin valor añadido claro.

Si además necesitas alertas sobre logs (via Loki) o métricas de negocio desde otras fuentes, la arquitectura recomendada es:

  1. Mantener kube-prometheus-stack con Alertmanager para todas las alertas de infraestructura y SLO/SLI basados en métricas.
  2. Añadir Grafana Alerting únicamente para los datasources no-Prometheus.
  3. Documentar explícitamente qué sistema es propietario de qué tipo de alerta.

Esta separación evita duplicaciones y mantiene la base de reglas de kube-prometheus-stack actualizada con el mínimo esfuerzo.


Preguntas frecuentes

¿Cuál es la diferencia entre Alertmanager y Grafana Alerting?

Prometheus Alertmanager es un motor independiente de routing de notificaciones que recibe alertas ya evaluadas desde Prometheus y las entrega a Slack, PagerDuty, email u otros receivers. No evalúa reglas de alerta. Grafana Alerting es una plataforma de alertas integrada dentro de Grafana que tanto evalúa reglas de alerta (consultando cualquier datasource compatible) como gestiona el routing de notificaciones. Alertmanager usa exclusivamente configuración YAML; Grafana Alerting ofrece UI, API y provisionamiento por ficheros. La diferencia fundamental: Alertmanager gestiona únicamente las fases de routing y notificación, mientras que Grafana Alerting gestiona el ciclo de vida completo desde la evaluación de la query hasta la notificación.

¿Puede Grafana Alerting reemplazar a Prometheus Alertmanager?

Sí, en muchos casos. Grafana Alerting evalúa reglas PromQL directamente contra datasources Prometheus, por lo que no es estrictamente necesaria una instancia separada de Alertmanager. Sin embargo, Alertmanager sigue siendo superior en ciertos escenarios: entornos muy orientados a GitOps, equipos que necesitan las reglas de inhibition maduras de Alertmanager, o arquitecturas donde la evaluación de reglas Prometheus ocurre externamente (Thanos Ruler, Mimir Ruler) con Alertmanager ya en el pipeline. Si tu único datasource es Prometheus y priorizas la configuración declarativa, Alertmanager sigue siendo más simple y ligero.

¿Es el Grafana Alertmanager lo mismo que el Prometheus Alertmanager?

No exactamente. Grafana Alerting usa internamente un fork del código de Prometheus Alertmanager para el routing de notificaciones, pero no son el mismo producto. El “Alertmanager” de Grafana visible en la interfaz es un componente embebido y gestionado con una interfaz de configuración diferente (políticas de notificación, contact points, mute timings) frente al Prometheus Alertmanager autónomo (árbol de routing, receivers, reglas de inhibition en YAML). Grafana también puede conectarse a un Prometheus Alertmanager externo como datasource, lo que crea confusión terminológica adicional. “Grafana Alertmanager” suele referirse al motor de routing embebido dentro de Grafana Alerting.

¿Cuáles son las mejores alternativas a Prometheus Alertmanager?

La alternativa más directa es Grafana Alerting, que recibe y enruta alertas de Prometheus mientras soporta otros datasources. Otras opciones: Grafana OnCall para gestión de guardias y escalación (usado habitualmente junto a Alertmanager, no en sustitución), PagerDuty u Opsgenie como plataformas gestionadas de respuesta a incidentes que reciben alertas directamente, Keep como plataforma open-source de gestión AIOps de alertas, y Mimir Alertmanager para entornos multi-tenant que ejecutan Grafana Mimir. La elección depende de si necesitas un reemplazo de Alertmanager (routing/notificación) o herramientas complementarias para escalación y respuesta a incidentes.

¿Debo usar alertas de Prometheus o de Grafana para monitoring de Kubernetes?

Para monitoring de Kubernetes específicamente, el kube-prometheus-stack (incluyendo Prometheus, Alertmanager y reglas de alerta pre-construidas y comprehensivas) sigue siendo el estándar de la industria. Estas reglas usan PromQL y están diseñadas para Alertmanager. Desplegar kube-prometheus-stack hace de Alertmanager la elección directa para alertas basadas en métricas. Añade Grafana Alerting si también necesitas alertas sobre logs (via Loki) o datasources no-métricos. Para monitoring de Kubernetes, combinar reglas Prometheus con routing Alertmanager es el enfoque más maduro y mejor soportado por la comunidad.

¿Qué ocurre si configuro mal el routing en Alertmanager y nadie recibe las páginas críticas?

Este es el riesgo real de la flexibilidad de Alertmanager. Un árbol de routing mal configurado puede hacer que alertas críticas caigan en un receiver que no notifica a nadie. La mitigación: usar amtool routing test en los pipelines de CI para validar que las alertas conocidas se enrutan correctamente antes de cada merge. Define un receiver de fallback explícito en la ruta por defecto que siempre notifique al canal correcto. Y prueba con alertas reales en staging antes de cortar a producción.


Reflexión final

El debate Alertmanager vs Grafana Alerting no es sobre qué herramienta es superior en abstracto — es sobre qué encaja mejor en tu contexto operativo concreto. Alertmanager es más simple, más ligero y más amigable para GitOps. Grafana Alerting es más versátil, más accesible para equipos orientados a la interfaz, y la única opción viable para alertas multi-datasource. Ejecutar ambos es perfectamente válido cuando los límites están claros.

El peor escenario no es elegir la herramienta “equivocada” — es acabar ejecutando ambas accidentalmente con cobertura solapada, notificaciones duplicadas y ninguna autoridad clara de propiedad. Sea cual sea tu elección: documenta la decisión, define los límites de propiedad, y asegúrate de que tu equipo de guardia sabe exactamente dónde silenciar alertas a las 3 de la madrugada. Eso es lo que marca la diferencia entre un sistema de alertas que funciona y uno que desgasta.

Istio ServiceEntry explicado: servicios externos, DNS y control de tráfico

Istio ServiceEntry explicado: servicios externos, DNS y control de tráfico

Todo clúster Kubernetes en producción se comunica con el exterior. Tus servicios llaman a APIs de pago, se conectan a bases de datos gestionadas, envían eventos a plataformas SaaS y contactan con sistemas legacy que nunca van a correr dentro del mesh. Por defecto, Istio permite que todo el tráfico de salida fluya libremente, o bien lo bloquea completamente si configuras outboundTrafficPolicy a REGISTRY_ONLY. Ninguno de los dos extremos te da lo que realmente necesitas: acceso selectivo, observable y controlado por políticas a los servicios externos.

Eso es exactamente lo que resuelve Istio ServiceEntry. Registra endpoints externos en el registro interno del mesh para que los sidecars de Envoy puedan aplicar las mismas capacidades de gestión de tráfico, seguridad y observabilidad a las llamadas salientes que ya disfrutas para el tráfico east-west. Sin proxies adicionales, sin egress gateways para el caso básico: solo un recurso YAML que le dice al mesh «este servicio externo existe, y así es como llegar a él».

En esta guía recorro todos los campos del spec de ServiceEntry, explico los cuatro modos de resolución DNS con casos de uso reales, y muestro patrones listos para producción con APIs externas, bases de datos, servicios TCP y cargas de trabajo legacy. También cubrimos cómo combinar ServiceEntry con DestinationRule y VirtualService para obtener circuit breaking, reintentos, connection pooling e incluso sticky sessions para dependencias externas.

Qué es un ServiceEntry

Istio mantiene un registro interno de servicios que fusiona los Kubernetes Services con cualquier entrada adicional que declares. Cuando un sidecar proxy necesita decidir cómo enrutar una petición, consulta ese registro. Los servicios dentro del mesh se registran automáticamente. Los servicios fuera del mesh no lo hacen, a menos que crees un ServiceEntry.

Un ServiceEntry es un recurso personalizado que añade una entrada al registro de servicios del mesh. Una vez registrado, el servicio externo se convierte en un ciudadano de primera clase: Envoy genera clusters, routes y listeners para él, lo que significa que obtienes métricas (istio_requests_total), access logs, trazas distribuidas, originación mTLS, reintentos, timeouts y circuit breaking. El conjunto completo de funcionalidades de Istio.

Sin un ServiceEntry, el tráfico de salida hacia un host externo pasa como una conexión TCP en bruto (en modo ALLOW_ANY) sin telemetría alguna, o se descarta con un 502/503 (en modo REGISTRY_ONLY). Ambos resultados son indeseables en producción. El ServiceEntry cierra esa brecha.

Anatomía de ServiceEntry: todos los campos explicados

Veamos un ServiceEntry completo y desglosemos cada campo.

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-api
  namespace: production
spec:
  hosts:
    - api.stripe.com
  location: MESH_EXTERNAL
  ports:
    - number: 443
      name: https
      protocol: TLS
  resolution: DNS
  exportTo:
    - "."
    - "istio-system"

hosts

Lista de hostnames asociados al servicio. Para servicios externos, suele ser el nombre DNS que utiliza tu aplicación (p. ej., api.stripe.com). Para servicios con protocolos HTTP, el campo hosts se compara con la cabecera HTTP Host. Para protocolos no HTTP o servicios sin nombre DNS, puedes usar un hostname sintético y combinarlo con addresses o endpoints estáticos.

addresses

Direcciones IP virtuales opcionales asociadas al servicio. Son útiles para servicios TCP en los que quieres asignar una VIP que el sidecar interceptará. No son necesarias para servicios HTTP/HTTPS que usan enrutamiento basado en hostname.

ports

Los puertos en los que el servicio externo está expuesto. Cada puerto requiere number, name y protocol. El protocolo importa: configurarlo como TLS le dice a Envoy que haga enrutamiento basado en SNI sin terminar TLS. Configurarlo como HTTPS implica HTTP sobre TLS. Para bases de datos, usarás típicamente TCP.

location

MESH_EXTERNAL o MESH_INTERNAL. Usa MESH_EXTERNAL para servicios fuera de tu clúster (APIs de terceros, bases de datos gestionadas). Usa MESH_INTERNAL para servicios dentro de tu infraestructura que no forman parte del mesh: por ejemplo, VMs corriendo en el mismo VPC que no tienen sidecar, o un Kubernetes Service en un namespace sin inyección de sidecar habilitada. La location afecta a cómo se aplica mTLS y cómo se etiquetan las métricas.

resolution

Determina cómo el sidecar resuelve las direcciones de los endpoints. Es el campo más crítico y le dedico la siguiente sección en su totalidad. Opciones: NONE, STATIC, DNS, DNS_ROUND_ROBIN.

endpoints

Lista explícita de endpoints de red. Obligatorio cuando resolution es STATIC. Opcional con resolución DNS para proporcionar labels o información de localidad. Cada endpoint puede tener address, ports, labels, network, locality y weight.

exportTo

Controla la visibilidad de este ServiceEntry entre namespaces. Usa "." para el namespace actual únicamente, "*" para todos los namespaces. En clústeres multi-equipo, restringe los exports para evitar la contaminación de namespaces.

Modos de resolución DNS: NONE vs STATIC vs DNS vs DNS_ROUND_ROBIN

El campo resolution determina cómo Envoy descubre las direcciones IP detrás del servicio. Equivocarse con este campo es la causa número uno de errores de configuración en ServiceEntry. A continuación, una explicación clara.

ResoluciónFuncionamientoMejor para
NONEEnvoy usa la IP de destino original de la conexión. Sin consulta DNS por parte del proxy.Entradas wildcard, escenarios pass-through, servicios donde la aplicación ya resolvió la IP.
STATICEnvoy enruta a las IPs listadas en el campo endpoints. Sin DNS.Servicios con IPs estables y conocidas (p. ej., bases de datos on-prem, VMs con IPs fijas).
DNSEnvoy resuelve el hostname en el momento de la conexión y crea un endpoint por IP devuelta. Usa DNS asíncrono con health checking por IP.APIs externas detrás de load balancers, bases de datos gestionadas con endpoints DNS (RDS, CloudSQL).
DNS_ROUND_ROBINEnvoy resuelve el hostname y usa un único endpoint lógico, rotando entre las IPs devueltas. Sin health checking por IP.Servicios externos simples, servicios donde no necesitas circuit breaking por endpoint.

Cuándo usar NONE

Usa NONE cuando quieras registrar un rango de IPs externas o hosts wildcard sin que Envoy realice ninguna resolución de direcciones. Esto es habitual para políticas de egress amplias: «permitir tráfico a *.googleapis.com en el puerto 443». Envoy simplemente reenvía el tráfico a la IP que la aplicación resolvió via kube-dns. El inconveniente: Envoy tiene capacidad limitada para aplicar políticas por endpoint.

Cuándo usar STATIC

Usa STATIC cuando el servicio externo tiene direcciones IP conocidas y estables que raramente cambian. Esto evita dependencias DNS por completo. Defines las IPs en la lista de endpoints. Caso de uso clásico: una base de datos Oracle legacy en una IP fija en tu centro de datos.

Cuándo usar DNS

Usa DNS para la mayoría de integraciones con APIs externas. Envoy realiza resolución DNS asíncrona y crea un endpoint en el cluster para cada IP devuelta. Esto habilita health checking y circuit breaking por endpoint, algo crítico para la fiabilidad en producción. Este es el modo que quieres para servicios como api.stripe.com o el endpoint de tu instancia RDS.

Cuándo usar DNS_ROUND_ROBIN

Usa DNS_ROUND_ROBIN cuando el hostname externo devuelve muchas IPs y no necesitas circuit breaking por IP. Envoy trata todas las IPs resueltas como un único endpoint lógico y hace round-robin entre ellas. Es más ligero que el modo DNS y evita crear un número elevado de endpoints en la configuración de clusters de Envoy.

Patrones prácticos

Patrón 1: API HTTP externa (api.stripe.com)

El patrón de ServiceEntry más habitual. Tu aplicación llama a una API HTTPS de terceros. Quieres telemetría de Istio y, opcionalmente, reintentos y timeouts.

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: stripe-api
  namespace: payments
spec:
  hosts:
    - api.stripe.com
  location: MESH_EXTERNAL
  ports:
    - number: 443
      name: tls
      protocol: TLS
  resolution: DNS

El protocolo es TLS, no HTTPS. Como tu aplicación inicia el handshake TLS directamente, Envoy gestiona esto como TLS opaco mediante enrutamiento basado en SNI. Si estuvieras terminando TLS en el sidecar y haciendo originación TLS via un DestinationRule, configurarías el protocolo como HTTP y gestionarías el upgrade por separado. Pero para la mayoría de APIs externas, deja que la aplicación gestione su propio TLS.

Patrón 2: Base de datos gestionada externa (RDS / CloudSQL)

Las bases de datos gestionadas exponen un endpoint DNS que resuelve a una o varias IPs. Durante un failover, el registro DNS cambia. Necesitas que Envoy respete los TTLs DNS y enrute al primary actual.

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: orders-database
  namespace: orders
spec:
  hosts:
    - orders-db.abc123.us-east-1.rds.amazonaws.com
  location: MESH_EXTERNAL
  ports:
    - number: 5432
      name: postgres
      protocol: TCP
  resolution: DNS

Para servicios TCP, Envoy no puede usar cabeceras HTTP para enrutar, por lo que depende de coincidencia basada en IP. El modo de resolución DNS garantiza que Envoy resuelva periódicamente el hostname y actualice su lista de endpoints. Esto es crítico para escenarios de failover RDS Multi-AZ, donde el endpoint DNS apunta a una nueva IP.

Patrón 3: Servicio interno legacy no incluido en el mesh

Tienes un servicio de monitorización corriendo en un conjunto de VMs con IPs conocidas dentro de tu VPC. No forma parte del mesh, pero tus servicios dentro del mesh necesitan comunicarse con él.

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: legacy-monitoring
  namespace: observability
spec:
  hosts:
    - legacy-monitoring.internal
  location: MESH_INTERNAL
  ports:
    - number: 8080
      name: http
      protocol: HTTP
  resolution: STATIC
  endpoints:
    - address: 10.0.5.10
    - address: 10.0.5.11
    - address: 10.0.5.12

Diferencias clave: location es MESH_INTERNAL porque el servicio vive dentro de tu red, y resolution es STATIC porque conocemos las IPs. El hostname legacy-monitoring.internal es sintético: tu aplicación lo usa, y el DNS proxy de Istio (o una entrada de CoreDNS) lo resuelve a uno de los endpoints listados.

Patrón 4: Servicios TCP con múltiples puertos

Algunos servicios externos exponen múltiples puertos TCP: por ejemplo, un clúster Elasticsearch con puertos de datos (9200) y de transporte (9300).

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-elasticsearch
  namespace: search
spec:
  hosts:
    - es.example.com
  location: MESH_EXTERNAL
  ports:
    - number: 9200
      name: http
      protocol: HTTP
    - number: 9300
      name: transport
      protocol: TCP
  resolution: DNS

Cada puerto obtiene su propia configuración de listener en Envoy. El puerto HTTP se beneficia de telemetría completa de capa 7 y gestión de tráfico. El puerto TCP obtiene métricas de capa 4 y políticas a nivel de conexión.

Combinando ServiceEntry con DestinationRule

Un ServiceEntry por sí solo registra el servicio externo. Para aplicar políticas de tráfico —connection pooling, circuit breaking, originación TLS, load balancing— lo combinas con un DestinationRule. Aquí es donde la cosa se pone interesante.

Connection pooling y circuit breaking

Las APIs externas tienen rate limits. Tu base de datos gestionada tiene un número máximo de conexiones. Proteger estas dependencias a nivel del mesh previene fallos en cascada.

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: stripe-api
  namespace: payments
spec:
  hosts:
    - api.stripe.com
  location: MESH_EXTERNAL
  ports:
    - number: 443
      name: tls
      protocol: TLS
  resolution: DNS
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: stripe-api-dr
  namespace: payments
spec:
  host: api.stripe.com
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 50
        connectTimeout: 5s
      http:
        h2UpgradePolicy: DO_NOT_UPGRADE
        maxRequestsPerConnection: 100
    outlierDetection:
      consecutive5xxErrors: 3
      interval: 30s
      baseEjectionTime: 60s
      maxEjectionPercent: 100

Esta configuración limita las conexiones de salida a Stripe a 50, establece un timeout de conexión de 5 segundos y expulsa los endpoints que devuelvan 3 errores 5xx consecutivos. En producción, esto evita que una API de terceros degradada consuma todos tus slots de conexión y provoque un efecto dominó en tus servicios.

Originación TLS

A veces tu aplicación habla HTTP en claro, pero el servicio externo requiere HTTPS. En lugar de modificar el código de la aplicación, puedes delegar la originación TLS al sidecar.

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-api
  namespace: default
spec:
  hosts:
    - api.external-service.com
  location: MESH_EXTERNAL
  ports:
    - number: 80
      name: http
      protocol: HTTP
    - number: 443
      name: https
      protocol: TLS
  resolution: DNS
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: external-api-tls
  namespace: default
spec:
  host: api.external-service.com
  trafficPolicy:
    portLevelSettings:
      - port:
          number: 443
        tls:
          mode: SIMPLE

Tu aplicación envía HTTP al puerto 80. Un VirtualService (que se muestra en la siguiente sección) redirige eso al puerto 443. El DestinationRule inicia TLS hacia el endpoint externo. La aplicación nunca sabe que TLS ocurrió.

Combinando ServiceEntry con VirtualService

VirtualService te da gestión de tráfico de capa 7 para servicios externos: reintentos, timeouts, inyección de fallos, enrutamiento basado en cabeceras y traffic shifting. Esto es invaluable cuando migras entre proveedores de API o necesitas políticas de resiliencia para dependencias externas poco fiables.

Reintentos y timeouts

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: stripe-api-vs
  namespace: payments
spec:
  hosts:
    - api.stripe.com
  http:
    - route:
        - destination:
            host: api.stripe.com
            port:
              number: 443
      timeout: 10s
      retries:
        attempts: 3
        perTryTimeout: 3s
        retryOn: connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes
        retryRemoteLocalities: true

Esto aplica un timeout global de 10 segundos con hasta 3 intentos de reintento (3 segundos cada uno) para condiciones de fallo específicas. Ten en cuenta que esto solo funciona para ServiceEntries con protocolo HTTP. Para entradas con protocolo TLS, donde Envoy no puede ver la capa HTTP, estás limitado a reintentos de conexión TCP configurados via DestinationRule.

Traffic shifting entre proveedores externos

¿Migrando de una API externa a otra? Usa weighted routing para desplazar el tráfico de forma gradual.

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: geocoding-primary
  namespace: geo
spec:
  hosts:
    - geocoding.internal
  location: MESH_EXTERNAL
  ports:
    - number: 443
      name: tls
      protocol: TLS
  resolution: STATIC
  endpoints:
    - address: api.old-geocoding-provider.com
      labels:
        provider: old
    - address: api.new-geocoding-provider.com
      labels:
        provider: new
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: geocoding-dr
  namespace: geo
spec:
  host: geocoding.internal
  trafficPolicy:
    tls:
      mode: SIMPLE
  subsets:
    - name: old-provider
      labels:
        provider: old
    - name: new-provider
      labels:
        provider: new
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: geocoding-vs
  namespace: geo
spec:
  hosts:
    - geocoding.internal
  http:
    - route:
        - destination:
            host: geocoding.internal
            subset: old-provider
          weight: 80
        - destination:
            host: geocoding.internal
            subset: new-provider
          weight: 20

Esto envía el 80% del tráfico de geocodificación al proveedor antiguo y el 20% al nuevo. Ajusta los pesos a medida que ganas confianza. Completamente reversible: basta con devolver el proveedor antiguo al 100%.

Patrones de resolución DNS: Istio DNS Proxy vs kube-dns

La resolución DNS de Istio para servicios externos implica dos capas: cómo tu aplicación resuelve el hostname (kube-dns / CoreDNS) y cómo el sidecar resuelve el hostname (DNS asíncrono de Envoy o el DNS proxy de Istio). Entender la interacción entre ambas capas es crucial para un comportamiento DNS fiable en Istio.

Flujo por defecto (sin Istio DNS Proxy)

Tu aplicación llama a api.stripe.com. kube-dns lo resuelve a una IP. La aplicación abre una conexión a esa IP. El sidecar intercepta la conexión y —si el ServiceEntry usa resolución DNS— Envoy resuelve api.stripe.com de forma independiente para determinar su lista de endpoints. Se producen dos consultas DNS separadas, lo que puede generar inconsistencias si los registros DNS cambian entre ambas resoluciones.

Con el DNS Proxy de Istio (dns.istio.io)

El sidecar de Istio incluye un DNS proxy que intercepta las consultas DNS de la aplicación. Cuando está habilitado (mediante meshConfig.defaultConfig.proxyMetadata.ISTIO_META_DNS_CAPTURE e ISTIO_META_DNS_AUTO_ALLOCATE), el proxy puede:

  • Auto-asignar IPs virtuales para hosts de ServiceEntry que no tienen addresses definidas, lo cual es crítico para ServiceEntries TCP que necesitan coincidencia basada en IP.
  • Resolver hosts de ServiceEntry directamente, evitando el viaje de ida y vuelta a kube-dns para servicios conocidos del mesh.
  • Garantizar consistencia entre la resolución DNS de la aplicación y la resolución de endpoints del sidecar.

En instalaciones modernas de Istio (1.18+), la captura DNS está habilitada por defecto. Verifica con:

istioctl proxy-config bootstrap <pod-name> -n <namespace> | grep -A2 "ISTIO_META_DNS"

Cuándo importa más el DNS Proxy

El DNS proxy es especialmente importante para ServiceEntries TCP sin un campo addresses explícito. Sin una VIP, Envoy no puede asociar una conexión TCP entrante al ServiceEntry correcto porque no hay una cabecera HTTP Host que inspeccionar. El DNS proxy resuelve esto auto-asignando una VIP del rango 240.240.0.0/16 y devolviéndola cuando la aplicación resuelve el hostname. El sidecar entonces intercepta el tráfico a esa VIP y lo enruta al endpoint externo correcto.

Sticky sessions con ServiceEntry

Algunos servicios externos requieren afinidad de sesión: por ejemplo, un servicio legacy que almacena el estado de sesión en memoria, o un endpoint WebSocket que debe mantener una conexión persistente al mismo backend. Istio soporta sticky sessions para servicios externos a través de consistent hashing en un DestinationRule.

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: legacy-session-service
  namespace: default
spec:
  hosts:
    - legacy-session.internal
  location: MESH_INTERNAL
  ports:
    - number: 8080
      name: http
      protocol: HTTP
  resolution: STATIC
  endpoints:
    - address: 10.0.1.10
    - address: 10.0.1.11
    - address: 10.0.1.12
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: legacy-session-dr
  namespace: default
spec:
  host: legacy-session.internal
  trafficPolicy:
    loadBalancer:
      consistentHash:
        httpCookie:
          name: SERVERID
          ttl: 3600s

Esta configuración hace hash sobre una cookie HTTP llamada SERVERID. Si la cookie no existe, Envoy genera una y la establece en la respuesta para que las peticiones posteriores del mismo cliente vayan al mismo endpoint.

También puedes hacer hash sobre:

  • Cabecera HTTP: consistentHash.httpHeaderName: "x-user-id" — útil cuando tu aplicación envía un identificador de usuario en cada petición.
  • IP de origen: consistentHash.useSourceIp: true — opción más sencilla, pero se rompe en entornos con NAT o IPs de egress compartidas.
  • Parámetro de query: consistentHash.httpQueryParameterName: "session_id" — para REST APIs que incluyen un identificador de sesión en la URL.

Las sticky sessions con ServiceEntry funcionan de forma idéntica a las sticky sessions dentro del mesh. El requisito clave es que el ServiceEntry debe usar resolución STATIC o DNS (no NONE) para que Envoy tenga múltiples endpoints entre los que aplicar el hash. Con DNS_ROUND_ROBIN, solo hay un endpoint lógico, por lo que el consistent hashing no tiene efecto.

Resolución de problemas comunes

Errores 503 al llamar a servicios externos

El problema más habitual con ServiceEntry. Empieza con esta secuencia de diagnóstico:

# Comprueba si el ServiceEntry está aplicado y visible para el proxy
istioctl proxy-config cluster <pod-name> -n <namespace> | grep <external-host>

# Comprueba los listeners
istioctl proxy-config listener <pod-name> -n <namespace> --port <port>

# Revisa los access logs de Envoy para la petición específica
kubectl logs <pod-name> -n <namespace> -c istio-proxy | grep <external-host>

Causas habituales de errores 503:

  • Protocolo incorrecto: establecer protocol: HTTPS cuando tu aplicación inicia TLS. Usa TLS para pass-through; usa HTTP solo si el sidecar hace la originación TLS.
  • ServiceEntry ausente en modo REGISTRY_ONLY: si outboundTrafficPolicy es REGISTRY_ONLY, cualquier host sin ServiceEntry queda bloqueado.
  • Restricción de exportTo: el ServiceEntry está en el namespace A, exportado solo a ".", y el pod que llama está en el namespace B.
  • Fallo de resolución DNS: Envoy no puede resolver el hostname. Comprueba que los servidores DNS sean accesibles desde el pod.

Fallos de resolución DNS

Cuando el resolvedor DNS asíncrono de Envoy falla, verás flags UH (upstream unhealthy) o UF (upstream connection failure) en los access logs.

# Verifica que DNS funciona desde dentro del sidecar
kubectl exec <pod-name> -n <namespace> -c istio-proxy -- \
  pilot-agent request GET /dns_resolve?proxyID=<pod-name>.<namespace>&host=api.stripe.com

# Comprueba el estado del cluster Envoy
istioctl proxy-config endpoint <pod-name> -n <namespace> | grep <external-host>

Si el endpoint aparece como UNHEALTHY, Envoy resolvió DNS pero la detección de anomalías expulsó el host. Si no aparece ningún endpoint, la resolución DNS está fallando. Solución habitual: asegúrate de que tus pods pueden llegar a un servidor DNS externo, o que CoreDNS está configurado para reenviar consultas del dominio externo.

La originación TLS no funciona

Si configuraste originación TLS via un DestinationRule pero el tráfico sigue fallando:

  1. Asegúrate de que el protocolo del puerto en el ServiceEntry es HTTP, no TLS. Si lo estableces como TLS, Envoy trata la conexión como TLS opaco pass-through y no aplicará la configuración TLS del DestinationRule.
  2. Verifica que el campo host del DestinationRule coincide exactamente con la entrada hosts del ServiceEntry.
  3. Comprueba que el VirtualService (si se usa) enruta al número de puerto correcto.

El ServiceEntry TCP no intercepta tráfico

Para ServiceEntries con protocolo TCP sin el DNS proxy, Envoy no puede asociar el tráfico por hostname. Debes hacer una de estas cosas:

  • Establecer un campo addresses explícito con una VIP a la que tu aplicación se dirija.
  • Habilitar el DNS proxy de Istio para que auto-asigne VIPs.
  • Asegurarte de que la IP de destino coincide con lo que resuelve el ServiceEntry.

Sin una de estas opciones, el tráfico TCP pasa por el PassthroughCluster y evita tu ServiceEntry por completo.

Preguntas frecuentes

¿Necesito un ServiceEntry si outboundTrafficPolicy está en ALLOW_ANY?

No lo necesitas para la conectividad: tus servicios pueden alcanzar hosts externos sin él. Pero deberías crearlo de todos modos. Sin ServiceEntry, el tráfico de salida pasa por el PassthroughCluster, lo que significa: sin métricas detalladas por destino, sin access logging con el hostname externo, sin circuit breaking, sin reintentos ni políticas de timeout. Un ServiceEntry es la diferencia entre «funciona» y «funciona de forma fiable con observabilidad».

¿Cuál es la diferencia entre protocol TLS y HTTPS en un puerto de ServiceEntry?

TLS le dice a Envoy que trate la conexión como TLS opaco. Envoy lee la cabecera SNI para determinar el enrutamiento, pero no descifra el payload. Úsalo cuando tu aplicación inicia TLS directamente. HTTPS le dice a Envoy que el protocolo es HTTP sobre TLS, lo que implica que Envoy debe gestionar TLS. En la práctica, para servicios externos donde la aplicación gestiona su propio TLS, usa TLS. Usa HTTP con originación TLS en un DestinationRule cuando quieras que el sidecar gestione TLS.

¿Puedo usar wildcards en los hosts de ServiceEntry?

Sí, pero con limitaciones. Puedes usar *.example.com para coincidir con cualquier subdominio de example.com. Sin embargo, las entradas wildcard solo funcionan con resolution: NONE porque Envoy no puede hacer consultas DNS para hostnames wildcard. Esto significa que pierdes la capacidad de aplicar políticas de tráfico por endpoint. Los ServiceEntries wildcard son más adecuados para control de acceso de egress amplio que para gestión de tráfico granular.

¿Cómo configuro sticky sessions para un servicio externo detrás de un ServiceEntry?

Crea un ServiceEntry con resolución STATIC o DNS (para que Envoy tenga múltiples endpoints) y combínalo con un DestinationRule que configure consistentHash bajo trafficPolicy.loadBalancer. Puedes hacer hash sobre una cookie HTTP, una cabecera, la IP de origen o un parámetro de query. El ServiceEntry debe exponer múltiples endpoints para que el consistent hashing tenga algún efecto. Consulta la sección «Sticky sessions con ServiceEntry» más arriba para un ejemplo YAML completo.

¿Cómo interactúa ServiceEntry con NetworkPolicy y Istio AuthorizationPolicy?

Un ServiceEntry no evita las Kubernetes NetworkPolicy. Si una NetworkPolicy bloquea el egress a la IP externa, el tráfico se descartará a nivel CNI antes de que Envoy pueda enrutarlo. Istio AuthorizationPolicy también puede restringir qué workloads tienen permitido llamar a hosts específicos de ServiceEntry. Para una defensa en profundidad: usa ServiceEntry para la gestión de tráfico y la observabilidad, AuthorizationPolicy para el control de acceso a nivel de workload y NetworkPolicy para la aplicación a nivel de red.

Conclusión

ServiceEntry es uno de los recursos de Istio más prácticos que usarás en producción. Transforma conexiones de salida opacas en tráfico gestionado, observable y controlado por políticas, y lo hace sin requerir cambios en el código de tu aplicación.

Empieza con lo básico: crea un ServiceEntry para cada dependencia externa, establece el tipo de resolución correcto y combínalo con un DestinationRule para límites de conexión y circuit breaking. A medida que madures, añade VirtualServices para reintentos y timeouts, configura sticky sessions donde sea necesario y habilita el DNS proxy para una integración perfecta de servicios TCP.

El patrón es siempre el mismo: registra el servicio, aplica políticas, observa el tráfico. Cada dependencia externa que formalices con un ServiceEntry es un punto ciego menos en tu mesh de producción.

HPA Kubernetes: por qué CPU funciona y memoria casi nunca

HPA Kubernetes: por qué CPU funciona y memoria casi nunca

Hay una configuración que aparece en prácticamente todos los clústeres de Kubernetes: un HorizontalPodAutoscaler con objetivo de 70% de CPU y 70% de memoria. Parece razonable. Sigue los ejemplos de la documentación oficial. Y en muchos casos, genera más problemas de los que resuelve — en silencio.

Los síntomas son predecibles: workloads que no hacen nada se escalan hacia arriba porque su footprint de memoria es naturalmente alto. APIs sensibles a la latencia escalan demasiado tarde porque el pico de CPU ya ha pasado cuando los nuevos pods están listos. Jobs batch oscilan entre escalar hacia arriba y hacia abajo durante operación normal. Y los equipos dedican horas a depurar comportamientos del autoescalado que deberían ser sencillos.

Este artículo explica por qué la configuración por defecto del HPA falla, en qué condiciones exactas el HPA basado en memoria tiene sentido (y cuándo no), y qué métricas alternativas — métricas custom, triggers basados en eventos y señales externas — producen un autoescalado que realmente refleja la demanda del workload.


Cómo decide el HPA escalar: la mecánica real

Antes de diagnosticar los problemas, conviene entender con precisión la mecánica. El HPA calcula el número deseado de réplicas con esta fórmula:

desiredReplicas = ceil(currentReplicas × (currentMetricValue / desiredMetricValue))

Para un objetivo de CPU del 70%, con 2 réplicas consumiendo actualmente una media del 140% de su CPU request, el HPA calcula ceil(2 × (140 / 70)) = 4 réplicas. Conceptualmente simple, pero con una dependencia crítica que la mayoría de configuraciones ignora: el valor de la métrica se expresa en relación al resource request, no al limit.

Esta distinción es fundamental para entender todos los modos de fallo que siguen. Si un contenedor tiene un CPU request de 100m y un limit de 2000m, y actualmente consume 80m, el HPA ve un 80% de utilización — aunque el contenedor esté usando solo el 4% de su techo permitido. Pon un threshold de HPA del 70% sobre un contenedor con CPU request de 100m y cualquier carga no trivial disparará el escalado de inmediato.

El controlador HPA sondea métricas cada 15 segundos por defecto (--horizontal-pod-autoscaler-sync-period). El escalado hacia arriba ocurre rápido — en uno a tres ciclos de sondeo cuando el threshold se supera de forma consistente. El escalado hacia abajo es deliberadamente lento: por defecto el controlador espera 5 minutos (--horizontal-pod-autoscaler-downscale-stabilization) antes de reducir réplicas, para evitar el thrashing. Esta asimetría importa cuando se depura oscilación.


HPA basado en CPU: cuándo funciona y cuándo no

CPU es un recurso compresible. Cuando un contenedor alcanza su limit de CPU, el kernel lo throttlea — el proceso se ralentiza pero no muere ni es desalojado. Esta propiedad convierte a CPU en un proxy razonable para la carga en muchos escenarios, pero no en todos.

Dónde funciona bien el HPA por CPU

Los workloads stateless de procesamiento de peticiones son el punto ideal para el HPA basado en CPU. Si tu servicio hace trabajo CPU-bound por petición — APIs REST que transforman datos, lógica de negocio computacionalmente intensiva, procesamiento de imágenes — entonces la utilización de CPU correlaciona bien con el volumen de peticiones. Más peticiones implica más CPU consumida, lo que hace que el HPA añada réplicas, distribuyendo la carga.

Los prerrequisitos clave para que el HPA por CPU funcione correctamente son:

CPU requests precisos. Ajusta los requests al consumo sostenido real del workload bajo carga normal, no a un placeholder bajo. Usa VPA en modo recomendación o datos históricos de Prometheus para dimensionar bien los requests antes de activar el HPA.

Ratio request/limit razonable. Un ratio de 1:4 o menos mantiene los thresholds en porcentaje significativos. Un contenedor con request 100m y limit 4000m convierte los thresholds porcentuales en algo casi inútil.

Consumo de CPU que sigue la carga de usuarios de forma lineal. Si tu servicio realiza trabajo pesado en background independiente de las peticiones entrantes, la utilización de CPU disparará el escalado independientemente de la demanda real.

Dónde falla el HPA por CPU

Servicios sensibles a la latencia con picos bruscos de tráfico. El HPA reacciona a la utilización media de CPU medida en la ventana de sondeo. Para un servicio que gestiona ráfagas de tráfico — una venta flash, un batch de llamadas API disparado por cron, un broadcast de notificaciones — cuando el controlador HPA detecta el pico, encola nuevos pods y esos pods superan los readiness checks, el pico puede haber terminado ya. El resultado: réplicas añadidas después de que el daño está hecho, con el coste añadido de un ciclo de scale-down posterior.

Workloads I/O-bound. Un servicio que pasa la mayor parte del tiempo esperando a consultas de base de datos, llamadas a APIs externas o lecturas de colas de mensajes mostrará CPU baja incluso bajo carga elevada. El HPA no añadirá réplicas mientras el servicio está degradado — ve CPUs ociosas mientras goroutines o threads están bloqueados esperando I/O.

Workloads con costes de cold-start elevados. Si una nueva réplica tarda 30-60 segundos en arrancar (cargando modelos de ML, estableciendo pools de conexiones, populando caches), las decisiones de escalado deben ocurrir antes — no en reacción al pico de CPU.


HPA basado en memoria: por qué casi siempre se rompe

Memory es un recurso incompresible. A diferencia de CPU — que puede throttlearse sin matar un proceso — cuando un contenedor agota su memory limit, el OOM killer lo termina. Esta propiedad única desencadena un conjunto de problemas fundamentales en el uso de memoria como trigger del HPA.

El problema central: la memoria no correlaciona naturalmente con la carga

Para la mayoría de servicios bien diseñados, el consumo de memoria es relativamente estable. Un servicio en Go asigna memoria al arrancar para sus estructuras de runtime, pools de conexiones y caches — y después mantiene aproximadamente ese footprint independientemente del tráfico. Una aplicación JVM asigna heap al arrancar y usa el garbage collector para gestionarlo. En ambos casos, el uso de memoria bajo 10 peticiones por segundo y bajo 10.000 peticiones por segundo puede ser prácticamente idéntico.

Esto significa que un HPA basado en memoria con un threshold del 70% hará una de dos cosas:

  • Nunca dispararse, porque el consumo de memoria del workload es estable y siempre está por debajo del threshold — dejando el HPA inútil.
  • Dispararse siempre, porque el consumo de memoria base del workload supera naturalmente el threshold — causando que el workload escale de forma permanente y nunca vuelva a bajar.

Ninguno de estos resultados corresponde a una necesidad real de escalado.

La trampa de la mala configuración de requests

Este es el modo de fallo más común y la causa más frecuente del “mi workload se escala sin motivo aparente”. Considera un servicio Java que necesita 512Mi de heap para funcionar con normalidad. El equipo establece el memory request en 256Mi — demasiado conservador, ya sea para ahorrar costes o porque la estimación inicial era incorrecta. El servicio consume inmediatamente el 200% de su memory request con solo estar en ejecución. Un HPA con un objetivo de memoria del 70% escalará este workload hasta el máximo de réplicas a los pocos minutos del despliegue, y se quedará ahí para siempre.

La solución nunca es “ajustar el threshold del HPA”. La solución es dimensionar correctamente el memory request. Pero esto revela el problema más profundo: el HPA basado en memoria es extremadamente sensible a la precisión de tus resource requests, y la mayoría de equipos no tienen requests precisos — especialmente en workloads nuevos o tras cambios de código que alteran el footprint de memoria.

Comportamiento de memoria en JVM y Go

Los workloads JVM son especialmente problemáticos. Por defecto, la JVM asigna heap hasta un máximo (-Xmx) y después lo retiene — no libera heap de vuelta al SO de forma agresiva, ni siquiera después del garbage collection. Un servicio JVM que gestiona una petición por hora mostrará prácticamente el mismo footprint de memoria que uno gestionando miles de peticiones por minuto. Además, el garbage collector de la JVM introduce picos de memoria durante los ciclos de recolección que no están relacionados con la carga.

En entornos JVM contenerizados, también hay que tener en cuenta el flag de awareness del memory limit del contenedor (-XX:+UseContainerSupport, activado por defecto desde JDK 11), que afecta a cómo la JVM calcula su techo de heap en relación con el container limit. Sin el tuning adecuado, la JVM puede asignar un heap que ocupa el 80-90% del memory limit del contenedor — disparando de inmediato cualquier HPA basado en memoria.

Los workloads en Go se comportan de forma diferente pero también mal con el HPA de memoria. El garbage collector de Go está diseñado para mantener baja latencia más que uso mínimo de memoria. El runtime puede retener memoria por encima de lo estrictamente necesario, y el footprint de memoria puede variar según los parámetros de tuning del GC (GOGC, GOMEMLIMIT) de maneras que no correlacionan con la carga de peticiones entrantes.

Cuándo el HPA de memoria sí tiene sentido

Hay casos concretos donde el HPA basado en memoria tiene justificación:

Workloads donde el consumo de memoria realmente sigue la carga de forma lineal. Algunos pipelines de procesamiento de datos, caches en memoria que crecen con el volumen de peticiones, o aplicaciones de streaming que buferizan datos proporcionalmente al throughput. Si puedes demostrar mediante métricas que memoria y carga tienen una correlación lineal fuerte, el HPA de memoria es defendible.

Como válvula de seguridad junto al HPA por CPU. Usar memoria como métrica secundaria (no primaria) para protegerse ante memory leaks o allocaciones descontroladas en un servicio que normalmente escala por CPU. En este caso, establece el threshold de memoria alto — 85-90% — de forma que solo se active en escenarios de sobreconsumo real.

Servicios de caché donde no es deseable el desalojo. Si un servicio usa memoria como caché de rendimiento y quieres escalar antes de que la presión de memoria cause desalojo de caché, la utilización de memoria puede ser un trigger útil — siempre que los requests estén correctamente dimensionados.

Fuera de estos casos específicos, eliminar memoria del spec del HPA y apoyarse en las señales que se describen más adelante producirá mejor comportamiento en prácticamente todos los escenarios.


Dimensionar correctamente los requests antes de añadir HPA

Ninguna estrategia de HPA funciona correctamente sin resource requests precisos. Antes de añadir cualquier autoescalador — CPU, memoria o métricas custom — ejecuta tu workload bajo carga representativa y mide el consumo real. La forma más sencilla es usar VPA en modo recomendación:

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: my-service-vpa
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-service
  updatePolicy:
    updateMode: "Off"   # Solo recomendación — no aplicar automáticamente

Tras 24-48 horas de tráfico, comprueba las recomendaciones del VPA:

kubectl describe vpa my-service-vpa

Los valores lowerBound, target y upperBound te dan una base fundamentada en datos para establecer los requests. Establece tus requests en o cerca del valor target del VPA antes de configurar el HPA. Este único paso elimina la causa más común de mal comportamiento del HPA.

Restricción crítica: VPA y HPA no pueden gestionar la misma métrica de recurso simultáneamente. Si el VPA está configurado para actualizar automáticamente CPU o memoria, y el HPA también escala sobre esas métricas, los dos controladores se pelearán entre sí. La combinación segura es: HPA sobre CPU/memoria con VPA en modo solo-recomendación, o HPA sobre métricas custom con VPA sobre CPU/memoria en modo automático.


Mejores señales: qué usar en lugar de memoria (y cuándo ir más allá de CPU)

El cambio fundamental es pasar de métricas de consumo de recursos (que describen el pasado) a métricas de demanda (que describen lo que el workload está siendo pedido hacer ahora mismo o será pedido en segundos).

Peticiones por segundo (RPS)

Para servicios HTTP, las peticiones por segundo por réplica suelen ser el proxy más preciso para la carga. A diferencia de CPU, mide la demanda directamente — no un efecto secundario de la demanda. Un HPA que mantiene 500 RPS por réplica escalará de forma predecible a medida que el tráfico crece, independientemente de si el servicio es CPU-bound, memory-bound o I/O-bound.

RPS está disponible como métrica custom desde tu service mesh (Istio lo expone como istio_requests_total), desde tu ingress controller (NGINX expone tasas de peticiones vía Prometheus), o desde las propias métricas Prometheus de tu aplicación. Configurar el HPA sobre métricas custom requiere el Prometheus Adapter o una implementación compatible de la custom metrics API.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-service-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-service
  minReplicas: 2
  maxReplicas: 20
  metrics:
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: "500"

Profundidad de cola y consumer lag

Los consumidores de colas de mensajes deben escalar en función del consumer lag — el backlog esperando ser procesado. Esto mide directamente si los consumidores están al día o si la cola crece más rápido de lo que se puede procesar. KEDA proporciona scalers integrados para Kafka, RabbitMQ, SQS y muchos otros sistemas.

Este enfoque es superior a CPU para consumers de mensajería porque un consumer puede estar idle (CPU baja) mientras la cola acumula millones de mensajes. Sin el lag como señal de escalado, el HPA no haría nada — exactamente lo contrario de lo que necesitas.

Latencia (P99)

La latencia P99 por réplica señala eficazmente la sobrecarga cuando se aproxima a los umbrales de SLO, especialmente para APIs de cara al usuario. Si la latencia sube, hay un problema — independientemente de lo que diga la CPU. Usar latencia como señal de escalado es especialmente útil cuando el workload tiene comportamientos de rendimiento no lineales bajo carga.

Escalado planificado y predictivo

Para patrones predecibles (horario laboral, ciclos semanales, eventos programados), el Cron scaler de KEDA permite pre-escalar de forma proactiva antes de que la carga aumente. Esto elimina por completo el problema del cold-start en los picos de tráfico esperados: en lugar de reaccionar a la carga, te anticipas a ella.


Configuración de HPA para producción: lo que realmente importa

Mínimo de réplicas

Establece siempre minReplicas: 2. Un único pod es un single point of failure; si el HPA escala hacia abajo a 1 réplica y ese pod es terminado durante un scale-down, tu servicio tiene un instante de downtime. Dos réplicas mínimas también garantizan que el HPA tenga datos de consumo medio sobre los que calcular.

Ventanas de estabilización

La ventana de scale-down por defecto de 5 minutos suele provocar oscilación en muchos workloads. Auméntala para que coincida con los ciclos del workload. Esta configuración es clave en producción:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-service-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-service
  minReplicas: 2
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 60
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 600
      policies:
      - type: Percent
        value: 25
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Percent
        value: 100
        periodSeconds: 30

Aquí se definen dos comportamientos distintos:

  • Scale-down conservador: ventana de estabilización de 10 minutos, máximo 25% de reducción por minuto. Evita que una caída temporal de carga destruya réplicas que volverán a necesitarse.
  • Scale-up agresivo: sin ventana de estabilización, hasta el doble de réplicas cada 30 segundos. El coste de escalar de más es menor que el de responder tarde.

Ajuste del threshold de CPU

Considera el tiempo de reacción al escalar. Un threshold del 70% suena razonable, pero implica que ya estás sobrecargado cuando el HPA comienza a reaccionar. Establece objetivos al 50-60% para mantener margen mientras arrancan los nuevos pods — especialmente si tu tiempo de cold-start es de más de unos pocos segundos.

PodDisruptionBudgets

Empareja siempre el HPA con un PodDisruptionBudget para evitar que el scale-down termine múltiples pods simultáneamente:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: my-service-pdb
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: my-service

Sin PDB, Kubernetes puede terminar varios pods al mismo tiempo durante el scale-down, causando degradación de servicio en el proceso de optimización de costes.


KEDA: escalado basado en eventos sin reemplazar el HPA

KEDA (Kubernetes Event-Driven Autoscaling) extiende el HPA sin reemplazarlo. En lugar de competir con él, KEDA implementa una custom metrics API que el HPA consume. No añades un nuevo controlador de escalado — añades una fuente de métricas más rica al sistema existente.

KEDA gestiona más de 60 scalers integrados para sistemas como Kafka, RabbitMQ, Azure Service Bus, AWS SQS, y también permite escalado basado en consultas Prometheus arbitrarias. Esto lo convierte en la herramienta de elección para:

  • Consumers de colas de mensajes: escala a cero cuando no hay mensajes, escala agresivamente cuando el lag crece.
  • Workloads event-driven: triggers basados en métricas externas que el HPA estándar no puede alcanzar.
  • Patrones de tráfico predecibles: el Cron scaler permite programar réplicas mínimas por franja horaria.

Un ejemplo de ScaledObject de KEDA para un consumer de Kafka:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: kafka-consumer-scaler
spec:
  scaleTargetRef:
    name: my-kafka-consumer
  minReplicaCount: 1
  maxReplicaCount: 30
  triggers:
  - type: kafka
    metadata:
      bootstrapServers: kafka-broker:9092
      consumerGroup: my-consumer-group
      topic: my-topic
      lagThreshold: "100"

Esto escala el consumer manteniendo un lag máximo de 100 mensajes por réplica. Cuando la cola crece, KEDA añade réplicas. Cuando está vacía, puede bajar a cero — algo imposible con el HPA estándar basado solo en CPU.


Tabla de decisión: qué señal usar según el workload

Tipo de workloadSeñal recomendadaHerramienta
API HTTP stateless (CPU-bound)CPU al 50-60%HPA
API HTTP stateless (I/O-bound)RPS por réplica o latencia P99HPA + custom metrics
Consumer de cola de mensajesConsumer lag / profundidad de colaKEDA
Event-driven (Kafka/SQS/RabbitMQ)Tasa de eventos o lagKEDA
Patrón de tráfico predeciblePlanificación horariaKEDA Cron
Riesgo de memory leakCPU primario + memoria al 85% secundarioHPA v2 multi-metric
Dimensionado previo al HPARecomendaciones históricas CPU/memoriaVPA en modo recomendación

Preguntas frecuentes sobre el autoescalado horizontal en Kubernetes

¿Qué ocurre si defino múltiples métricas en un HPA?

El HPA evalúa todas las métricas definidas y escala para satisfacer la más exigente. Si CPU dice 3 réplicas y RPS dice 5, el HPA usará 5. Nunca se toma el mínimo — siempre el máximo — lo que puede causar sorpresas si no se entiende esta semántica.

Mi workload se escala hacia arriba inmediatamente después del despliegue. ¿Por qué?

Esto casi siempre indica una mala configuración de requests. Usa kubectl top pods para ver el consumo real y compáralo con los requests definidos. Si el consumo supera el request, el HPA verá utilización superior al 100% y escalará agresivamente. La solución es ajustar los requests, no el threshold del HPA.

El HPA escala hacia abajo demasiado rápido y luego vuelve a escalar. ¿Cómo lo soluciono?

Aumenta stabilizationWindowSeconds en la sección scaleDown del behavior. También considera añadir una política de porcentaje (type: Percent) para limitar cuántas réplicas se eliminan por ciclo. El objetivo es que el scale-down sea conservador y el scale-up sea ágil.

¿Puedo usar HPA con StatefulSets?

Técnicamente sí, pero generalmente no deberías. Los StatefulSets implican identidad estable de pod, almacenamiento persistente y orden de escala — propiedades que chocan con el escalado dinámico del HPA. Para workloads stateful, el escalado manual suele ser más seguro.

Mi CPU request es de 50m. ¿Tiene sentido usar HPA por CPU?

Con requests muy bajos, las métricas en porcentaje se vuelven muy granulares — un cambio de 10m en consumo supone un 20% de cambio en utilización. El escalado se vuelve ruidoso. Considera usar métricas custom como RPS en su lugar, o al menos aumentar el request a un valor más representativo.

¿Cómo depuro el HPA cuando no se comporta como espero?

kubectl describe hpa <nombre>

Este comando muestra las métricas actuales, los eventos de escalado recientes y la razón por la que el HPA está o no está escalando. Es el primer sitio donde mirar. Para más detalle, los logs del controlador kube-controller-manager contienen información de depuración sobre las decisiones del HPA.


Conclusión

La configuración por defecto del HPA — CPU al 70%, memoria al 70% — falla de formas predecibles en la mayoría de workloads de producción. La memoria casi nunca es una señal de escalado útil porque no correlaciona con la carga en la mayoría de arquitecturas. CPU funciona bien para servicios CPU-bound con requests bien dimensionados, pero falla en escenarios I/O-bound, picos bruscos y cold-starts lentos.

El autoescalado horizontal en Kubernetes que realmente funciona en producción requiere tres cosas:

  1. Resource requests precisos — sin esto, cualquier estrategia de HPA es aleatoria.
  2. La señal correcta para el tipo de workload — CPU para servicios CPU-bound, RPS/latencia para I/O-bound, consumer lag para colas.
  3. Configuración de comportamiento explícita — ventanas de estabilización, políticas de porcentaje, PDBs.

El resto es ruido. Empieza por los requests, elige la señal adecuada para tu patrón de carga y configura el comportamiento de scale-down de forma conservadora. El autoescalado eficaz no es magia — es ingeniería aplicada con las métricas correctas.

Depurar Contenedores Distroless en Kubernetes: kubectl debug, Ephemeral Containers y Cuándo Usar Cada Técnica

Developer inspecting a distroless container with magnifying glass

El contenedor funciona perfectamente en CI. Se despliega sin problemas en staging. Luego algo falla en producción y escribes el comando de siempre: kubectl exec -it my-pod -- /bin/bash. La respuesta es inmediata: OCI runtime exec failed: exec failed: unable to start container process: exec: "/bin/bash": stat /bin/bash: no such file or directory.

Pruebas con /bin/sh. Mismo error. Pruebas con ls. Mismo error. La imagen del contenedor es distroless — contiene únicamente el binario de tu aplicación y sus dependencias de runtime, sin shell, sin gestor de paquetes, sin ningún tipo de herramienta de diagnóstico. Esto es intencionado y correcto desde el punto de vista de seguridad. También es un desafío operativo significativo la primera vez que te lo encuentras en producción.

Este artículo cubre todas las técnicas prácticas para depurar contenedores distroless en Kubernetes: kubectl debug con ephemeral containers (el enfoque estándar), estrategia pod copy (para versiones de Kubernetes sin soporte de ephemeral containers, o cuando necesitas modificar el spec del pod en ejecución), variantes de imagen debug (el atajo pragmático para desarrolladores), cdebug (una herramienta específica que simplifica el proceso), y depuración a nivel de nodo (el último recurso, con mayor poder). Para cada técnica explicaré qué puede y qué no puede hacer, qué versión de Kubernetes o permisos RBAC requiere, y en qué escenario — desarrollador en local, platform engineer en staging, ops en producción — es la opción más adecuada.

Por Qué los Contenedores Distroless Rompen el Flujo Normal de Depuración

La depuración tradicional de contenedores asume que puedes hacer exec dentro del contenedor y usar herramientas de shell: ps, netstat, strace, curl, un editor de texto. Las imágenes distroless eliminan todo esto por diseño. El proyecto Google distroless, las imágenes basadas en Wolfi de Chainguard, y el ecosistema de imágenes minimalistas en general excluyen deliberadamente todo lo que no es necesario para ejecutar la aplicación. El resultado es una superficie de ataque drásticamente reducida: sin shell no hay RCE mediante shell injection, sin gestor de paquetes no hay vía de escalada fácil, con menos binarios hay menos CVEs en el escaneo de la imagen.

El tradeoff es operativo: cuando algo falla, no puedes usar las herramientas que el propio proceso no tiene permitido ejecutar. Una aplicación Java en gcr.io/distroless/java17-debian12 tiene el JRE y nada más. Un binario de Go compilado con CGO deshabilitado y empaquetado en gcr.io/distroless/static-debian12 tiene literalmente solo el binario y los certificados CA y datos de zona horaria necesarios. No hay wget para descargar un binario de depuración, no hay apt para instalar uno, no hay bash para ejecutar un script.

Kubernetes resuelve esto a nivel de plataforma con los ephemeral containers, añadidos como estables en Kubernetes 1.25. El principio es que un contenedor de depuración — que puede tener una shell completa y cualquier herramienta que necesites — puede inyectarse en un pod en ejecución y compartir su process namespace, network namespace y montajes del sistema de ficheros sin modificar el contenedor original ni reiniciar el pod.

Opción 1: kubectl debug con Ephemeral Containers

Los ephemeral containers son la solución canónica. Desde Kubernetes 1.25 (estable), kubectl debug puede inyectar un contenedor temporal en un pod en ejecución. El contenedor comparte el network namespace del pod destino por defecto, y con --target también puede compartir el process namespace de un contenedor específico, lo que permite inspeccionar sus procesos en ejecución y file descriptors abiertos.

La invocación básica es:

kubectl debug -it my-pod \
  --image=busybox:latest \
  --target=my-container

El flag --target es la pieza crítica. Sin él, el ephemeral container tiene su propio process namespace. Con él, comparte el process namespace del contenedor especificado — lo que significa que puedes ejecutar ps aux y ver los procesos de la aplicación, usar ls -la /proc/<pid>/fd para inspeccionar file descriptors abiertos, y leer el entorno de la aplicación mediante cat /proc/<pid>/environ.

Para un entorno de depuración más potente, reemplaza busybox con una imagen más completa:

kubectl debug -it my-pod \
  --image=nicolaka/netshoot \
  --target=my-container

nicolaka/netshoot incluye tcpdump, curl, dig, nmap, ss, iperf3 y docenas de otras herramientas de diagnóstico de red, lo que la convierte en la opción estándar para escenarios de depuración de red.

Qué Puedes y Qué No Puedes Hacer

Los ephemeral containers comparten el network namespace del pod y, cuando se usa --target, el process namespace. Esto te da:

  • Visibilidad completa sobre el tráfico de red de la aplicación desde dentro del pod (tcpdump, ss, netstat)
  • Inspección de procesos mediante /proc/<pid> — ficheros abiertos, memory maps, variables de entorno, uso de CPU y memoria
  • Acceso al contexto de resolución DNS del pod — exactamente el mismo /etc/resolv.conf que ve la aplicación
  • Capacidad de realizar llamadas de red salientes desde el mismo network namespace (probar endpoints de servicios, resolución DNS)

Lo que no obtienes con ephemeral containers:

  • Acceso al sistema de ficheros del contenedor de aplicación. El ephemeral container tiene su propio root filesystem. No puedes hacer cat /app/config.yaml del sistema de ficheros del contenedor de aplicación a menos que accedas a través de /proc/<pid>/root/.
  • Capacidad de eliminar el contenedor una vez añadido. Los ephemeral containers son permanentes hasta que se elimina el pod. Esto es por diseño — la API de Kubernetes no permite eliminarlos tras su creación.
  • Modificaciones de montajes de volúmenes mediante CLI. No puedes añadir montajes de volúmenes a un ephemeral container mediante kubectl debug (aunque el spec de la API lo soporta, la CLI no expone esta opción).
  • Límites de recursos. Los ephemeral containers no soportan resource requests y limits en la CLI de kubectl debug, aunque esto está evolucionando.

Acceder al Sistema de Ficheros de la Aplicación

La sorpresa más común para los desarrolladores que se inician con ephemeral containers es que no pueden explorar directamente el sistema de ficheros del contenedor de aplicación. La solución es el sistema de ficheros /proc:

# Encontrar el PID de la aplicación
ps aux

# Explorar su sistema de ficheros mediante /proc
ls /proc/1/root/app/
cat /proc/1/root/etc/config.yaml

# O establecer el root al del sistema de ficheros de la aplicación
chroot /proc/1/root /bin/sh  # solo si /bin/sh existe en la imagen de la aplicación

La ruta /proc/<pid>/root es un enlace simbólico al root filesystem del contenedor tal como lo ve el process namespace. Dado que el ephemeral container comparte el process namespace con --target, el PID de la aplicación es normalmente 1, y /proc/1/root te da acceso completo de lectura a su sistema de ficheros.

Requisitos RBAC

Los ephemeral containers requieren el permiso de subrecurso pods/ephemeralcontainers. Esto es independiente de pods/exec, que controla kubectl exec. Un error frecuente es conceder pods/exec para propósitos de depuración sin darse cuenta de que los ephemeral containers requieren un permiso adicional:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: ephemeral-debugger
rules:
- apiGroups: [""]
  resources: ["pods/ephemeralcontainers"]
  verbs: ["update", "patch"]
- apiGroups: [""]
  resources: ["pods/attach"]
  verbs: ["create", "get"]
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]

En entornos de producción, este permiso debe estar estrictamente acotado: con límite de tiempo mediante RoleBinding en lugar de ClusterRoleBinding permanente, restringido a namespaces específicos, e idealmente protegido por un flujo de aprobación. El contenedor de depuración se ejecuta como root por defecto, lo que puede crear vías de escalada de privilegios si el contenedor de aplicación se ejecuta como un usuario no-root con process namespace compartido — el contenedor de debug puede adjuntarse a los procesos de la aplicación con privilegios más elevados.

Opción 2: kubectl debug –copy-to (Estrategia Pod Copy)

Cuando necesitas modificar el spec del contenedor del pod — reemplazar la imagen, cambiar variables de entorno, añadir un sidecar con un sistema de ficheros compartido — el flag --copy-to crea una copia completa del pod con tus modificaciones aplicadas:

kubectl debug my-pod \
  -it \
  --copy-to=my-pod-debug \
  --image=my-app:debug \
  --share-processes

Esto crea un nuevo pod llamado my-pod-debug que es una copia de my-pod pero con la imagen del contenedor reemplazada por my-app:debug. Si my-app:debug es tu imagen de aplicación construida con herramientas de depuración incluidas (o una variante debug de tu registro), esto te permite interactuar con el mismo binario exacto en la misma configuración que el pod original.

Un uso más habitual de --copy-to es adjuntar un contenedor de debug junto al contenedor de aplicación existente manteniendo la imagen original sin cambios:

kubectl debug my-pod \
  -it \
  --copy-to=my-pod-debug \
  --image=busybox \
  --share-processes \
  --container=debugger

Esto crea el pod copia con los contenedores originales más un nuevo contenedor debugger compartiendo el process namespace. A diferencia de los ephemeral containers, este enfoque soporta montajes de volúmenes y límites de recursos, y el pod de debug puede eliminarse de forma limpia cuando termines.

Limitaciones de la Estrategia Copy

La estrategia de copia de pods tiene una limitación crítica: no estás depurando el pod original. Crea un nuevo pod que puede comportarse de manera diferente porque:

  • No comparte el estado en memoria del pod original — si el problema es una goroutine leak o corrupción de heap que ha estado acumulándose durante horas, la copia reciente no lo exhibirá inmediatamente
  • Crea un nuevo Pod UID, lo que significa que cualquier admission webhook, network policy, o security context a nivel de pod que dependa de la identidad del pod puede aplicarse de forma diferente
  • Si el pod original está crasheando (CrashLoopBackOff), la copia también lo hará — esta técnica no sirve para depurar crashes a menos que también cambies el entrypoint

Para depurar crashes específicamente, combina --copy-to con un entrypoint modificado para mantener el contenedor activo:

kubectl debug my-crashing-pod \
  -it \
  --copy-to=my-pod-debug \
  --image=busybox \
  --share-processes \
  -- sleep 3600

Opción 3: Variantes de Imagen Debug

El enfoque más pragmático — y el más adecuado para flujos de trabajo de desarrolladores — es mantener una variante debug de tu imagen de aplicación que incluya herramientas de shell. Tanto el proyecto Google distroless como Chainguard ofrecen este patrón de forma oficial.

Las imágenes Google distroless tienen un tag :debug que añade BusyBox a la imagen:

# Imagen de producción
FROM gcr.io/distroless/java17-debian12

# Variante debug — idéntica pero con shell BusyBox
FROM gcr.io/distroless/java17-debian12:debug

Las imágenes de Chainguard siguen una convención similar con variantes :latest-dev que incluyen apk, una shell y utilidades comunes:

# Producción (sin shell, footprint mínimo)
FROM cgr.dev/chainguard/go:latest

# Variante de desarrollo/debug
FROM cgr.dev/chainguard/go:latest-dev

Si construyes tus propias imágenes base, el enfoque recomendado es usar multi-stage builds y mantener build targets separados:

FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .

# Producción: imagen estática distroless
FROM gcr.io/distroless/static-debian12 AS production
COPY --from=builder /app/myapp /myapp
ENTRYPOINT ["/myapp"]

# Variante debug: mismo binario, con herramientas de shell
FROM gcr.io/distroless/static-debian12:debug AS debug
COPY --from=builder /app/myapp /myapp
ENTRYPOINT ["/myapp"]

En tu pipeline de CI/CD, construye ambos targets y publica my-app:${VERSION} (producción) y my-app:${VERSION}-debug (variante debug) en tu registro. La imagen debug nunca se despliega en producción por defecto, pero existe y está lista para usarse con kubectl debug --copy-to cuando sea necesario.

Consideraciones de Seguridad para las Variantes Debug

Las variantes de imagen debug anulan gran parte del beneficio de seguridad de distroless si se usan en producción, aunque sea temporalmente. Lleva un control del uso: registra cuándo se despliegan imágenes debug, requiere aprobación explícita y asegúrate de que se eliminan tras la sesión de depuración. En entornos regulados, considera si desplegar una variante debug en namespaces de producción está permitido por tu política de seguridad — en muchos casos no lo está, y debes usar ephemeral containers (que añaden un proceso de debug al pod sin modificar la imagen de la aplicación) en su lugar.

Opción 4: cdebug

cdebug es una herramienta CLI de código abierto que simplifica la depuración de contenedores distroless envolviendo kubectl debug con defaults más ergonómicos y capacidades adicionales. Su valor principal está en hacer que la depuración con ephemeral containers se sienta como una experiencia nativa de shell:

# Instalación
brew install cdebug
# o: go install github.com/iximiuz/cdebug@latest

# Depurar un pod en ejecución
cdebug exec -it my-pod

# Especificar un namespace y contenedor
cdebug exec -it -n production my-pod -c my-container

# Usar una imagen de debug específica
cdebug exec -it my-pod --image=nicolaka/netshoot

Lo que cdebug añade sobre el raw kubectl debug:

  • Chroot automático del sistema de ficheros. cdebug exec establece automáticamente el root del sistema de ficheros del contenedor de debug al del contenedor destino, de modo que navegas por / y ves los ficheros de la aplicación — no los del debug image. Esto resuelve el principal punto de fricción con kubectl debug.
  • Integración con Docker. cdebug exec funciona de forma idéntica para contenedores Docker (cdebug exec -it <container-id>), haciendo que sea el mismo flujo de trabajo para depuración local y en cluster.
  • Sin complicaciones RBAC para desarrollo local basado en Docker — útil para flujos de trabajo de desarrolladores antes de que el código llegue a Kubernetes.

El tradeoff: cdebug es una dependencia de terceros y requiere instalación. En entornos con políticas estrictas de tooling (industrias reguladas, clusters air-gapped), puede que no sea una opción. En esos casos, el flujo de trabajo raw de kubectl debug con navegación del sistema de ficheros mediante /proc/1/root es la línea base.

Opción 5: Depuración a Nivel de Nodo

Cuando todo lo demás falla — el pod está en CrashLoopBackOff demasiado rápido para adjuntarse, el problema es a nivel de kernel, o necesitas herramientas como strace que requieren privilegios elevados — la depuración a nivel de nodo te da acceso directo a los procesos del contenedor desde el nodo host.

kubectl debug node/ crea un pod privilegiado en el nodo destino que monta el root filesystem del nodo bajo /host:

kubectl debug node/my-node-name \
  -it \
  --image=nicolaka/netshoot

Desde este pod privilegiado, puedes usar nsenter para entrar en los namespaces de cualquier contenedor que se ejecute en el nodo:

# Encontrar el PID del contenedor en el nodo
# (desde dentro del pod de debug del nodo)
crictl ps | grep my-container
crictl inspect <container-id> | grep pid

# Entrar en los namespaces del contenedor
nsenter -t <pid> -m -u -i -n -p -- /bin/sh

# O solo el network namespace (para depuración de red)
nsenter -t <pid> -n -- ip a

El enfoque con nsenter te permite ejecutar herramientas del toolset del nodo o del contenedor de debug mientras operas en los namespaces del contenedor destino. Así es como ejecutas strace contra un proceso distroless: strace no está en el contenedor de aplicación, pero puedes ejecutarlo desde el nivel de nodo apuntando al PID de la aplicación:

# Trazar todas las syscalls del proceso de la aplicación
nsenter -t <pid> -- strace -p <pid> -f -e trace=network

RBAC y Seguridad para la Depuración a Nivel de Nodo

La depuración a nivel de nodo requiere nodes/proxy y la capacidad de crear pods privilegiados, lo que en la mayoría de clusters de producción está restringido a administradores del cluster. El pod de debug se ejecuta con hostPID: true y hostNetwork: true, dándole visibilidad sobre todos los procesos y el tráfico de red del nodo — no solo del contenedor destino. Esto es significativo: todos los procesos que se ejecutan en el nodo, incluidos los de namespaces de otros tenants, son visibles.

Esta técnica debe tratarse como un procedimiento de break-glass: registra el acceso, requiere doble aprobación en entornos de producción, y limpia inmediatamente tras la sesión de depuración con kubectl delete pod --selector=app=node-debugger.

Cómo Elegir el Enfoque Correcto: Matriz de Perfil de Acceso y Entorno

La técnica que debes usar depende de dos ejes: quién eres (desarrollador, platform engineer, ops/SRE) y dónde está el problema (desarrollo local, staging, producción). Los requisitos y restricciones difieren significativamente entre estas combinaciones.

Desarrollador — Cluster Local o de Desarrollo

Objetivo: Reproducir y entender un bug, inspeccionar la configuración, verificar la conectividad de red a los servicios.
Restricciones: Ninguna significativa — admin completo del cluster en el entorno local o en el namespace de desarrollo personal.
Enfoque recomendado: Variantes de imagen debug o cdebug.

En desarrollo local (Minikube, Kind, Docker Desktop), el camino más rápido es construir la variante debug de tu imagen y desplegarla directamente. Si trabajas con el servicio de otro equipo, cdebug exec te da una shell en el contenedor con el root del sistema de ficheros automático sin necesidad de RBAC especial. El objetivo es velocidad e iteración — reserva los enfoques más estructurados para entornos superiores.

Desarrollador — Cluster de Staging

Objetivo: Depurar problemas de integración, inspeccionar configuración en vivo, verificar el comportamiento específico del entorno.
Restricciones: Cluster compartido — no se pueden desplegar workloads arbitrarios en los namespaces de otros equipos, pero se tiene pods/ephemeralcontainers en el propio namespace.
Enfoque recomendado: kubectl debug con ephemeral containers (--target), acotado al propio namespace.

Staging es donde los ephemeral containers demuestran su valor. Puedes adjuntarte a un pod en ejecución sin reiniciarlo, sin modificar el spec del deployment, y sin afectar a otros usuarios del mismo cluster. Concede a los desarrolladores pods/ephemeralcontainers en los namespaces de su equipo y podrán depurar de forma autónoma sin necesitar intervención de ops.

Platform Engineer / SRE — Producción

Objetivo: Diagnosticar un incidente de producción en vivo. El pod se comporta de forma inesperada — alta latencia, crecimiento de memoria, conexiones inesperadas, respuestas incorrectas.
Restricciones: Los cambios en pods en ejecución son de alto riesgo. Cualquier despliegue de imagen debug debe estar controlado. El problema está activo y afectando a usuarios.
Enfoque recomendado: kubectl debug con ephemeral containers (los ephemeral containers no reinician el pod, no modifican el deployment y son auditables mediante los audit logs de la API).

Los requisitos clave en producción son la auditabilidad y el blast radius mínimo. Los ephemeral containers satisfacen ambos: quedan registrados en el audit log de la API de Kubernetes (quién se adjuntó, cuándo, a qué pod), no modifican el contenedor de aplicación en ejecución, y están limitados al network y process namespaces del propio pod. Documenta la sesión de depuración en tu ticket de incidencia: nombre del pod, hora, qué se observó, quién ejecutó el contenedor de debug.

La estrategia --copy-to generalmente no es apropiada para respuesta a incidentes de producción: crea un nuevo pod que puede o no exhibir el problema, añade carga al cluster durante un incidente, y si está conectado a los mismos servicios (bases de datos, APIs downstream), produce tráfico adicional que complica la investigación forense.

Platform Engineer — Producción, Problema a Nivel de Nodo

Objetivo: Diagnosticar un problema a nivel de kernel, un problema del container runtime, un problema de red que abarca múltiples pods, o una situación en la que el pod crashea demasiado rápido para adjuntarse.
Restricciones: Se requiere el máximo privilegio. Alto riesgo operativo.
Enfoque recomendado: Pod de debug a nivel de nodo con nsenter. Tratar como break-glass.

Para este escenario, crea un rol RBAC dedicado que conceda acceso a nodes/proxy y la capacidad de crear pods con hostPID: true en un namespace de debug dedicado. Vincúlalo solo a usuarios específicos, requiere un paso de autenticación separado (por ejemplo, comprobar kubectl auth can-i contra un binding de tiempo limitado), y registra todos los accesos. Este nivel de acceso debe generar una alerta estilo PagerDuty para que el equipo de seguridad sepa que hay una sesión de debug privilegiada activa en producción.

Errores Comunes y Soluciones

ErrorCausaSolución
“ephemeral containers are disabled for this cluster”Kubernetes < 1.25 o feature gate deshabilitadoActualiza el cluster o habilita el feature gate EphemeralContainers
“cannot update ephemeralcontainers”Falta permiso RBACConcede el rol pods/ephemeralcontainers descrito arriba
“container not found” con --targetNombre de contenedor incorrectoVerifica con kubectl get pod -o jsonpath='{.spec.containers[*].name}'
No se puede leer /proc/1/rootFalta CAP_SYS_PTRACEUsa el perfil PSS Baseline o añade explícitamente la capability
tcpdump no muestra tráficoCapturando en la interfaz equivocadaUsa tcpdump -i any para capturar en todas las interfaces

“ephemeral containers are disabled for this cluster”

Los ephemeral containers requieren Kubernetes 1.16+ (alpha, tras feature gate) y son estables desde la versión 1.25. Si estás en la versión 1.16–1.22, necesitas habilitar el feature gate EphemeralContainers en el API server y en kubelet. Desde la versión 1.23 era beta y estaba habilitado por defecto. Desde la versión 1.25 es estable y siempre está activo. En servicios de Kubernetes gestionados (EKS, GKE, AKS), comprueba la versión del cluster — las versiones anteriores a la 1.25 pueden tenerlo deshabilitado según tu configuración.

“cannot update ephemeralcontainers” (RBAC)

Tienes pods/exec pero no pods/ephemeralcontainers. Añade el permiso mostrado en la sección RBAC anterior. Ten en cuenta que pods/exec y pods/ephemeralcontainers son subrecursos separados — tener uno no implica el otro.

“container not found” con –target

El nombre de contenedor en --target debe coincidir exactamente con el nombre del contenedor tal como está definido en el spec del Pod — no el nombre de la imagen. Compruébalo con kubectl get pod my-pod -o jsonpath='{.spec.containers[*].name}' para obtener los nombres exactos de los contenedores.

Puedo ver procesos pero no puedo leer /proc/1/root

El contenedor de aplicación se ejecuta como usuario no-root (por ejemplo, UID 1000) y el ephemeral container se ejecuta como root. El sistema de ficheros de la aplicación puede tener ficheros propiedad de UID 1000 que no son legibles por otros UIDs según los permisos. La propia ruta /proc/<pid>/root requiere la capability CAP_SYS_PTRACE. Si los PodSecurityStandards (PSS) de tu cluster están configurados como restricted, el contenedor de debug puede no tener esta capability. Usa el perfil PSS Baseline para namespaces de debug o añade explícitamente SYS_PTRACE al securityContext del ephemeral container.

tcpdump no muestra tráfico

Cuando uses nicolaka/netshoot para depuración de red, asegúrate de que el ephemeral container se crea sin --target si tu objetivo es capturar todo el tráfico en la interfaz de red del pod (no solo el del proceso del contenedor específico). Con --target, compartes el process namespace pero el network namespace se comparte a nivel de pod independientemente. Ejecuta tcpdump -i any para capturar en todas las interfaces incluyendo loopback, que es por donde viaja el tráfico entre contenedores dentro de un pod.

Marco de Decisión

Usa esto como punto de partida para seleccionar la técnica adecuada a tu situación:

EscenarioTécnicaRequisito
Incidente de producción activo, pod en ejecuciónkubectl debug + ephemeral containerRBAC pods/ephemeralcontainers, k8s 1.25+
Pod crasheando demasiado rápido para adjuntarsekubectl debug --copy-to + entrypoint modificadoCapacidad de crear pods en el namespace
Desarrollador depurando en dev/stagingcdebug exec o kubectl debugpods/ephemeralcontainers o pod create
Necesitas acceso completo al sistema de ficheroskubectl debug --copy-to + variante debugImagen debug en registro, pod create
Necesitas strace o kernel tracingDebug a nivel de nodo con nsenternodes/proxy, equivalente a cluster admin
Captura de paquetes de redkubectl debug + nicolaka/netshootpods/ephemeralcontainers
Depuración local en Dockercdebug exec <container-id>Acceso al Docker socket
Entorno de debug reproducible en CIVariante debug en build target separadoTag de imagen separado en registro

Diseño RBAC para Producción

Un diseño RBAC limpio para la depuración de contenedores distroless en producción separa tres roles con diferentes niveles de privilegio:

# Tier 1: Autoservicio del desarrollador en namespaces de equipo
# Permite adjuntar ephemeral containers, sin acceso a nodo
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: distroless-debugger
  namespace: team-namespace
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]
- apiGroups: [""]
  resources: ["pods/ephemeralcontainers"]
  verbs: ["update", "patch"]
- apiGroups: [""]
  resources: ["pods/attach"]
  verbs: ["create", "get"]
---
# Tier 2: Acceso SRE a incidentes de producción
# Ephemeral containers en todos los namespaces
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: sre-distroless-debugger
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]
- apiGroups: [""]
  resources: ["pods/ephemeralcontainers"]
  verbs: ["update", "patch"]
- apiGroups: [""]
  resources: ["pods/attach"]
  verbs: ["create", "get"]
---
# Tier 3: Acceso break-glass a nodo
# Solo para el equipo de plataforma, binding de tiempo limitado recomendado
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: node-debugger
rules:
- apiGroups: [""]
  resources: ["nodes/proxy"]
  verbs: ["get"]
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["create", "get", "list", "delete"]
  # Restringir al namespace de debug mediante RoleBinding, no ClusterRoleBinding

Vincula el Tier 1 permanentemente a tus desarrolladores. Vincula el Tier 2 a los SREs de forma permanente pero con alertas de auditoría sobre su uso. Vincula el Tier 3 solo bajo demanda (mediante un operador de Kubernetes que cree RoleBinding de tiempo limitado) y nunca como ClusterRoleBinding permanente.

La clave de este modelo de tres niveles es que cada escalada de privilegios es visible y tiene coste operativo. Un desarrollador puede depurar su propio namespace sin coordinación. Un SRE que necesita acceso a producción usa el Tier 2, que queda registrado en los audit logs. Un platform engineer que necesita acceso a nivel de nodo activa el Tier 3, que genera alertas y requiere un binding temporal — haciendo que el acceso privilegiado sea auditable y difícil de mantener silenciosamente durante más tiempo del necesario.

Conclusión

Los contenedores distroless son la elección correcta para workloads de producción. Reducen la superficie de ataque, eliminan CVEs innecesarios y obligan a una separación más limpia entre aplicación y tooling. El coste operativo es que tu flujo de trabajo habitual de depuración — exec dentro del contenedor, ejecutar algunos comandos — ya no funciona por defecto.

Kubernetes ofrece una respuesta clara con los ephemeral containers y kubectl debug: inyecta un contenedor de debug con las herramientas que necesites en el pod en ejecución, compartiendo sus namespaces de red y de procesos, sin reiniciar ni modificar la aplicación. Para escenarios donde los ephemeral containers son insuficientes — acceso al sistema de ficheros, depuración de crashes, investigación a nivel de kernel — la estrategia copy y el debug a nivel de nodo cubren los huecos restantes.

La clave para que esto funcione a escala no es la técnica en sí, sino el modelo de acceso: los desarrolladores tienen acceso de autoservicio a ephemeral containers en sus propios namespaces, los SREs tienen acceso a ephemeral containers en todo el cluster para incidentes de producción, y el acceso a nivel de nodo es un procedimiento de break-glass con trail de auditoría y límites de tiempo. Con ese modelo en su lugar, los contenedores distroless se convierten en una cuestión operativa resuelta, no en un obstáculo.