29 de enero de 2026•Por Andrés González•8 min de lectura

    10 consejos prácticos para un cluster de Kubernetes escalable, estable y eficiente en costos

    Lecciones de operar Open edX y Owly en producción: bases de datos, StatefulSets, zonas de disponibilidad, requests y limits, Karpenter, HPA, PDBs y probes.

    10 consejos prácticos para un cluster de Kubernetes escalable, estable y eficiente en costos

    Este artículo está pensado para ingenieros, arquitectos y líderes técnicos que operan Kubernetes en producción, especialmente en entornos SaaS y multi-tenant.

    Después de muchos años desplegando Open edX, Owly (nuestro asistente de IA) y otras cargas de trabajo productivas en la nube, acumulé una larga lista de lecciones aprendidas sobre cómo operar clusters de Kubernetes que sean performantes, resilientes y económicos.

    Son recomendaciones prácticas, basadas en incidentes reales y en decisiones de compromiso. Cada tema podría merecer un artículo propio. No todos los consejos aplican a todos los casos, pero la mayoría son relevantes para aplicaciones SaaS web típicas, plataformas multi-tenant y cargas de trabajo de negocio.


    1. Mantené las bases de datos fuera del cluster

    No voy a decir que no se pueden correr bases de datos dentro de Kubernetes, aunque estoy tentado.
    Salvo que tu producto se trate justamente de correr bases de datos en Kubernetes, solo voy a decir esto: si querés dormir tranquilo, mantené tus bases de datos fuera del cluster.

    Para cargas OLTP, los servicios de bases de datos gestionadas de tu proveedor de nube suelen ser la mejor opción:

    • La infraestructura está optimizada para el rendimiento de la base de datos

    • Redundancia, backups y mantenimiento incluidos

    • Podés apagar o recrear el cluster sin perder datos

    • La carga de la base de datos no compite con las aplicaciones

    Si los servicios gestionados no son una opción, corré las bases de datos en infraestructura dedicada, no dentro del mismo cluster que aloja los pods de tu aplicación.


    2. Tené mucho cuidado con los StatefulSets

    La escalabilidad de Kubernetes se basa en la idea de que las cargas de trabajo son stateless y descartables. Los pods se pueden crear, destruir y reprogramar en cualquier lugar y en cualquier momento.

    Las cargas con estado rompen ese modelo.

    Usar PersistentVolumes trae varios desafíos:

    • Tenés que resolver vos mismo el acceso concurrente y los bloqueos

    • Los volúmenes suelen estar atados a una sola zona de disponibilidad

    • Kubernetes no gestiona de forma nativa la replicación ni los backups de los PersistentVolumes; eso depende del proveedor de almacenamiento

    Siempre que puedas, preferí:

    • Almacenamiento de objetos (S3, GCS, Azure Blob)

    • Sistemas compatibles con S3, como MinIO

    • Volúmenes efímeros, si perder los datos es aceptable

    Incidente real:
    Una vez tuvimos un pod con Caddy que guardaba los certificados TLS en un PV. Cuando el pod tuvo que reiniciarse, los únicos nodos disponibles estaban en otra AZ. La afinidad del volumen impidió programarlo y toda la aplicación se cayó.

    La solución: pasar la gestión de certificados a cert-manager + NGINX ingress y eliminar el PV por completo, dejando Caddy solo como proxy. Ahora el pod puede reprogramarse en cualquier lugar.


    3. Distribuí el cluster en varias zonas de disponibilidad y dimensioná bien las redes

    Una sola zona de disponibilidad suele ser confiable, pero no infalible.

    Distribuir el cluster en dos o más AZs da un buen equilibrio entre resiliencia y costo, sin la complejidad de un despliegue multi-región.

    Dos puntos importantes para tener en cuenta:

    • Los pods y los nodos consumen direcciones IP

    • Los pods de sistema y de monitoreo también cuentan

    Si tu rango CIDR es demasiado chico, te vas a quedar sin IPs antes de lo esperado.

    Regla práctica:
    Dimensioná la VPC y las subredes para la cantidad máxima esperada de nodos y pods, no para el tamaño inicial del cluster. Si te quedás sin direcciones IP, vas a tener que sumar redes o subredes, y eso es mucho más fácil de hacer al principio que con el cluster en producción.


    4. Configurá el monitoreo desde el principio

    Cuando el cluster crece, ajustarlo solo desde la línea de comandos se vuelve un suplicio.

    Como mínimo, instalá:

    • metrics-server (necesario para HPA y el autoescalado)

    Un stack de monitoreo sólido:

    • Prometheus para métricas

    • Grafana para visualización

    • Loki para logs

    No se puede optimizar lo que no se ve.


    5. Definí siempre requests y limits de CPU y memoria

    Kubernetes decide dónde programar los pods en base a los requests, no al uso real.

    Los limits, en cambio, definen qué pasa cuando algo sale mal.

    Ejemplo:

    resources:
      requests:
        cpu: "500m"
        memory: "2Gi"
      limits:
        cpu: "1000m"
        memory: "4Gi"
    

    La suma de los requests de todos los pods asignados a un nodo va a ser menor o igual a la capacidad del nodo, tanto en CPU como en memoria.

    Los pods pueden consumir recursos por encima de sus requests hasta los limits configurados, compitiendo con otros pods por los recursos disponibles del nodo.

    En este ejemplo:

    • El pod puede usar más de 500m de CPU, pero se va a limitar (throttling) en 1000m

    • El throttling de CPU vuelve lentas a las aplicaciones y las expone a timeouts

    • La memoria se comporta distinto: si el pod supera los 4Gi aunque sea por un byte, se termina de inmediato (OOM)

    Puntos clave:

    • Los requests determinan dónde se pueden programar los pods

    • Los limits de CPU provocan throttling

    • Los limits de memoria provocan OOM kills

    • Sin limits de memoria, un solo pod puede desestabilizar un nodo entero

    Como barandas de seguridad, definí:

    • ResourceQuota por namespace

    • Valores por defecto con LimitRange para los contenedores

    También podés activar VPA en modo recomendación (“off”) para observar los patrones de uso reales antes de aplicar cambios.


    6. Usá un registro de contenedores privado

    Los registros públicos funcionan… hasta que dejan de funcionar.

    Una vez rotamos varios nodos al mismo tiempo. Los pods nuevos no pudieron arrancar porque superamos los límites de descarga de Docker Hub.

    • Descargas anónimas: ~100 cada 6 horas

    • Descargas autenticadas: ~200 cada 6 horas

    En un cluster real, es sorprendentemente fácil llegar a ese límite.

    Si estás en AWS, ECR es la opción obvia. Si no, usá cualquier registro privado o un plan pago de Docker Hub.


    7. Usá Karpenter, pero aislá las cargas de sistema

    El aprovisionamiento de nodos importa.

    Los node groups gestionados tradicionales escalan en base a métricas a nivel de nodo.
    Karpenter, en cambio, aprovisiona nodos en base a los requests de recursos de los pods, lo que da un control mucho más fino.

    Sin embargo, Karpenter corre dentro del cluster que gestiona.

    Eso es peligroso.

    Buena práctica:

    • Un node group gestionado y chico para los pods de sistema

    • Uno o más node groups gestionados por Karpenter para las aplicaciones

    Los nodos de sistema deberían alojar:

    • Karpenter

    • CoreDNS

    • Metrics server

    • Otros componentes críticos

    Usá taints y tolerations para forzar esa separación.

    Además: evitá las instancias burstable (t*) para los nodos. Los créditos de CPU se pueden agotar en el peor momento posible.


    8. Configurá el HPA con cuidado (sobre todo con memoria)

    El HPA escala los pods en base a los requests, no a los limits.

    Podés escalar por:

    • CPU (recomendado)

    • Memoria (con precaución)

    Un pico de CPU puede pasar de casi cero al limit configurado (o al 100% de la CPU del nodo si no hay límite) en una fracción de segundo y quedarse ahí mucho tiempo. Si no está bien controlado, eso puede disparar múltiples réplicas. Hasta un bug simple (por ejemplo, un loop infinito involuntario) puede tirar abajo todo el cluster.

    El HPA por memoria es delicado porque el consumo base de memoria casi nunca es cero. Un pod en reposo puede estar en 0 de CPU, pero normalmente consume algo de memoria. Si el umbral está muy cerca de ese consumo base, los pods van a escalar hacia arriba pero nunca hacia abajo. Y ese consumo base se multiplica por la cantidad de réplicas, ocupando recursos de los nodos sin necesidad.

    Regla práctica:
    Poné los objetivos de memoria bastante por encima del consumo base (muchas veces más de 2×) para que pueda escalar hacia abajo.

    Conclusión: el HPA por CPU suele ser seguro; el HPA por memoria requiere conocer muy bien el consumo base y conviene usarlo con moderación.


    9. Protegé la disponibilidad con PodDisruptionBudgets

    Los PodDisruptionBudgets (PDBs) controlan cómo se desalojan los pods durante:

    • El drenado de nodos

    • La consolidación de Karpenter

    • Los rolling updates

    Sin PDBs, un deployment con una sola réplica puede desaparecer momentáneamente mientras se reprograma.

    Ejemplo:

    apiVersion: policy/v1
    kind: PodDisruptionBudget
    metadata:
      name: myapp-pdb
      namespace: mynamespace
    spec:
      minAvailable: 1
      selector:
        matchLabels:
          app.kubernetes.io/name: myapp
    

    Cuidado: los PDBs demasiado estrictos pueden bloquear despliegues y el autoescalado. Si algo se traba, subí las réplicas temporalmente para recuperar flexibilidad.


    10. Usá bien los probes de liveness y readiness

    Los probes le indican a Kubernetes si un pod:

    • Está vivo

    • Está listo para recibir tráfico

    Buenas prácticas:

    • Liveness: barato y rápido (chequeo del proceso, un endpoint simple)

    • Readiness: salud a nivel de aplicación (dependencias, conexión a la base de datos)

    • Para arranques lentos, considerá un startupProbe

    Siempre que puedas, exponé:

    • /live → devuelve 200 OK de inmediato

    • /ready → valida las dependencias

    Ejemplo:

    livenessProbe:
      exec:
        command:
          - sh
          - -c
          - "pgrep -f uwsgi"
      initialDelaySeconds: 10
      periodSeconds: 10
      timeoutSeconds: 3
      failureThreshold: 3
    readinessProbe:
      httpGet:
        path: /health/
        port: 5000
        httpHeaders:
          - name: Host
            value: backend.myapp.com
          - name: X-Forwarded-Proto
            value: https
      initialDelaySeconds: 30
      periodSeconds: 30
      timeoutSeconds: 10
      failureThreshold: 3
    

    Acá se ejecuta pgrep -f uwsgi para verificar que el servicio uWSGI esté corriendo. Después se consulta el endpoint /health/ en el puerto 5000 para comprobar que la aplicación responde. Ese endpoint debería devolver 200 OK.

    Esto mejora muchísimo la resiliencia y el comportamiento de los despliegues.


    Reflexiones finales

    Operar Kubernetes en producción no es trivial. Pero una vez que entendés cómo funcionan de verdad la programación de pods, el escalado y los modos de falla, se convierte en una plataforma increíblemente potente.

    La mayoría de las caídas no las causa Kubernetes en sí, sino las suposiciones implícitas sobre cómo se comporta.

    Kubernetes tiene sus propias reglas. El éxito en producción viene de entenderlas y diseñar con ellas, no en su contra.

    KubernetesOpen edXDevOpsKarpenterCostos de nube
    A

    Andrés González

    CTO y cofundador de Aulasneo