Balanceo de carga nativo de contenedores mediante objetos Ingress independientes

En esta página se explica cómo usar el balanceo de carga nativo de contenedores en Google Kubernetes Engine (GKE). El balanceo de carga nativo de contenedores permite que los balanceadores de carga se dirijan directamente a los pods de Kubernetes y distribuyan el tráfico de forma uniforme entre los pods.

Para obtener más información sobre las ventajas, los requisitos y las limitaciones del balanceo de carga nativo de contenedores, consulta el artículo Balanceo de carga nativo de contenedores.

Antes de empezar

Antes de empezar, asegúrate de que has realizado las siguientes tareas:

  • Habilita la API de Google Kubernetes Engine.
  • Habilitar la API de Google Kubernetes Engine
  • Si quieres usar Google Cloud CLI para esta tarea, instálala y, a continuación, inicialízala. Si ya has instalado la gcloud CLI, obtén la versión más reciente ejecutando gcloud components update.

Usar el balanceo de carga nativo de contenedores

En las siguientes secciones se explica cómo configurar el balanceo de carga nativo de contenedores en GKE.

Crea un clúster nativo de VPC

Para usar el balanceo de carga nativo de contenedores, tu clúster de GKE debe tener habilitadas las IPs con alias.

Por ejemplo, el siguiente comando crea un clúster de GKE, neg-demo-cluster, con una subred aprovisionada automáticamente:

  • En el modo Autopilot, las direcciones IP de alias están habilitadas de forma predeterminada:

    gcloud container clusters create-auto neg-demo-cluster \
        --location=COMPUTE_LOCATION
    

    Sustituye COMPUTE_LOCATION por la ubicación de Compute Engine del clúster.

  • En el modo Estándar, habilita las direcciones IP de alias al crear el clúster:

    gcloud container clusters create neg-demo-cluster \
        --enable-ip-alias \
        --create-subnetwork="" \
        --network=default \
        --location=us-central1-a
    

Crear un despliegue

En el siguiente ejemplo de Deployment, neg-demo-app, se ejecuta una sola instancia de un servidor HTTP contenedorizado. Te recomendamos que uses cargas de trabajo que utilicen la señal Pod readiness.

Usar los comentarios sobre la disponibilidad de los pods

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: neg-demo-app # Label for the Deployment
  name: neg-demo-app # Name of Deployment
spec:
  selector:
    matchLabels:
      run: neg-demo-app
  template: # Pod template
    metadata:
      labels:
        run: neg-demo-app # Labels Pods from this Deployment
    spec: # Pod specification; each Pod created by this Deployment has this specification
      containers:
      - image: registry.k8s.io/serve_hostname:v1.4 # Application to run in Deployment's Pods
        name: hostname # Container name
        ports:
        - containerPort: 9376
          protocol: TCP
  

Usar un retraso codificado

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: neg-demo-app # Label for the Deployment
  name: neg-demo-app # Name of Deployment
spec:
  minReadySeconds: 60 # Number of seconds to wait after a Pod is created and its status is Ready
  selector:
    matchLabels:
      run: neg-demo-app
  template: # Pod template
    metadata:
      labels:
        run: neg-demo-app # Labels Pods from this Deployment
    spec: # Pod specification; each Pod created by this Deployment has this specification
      containers:
      - image: registry.k8s.io/serve_hostname:v1.4 # Application to run in Deployment's Pods
        name: hostname # Container name
      # Note: The following line is necessary only on clusters running GKE v1.11 and lower.
      # For details, see https://cloud.google.com/kubernetes-engine/docs/how-to/container-native-load-balancing#align_rollouts
        ports:
        - containerPort: 9376
          protocol: TCP
      terminationGracePeriodSeconds: 60 # Number of seconds to wait for connections to terminate before shutting down Pods
  

En esta implementación, cada contenedor ejecuta un servidor HTTP. El servidor HTTP devuelve el nombre de host del servidor de aplicaciones (el nombre del pod en el que se ejecuta el servidor) como respuesta.

Guarda este archivo de manifiesto como neg-demo-app.yaml y, a continuación, crea la implementación:

kubectl apply -f neg-demo-app.yaml

