Usar Envoy Proxy para balancear la carga de servicios gRPC en GKE

Last reviewed 2019-05-30 UTC

En este tutorial se muestra cómo exponer varios servicios de gRPC desplegados en Google Kubernetes Engine (GKE) en una única dirección IP externa mediante un balanceador de carga de red de pases externo y Envoy Proxy. En el tutorial se destacan algunas de las funciones avanzadas que proporciona Envoy para gRPC.

Introducción

gRPC es un framework de RPC de código abierto e independiente del lenguaje basado en HTTP/2 que usa búferes de protocolo para una representación eficiente en la conexión y una serialización rápida. Inspirado en Stubby, el framework interno de RPC de Google, gRPC permite la comunicación de baja latencia entre microservicios y entre clientes móviles y APIs.

gRPC se ejecuta a través de HTTP/2 y ofrece varias ventajas con respecto a HTTP/1.1, como la codificación binaria eficiente, la multiplexación de solicitudes y respuestas a través de una sola conexión y el control de flujo automático. gRPC también ofrece varias opciones para el balanceo de carga. Este tutorial se centra en situaciones en las que los clientes no son de confianza, como los clientes móviles y los clientes que se ejecutan fuera del límite de confianza del proveedor de servicios. De las opciones de balanceo de carga que ofrece gRPC, en este tutorial se utiliza el balanceo de carga basado en proxy.

En el tutorial, implementas un servicio de Kubernetes de TYPE=LoadBalancer, que se expone como un balanceador de carga de red de paso a través externo de capa de transporte (capa 4) enGoogle Cloud. Este servicio proporciona una única dirección IP pública y transfiere las conexiones TCP directamente a los backends configurados. En el tutorial, el backend es un Deployment de Kubernetes de instancias de Envoy.

Envoy es un proxy de capa de aplicación (capa 7) de código abierto que ofrece muchas funciones avanzadas. En este tutorial, lo usarás para finalizar las conexiones TLS y enrutar el tráfico de gRPC al servicio de Kubernetes adecuado. En comparación con otras soluciones de capa de aplicación, como Kubernetes Ingress, el uso directo de Envoy ofrece varias opciones de personalización, como las siguientes:

  • Descubrimiento de servicios
  • Algoritmos de balanceo de carga
  • Transformar solicitudes y respuestas (por ejemplo, a JSON o gRPC-Web)
  • Autenticar solicitudes validando tokens JWT
  • Comprobaciones del estado de gRPC

Si combinas un balanceador de carga de red con paso a través externo con Envoy, puedes configurar un endpoint (dirección IP externa) que reenvíe el tráfico a un conjunto de instancias de Envoy que se ejecuten en un clúster de Google Kubernetes Engine. Estas instancias usan información de la capa de aplicación para proxy las solicitudes a diferentes servicios gRPC que se ejecutan en el clúster. Las instancias de Envoy usan el DNS del clúster para identificar y balancear la carga de las solicitudes gRPC entrantes a los pods en buen estado y en ejecución de cada servicio. Esto significa que el tráfico se balancea entre los pods por solicitud RPC en lugar de por conexión TCP del cliente.

Arquitectura

En este tutorial, desplegarás dos servicios de gRPC, echo-grpc y reverse-grpc, en un clúster de Google Kubernetes Engine (GKE) y los expondrás a Internet en una dirección IP pública. En el siguiente diagrama se muestra la arquitectura para exponer estos dos servicios a través de un único endpoint:

Arquitectura para exponer `echo-grpc` y `reverse-grpc` a través de un único endpoint

Un balanceador de carga de red de paso a través externo acepta solicitudes entrantes de Internet (por ejemplo, de clientes móviles o consumidores de servicios que no pertenecen a tu empresa). El balanceador de carga de red de paso a través externo realiza las siguientes tareas:

  • Balancea la carga de las conexiones entrantes a los nodos del grupo. El tráfico se reenvía al servicio de Kubernetes envoy, que se expone en todos los nodos del clúster. El proxy de red de Kubernetes reenvía estas conexiones a los pods que ejecutan Envoy.
  • Realiza comprobaciones del estado HTTP en los nodos del clúster.

