Balanceo de cargas nativo del contenedor a través de Ingress


En esta página, se explica cómo usar el balanceo de cargas nativo del contenedor en Google Kubernetes Engine (GKE). El balanceo de cargas nativo del contenedor permite que los balanceadores de cargas se orienten de forma directa a los pods de Kubernetes y distribuyan el tráfico de manera uniforme a los pods.

Para obtener más información sobre los beneficios, los requisitos y las limitaciones del balanceo de cargas nativo del contenedor, consulta Balanceo de cargas nativo del contenedor.

Antes de comenzar

Antes de comenzar, asegúrate de haber realizado las siguientes tareas:

  • Habilita la API de Google Kubernetes Engine.
  • Habilitar la API de Google Kubernetes Engine
  • Si deseas usar Google Cloud CLI para esta tarea, instala y, luego, inicializa gcloud CLI. Si ya instalaste gcloud CLI, ejecuta gcloud components update para obtener la versión más reciente.

Usa el balanceo de cargas nativo del contenedor

En las siguientes secciones, se explica la configuración de balanceo de cargas nativo del contenedor en GKE. Ten en cuenta que, para los clústeres de GKE que ejecutan la versión 1.17 o posterior y bajo ciertas condiciones, el balanceo de cargas nativo del contenedor es predeterminado y no requiere una anotación de Service cloud.google.com/neg: '{"ingress": true}' explícita.

Crea un clúster nativo de la VPC

Para usar el balanceo de cargas nativo del contenedor, tu clúster de GKE debe tener habilitadas las IP de 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
    

    Reemplaza COMPUTE_LOCATION por la ubicación de Compute Engine para el clúster.

  • En el modo Standard, habilita las direcciones IP de alias cuando creas el clúster:

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

Crear una implementación

En la siguiente Implementación de muestra, neg-demo-app, se ejecuta una única instancia de un servidor HTTP con contenedores. Recomendamos que uses cargas de trabajo que utilicen comentarios de preparación de pods.

Usa comentarios de preparación de 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
  

Usa 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 solo devuelve el nombre del host del servidor de la aplicación (el nombre del pod en el que se ejecuta el servidor) como respuesta.

Guarda este manifiesto como neg-demo-app.yaml y, luego, crea el Deployment:

kubectl apply -f neg-demo-app.yaml

Crea un servicio para un balanceador de cargas nativo del contenedor

Una vez creada una implementación, tienes que agrupar sus pods en un Servicio.

El siguiente servicio de muestra, neg-demo-svc, se dirige a la implementación de muestra que creaste en la sección anterior:

apiVersion: v1
kind: Service
metadata:
  name: neg-demo-svc # Name of Service
  annotations:
    cloud.google.com/neg: '{"ingress": true}' # Creates a NEG after an Ingress is created
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

La anotación del servicio, cloud.google.com/neg: '{"ingress": true}', habilita el balanceo de cargas nativo del contenedor. Sin embargo, el balanceador de cargas no se creará hasta que crees un Ingress para el servicio.

Guarda este manifiesto como neg-demo-svc.yaml y crea el Service:

kubectl apply -f neg-demo-svc.yaml

Crea un Ingress para el servicio

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

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 crea el Ingress:

kubectl apply -f neg-demo-ing.yaml

Una vez creado el Ingress, se crea un balanceador de cargas de aplicaciones en el proyecto y se crean grupos de extremos de red (NEG) en cada zona en la que se ejecuta el clúster. Los extremos en el NEG y los del servicio se mantienen sincronizados.

Verifica el Ingress

Después de que implementaste una carga de trabajo, agrupaste sus pods en un servicio y creaste un Ingress para el servicio, tienes que verificar si el Ingress aprovisionó el balanceador de cargas nativo del contenedor de forma correcta.

Recupera el estado del Ingress:

kubectl describe ingress neg-demo-ing

El resultado incluye los 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

Prueba el balanceador de cargas

En las secciones a continuación, se explica cómo puedes probar la funcionalidad de un balanceador de cargas nativo del contenedor.

Visita la dirección IP del Ingress

Espera varios minutos hasta que el balanceador de cargas de aplicaciones se configure.

Puedes verificar que el balanceador de cargas nativo del contenedor funciona si visitas la dirección IP del Ingress.

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

kubectl get ingress neg-demo-ing

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

Verifica el estado del servicio de backend

También puedes obtener el estado del servicio de backend de los balanceadores de cargas.

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

    gcloud compute backend-services list
    

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

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

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

    Reemplaza BACKEND_SERVICE_NAME por el nombre del servicio de backend.

Prueba el Ingress:

Otra manera de probar que el balanceador de cargas funciona como lo esperado es a través del escalamiento de la implementación de muestra, el envío de solicitudes de prueba al Ingress y la verificación de la respuesta de la cantidad correcta de réplicas.

  1. Escala el Deployment neg-demo-app de una instancia a dos instancias:

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

    Este comando puede tardar varios minutos en completarse.

  2. Verifica si se completó el lanzamiento:

    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 del Ingress:

    kubectl describe ingress neg-demo-ing
    

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

  4. Cuenta la cantidad de respuestas distintas del balanceador de cargas:

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

    Reemplaza IP_ADDRESS por la dirección IP del Ingress.

    El resultado es similar a este:

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

    En este resultado, la cantidad de respuestas distintas es la misma que la cantidad de réplicas, lo que indica que todos los pods de backend entregan tráfico.