Crear un servicio para un balanceador de carga nativo de contenedores

Una vez que hayas creado una implementación, tendrás que agrupar sus pods en un servicio.

El siguiente servicio de ejemplo, neg-demo-svc, se dirige al ejemplo de Deployment que has creado en la sección anterior:

apiVersion: v1
kind: Service
metadata:
  name: neg-demo-svc # Name of Service
spec: # Service's specification
  type: ClusterIP
  selector:
    run: neg-demo-app # Selects Pods labelled run: neg-demo-app
  ports:
  - name: http
    port: 80 # Service's port
    protocol: TCP
    targetPort: 9376

El balanceador de carga no se crea hasta que crea un objeto Ingress para el servicio.

Guarda este archivo de manifiesto como neg-demo-svc.yaml y, a continuación, crea el servicio:

kubectl apply -f neg-demo-svc.yaml

Crear un objeto Ingress para el servicio

En el siguiente ejemplo de Ingress, neg-demo-ing, se dirige al servicio que has creado:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: neg-demo-ing
spec:
  defaultBackend:
    service:
      name: neg-demo-svc # Name of the Service targeted by the Ingress
      port:
        number: 80 # Should match the port used by the Service

Guarda este manifiesto como neg-demo-ing.yaml y, a continuación, crea el Ingress:

kubectl apply -f neg-demo-ing.yaml

Al crear el Ingress, se crea un balanceador de carga de aplicaciones en el proyecto y se crean grupos de puntos finales de red(NEGs) en cada zona en la que se ejecuta el clúster. Los endpoints de la NEG y los del servicio se mantienen sincronizados.

Verificar el Ingress

Una vez que hayas desplegado una carga de trabajo, agrupado sus pods en un servicio y creado un Ingress para el servicio, debes verificar que el Ingress haya aprovisionado el balanceador de carga nativo de contenedor correctamente.

Consulta el estado del Ingress:

kubectl describe ingress neg-demo-ing

El resultado incluye eventos ADD y CREATE:

Events:
Type     Reason   Age                From                     Message
----     ------   ----               ----                     -------
Normal   ADD      16m                loadbalancer-controller  default/neg-demo-ing
Normal   Service  4s                 loadbalancer-controller  default backend set to neg-demo-svc:32524
Normal   CREATE   2s                 loadbalancer-controller  ip: 192.0.2.0

Probar el balanceador de carga

En las siguientes secciones se explica cómo puedes probar la funcionalidad de un balanceador de carga nativo de contenedor.

Visitar la dirección IP de Ingress

Espera varios minutos a que se configure el balanceador de carga de aplicaciones.

Para comprobar que el balanceador de carga nativo de contenedores funciona, visita la dirección IP de Ingress.

Para obtener la dirección IP de Ingress, ejecuta el siguiente comando:

kubectl get ingress neg-demo-ing

En la salida del comando, la dirección IP de Ingress se muestra en la columna ADDRESS. Visita la dirección IP en un navegador web.

Comprobar el estado de los servicios backend

También puedes consultar el estado de salud del servicio de backend del balanceador de carga.

  1. Obtén una lista de los servicios backend que se ejecutan en tu proyecto:

    gcloud compute backend-services list
    

    Anota el nombre del servicio de backend, que incluye el nombre del servicio, como neg-demo-svc.

  2. Obtén el estado de salud del servicio backend:

    gcloud compute backend-services get-health BACKEND_SERVICE_NAME --global
    

    Sustituye BACKEND_SERVICE_NAME por el nombre del servicio backend.

Probar el objeto Ingress