Envoy realiza las siguientes tareas:

  • Finaliza las conexiones TLS.
  • Descubre los pods que ejecutan los servicios gRPC consultando el servicio DNS del clúster interno.
  • Enruta y balancea la carga del tráfico a los pods del servicio gRPC.
  • Realiza comprobaciones del estado de los servicios gRPC según el protocolo de comprobación del estado de gRPC.
  • Expone un endpoint para comprobar el estado de las instancias de Envoy mediante el balanceador de carga de red de paso a través externo.

Los servicios de gRPC (echo-grpc y reverse-grpc) se exponen como servicios sin encabezado de Kubernetes. Esto significa que no se asigna ninguna dirección clusterIP y que el proxy de red de Kubernetes no equilibra la carga del tráfico a los pods. En su lugar, se crea un registro A de DNS que contiene las direcciones IP del pod en el servicio DNS del clúster. Envoy descubre las direcciones IP de los pods a partir de esta entrada DNS y equilibra la carga entre ellos según la política configurada en Envoy.

En el siguiente diagrama se muestran los objetos de Kubernetes que se usan en este tutorial:

Objetos de Kubernetes que se usan en este tutorial, como servicios, archivos YAML, registros A de DNS, secretos, pods y entradas de proxy

Costes

En este documento, se utilizan los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costes basada en el uso previsto, utiliza la calculadora de precios.

Los usuarios nuevos Google Cloud pueden disfrutar de una prueba gratuita.

Cuando termines las tareas que se describen en este documento, puedes evitar que se te siga facturando eliminando los recursos que has creado. Para obtener más información, consulta la sección Limpiar.

