Como usar o Envoy Proxy para balancear a carga de serviços do gRPC no GKE

Last reviewed 2019-05-30 UTC

Neste tutorial, demonstramos como expor vários serviços gRPC implantados no Google Kubernetes Engine (GKE) em um único endereço IP externo usando um balanceador de carga de rede de passagem externo e o proxy Envoy. O tutorial destaca alguns dos recursos avançados do Envoy para gRPC.

Introdução

O gRPC é um framework de RPC de código aberto, independente de linguagem, baseado em HTTP/2 e que usa buffers de protocolo para uma representação de transmissão eficiente e uma serialização rápida. Inspirado no Stubby, um framework interno de RPC do Google, o gRPC permite comunicação de baixa latência entre microsserviços e entre clientes móveis e APIs.

Ele é executado via HTTP/2 e oferece várias vantagens em relação ao HTTP/1.1, como codificação binária eficiente, multiplexação de solicitações e respostas em uma única conexão e controle de fluxo automático. Além disso, também oferece várias opções de balanceamento de carga. Este tutorial se concentra em situações em que os clientes não são confiáveis, como clientes móveis e aqueles executados fora do limite de confiança do provedor de serviços. Dentre todas as opções de balanceamento de carga fornecidas pelo gRPC, você usará neste tutorial o balanceamento de carga baseado em proxy.

No tutorial, você implanta um serviço do Kubernetes de TYPE=LoadBalancer, que é exposto como balanceamento de carga de rede de passagem externo de camada de transporte (camada 4) no Google Cloud. Esse serviço fornece um endereço IP público e transmite conexões TCP diretamente para os back-ends configurados. Nesse caso, o back-end será uma implantação do Kubernetes de instâncias do Envoy.

O Envoy é um proxy de camada de aplicativo de código aberto (camada 7) que oferece muitos recursos avançados. Neste tutorial, ele será utilizado para encerrar conexões TLS e encaminhar o tráfego do gRPC ao serviço do Kubernetes apropriado. Comparando com outras soluções de camada de aplicativo, como o Kubernetes Ingress, o Envoy proporciona várias opções de personalização, como as seguintes:

  • Descoberta de serviço
  • Algoritmos de balanceamento de carga
  • Transformação de solicitações e respostas, por exemplo, para JSON ou gRPC-Web
  • Autenticação de solicitações validando tokens JWT
  • Verificações de integridade do gRPC

Ao combinar um balanceador de carga de rede de passagem externo com o Envoy, é possível configurar um endpoint (endereço IP externo) que encaminha o tráfego para um conjunto de instâncias do Envoy em execução em um cluster do Google Kubernetes Engine. Então, essas instâncias usam as informações da camada de aplicativo sobre solicitações de proxy nos diferentes serviços do gRPC em execução no cluster. As instâncias do Envoy usam o DNS do cluster para identificar e balancear a carga das solicitações de gRPC de entrada para os pods íntegros e em execução de cada serviço. Isso significa que a carga de tráfego é balanceada para os pods por solicitação de RPC, e não por conexão TCP do cliente.

Arquitetura

Neste tutorial, você implanta dois serviços do gRPC, echo-grpc e reverse-grpc, em um cluster do Google Kubernetes Engine (GKE) e os expõe à Internet em um endereço IP público. Veja no diagrama a seguir a arquitetura de envio desses dois serviços por meio de um único endpoint:

arquitetura para exibição de "echo-grpc" e "reverse-grpc" por meio de um único endpoint

Um balanceador de carga de rede de passagem externo aceita solicitações recebidas da Internet (por exemplo, de clientes móveis ou consumidores de serviço fora da sua empresa). O balanceador de carga de rede de passagem externo executa as seguintes tarefas:

  • Faz o balanceamento de carga das conexões de entrada para os nós no pool. O tráfego é encaminhado para o serviço envoy do Kubernetes, que é exposto em todos os nós no cluster. O proxy de rede do Kubernetes encaminha as conexões para os pods que estão executando o Envoy.
  • Executa verificações de integridade HTTP em nós no cluster.