Limpia

Después de completar las tareas de esta página, sigue estos pasos para quitar los recursos a continuación y evitar que se hagan cargos no deseados a tu cuenta:

Borra el clúster

gcloud

gcloud container clusters delete neg-demo-cluster

Console

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

    Ir a Google Kubernetes Engine

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

  3. Cuando se te solicite confirmar, haz clic en Borrar.

Soluciona problemas

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

  • Consulta la documentación del balanceo de cargas para obtener información sobre cómo enumerar los grupos de extremos de red.

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

    kubectl get svc SVC_NAME -o yaml
    

    La anotación metadata:annotations:cloud.google.com/neg-status genera una lista del NEG correspondiente del servicio y las zonas del NEG.

  • Puedes verificar el estado del servicio de 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, usa el siguiente comando:

    kubectl describe svc SERVICE_NAME
    

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

El tráfico no alcanza los extremos

Síntomas
502/503 o conexiones rechazadas.
Causas posibles

A los extremos nuevos, por lo general, se los puede alcanzar después de adjuntarlos al balanceador de cargas, siempre que respondan a la verificación de estado. Tal vez encuentres errores 502 o conexiones rechazadas si el tráfico no puede alcanzar los extremos.

Un contenedor que no controla SIGTERM, también puede provocar los errores 502 y las conexiones rechazadas. Si un contenedor no controla SIGTERM de forma explícita, termina y deja de controlar solicitudes de inmediato. El balanceador de cargas continúa enviando tráfico entrante al contenedor finalizado, lo que genera errores.

El balanceador de cargas nativo del contenedor solo tiene un extremo de backend. Durante una actualización progresiva, el extremo anterior se desprograma antes de que se programe el nuevo extremo.

Los Pods del backend se implementan en una zona nueva por primera vez después de que se aprovisiona un balanceador de cargas nativo del contenedor. La infraestructura del balanceador de cargas se programa en una zona cuando hay al menos un extremo en la zona. Cuando se agrega un extremo nuevo a una zona, se programa la infraestructura del balanceador de cargas y se interrumpe el servicio.

Solución

Configura los contenedores para manejar SIGTERM y continúa respondiendo a las solicitudes durante todo el período de gracia de terminación (30 segundos de forma predeterminada). Configura pods para comenzar a fallar las verificaciones de estado cuando reciben SIGTERM. Esto le indica al balanceador de cargas que deje de enviar tráfico al pod mientras la desprogramación del extremo está en progreso.

Si la aplicación no se cierra de forma correcta y deja de responder a las solicitudes cuando se recibe un SIGTERM, el hook preStop puede usarse para manejar SIGTERM y seguir entregando tráfico mientras la desprogramación del extremo está en curso.

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

Consulta la documentación sobre la terminación de Pods.

Si el backend del balanceador de cargas tiene una sola instancia, configura la estrategia de implementación para evitar que se destruya la única instancia antes de que la instancia nueva se programe por completo. En el caso de los pods de aplicaciones administrados por la carga de trabajo de Deployment, esto se puede lograr a través de la configuración de la estrategia de lanzamiento con el parámetro maxUnavailable igual a 0.

strategy:
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

Para solucionar problemas de tráfico que no llega a los extremos, verifica que las reglas de firewall permitan el tráfico entrante de TCP a tus extremos en los rangos 130.211.0.0/22 y 35.191.0.0/16. Para obtener más información, consulta Crea verificaciones de estado en la documentación de Cloud Load Balancing.

Revisa los servicios de backend de tu proyecto. La string de nombre del servicio de backend relevante incluye el nombre y el espacio de nombres del servicio de GKE correspondiente:

gcloud compute backend-services list

Recupera el estado del backend desde el servicio de backend:

gcloud compute backend-services get-health BACKEND_SERVICE_NAME

Si todos los backends están en mal estado, puede que tu firewall, Ingress o servicio estén mal configurados.

Si algunos backends están en mal estado durante un período breve, la causa podría ser la latencia de programación de la red.

Si algunos backends no aparecen en la lista de servicios de backend, la causa podría ser la latencia de programación de la red. Puedes verificar esto con la ejecución del comando siguiente, en que NEG_NAME es el nombre del servicio de backend. (Los NEG y los servicios de backend comparten el mismo nombre):

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

Verifica que todos los extremos esperados estén en el NEG.

Si tienes un pequeño número de backends (por ejemplo, 1 Pod) seleccionados por un balanceador de cargas nativo del contenedor, considera aumentar el número de réplicas y distribuir los pods de backend en todas las zonas que el clúster de GKE. intervalos. Esto garantizará que la infraestructura del balanceador de cargas subyacente esté por completo programada. De lo contrario, considera restringir los Pods del backend a una sola zona.

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

Lanzamiento suspendido

