Usa el proxy Envoy para balancear las cargas de los servicios de gRPC en GKE

Last reviewed 2019-05-30 UTC

En este instructivo, se explica cómo exponer varios servicios de gRPC implementados en Google Kubernetes Engine (GKE) en una sola dirección IP externa mediante el balanceador de cargas de red de transferencia externo y el proxy Envoy. En este instructivo, se destacan algunas de las funciones avanzadas que Envoy proporciona a gRPC.

Introducción

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

gRPC se ejecuta en HTTP/2 y ofrece varias ventajas respecto de HTTP/1.1, como codificación binaria eficiente, multiplexación de solicitudes y respuestas en una sola conexión, y control de flujo automático. gRPC también ofrece varias opciones para el balanceo de cargas. En este instructivo, nos enfocaremos en situaciones en las que los clientes no son de confianza, como clientes móviles y clientes que se ejecutan fuera del límite de confianza del proveedor de servicios. De las opciones de balanceo de cargas que proporciona gRPC, en este instructivo, se usa el balanceo de cargas basado en proxy.

En el instructivo, debes implementar un Service de Kubernetes de TYPE=LoadBalancer, que se expone como balanceador de cargas de red de transferencia externo de la capa de transporte (capa 4) en Google Cloud. Este servicio proporciona una única dirección IP pública y pasa las conexiones TCP directamente a los backends configurados. En el instructivo, el backend es una implementación de Kubernetes de las 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 instructivo, lo usarás para finalizar las conexiones TLS y enrutar el tráfico de gRPC al Service de Kubernetes correspondiente. En comparación con otras soluciones de capa de aplicación, como Ingress de Kubernetes, el uso de Envoy proporciona varias opciones de personalización, como las siguientes:

  • Descubrimiento de servicios
  • Algoritmos de balanceo de cargas
  • Transformación de solicitudes y respuestas (por ejemplo, a JSON o gRPC-Web)
  • Autenticación de solicitudes mediante la validación de tokens JWT
  • Verificaciones de estado de gRPC

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

Arquitectura

En este instructivo, implementará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 extremo único:

Arquitectura para exponer “echo-grpc” y “reverse-grpc” a través de un extremo único

Un balanceador de cargas de red de transferencia externo acepta solicitudes entrantes de Internet (por ejemplo, de clientes móviles o consumidores de servicios externos). El balanceador de cargas de red de transferencia externo realiza las siguientes tareas:

  • Balancea las cargas de las conexiones entrantes a los nodos en el grupo. El tráfico se reenvía al Service 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 verificaciones de estado HTTP en los nodos del clúster.

Envoy realiza las siguientes tareas:

  • Finaliza las conexiones TLS.
  • Descubre los Pods que ejecutan los servicios de gRPC mediante una consulta al servicio DNS del clúster interno.
  • Enruta y balancea las cargas del tráfico a los Pods de servicio de gRPC.
  • Realiza verificaciones de estado de los servicios de gRPC de acuerdo con el Protocolo de verificación de estado de gRPC.
  • Expone un extremo para la verificación de estado de las instancias de Envoy mediante el balanceador de cargas de red de transferencia externo.

Los servicios de gRPC (echo-grpc y reverse-grpc) se exponen como Servicios sin interfaz gráfica de Kubernetes. Esto significa que no se asigna ninguna dirección clusterIP, y que el proxy de red de Kubernetes no balancea las cargas 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 del Pod de esta entrada de DNS y balancea las cargas entre ellas de acuerdo con la política configurada en Envoy.

En el siguiente diagrama, se muestran los objetos de Kubernetes involucrados en este instructivo:

Objetos de Kubernetes que se usan en este instructivo, incluidos servicios, archivos YAML, registros A de DNS, secretos, Pods y entradas de proxy

Costos

En este documento, usarás los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios. Es posible que los usuarios nuevos de Google Cloud califiquen para obtener una prueba gratuita.