Otra forma de comprobar que el balanceador de carga funciona correctamente es escalando el Deployment de ejemplo, enviando solicitudes de prueba al Ingress y verificando que responde el número correcto de réplicas.

  1. Escala la neg-demo-app implementación de una a dos instancias:

    kubectl scale deployment neg-demo-app --replicas 2
    

    Este comando puede tardar varios minutos en completarse.

  2. Verifica que el lanzamiento se haya completado:

    kubectl get deployment neg-demo-app
    

    El resultado debe incluir dos réplicas disponibles:

    NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    neg-demo-app   2         2         2            2           26m
    
  3. Obtén la dirección IP de Ingress:

    kubectl describe ingress neg-demo-ing
    

    Si este comando devuelve un error 404, espera unos minutos a que se inicie el balanceador de carga y vuelve a intentarlo.

  4. Cuenta el número de respuestas distintas del balanceador de carga:

    for i in `seq 1 100`; do \
      curl --connect-timeout 1 -s IP_ADDRESS && echo; \
    done  | sort | uniq -c
    

    Sustituye IP_ADDRESS por la dirección IP de entrada.

    El resultado debería ser similar al siguiente:

    44 neg-demo-app-7f7dfd7bc6-dcn95
    56 neg-demo-app-7f7dfd7bc6-jrmzf
    

    En esta salida, el número de respuestas distintas es el mismo que el número de réplicas, lo que indica que todos los pods de backend están sirviendo tráfico.

Limpieza

Después de completar las tareas de esta página, siga estos pasos para eliminar los recursos y evitar que se le cobren cargos no deseados en su cuenta:

Elimina el clúster

gcloud

gcloud container clusters delete neg-demo-cluster

Consola

  1. Ve a la página Google Kubernetes Engine en la Trusted Cloud consola.

    Ir a Google Kubernetes Engine

  2. Selecciona neg-demo-cluster y haz clic en Eliminar.

  3. Cuando se te pida que confirmes la acción, haz clic en Eliminar.

Solución de problemas

Usa las técnicas que se indican a continuación para verificar tu configuración de red. En las siguientes secciones se explica cómo resolver problemas específicos relacionados con el balanceo de carga nativo del contenedor.

  • Consulta la documentación sobre balanceo de carga para saber cómo enumerar tus grupos de endpoints de red.

  • Puedes encontrar el nombre y las zonas del NEG que corresponden a un servicio en la anotación neg-status del servicio. Obtén la especificación del servicio con lo siguiente:

    kubectl get svc SVC_NAME -o yaml
    

    La anotación metadata:annotations:cloud.google.com/neg-status muestra el nombre del NEG correspondiente al servicio y las zonas del NEG.

  • Puedes comprobar el estado del servicio backend que corresponde a un NEG con el siguiente comando:

    gcloud compute backend-services --project PROJECT_NAME \
        get-health BACKEND_SERVICE_NAME --global
    

    El servicio de backend tiene el mismo nombre que su NEG.

  • Para imprimir los registros de eventos de un servicio, sigue estos pasos:

    kubectl describe svc SERVICE_NAME
    

    La cadena de nombre del servicio incluye el nombre y el espacio de nombres del servicio de GKE correspondiente.

No se puede crear un clúster con IPs de alias

Síntomas

Cuando intentes crear un clúster con IPs de alias, puede que se produzca el siguiente error:

ResponseError: code=400, message=IP aliases cannot be used with a legacy network.
Posibles causas

Este error se produce si intentas crear un clúster con IPs de alias que también use una red antigua.

Resolución

Asegúrate de no crear un clúster con IPs de alias y una red antigua habilitada al mismo tiempo. Para obtener más información sobre cómo usar IPs con alias, consulta el artículo Crear un clúster nativo de VPC.

El tráfico no llega a los endpoints

Síntomas
Errores 502 o 503, o conexiones rechazadas.
Posibles causas

Los nuevos endpoints suelen estar disponibles después de asociarlos al balanceador de carga, siempre que respondan a las comprobaciones de estado. Es posible que se produzcan errores 502 o que se rechacen conexiones si el tráfico no puede llegar a los endpoints.

Los errores 502 y las conexiones rechazadas también pueden deberse a un contenedor que no gestione SIGTERM. Si un contenedor no gestiona explícitamente SIGTERM, se cierra inmediatamente y deja de gestionar solicitudes. El balanceador de carga sigue enviando tráfico entrante al contenedor finalizado, lo que provoca errores.

El balanceador de carga nativo de contenedores solo tiene un endpoint de backend. Durante una actualización gradual, el endpoint antiguo se desprograma antes de que se programe el nuevo.