Síntomas
El lanzamiento de puestos de implementación actualizados y la cantidad de réplicas actualizadas no coincide con la cantidad deseada de réplicas.
Causas posibles

Las verificaciones de estado de la implementación están fallando. La imagen del contenedor puede ser mala o la verificación de estado puede estar mal configurada. El reemplazo continuo de pods espera hasta que el pod recién iniciado pase su puerta de preparación de pod. Esto solo ocurre si el Pod responde a las verificaciones de estado del balanceador de cargas. Si el pod no responde o la verificación de estado está mal configurada, no se pueden cumplir las condiciones de la puerta de preparación y el lanzamiento no puede continuar.

Si usas kubectl 1.13 o superior, puedes verificar el estado de las puertas de preparación de un pod con el siguiente comando:

kubectl get pod POD_NAME -o wide

Verifica la columna READINESS GATES.

Esta columna no existe en kubectl 1.12 y versiones anteriores. Un pod que está marcado en estado READY puede tener una puerta de preparación con errores. Para verificar esto, usa el siguiente comando:

kubectl get pod POD_NAME -o yaml

Las puertas de preparación y sus estados se enumeran en la salida.

Solución

Verifica que la imagen del contenedor en la especificación de su pod de implementación funcione de forma correcta y pueda responder a las verificaciones de estado. Comprueba que las verificaciones de estado estén configuradas de forma correcta.

Errores de modo degradado

Síntomas

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

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

Estas advertencias indican que GKE detectó parámetros de configuración incorrectos de extremo durante una actualización de NEG basada en objetos EndpointSlice, lo que activa un proceso de cálculo más detallado llamado modo degradado. GKE continúa actualizando los NEG sobre la base del mejor esfuerzo, ya que corrige la configuración incorrecta o excluye los extremos no válidos de las actualizaciones del NEG.

A continuación, se indican algunos de los errores comunes:

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

Por lo general, los estados transitorios generan estos eventos y se fijan por sí solos. Sin embargo, los eventos causados por parámetros de configuración incorrectos en objetos EndpointSlice personalizados no se resuelven. Para comprender la configuración incorrecta, examina los objetos EndpointSlice correspondientes al servicio:

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

Valida cada extremo según el error en el evento.

Para resolver el problema, debes modificar de forma manual los objetos EndpointSlice. La actualización activa los NEG para que se actualicen de nuevo. Una vez que la configuración incorrecta ya no existe, el resultado es similar al siguiente:

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

Problemas conocidos

El balanceo de cargas nativo del contenedor en GKE tiene los siguientes problemas conocidos:

Recolección incompleta de elementos no usados

GKE recolecta elementos no utilizados de los balanceadores de cargas nativos de contenedores cada dos minutos. Si se borra un clúster antes de que los balanceadores de cargas se quiten por completo, tienes que borrar los NEG del balanceador de cargas de forma manual.

Para revisar los NEG en tu proyecto, ejecuta el comando siguiente:

gcloud compute network-endpoint-groups list

En el resultado del comando, busca los NEG relevantes.

Para borrar un NEG, ejecuta el siguiente comando y reemplaza NEG_NAME por el nombre del NEG:

gcloud compute network-endpoint-groups delete NEG_NAME

Alinea los lanzamientos de carga de trabajo con la propagación de extremos

Cuando implementas una carga de trabajo en tu clúster o actualizas una carga de trabajo existente, el balanceador de cargas nativo del contenedor puede demorar más en propagar extremos nuevos que lo que demora en terminar el lanzamiento de la carga de trabajo. En la implementación de muestra que se implementa en esta guía, se usan dos campos para alinear su lanzamiento con la propagación de extremos: terminationGracePeriodSeconds y minReadySeconds.

terminationGracePeriodSeconds permite al Pod cerrarse de forma correcta. Esto es posible, ya que espera que las conexiones terminen después de que se programe la eliminación de un Pod.

minReadySeconds agrega un período de latencia después de que se crea un Pod. Especifica un número mínimo de segundos durante los cuales un pod nuevo debe estar en estado Ready, sin que ninguno de sus contenedores falle, para que el pod se considere disponible.

Debes configurar los valores de las cargas de trabajo minReadySeconds y terminationGracePeriodSeconds en 60 segundos o más para garantizar que el servicio no se interrumpa por lanzamientos de cargas de trabajo.

terminationGracePeriodSeconds está disponible en todas las especificaciones de pod y minReadySeconds está disponible para implementaciones y DaemonSets.

Para obtener más información sobre el ajuste preciso de los lanzamientos, consulta RollingUpdateStrategy.

No se respeta initialDelaySeconds en el readinessProbe del Pod

Quizás esperes que el balanceador de cargas nativo del contenedor respete la configuración de initialDelaySeconds en el readinessProbe del Pod. Sin embargo, kubelet implementa el readinessProbe, y la configuración de initialDelaySeconds controla la verificación de estado de kubelet, no el balanceador de cargas nativo del contenedor. El balanceo de cargas nativo del contenedor tiene su propia verificación de estado del balanceo de cargas.

¿Qué sigue?