Cuando finalices las tareas que se describen en este documento, puedes borrar los recursos que creaste para evitar que continúe la facturación. Para obtener más información, consulta Cómo realizar una limpieza.

Antes de comenzar

  1. En la página del selector de proyectos de la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud.

    Ir al selector de proyectos

  2. Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud.

  3. En la consola de Google Cloud, activa Cloud Shell.

    Activar Cloud Shell

Prepare el entorno

  1. En Cloud Shell, configura el proyecto de Google Cloud que deseas usar para este instructivo:

    gcloud config set project PROJECT_ID
    

    Reemplaza PROJECT_ID con tu ID del proyecto de Google Cloud.

  2. Habilita las APIs de 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 los servicios de gRPC:

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

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

  2. Genera una lista de los nodos en el clúster para verificar que se haya configurado el contexto kubectl:

    kubectl get nodes --output name
    

    El resultado es similar al siguiente:

    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
    

Crea el repositorio de Artifact Registry

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

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

    El repositorio se crea en la misma región que el clúster de GKE para ayudar a optimizar la latencia y el ancho de banda de la red cuando los nodos extraen imágenes de contenedor.

  2. Otorga el rol Lector de Artifact Registry en el repositorio a la cuenta de servicio de Google que usan las VMs del 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. Agrega una entrada auxiliar 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 auxiliar de credenciales permite que las herramientas de imagen de contenedor que se ejecutan en Cloud Shell se autentiquen en la ubicación del repositorio de Artifact Registry para extraer y enviar imágenes.

Implementa los servicios de gRPC

Para enrutar el tráfico a varios servicios de gRPC detrás de un balanceador de cargas, implementa dos servicios de gRPC de muestra: echo-grpc y reverse-grpc. Ambos servicios exponen un método unario que toma una string en el campo de la solicitud content. echo-grpc responde con el contenido sin alteraciones, mientras que reverse-grpc responde con la string de contenido invertida.

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

    git clone https://github.com/GoogleCloudPlatform/grpc-gke-nlb-tutorial.git ~/grpc-gke-nlb-tutorial
    
    cd ~/grpc-gke-nlb-tutorial
    
  2. Crea una clave privada y un certificado TLS autofirmado:

    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 la clave privada cuando finaliza las conexiones TLS.

  4. Compila las imágenes del contenedor para las apps de muestra echo-grpc y reverse-grpc, envía las imágenes a Artifact Registry e implementa las apps en el clúster de GKE mediante 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. Implementa Envoy en el clúster de GKE con Skaffold:

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

    kubectl get deployments
    

    El resultado es similar al siguiente. Los valores de READY deben ser 2/2 para 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 Services de Kubernetes:

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

    El resultado es similar al siguiente. echo-grpc y 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
    

Prueba los servicios de gRPC

Para probar los servicios, usa la herramienta de línea de comandos de 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 Service de Kubernetes envoy y almacénala 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 app de muestra 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
    

    La salida es similar a la siguiente:

    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 controló 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 Reverse de gRPC:

    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 es similar al siguiente:

    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 comprender 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 enrutan las solicitudes entrantes a las apps de muestra 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 apps de muestra 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 en la definición del clúster especifican que Envoy realiza búsquedas de DNS del nombre de host especificado en el campo address y balancea las cargas en las direcciones IP de la respuesta a la búsqueda de DNS. La respuesta contiene varias direcciones IP porque los objetos Service de Kubernetes que definen las apps de muestra especifican servicios sin interfaz gráfica.

En el campo http2_protocol_options, se especifica que Envoy usa el protocolo HTTP/2 en las apps de muestra.

En el campo grpc_health_check de la sección health_checks, se especifica que Envoy usa el protocolo de verificación de estado de gRPC para determinar el estado de las apps de muestra.

Solución de problemas

Si tienes problemas con este instructivo, te recomendamos que revises los siguientes documentos:

También puedes explorar 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 de 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 el siguiente resultado en la consola:

    Forwarding from 127.0.0.1:8080 -> 8090
    
  3. Haz clic en el botón Vista previa en la Web en Cloud Shell y selecciona Vista previa en el puerto 8080 (Preview on port 8080). Se abrirá una nueva ventana del navegador que mostrará la interfaz de administración.

    Interfaz de administrador de Envoy con la vista previa seleccionada

  4. Cuando termines, vuelve a Cloud Shell y presiona Control+C para finalizar la redirección de puertos.

Formas alternativas de enrutar el tráfico de gRPC

Puedes modificar esta solución de varias maneras para adaptarla al entorno.

Balanceadores de cargas alternativos de la capa de aplicación

Algunas de las funciones de la capa de aplicación que proporciona Envoy también se pueden proporcionar con las siguientes soluciones de balanceo de cargas:

  • Puedes usar un balanceador de cargas de aplicaciones externo global o un balanceador de cargas de aplicaciones externo regional en lugar de un balanceador de cargas de red de transferencia externo y Envoy autoadministrado. El uso de un balanceador de cargas de aplicaciones externo proporciona varios beneficios en comparación con un balanceador de cargas de red de transferencia externo, como la capacidad avanzada de administración de tráfico, los certificados TLS administrados y la integración con otros productos de Google Cloud, como Cloud CDN, Google Cloud Armor e IAP.

    Recomendamos que uses un balanceador de cargas aplicaciones externo global o un balanceador de cargas de aplicaciones externo regional si las funciones de administración del tráfico que ofrecen satisfacen tus casos de uso y si no necesitas asistencia para la autenticación de clientes basada en certificados, también conocida como autenticación TLS mutua (mTLS). Para obtener más información, consulta los siguientes documentos:

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

    Para que los servicios de ejemplo de este instructivo se puedan enrutar en Anthos Service Mesh o Istio, debes quitar la línea clusterIP: None de los manifiestos del Service de Kubernetes (echo-service.yaml y reverse-service.yaml). Esto significa que debes usar la función de descubrimiento de servicios y balanceo de cargas de Anthos Service Mesh o Istio en lugar de la función similar en Envoy.

    Si ya usas Anthos Service Mesh o Istio, te recomendamos usar la puerta de enlace de entrada para enrutar a los servicios de gRPC.

  • Puedes usar NGINX en lugar de Envoy, ya sea como una implementación o mediante el Controlador de Ingress de NGINX para Kubernetes. Envoy se usa en este instructivo porque proporciona una función de gRPC más avanzada, como la compatibilidad con el protocolo de verificación de estado de gRPC.

Conectividad de red de VPC interna

Si deseas exponer los servicios fuera del clúster de GKE, pero solo dentro de la red de VPC, puedes usar un balanceador de cargas de red de transferencia interno o un balanceador de cargas de aplicaciones interno.

Para usar un balanceador de cargas de red de transferencia interno en lugar de un balanceador de cargas de red de transferencia externo, agrega la anotación cloud.google.com/load-balancer-type: "Internal" al manifiesto envoy-service.yaml.

Si deseas usar un balanceador de cargas de aplicaciones interno, consulta la documentación sobre cómo configurar Ingress para balanceadores de cargas de aplicaciones internos.

Limpia

Una vez que completes el instructivo, puedes limpiar los recursos que creaste para que dejen de usar la cuota y generar cargos. En las siguientes secciones, se describe cómo borrar o desactivar estos recursos.

Borra el proyecto

  1. En la consola de Google Cloud, ve a la página Administrar recursos.

    Ir a Administrar recursos

  2. En la lista de proyectos, elige el proyecto que quieres borrar y haz clic en Borrar.
  3. En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.

Borra recursos

Si deseas conservar el proyecto Google Cloud que usaste en este instructivo, borra los recursos individuales:

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

    cd ; rm -rf ~/grpc-gke-nlb-tutorial
    
  2. Borra el clúster de GKE:

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

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

¿Qué sigue?