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

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 balanceo de cargas de red 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 Servicio de Kubernetes de TYPE=LoadBalancer, que se expone como balanceo de cargas de red 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 SSL/TLS y enrutar el tráfico de gRPC al Servicio 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 el balanceo de cargas de red mediante 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 GKE. 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.

Costos

En este instructivo, se usan 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 sean aptos para obtener una prueba gratuita.

Cuando finalices este instructivo, podrás borrar los recursos creados para evitar que se te siga facturando. Para obtener más información, consulta cómo hacer una limpieza.

Antes de comenzar

  1. Accede a tu Cuenta de Google.

    Si todavía no tienes una cuenta, regístrate para obtener una nueva.

  2. En la página de selección de proyectos de Cloud Console, selecciona o crea un proyecto de Cloud.

    Ir a la página Selector de proyectos

  3. Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud. Obtén información sobre cómo confirmar que tienes habilitada la facturación para tu proyecto.

  4. Habilita las API de Cloud Build, Container Registry, and Container Analysis.

    Habilita las API

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

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

  • Balancea las cargas de las conexiones entrantes a los nodos trabajadores en el grupo. El tráfico se reenvía al Servicio de Kubernetes envoy, que se expone en todos los nodos trabajadores 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 trabajadores del clúster.

Envoy realiza las siguientes tareas:

  • Finaliza las conexiones SSL/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 mediante el balanceo de cargas de red.

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

Inicializa el entorno

En esta sección, debes establecer variables de entorno que se usarán más adelante en el instructivo.

  1. Abre Cloud Shell:

    IR A Cloud Shell

    Usa Cloud Shell para ejecutar todos los comandos de este instructivo.

  2. En Cloud Shell, muestra el ID del proyecto actual:

    gcloud config list --format 'value(core.project)'
    
  3. Si mediante el comando no se muestra el ID del proyecto que seleccionaste, configura Cloud Shell para usar tu proyecto y reemplaza project-id por el nombre de tu proyecto:

    gcloud config set project project-id
    
  4. Define las variables de entorno para la región y la zona que deseas usar en este instructivo:

    REGION=us-central1
    ZONE=$REGION-b
    

    En este instructivo, se usa la región us-central1 y la zona us-central1-b. Sin embargo, puedes cambiar la región y la zona para que se adapten a tus necesidades.

Crea el clúster de GKE

  1. Crea un clúster de GKE para ejecutar los servicios de gRPC:

    gcloud container clusters create grpc-cluster --zone $ZONE
    
  2. Genera una lista de los nodos trabajados en el clúster para verificar que se haya configurado el contexto kubectl:

    kubectl get nodes -o name
    

    El resultado es similar al siguiente:

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

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 simples: echo-grpc y reverse-grpc. Ambos servicios exponen un método unario que toma una string en el campo de la solicitud de contenido. echo-grpc responde con el contenido sin alteraciones, mientras que reverse-grpc responde con la string de contenido invertida.

  1. Clona el repositorio que contiene los servicios de gRPC y cambia al directorio de trabajo:

    git clone https://github.com/GoogleCloudPlatform/grpc-gke-nlb-tutorial
    cd grpc-gke-nlb-tutorial
    
  2. Mediante Cloud Build, crea las imágenes del contenedor para los servicios de gRPC de Echo y Reverse, y almacénalas en Container Registry:

    gcloud builds submit -t gcr.io/$GOOGLE_CLOUD_PROJECT/echo-grpc echo-grpc
    
    gcloud builds submit -t gcr.io/$GOOGLE_CLOUD_PROJECT/reverse-grpc reverse-grpc
    
  3. Verifica que las imágenes existan en Container Registry:

    gcloud container images list --repository gcr.io/$GOOGLE_CLOUD_PROJECT
    

    El resultado es similar al siguiente:

    NAME
    gcr.io/grpc-gke-nlb-tutorial/echo-grpc
    gcr.io/grpc-gke-nlb-tutorial/reverse-grpc
    
  4. Crea implementaciones de Kubernetes para echo-grpc y reverse-grpc:

    sed s/GOOGLE_CLOUD_PROJECT/$GOOGLE_CLOUD_PROJECT/ \
        k8s/echo-deployment.yaml | kubectl apply -f -
    
    sed s/GOOGLE_CLOUD_PROJECT/$GOOGLE_CLOUD_PROJECT/ \
        k8s/reverse-deployment.yaml | kubectl apply -f -
    
  5. Verifica que haya dos Pods disponibles para cada implementación:

    kubectl get deployments
    

    El resultado es similar al siguiente. Los valores de DESIRED, CURRENT, UP-TO-DATE y AVAILABLE deben ser 2 para ambas implementaciones.

    NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    echo-grpc      2         2         2            2           1m
    reverse-grpc   2         2         2            2           1m
    
  6. Crea Servicios sin interfaz gráfica de Kubernetes para echo-grpc y reverse-grpc. Estos comandos crean registros A de DNS en el servicio de DNS del clúster, pero no asignan direcciones IP virtuales.

    kubectl apply -f k8s/echo-service.yaml
    kubectl apply -f k8s/reverse-service.yaml
    
  7. Comprueba que echo-grpc y reverse-grpc existan como Servicios de Kubernetes:

    kubectl get services
    

    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   35s
    kubernetes     ClusterIP   10.0.0.1     <none>        443/TCP    47m
    reverse-grpc   ClusterIP   None         <none>        8082/TCP   21s
    