Los pods de backend se implementan en una zona nueva por primera vez después de aprovisionar un balanceador de carga nativo de contenedor. La infraestructura del balanceador de carga se programa en una zona cuando hay al menos un endpoint en ella. Cuando se añade un nuevo endpoint a una zona, se programa la infraestructura del balanceador de carga, lo que provoca interrupciones del servicio.

Resolución

Configura los contenedores para que gestionen SIGTERM y sigan respondiendo a las solicitudes durante el periodo de gracia de finalización (30 segundos de forma predeterminada). Configura los pods para que empiecen a fallar las comprobaciones del estado cuando reciban SIGTERM. Esto indica al balanceador de carga que deje de enviar tráfico al pod mientras se está desprogramando el endpoint.

Si tu aplicación no se cierra correctamente y deja de responder a las solicitudes al recibir una señal SIGTERM, puedes usar el hook preStop para gestionar la señal SIGTERM y seguir sirviendo tráfico mientras se está desprogramando el endpoint.

lifecycle:
  preStop:
    exec:
      # if SIGTERM triggers a quick exit; keep serving traffic instead
      command: ["sleep","60"]

Consulta la documentación sobre la finalización de pods.

Si el backend del balanceador de carga solo tiene una instancia, configura la estrategia de lanzamiento para evitar que se cierre la única instancia antes de que se programe por completo la nueva. En el caso de los pods de aplicaciones gestionados por la carga de trabajo Deployment, esto se puede conseguir configurando la estrategia de lanzamiento con el parámetro maxUnavailable igual a 0.

strategy:
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

Para solucionar el problema de que el tráfico no llega a los endpoints, compruebe que las reglas del cortafuegos permiten el tráfico TCP entrante a sus endpoints en los intervalos 130.211.0.0/22 y 35.191.0.0/16. Para obtener más información, consulta el artículo Añadir comprobaciones del estado de la documentación de Cloud Load Balancing.

Consulta los servicios de backend de tu proyecto. La cadena de nombre del servicio backend correspondiente incluye el nombre y el espacio de nombres del servicio de GKE correspondiente:

gcloud compute backend-services list

Recupera el estado de salud del backend del servicio de backend:

gcloud compute backend-services get-health BACKEND_SERVICE_NAME

Si todos los backends están en mal estado, es posible que tu cortafuegos, Ingress o servicio estén mal configurados.

Si algunos back-ends no están en buen estado durante un breve periodo, puede que la causa sea la latencia de programación de la red.

Si algunos back-ends no aparecen en la lista de servicios de back-end, puede que se deba a la latencia de programación. Para comprobarlo, ejecuta el siguiente comando, donde NEG_NAME es el nombre del servicio de backend. (Los NEG y los servicios de backend tienen el mismo nombre):

gcloud compute network-endpoint-groups list-network-endpoints NEG_NAME

Comprueba si todos los endpoints esperados están en el NEG.

Si tienes un número reducido de backends (por ejemplo, 1 pod) seleccionado por un balanceador de carga nativo de contenedor, te recomendamos que aumentes el número de réplicas y distribuyas los pods de backend en todas las zonas que abarca el clúster de GKE. De esta forma, se asegurará de que la infraestructura del balanceador de carga subyacente esté totalmente programada. De lo contrario, considera la posibilidad de restringir los pods de backend a una sola zona.

Si configuras una política de red para el endpoint, asegúrate de que se permita el tráfico de entrada desde la subred de solo proxy.

Lanzamiento detenido

Síntomas
El lanzamiento de una implementación actualizada se detiene y el número de réplicas actualizadas no coincide con el número de réplicas deseado.
Posibles causas

Las comprobaciones del estado de la implementación están fallando. Puede que la imagen del contenedor sea incorrecta o que la comprobación del estado no se haya configurado correctamente. La sustitución gradual de pods espera hasta que el pod recién iniciado supere su puerta de disponibilidad de pods. Esto solo ocurre si el pod responde a las comprobaciones del estado del balanceador de carga. Si el pod no responde o si la comprobación del estado está mal configurada, no se pueden cumplir las condiciones de la puerta de disponibilidad y el lanzamiento no puede continuar.

Si usas kubectl 1.13 o una versión posterior, puedes comprobar el estado de las puertas de preparación de un pod con el siguiente comando:

kubectl get pod POD_NAME -o wide

Consulta la columna READINESS GATES.

Esta columna no existe en kubectl 1.12 ni en versiones anteriores. Un pod marcado como en estado READY puede tener una comprobación de disponibilidad fallida. Para verificarlo, usa el siguiente comando:

kubectl get pod POD_NAME -o yaml

Los readiness gates y su estado se muestran en el resultado.

Resolución

Verifica que la imagen del contenedor de la especificación del pod de tu Deployment funcione correctamente y pueda responder a las comprobaciones de estado. Verifica que las comprobaciones de estado estén configuradas correctamente.

Errores del modo degradado

Síntomas

A partir de la versión 1.29.2-gke.1643000 de GKE, es posible que aparezcan las siguientes advertencias en tu servicio en Explorador de registros cuando se actualicen los NEGs:

Entering degraded mode for NEG <service-namespace>/<service-name>-<neg-name>... due to sync err: endpoint has missing nodeName field
Posibles causas

Estas advertencias indican que GKE ha detectado errores de configuración de endpoints durante una actualización de NEG basada en objetos EndpointSlice, lo que ha activado un proceso de cálculo más detallado llamado modo degradado. GKE sigue actualizando los NEGs de la mejor forma posible, ya sea corrigiendo la configuración incorrecta o excluyendo los endpoints no válidos de las actualizaciones de los NEGs.

Algunos errores habituales son los siguientes:

  • endpoint has missing pod/nodeName field
  • endpoint corresponds to an non-existing pod/node
  • endpoint information for attach/detach operation is incorrect
Resolución

Normalmente, estos eventos se deben a estados transitorios y se solucionan solos. Sin embargo, los eventos causados por errores de configuración en objetos EndpointSlice personalizados siguen sin resolverse. Para entender la configuración incorrecta, examina los objetos EndpointSlice correspondientes al servicio:

kubectl get endpointslice -l kubernetes.io/service-name=<service-name>

Valida cada endpoint en función del error del evento.

Para solucionar el problema, debe modificar manualmente los objetos EndpointSlice. La actualización hace que los NEGs se actualicen de nuevo. Una vez que se haya corregido la configuración incorrecta, el resultado será similar al siguiente:

NEG <service-namespace>/<service-name>-<neg-name>... is no longer in degraded mode

Problemas conocidos

El balanceo de carga nativo de contenedores en GKE tiene los siguientes problemas conocidos:

Condición de carrera de la comprobación de preparación de NEG

En determinadas condiciones, las comprobaciones de disponibilidad pueden devolver un estado "false positive" antes de que la comprobación de estado de entrada informe de un estado correcto, lo que genera eventos de error como los siguientes en el objeto Ingress:

NEG is not attached to any BackendService with health checking. Marking condition "cloud.google.com/load-balancer-neg-ready" to True.

Síntomas

Este problema provoca que el balanceador de carga informe de un error 503 (failed_to_pick_backend) al tráfico mientras GKE realiza la actualización continua de la carga de trabajo de la implementación.

Causas

Aunque el controlador NEG de GKE se basa en la información de la comprobación del estado de NEG de Compute Engine para informar de si el endpoint está en buen estado, ten en cuenta la siguiente secuencia de eventos:

  1. Se crea un nuevo pod a partir de una actualización continua.
  2. El controlador de NEG de GKE añade esta nueva dirección IP de Pod al grupo de puntos finales de red.
  3. El controlador NEG de GKE solicita el estado de salud del endpoint recién añadido.
  4. El servicio NEG de Compute Engine aún no tiene información sobre el estado y devuelve un estado vacío.
  5. El controlador NEG de GKE da por hecho que un estado vacío significa que no se ha configurado ninguna comprobación de estado y marca el pod como Ready.
  6. GKE elimina el Pod antiguo porque cree que el nuevo está listo para servir tráfico.
  7. Si el nuevo Pod es el único backend que queda para el balanceador de carga, este devuelve una respuesta 503 Service Unavailable.
  8. Una vez que el pod empiece a superar su comprobación de estado, el balanceador de carga empezará a devolver respuestas 200 OK como se espera.