O Envoy executa as seguintes tarefas:

  • Encerra as conexões TLS.
  • Descobre os pods que executam os serviços do gRPC consultando o serviço de DNS do cluster interno.
  • Encaminha e balanceia a carga de tráfego para os pods de serviço do gRPC.
  • Executa verificações de integridade dos serviços do gRPC de acordo com o protocolo de verificação de integridade do gRPC (em inglês).
  • Expõe um endpoint para verificação de integridade das instâncias do Envoy pelo balanceador de carga de rede de passagem externo.

Os serviços do gRPC (echo-grpc e reverse-grpc) são expostos como serviços headless do Kubernetes (em inglês). Isso significa que nenhum endereço clusterIP é atribuído, e o proxy de rede do Kubernetes não faz o balanceamento de carga do tráfego para os pods. Em vez disso, um registro A de DNS que contém os endereços IP dos pods é criado no serviço de DNS do cluster. O Envoy descobre os endereços a partir dessa entrada de DNS e divide a carga entre eles de acordo com a política configurada.

Veja no diagrama a seguir os objetos do Kubernetes usados neste tutorial:

Objetos do Kubernetes usados neste tutorial, incluindo serviços, arquivos YAML, registros A de DNS, secrets, pods e entrada de proxy

Custos

Neste documento, você usará os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Ao concluir as tarefas descritas neste documento, é possível evitar o faturamento contínuo excluindo os recursos criados. Saiba mais em Limpeza.

Antes de começar

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

    Go to project selector

  2. Make sure that billing is enabled for your Google Cloud project.

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

    Activate Cloud Shell

Prepare o ambiente

  1. No Cloud Shell, defina o projeto do Google Cloud que você quer usar neste tutorial:

    gcloud config set project PROJECT_ID
    

    Substitua PROJECT_ID pelo ID do projeto do Google Cloud.

  2. Ative as APIs Artifact Registry e GKE:

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

Criar o cluster do GKE

  1. No Cloud Shell, crie um cluster do GKE para executar os serviços do 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
    

    Este tutorial usa a zona us-central1-f. É possível usar uma zona ou região diferente.

  2. Verifique se o contexto kubectl foi configurado listando os nós no cluster:

    kubectl get nodes --output name
    

    A saída é semelhante a:

    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
    

Criar o repositório do Artifact Registry

  1. No Cloud Shell, crie um novo repositório para armazenar imagens de contêiner:

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

    Crie o repositório na mesma região do cluster do GKE para ajudar a otimizar a latência e a largura de banda da rede quando os nós extraírem imagens de contêiner.

  2. Conceda o papel Leitor do Artifact Registry no repositório para a conta de serviço do Google usada pelas VMs de nó do cluster do 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. Adicione uma entrada auxiliar de credencial para o nome do host do repositório no arquivo de configuração do Docker no diretório principal do Cloud Shell:

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

    A entrada auxiliar de credenciais permite que as ferramentas de imagem de contêiner em execução no Cloud Shell sejam autenticadas no local do repositório do Artifact Registry para extrair e enviar imagens.

Implantar os serviços do gRPC