Antes de empezar

  1. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  2. Verify that billing is enabled for your Google Cloud project.

  3. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

    Preparar el entorno

    1. En Cloud Shell, define el proyecto de Google Cloud que quieras usar en este tutorial:

      gcloud config set project PROJECT_ID

      Sustituye PROJECT_ID por elGoogle Cloud ID de tu proyecto.

    2. Habilita las APIs Artifact Registry y GKE:

      gcloud services enable artifactregistry.googleapis.com \
          container.googleapis.com
      

    Crea el clúster de GKE.

    1. En Cloud Shell, crea un clúster de GKE para ejecutar tus servicios gRPC:

      gcloud container clusters create envoy-grpc-tutorial \
          --enable-ip-alias \
          --release-channel rapid \
          --scopes cloud-platform \
          --workload-pool PROJECT_ID.svc.id.goog \
          --location us-central1-f
      

      En este tutorial se usa la zona us-central1-f. Puedes usar una zona o región diferente.

    2. Verifica que se ha configurado el contexto kubectl enumerando los nodos de tu clúster:

      kubectl get nodes --output name
      

      El resultado debe ser similar a este:

      node/gke-envoy-grpc-tutorial-default-pool-c9a3c791-1kpt
      node/gke-envoy-grpc-tutorial-default-pool-c9a3c791-qn92
      node/gke-envoy-grpc-tutorial-default-pool-c9a3c791-wf2h
      

    Crear el repositorio de Artifact Registry

    1. En Cloud Shell, crea un repositorio para almacenar imágenes de contenedor:

      gcloud artifacts repositories create envoy-grpc-tutorial-images \
          --repository-format docker \
          --location us-central1
      

      Crea el repositorio en la misma región que el clúster de GKE para optimizar la latencia y el ancho de banda de la red cuando los nodos extraigan imágenes de contenedor.

    2. Asigna el rol Lector de Artifact Registry al repositorio a la cuenta de servicio de Google que usan las VMs de nodo del clúster de GKE:

      PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format 'value(projectNumber)')
      
      gcloud artifacts repositories add-iam-policy-binding envoy-grpc-tutorial-images \
          --location us-central1 \
          --member serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com \
          --role roles/artifactregistry.reader
      
    3. Añade una entrada de asistente de credenciales para el nombre de host del repositorio al archivo de configuración de Docker en tu directorio principal de Cloud Shell:

      gcloud auth configure-docker us-central1-docker.pkg.dev
      

      La entrada del asistente de credenciales permite que las herramientas de imágenes de contenedor que se ejecutan en Cloud Shell se autentiquen en la ubicación del repositorio de Artifact Registry para extraer e insertar imágenes.

    Desplegar los servicios gRPC

    Para dirigir el tráfico a varios servicios gRPC que se encuentran detrás de un balanceador de carga, implementa dos servicios gRPC de ejemplo: echo-grpc y reverse-grpc. Ambos servicios exponen un método unario que toma una cadena en el campo de solicitud content. echo-grpc responde con el contenido sin modificar, mientras que reverse-grpc responde con la cadena de contenido invertida.

    1. En Cloud Shell, clona el repositorio que contiene los servicios gRPC y cambia al directorio del repositorio:

      git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
      cd kubernetes-engine-samples/networking/grpc-gke-nlb-tutorial/
      
    2. Crea un certificado TLS con firma automática y una clave privada:

      openssl req -x509 -newkey rsa:4096 -nodes -sha256 -days 365 \
          -keyout privkey.pem -out cert.pem -extensions san \
          -config \
          <(echo "[req]";
            echo distinguished_name=req;
            echo "[san]";
            echo subjectAltName=DNS:grpc.example.com
           ) \
          -subj '/CN=grpc.example.com'
      
    3. Crea un secreto de Kubernetes llamado envoy-certs que contenga el certificado TLS autofirmado y la clave privada:

      kubectl create secret tls envoy-certs \
          --key privkey.pem --cert cert.pem \
          --dry-run=client --output yaml | kubectl apply --filename -
      

      Envoy usa este certificado TLS y esta clave privada cuando finaliza las conexiones TLS.

    4. Crea las imágenes de contenedor de las aplicaciones de ejemplo echo-grpc y reverse-grpc, sube las imágenes a Artifact Registry y despliega las aplicaciones en el clúster de GKE con Skaffold:

      skaffold run \
          --default-repo=us-central1-docker.pkg.dev/PROJECT_ID/envoy-grpc-tutorial-images \
          --module=echo-grpc,reverse-grpc \
          --skip-tests
      

      Skaffold es una herramienta de código abierto de Google que automatiza los flujos de trabajo para desarrollar, compilar, enviar e implementar aplicaciones como contenedores.

    5. Despliega Envoy en el clúster de GKE con Skaffold:

      skaffold run \
          --digest-source=none \
          --module=envoy \
          --skip-tests
      
    6. Comprueba que haya dos pods listos para cada implementación:

      kubectl get deployments
      

      La salida tiene un aspecto similar al siguiente. Los valores de READY deben ser 2/2 en todas las implementaciones.

      NAME           READY   UP-TO-DATE   AVAILABLE   AGE
      echo-grpc      2/2     2            2           1m
      envoy          2/2     2            2           1m
      reverse-grpc   2/2     2            2           1m
      
    7. Verifica que echo-grpc, envoy y reverse-grpc existan como servicios de Kubernetes:

      kubectl get services --selector skaffold.dev/run-id
      

      La salida tiene un aspecto similar al siguiente. Tanto echo-grpc como reverse-grpc deben tener TYPE=ClusterIP y CLUSTER-IP=None.

      NAME           TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)         AGE
      echo-grpc      ClusterIP      None          <none>           8081/TCP        2m
      envoy          LoadBalancer   10.40.2.203   203.0.113.1      443:31516/TCP   2m
      reverse-grpc   ClusterIP      None          <none>           8082/TCP        2m
      

    Probar los servicios gRPC

    Para probar los servicios, utiliza la herramienta de línea de comandos grpcurl.

    1. En Cloud Shell, instala grpcurl:

      go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
      
    2. Obtén la dirección IP externa del servicio de Kubernetes envoy y guárdala en una variable de entorno:

      EXTERNAL_IP=$(kubectl get service envoy \
          --output=jsonpath='{.status.loadBalancer.ingress[0].ip}')
      
    3. Envía una solicitud a la aplicación de ejemplo de echo-grpc:

      grpcurl -v -d '{"content": "echo"}' \
          -proto echo-grpc/api/echo.proto \
          -authority grpc.example.com -cacert cert.pem \
          $EXTERNAL_IP:443 api.Echo/Echo
      

      El resultado debe ser similar a este:

      Resolved method descriptor:
      rpc Echo ( .api.EchoRequest ) returns ( .api.EchoResponse );
      
      Request metadata to send:
      (empty)
      
      Response headers received:
      content-type: application/grpc
      date: Wed, 02 Jun 2021 07:18:22 GMT
      hostname: echo-grpc-75947768c9-jkdcw
      server: envoy
      x-envoy-upstream-service-time: 3
      
      Response contents:
      {
        "content": "echo"
      }
      
      Response trailers received:
      (empty)
      Sent 1 request and received 1 response
      

      El encabezado de respuesta hostname muestra el nombre del pod echo-grpc que ha gestionado la solicitud. Si repites el comando varias veces, deberías ver dos valores diferentes para el encabezado de respuesta hostname, que corresponden a los nombres de los pods echo-grpc.

    4. Verifica el mismo comportamiento con el servicio gRPC inverso:

      grpcurl -v -d '{"content": "reverse"}' \
          -proto reverse-grpc/api/reverse.proto \
          -authority grpc.example.com -cacert cert.pem \
          $EXTERNAL_IP:443 api.Reverse/Reverse
      

      El resultado debe ser similar a este:

      Resolved method descriptor:
      rpc Reverse ( .api.ReverseRequest ) returns ( .api.ReverseResponse );
      
      Request metadata to send:
      (empty)
      
      Response headers received:
      content-type: application/grpc
      date: Wed, 02 Jun 2021 07:20:15 GMT
      hostname: reverse-grpc-5c9b974f54-wlfwt
      server: envoy
      x-envoy-upstream-service-time: 1
      
      Response contents:
      {
        "content": "esrever"
      }
      
      Response trailers received:
      (empty)
      Sent 1 request and received 1 response
      

    Configuración de Envoy

    Para entender mejor la configuración de Envoy, puedes consultar el archivo de configuración envoy/k8s/envoy.yaml en el repositorio de Git.

    En la sección route_config se especifica cómo se dirigen las solicitudes entrantes a las aplicaciones de ejemplo echo-grpc y reverse-grpc.

    route_config:
      name: local_route
      virtual_hosts:
      - name: local_service
        domains:
        - "*"
        routes:
        - match:
            prefix: "/api.Echo/"
          route:
            cluster: echo-grpc
        - match:
            prefix: "/api.Reverse/"
          route:
            cluster: reverse-grpc

    Las aplicaciones de ejemplo se definen como clústeres de Envoy.

    clusters:
    - name: echo-grpc
      connect_timeout: 0.5s
      type: STRICT_DNS
      dns_lookup_family: V4_ONLY
      lb_policy: ROUND_ROBIN
      http2_protocol_options: {}
      load_assignment:
        cluster_name: echo-grpc
        endpoints:
        - lb_endpoints:
          - endpoint:
              address:
                socket_address:
                  address: echo-grpc.default.svc.cluster.local
                  port_value: 8081
      health_checks:
        timeout: 1s
        interval: 10s
        unhealthy_threshold: 2
        healthy_threshold: 2
        grpc_health_check: {}

    Los campos type: STRICT_DNS y lb_policy: ROUND_ROBIN de la definición del clúster especifican que Envoy realiza búsquedas de DNS del nombre de host especificado en el campo address y equilibra la carga entre las direcciones IP de la respuesta a la búsqueda de DNS. La respuesta contiene varias direcciones IP porque los objetos de servicio de Kubernetes que definen las aplicaciones de ejemplo especifican servicios sin encabezado.

    El campo http2_protocol_options especifica que Envoy usa el protocolo HTTP/2 en las aplicaciones de muestra.

    El campo grpc_health_check de la sección health_checks especifica que Envoy usa el protocolo de comprobación de estado de gRPC para determinar el estado de las aplicaciones de ejemplo.

    Solucionar problemas

    Si tienes problemas con este tutorial, te recomendamos que consultes estos documentos:

    También puedes consultar la interfaz de administración de Envoy para diagnosticar problemas con la configuración de Envoy.

    1. Para abrir la interfaz de administración, configura la redirección de puertos desde Cloud Shell al puerto admin de uno de los pods de Envoy:

      kubectl port-forward \
          $(kubectl get pods -o name | grep envoy | head -n1) 8080:8090
      
    2. Espera hasta que veas este resultado en la consola:

      Forwarding from 127.0.0.1:8080 -> 8090
      
    3. En Cloud Shell, haz clic en el botón Vista previa web y selecciona Obtener vista previa en el puerto 8080. Se abrirá una nueva ventana del navegador con la interfaz de administración.

      Interfaz de administrador de Envoy con la vista previa seleccionada

    4. Cuando hayas terminado, vuelve a Cloud Shell y pulsa Control+C para finalizar el reenvío de puertos.

    Formas alternativas de enrutar el tráfico de gRPC

    Puedes modificar esta solución de varias formas para adaptarla a tu entorno.

    Balanceadores de carga de capa de aplicación alternativos

    Algunas de las funciones de la capa de aplicación que ofrece Envoy también pueden proporcionarse mediante otras soluciones de balanceo de carga:

    • Puedes usar un balanceador de carga de aplicación externo global o un balanceador de carga de aplicación externo regional en lugar de un balanceador de carga de red con paso a través externo y Envoy autogestionado. Usar un balanceador de carga de aplicaciones externo ofrece varias ventajas en comparación con un balanceador de carga de red de transferencia externo, como funciones avanzadas de gestión del tráfico, certificados TLS gestionados e integración con otros productos, como Cloud CDN, Google Cloud Armor e IAP. Google Cloud

      Te recomendamos que uses un balanceador de carga de aplicación externo global o regional si las funciones de gestión del tráfico que ofrecen se ajustan a tus casos prácticos y si no necesitas compatibilidad con la autenticación basada en certificados de cliente, también conocida como autenticación TLS mutua (mTLS). Para obtener más información, consulte los documentos siguientes:

    • Si utilizas Cloud Service Mesh o Istio, puedes usar sus funciones para enrutar y balancear la carga del tráfico de gRPC. Tanto Cloud Service Mesh como Istio proporcionan una puerta de enlace de entrada que se implementa como un balanceador de carga de red de transferencia externo con un backend de Envoy, de forma similar a la arquitectura de este tutorial. La principal diferencia es que Envoy se configura a través de los objetos de enrutamiento del tráfico de Istio.

      Para que los servicios de ejemplo de este tutorial se puedan enrutar en la malla de servicios de Cloud o en la de Istio, debes quitar la línea clusterIP: None de los manifiestos de servicio de Kubernetes (echo-service.yaml y reverse-service.yaml). Esto significa que debes usar las funciones de descubrimiento de servicios y de balanceo de carga de la malla de servicios de Cloud o de Istio en lugar de las funciones similares de Envoy.

      Si ya usas Cloud Service Mesh o Istio, te recomendamos que uses la puerta de enlace de entrada para enrutar a tus servicios gRPC.

    • Puedes usar NGINX en lugar de Envoy, ya sea como un Deployment o con el controlador de entradas NGINX para Kubernetes. En este tutorial se usa Envoy porque proporciona funciones de gRPC más avanzadas, como la compatibilidad con el protocolo de comprobación de estado de gRPC.

    Conectividad de red VPC interna

    Si quieres exponer los servicios fuera de tu clúster de GKE, pero solo dentro de tu red de VPC, puedes usar un balanceador de carga de red de transferencia interno o un balanceador de carga de aplicaciones interno.

    Para usar un balanceador de carga de red de paso a través interno en lugar de uno externo, añade la anotación cloud.google.com/load-balancer-type: "Internal" al manifiesto envoy-service.yaml.

    Para usar un balanceador de carga de aplicación interno, consulta la documentación sobre cómo configurar objetos Ingress para balanceadores de carga de aplicación internos.

    Limpieza

    Cuando hayas terminado el tutorial, puedes eliminar los recursos que has creado para que dejen de usar cuota y generar cargos. En las siguientes secciones se explica cómo eliminar o desactivar dichos recursos.

    Eliminar el proyecto

    1. In the Google Cloud console, go to the Manage resources page.

      Go to Manage resources

    2. In the project list, select the project that you want to delete, and then click Delete.
    3. In the dialog, type the project ID, and then click Shut down to delete the project.

    Eliminar los recursos

    Si quieres conservar el Google Cloud proyecto que has usado en este tutorial, elimina los recursos:

    1. En Cloud Shell, elimina la clonación del repositorio de Git local:

      cd ; rm -rf kubernetes-engine-samples/networking/grpc-gke-nlb-tutorial/
      
    2. Elimina el clúster de GKE:

      gcloud container clusters delete envoy-grpc-tutorial \
          --location us-central1-f --async --quiet
      
    3. Elimina el repositorio en Artifact Registry:

      gcloud artifacts repositories delete envoy-grpc-tutorial-images \
          --location us-central1 --async --quiet
      

    Siguientes pasos