El controlador NEG de GKE no puede distinguir entre dos estados de comprobación del estado diferentes: missing-health-check-because-not-attached-to-backend y missing-health-check-because-health-check-is-not-yet-programmed.

Como el controlador de NEG no puede hacer esta distinción, el controlador de NEG de GKE debe asumir que, si ninguno de los endpoints del NEG tiene información de comprobación de estado, este NEG no debe pertenecer a ningún BackendService.

Aunque es poco probable que se dé esta situación, si tienes un número relativamente pequeño de pods (por ejemplo, 2) en comparación con el número de NEGs, aumentará el riesgo de que se produzca esta condición de carrera. Recuerde que las NEGs se crean para cada región, con una NEG por zona, lo que suele dar como resultado tres NEGs.

Por lo tanto, si hay un número relativamente mayor de pods, de forma que cada NEG siempre tenga varios pods antes de que empiece la actualización gradual, es muy poco probable que se active esta condición de carrera.

Soluciones:

La mejor forma de evitar esta condición de carrera (y, en última instancia, de evitar respuestas 503 Service Unavailable durante las actualizaciones continuas) es tener más back-ends en el grupo de endpoints de red.

Asegúrate de que la estrategia de actualización continua esté configurada para que siempre se ejecute al menos un pod.

Por ejemplo, si solo se ejecutan 2 pods con normalidad, una configuración de ejemplo podría ser la siguiente:

strategy:
   type: RollingUpdate
   rollingUpdate:
     maxUnavailable: 0
     maxSurge: 1

El ejemplo anterior es una sugerencia. Debe actualizar la estrategia en función de varios factores, como el número de réplicas.

Recolección de elementos no utilizados incompleta

GKE recoge los elementos no utilizados de los balanceadores de carga nativos de contenedores cada dos minutos. Si se elimina un clúster antes de que se eliminen por completo los equilibradores de carga, debes eliminar manualmente los NEG del equilibrador de carga.

Para ver los NEGs de tu proyecto, ejecuta el siguiente comando:

gcloud compute network-endpoint-groups list

En el resultado del comando, busca los NEGs correspondientes.

Para eliminar un NEG, ejecuta el siguiente comando y sustituye NEG_NAME por el nombre del NEG:

gcloud compute network-endpoint-groups delete NEG_NAME

Alinear las implementaciones de cargas de trabajo con la propagación de endpoints

Cuando despliegas una carga de trabajo en tu clúster o actualizas una carga de trabajo, el balanceador de carga nativo de contenedor puede tardar más en propagar los nuevos endpoints que en finalizar el lanzamiento de la carga de trabajo. El Deployment de ejemplo que vas a desplegar en esta guía usa dos campos para alinear su lanzamiento con la propagación de los endpoints: terminationGracePeriodSeconds y minReadySeconds.

terminationGracePeriodSeconds permite que el pod se cierre correctamente esperando a que finalicen las conexiones después de que se haya programado la eliminación de un pod.

minReadySeconds añade un periodo de latencia después de que se cree un pod. Especificas el número mínimo de segundos en que debe estar un pod nuevo en el estado Ready, sin que falle ninguno de sus contenedores, para que se considere que está disponible.

Debes configurar los valores minReadySeconds y terminationGracePeriodSeconds de tus cargas de trabajo en 60 segundos o más para asegurarte de que el servicio no se interrumpa debido a las implementaciones de cargas de trabajo.

terminationGracePeriodSeconds está disponible en todas las especificaciones de Pod y minReadySeconds está disponible en Deployments y DaemonSets.

Para obtener más información sobre cómo ajustar los lanzamientos, consulta RollingUpdateStrategy.

initialDelaySeconds en el pod readinessProbe no se respeta

Podrías esperar que el balanceador de carga nativo de contenedor respete la configuración initialDelaySeconds del Pod readinessProbe. Sin embargo, readinessProbe se implementa mediante kubelet y la configuración initialDelaySeconds controla la comprobación de estado de kubelet, no el balanceador de carga nativo de contenedor. El balanceo de carga nativo de contenedores tiene su propia comprobación de estado.

Siguientes pasos