Para rotear o tráfego para vários serviços do gRPC por trás de um balanceador de carga, implante dois serviços de amostra do gRPC: echo-grpc e reverse-grpc. Os dois serviços expõem um método unário que usa uma string no campo de solicitação content. echo-grpc responde com o conteúdo inalterado, enquanto reverse-grpc responde com a string de conteúdo invertida.

  1. No Cloud Shell, clone o repositório que contém os serviços gRPC e mude para o diretório do repositório:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    cd kubernetes-engine-samples/networking/grpc-gke-nlb-tutorial/
    
  2. Crie um certificado TLS autoassinado e uma chave 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. Crie um secret do Kubernetes chamado envoy-certs que contenha a chave privada e o certificado TLS autoassinado:

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

    O Envoy usa esse certificado TLS e a chave privada quando encerra as conexões TLS.

  4. Crie as imagens de contêiner para os aplicativos de amostra echo-grpc e reverse-grpc, envie as imagens para o Artifact Registry e implante os aplicativos no cluster do GKE usando o Skaffold:

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

    O Skaffold é uma ferramenta de código aberto do Google que automatiza fluxos de trabalho para desenvolvimento, criação, envio e implantação de aplicativos como contêineres.

  5. Implante o Envoy no cluster do GKE usando o Skaffold:

    skaffold run \
        --digest-source=none \
        --module=envoy \
        --skip-tests
    
  6. Verifique se dois pods estão prontos para cada implantação:

    kubectl get deployments
    

    A resposta será similar à mostrada a seguir. Os valores de READY precisam ser 2/2 para todas as implantações.

    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. Verifique se echo-grpc, envoy e reverse-grpc existem como serviços do Kubernetes:

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

    A resposta será similar à mostrada a seguir. echo-grpc e reverse-grpc devem ter TYPE=ClusterIP e 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
    

Testar os serviços do gRPC

Para testar os serviços, use a ferramenta de linha de comando grpcurl.

  1. No Cloud Shell, instale grpcurl:

    go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
    
  2. Consiga o endereço IP externo do serviço envoy do Kubernetes e armazene-o em uma variável de ambiente:

    EXTERNAL_IP=$(kubectl get service envoy \
        --output=jsonpath='{.status.loadBalancer.ingress[0].ip}')
    
  3. Envie uma solicitação para o app de amostra 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
    

    A saída é semelhante a:

    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
    

    O cabeçalho de resposta hostname mostra o nome do pod echo-grpc que processou a solicitação. Se você repetir o comando algumas vezes, verá dois valores diferentes para o cabeçalho de resposta hostname, correspondentes aos nomes dos pods de echo-grpc.

  4. Verifique o mesmo comportamento com o serviço Reverse do 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
    

    A saída é semelhante a:

    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
    

Configuração do Envoy

Para entender melhor a configuração do Envoy, consulte o arquivo de configuração envoy/k8s/envoy.yaml no repositório Git.

A seção route_config especifica como as solicitações de entrada são roteadas para os aplicativos de amostra echo-grpc e 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

Os apps de amostra são definidos como clusters do 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: {}

Os campos type: STRICT_DNS e lb_policy: ROUND_ROBIN na definição do cluster especificam que o Envoy executa pesquisas de DNS no nome do host especificado no campo address e faz um balanceamento de carga nos endereços IP na resposta à busca DNS. A resposta contém vários endereços IP porque os objetos do serviço do Kubernetes que definem os apps de amostra especificam serviços headless.

O campo http2_protocol_options especifica que o Envoy usa o protocolo HTTP/2 para os apps de amostra.

O campo grpc_health_check na seção health_checks especifica que o Envoy usa o protocolo de verificação de integridade gRPC para determinar a integridade dos apps de amostra.

Resolver problemas

Se você tiver problemas ao seguir este tutorial, recomendamos que consulte estes documentos:

Você também pode analisar a interface de administração do Envoy para diagnosticar problemas com a configuração do Envoy.

  1. Para abrir a interface de administração, configure o encaminhamento de portas do Cloud Shell para a porta admin de um dos pods do Envoy:

    kubectl port-forward \
        $(kubectl get pods -o name | grep envoy | head -n1) 8080:8090
    
  2. Espere até que esta saída seja exibida no console:

    Forwarding from 127.0.0.1:8080 -> 8090
    
  3. Clique no botão Visualização da Web no Cloud Shell e selecione Visualizar na porta 8080. Isso abre uma nova janela do navegador que mostra a interface de administração.

    Interface administrativa de Envoy com visualização selecionada

  4. Quando terminar, volte para o Cloud Shell e pressione Control+C para encerrar o encaminhamento de portas.