Configura el balanceo de cargas de red

  1. Crea un Servicio de Kubernetes de tipo LoadBalancer en el clúster:

    kubectl apply -f k8s/envoy-service.yaml
    

    Mediante este comando, se aprovisionan los recursos necesarios para el balanceo de cargas de red y se asigna una dirección IP pública efímera. La asignación de la dirección IP pública puede tomar unos minutos.

  2. Ejecuta el siguiente comando y espera hasta que el valor de EXTERNAL-IP para el servicio envoy cambie de <pending> a una dirección IP pública:

    kubectl get services envoy --watch
    
  3. Presiona Control+C para dejar de esperar.

Crea un certificado SSL/TLS autofirmado

Envoy usa un certificado y una clave cuando finaliza las conexiones SSL/TLS. Para comenzar, crea un certificado SSL/TLS autofirmado.

  1. Crea una variable de entorno para almacenar la dirección IP pública del servicio de Envoy que creaste en la sección anterior:

    EXTERNAL_IP=$(kubectl get service envoy -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    
  2. Crea una clave y un certificado SSL/TLS autofirmado:

    openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
        -keyout privkey.pem -out cert.pem -subj "/CN=$EXTERNAL_IP"
    
  3. Crea un secreto TLS de Kubernetes llamado envoy-certs que contenga la clave y el certificado SSL/TLS autofirmado:

    kubectl create secret tls envoy-certs \
        --key privkey.pem --cert cert.pem \
        --dry-run -o yaml | kubectl apply -f -
    

Implementa Envoy

  1. Crea un ConfigMap de Kubernetes para almacenar el archivo de configuración de Envoy (envoy.yaml):

    kubectl apply -f k8s/envoy-configmap.yaml
    
  2. Crea una implementación de Kubernetes para Envoy:

    kubectl apply -f k8s/envoy-deployment.yaml
    
  3. Verifica que haya dos Pods envoy en ejecución:

    kubectl get deployment envoy
    

    El resultado es similar al siguiente:

    NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    envoy     2         2         2            2           1m
    

Ya estás listo para probar los servicios de gRPC.

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 get github.com/fullstorydev/grpcurl
    go install github.com/fullstorydev/grpcurl/cmd/grpcurl
    
  2. Envía una solicitud al servicio Echo de gRPC:

    grpcurl -d '{"content": "echo"}' -proto echo-grpc/api/echo.proto \
        -insecure -v $EXTERNAL_IP:443 api.Echo/Echo
    

    El resultado es similar al 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, 27 Feb 2019 04:40:19 GMT
    hostname: echo-grpc-5c4f59c578-wcsvr
    server: envoy
    x-envoy-upstream-service-time: 0
    
    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.

  3. Verifica el mismo comportamiento con el servicio Reverse de gRPC:

    grpcurl -d '{"content": "reverse"}' -proto reverse-grpc/api/reverse.proto \
        -insecure -v $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, 27 Feb 2019 04:45:56 GMT
    hostname: reverse-grpc-74cdc4849f-tvsfb
        server: envoy
    x-envoy-upstream-service-time: 2
    
    Response contents:
    {
      "content": "esrever"
    }
    
    Response trailers received:
    (empty)
    Sent 1 request and received 1 response
    

Soluciona 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 configurar el balanceo de cargas de HTTP(S) mediante un objeto Ingress de Kubernetes y usarlo en lugar del balanceo de cargas de red y Envoy. El uso del balanceo de cargas de HTTP(S) proporciona varios beneficios en comparación con el balanceo de cargas de red, como los certificados SSL/TLS administrados y la integración con otros productos de Google Cloud, como IAP y Cloud CDN.

    Recomendamos que uses el balanceo de cargas de HTTP(S) cuando no necesites asistencia para realizar las siguientes tareas:

    • Verificaciones de estado de gRPC
    • Control detallado del algoritmo de balanceo de cargas
    • Exposición de más de 50 servicios

    Para obtener más información sobre cómo implementar el balanceo de cargas de HTTP(S) mediante un servicio de gRPC de muestra, consulta la Documentación de Google Kubernetes Engine sobre Ingress y el Instructivo de balanceo de cargas del Ingress de gRPC de GKE en GitHub.

  • Si usas Istio, puedes usar sus funciones para enrutar y balancear la carga del tráfico de gRPC. La puerta de enlace de Ingress de Istio se implementa como balanceo de cargas de red mediante un backend de Envoy, similar a la arquitectura de este instructivo. La diferencia principal es que el proxy de 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 la malla de servicios de Istio, debes quitar la línea clusterIP: None de los manifiestos del Servicio 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 Istio en lugar de la función similar en Envoy. Si ya usas Istio, te recomendamos usar la puerta de enlace de Ingress para enrutar 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.

  • Puedes usar Ambassador y Contour, que proporcionan controladores de Ingress de Kubernetes y se basan en Envoy.

  • Puedes usar Voyager, que es un controlador de Ingress de Kubernetes basado en HAProxy.

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 el balanceo de cargas TCP/UDP interno en lugar del balanceo de cargas de red. Para hacerlo, agrega la anotación cloud.google.com/load-balancer-type: "Internal" al manifiesto envoy-service.yaml.

Implementación de Envoy en comparación con DaemonSet

En este instructivo, Envoy está configurado como una implementación de Kubernetes. Esta configuración significa que la configuración replica en el manifiesto de implementación determina la cantidad de Pods de Envoy. Si el balanceador de cargas reenvía las solicitudes entrantes a un nodo trabajador que no ejecuta un Pod de Envoy, el proxy de red de Kubernetes reenvía la solicitud a un nodo trabajador que sí ejecuta un Pod de Envoy.

DaemonSet es una alternativa a la implementación de Envoy. Con un DaemonSet, un Pod de Envoy se ejecuta en cada nodo trabajador en el clúster de GKE. Esta alternativa supone un mayor uso de recursos en clústeres grandes (más Pods de Envoy), pero también supone que las solicitudes entrantes siempre llegan a un nodo trabajador que ejecuta un Pod de Envoy. El resultado es menos tráfico de red en el clúster y menor latencia promedio, ya que las solicitudes no se reenvían entre los nodos trabajadores para alcanzar un Pod de Envoy.

Realiza una limpieza

Una vez que termines este instructivo, podrás limpiar los recursos que creaste en Google Cloud para que no consuman la cuota y no se te cobre por ellos en el futuro. En las siguientes secciones, se describe cómo borrar o desactivar estos recursos.

Borra el proyecto

  1. En Cloud Console, ve a la página Administrar recursos.

    Ir a la página Administrar recursos

  2. En la lista de proyectos, selecciona el proyecto que deseas borrar y haz clic en Borrar .
  3. En el cuadro de diálogo, escribe el ID del proyecto y 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 las imágenes en Container Registry.

    gcloud container images list-tags gcr.io/$GOOGLE_CLOUD_PROJECT/echo-grpc \
        --format 'value(digest)' | xargs -I {} gcloud container images delete \
        --force-delete-tags --quiet gcr.io/$GOOGLE_CLOUD_PROJECT/echo-grpc@sha256:{}
    
    gcloud container images list-tags gcr.io/$GOOGLE_CLOUD_PROJECT/reverse-grpc \
        --format 'value(digest)' | xargs -I {} gcloud container images delete \
        --force-delete-tags --quiet gcr.io/$GOOGLE_CLOUD_PROJECT/reverse-grpc@sha256:{}
    
  3. Borra el clúster de Google Kubernetes Engine:

    gcloud container clusters delete grpc-cluster --zone $ZONE --quiet  --async
    

Próximos pasos