Outras formas de rotear o tráfego do gRPC

É possível modificar essa solução de várias maneiras para adequá-la ao seu ambiente.

Balanceadores de carga de camada de aplicativo alternativos

Algumas das funcionalidades de camada do aplicativo fornecidas pelo Envoy também são disponibilizadas por outras soluções de balanceamento de carga:

  • É possível usar um balanceador de carga de aplicativo externo global ou regional em vez de um balanceador de carga de rede de passagem externo e um Envoy autogerenciado. O uso de um balanceador de carga de aplicativo externo oferece vários benefícios em comparação com um balanceador de carga de rede de passagem externo, como o recurso de gerenciamento de tráfego avançado, certificados TLS gerenciados e integração com outros produtos do Google Cloud, como o Cloud CDN, Google Cloud Armor e IAP.

    Recomendamos o uso de um balanceador de carga de aplicativo externo global ou regional se os recursos de gerenciamento de tráfego que eles oferecem atenderem aos seus casos de uso e se você não precisar de suporte ao cliente para autenticação com base no certificado, também conhecida como autenticação TLS mútua (mTLS, na sigla em inglês). Para mais informações, consulte estes documentos:

  • Se você usar o Cloud Service Mesh ou o Istio, é possível usar os recursos deles para rotear e fazer o balanceamento de carga do tráfego do gRPC. O Cloud Service Mesh e o Istio fornecem um gateway de entrada que é implantado como balanceador de carga de rede de passagem externo com um back-end do Envoy, semelhante à arquitetura neste tutorial. A principal diferença é que o Envoy é configurado por meio dos objetos de roteamento de tráfego do Istio.

    Para tornar os serviços de exemplo neste tutorial roteáveis na malha de serviço do Cloud Service Mesh ou do Istio, é necessário remover a linha clusterIP: None dos manifestos do serviço do Kubernetes (echo-service.yaml e reverse-service.yaml). Isso significa usar a funcionalidade de descoberta de serviço e balanceamento de carga do Cloud Service Mesh ou do Istio em vez da funcionalidade semelhante no Envoy.

    Se você já usa o Cloud Service Mesh ou o Istio, recomendamos o uso do gateway de entrada para rotear aos serviços do gRPC.

  • É possível usar o NGINX em vez do Envoy como uma implantação ou usando o controlador de entrada do NGINX para o Kubernetes (em inglês). Usamos o Envoy neste tutorial porque ele fornece funcionalidades de gRPC mais avançadas, como suporte ao protocolo de verificação de integridade do gRPC (em inglês).

Conectividade de rede interna VPC

Se você quiser expor os serviços fora do cluster do GKE, mas dentro da rede VPC, poderá usar um balanceador de carga de rede de passagem interno ou um balanceador de carga de aplicativo interno

Para usar um balanceador de carga de rede de passagem interno no lugar de um externo, adicione a anotação cloud.google.com/load-balancer-type: "Internal" ao manifesto envoy-service.yaml.

Para usar um balanceador de carga de aplicativo interno, consulte a documentação sobre Como configurar o Ingress para balanceadores de carga de aplicativo internos.

Limpar

Depois de concluir o tutorial, você pode limpar os recursos que criou para que eles parem de usar a cota e gerar cobranças. Nas seções a seguir, você aprenderá a excluir e desativar esses recursos.

Exclua o projeto

  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.

Excluir os recursos

Se você quiser manter o projeto do Google Cloud usado neste tutorial, exclua os recursos individuais:

  1. No Cloud Shell, exclua o clone do repositório Git local:

    cd ; rm -rf kubernetes-engine-samples/networking/grpc-gke-nlb-tutorial/
    
  2. Exclua o cluster do GKE:

    gcloud container clusters delete envoy-grpc-tutorial \
        --zone us-central1-f --async --quiet
    
  3. Exclua o repositório no Artifact Registry:

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

A seguir