Utiliser le proxy Envoy pour équilibrer la charge des services gRPC sur GKE

Last reviewed 2019-05-30 UTC

Ce tutoriel explique comment exposer plusieurs services gRPC déployés sur Google Kubernetes Engine (GKE) avec une seule adresse IP externe à l'aide d'un équilibreur de charge réseau passthrough externe et du proxy Envoy. Il présente certaines des fonctionnalités avancées fournies par Envoy pour gRPC.

Présentation

gRPC est un framework RPC open source, indépendant du langage et basé sur HTTP/2, qui utilise le format Protocol Buffers pour une représentation efficace des messages sur le réseau et une sérialisation rapide. Inspiré de Stubby, le framework RPC interne à Google, gRPC permet une communication à faible latence entre microservices, et entre clients mobiles et API.

gRPC s'exécute sur HTTP/2, ce qui présente plusieurs avantages par rapport à HTTP/1.1, comme l'encodage binaire efficace, le multiplexage des requêtes et des réponses via une connexion unique, et le contrôle de flux automatique. gRPC propose également plusieurs options pour l'équilibrage de charge. Ce tutoriel porte sur les cas où les clients ne sont pas approuvés, comme par exemple les clients mobiles et les clients s'exécutant en dehors de la limite de confiance du fournisseur de services. Dans ce tutoriel, vous allez utiliser l'équilibrage de charge basé sur un proxy, l'une des options d’équilibrage de charge fournies par gRPC.

Vous allez déployer un service Kubernetes de type TYPE=LoadBalancer, exposé sur Google Cloud en tant qu'équilibreur de charge réseau passthrough externe au niveau de la couche de transport (couche 4). Ce service fournit une adresse IP publique unique et transmet les connexions TCP directement aux backends configurés. Dans ce tutoriel, le backend est un déploiement Kubernetes d'instances Envoy.

Envoy est un proxy open source situé au niveau de la couche d'application (couche 7) qui offre de nombreuses fonctionnalités avancées. Dans ce tutoriel, vous allez l'utiliser pour mettre fin aux connexions TLS et acheminer le trafic gRPC vers le service Kubernetes approprié. Par rapport à d'autres solutions de couche d'application, telles que les ressources d'entrée Kubernetes, Envoy, lorsqu'il est utilisé directement, fournit plusieurs options de personnalisation, telles que :

  • Recherche de services
  • Algorithmes d'équilibrage de charge
  • Transcodage de requêtes et de réponses, par exemple, au format JSON ou gRPC-Web
  • Authentification des requêtes en validant les jetons JWT
  • Vérifications de l'état gRPC

En combinant un équilibreur de charge réseau passthrough externe avec Envoy, vous pouvez configurer un point de terminaison (adresse IP externe) qui transfère le trafic vers un ensemble d'instances Envoy s'exécutant dans un cluster Google Kubernetes Engine. Ces instances utilisent ensuite les informations de la couche d'application en guise de proxy pour les requêtes envoyées à différents services gRPC s'exécutant dans le cluster. Les instances Envoy utilisent le DNS du cluster pour identifier et équilibrer la charge des requêtes gRPC entrantes sur les pods sains et en cours d'exécution pour chaque service. Cela signifie que le trafic est équilibré en fonction du nombre de pods par requête RPC plutôt que par connexion TCP du client.

Architecture

Dans ce tutoriel, vous allez déployer deux services gRPC (echo-grpc et reverse-grpc) dans un cluster Google Kubernetes Engine (GKE) et les exposer à Internet sur une adresse IP publique. Le schéma suivant illustre l'architecture permettant d'exposer ces deux services via un seul point de terminaison :

architecture pour exposer 'echo-grpc' et 'reverse-grpc' via un seul point de terminaison

Un équilibreur de charge réseau passthrough externe accepte les requêtes entrantes provenant d'Internet (par exemple, de clients mobiles ou de clients de services extérieurs à votre entreprise). L'équilibreur de charge réseau passthrough externe effectue les tâches suivantes :

  • Équilibrage de charge des connexions entrantes vers les nœuds du pool. Le trafic est transféré vers le service Kubernetes envoy, qui est exposé sur tous les nœuds du cluster. Le proxy réseau Kubernetes transmet ces connexions aux pods exécutant Envoy.
  • Vérifications de l'état HTTP sur les nœuds du cluster

Envoy effectue les tâches suivantes :

  • Désactivation des connexions TLS
  • Découverte des pods exécutant les services gRPC via l'interrogation du service DNS du cluster interne
  • Routage et équilibrage de charge du trafic vers les pods de service gRPC
  • Vérifications de l'état des services gRPC conformément au protocole de vérification de l'état gRPC
  • Exposition d'un point de terminaison pour la vérification de l'état des instances Envoy par l'équilibreur de charge réseau passthrough externe

Les services gRPC echo-grpc et reverse-grpc sont exposés en tant que services Kubernetes sans adresse IP de cluster. Cela signifie qu'aucune adresse clusterIP n'est attribuée et que le proxy réseau Kubernetes n'équilibre pas le trafic vers les pods. À la place, un enregistrement DNS A contenant les adresses IP des pods est créé dans le service DNS du cluster. Envoy découvre les adresses IP du pod à partir de cette entrée DNS et répartit la charge entre elles en fonction de la règle configurée dans Envoy.

Le schéma suivant illustre les objets Kubernetes impliqués dans ce tutoriel :

Objets Kubernetes utilisés dans ce tutoriel, y compris services, fichiers YAML, enregistrements DNS A, secrets, pods et entrée de proxy

Coûts

Dans ce document, vous utilisez les composants facturables suivants de Google Cloud :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût. Les nouveaux utilisateurs de Google Cloud peuvent bénéficier d'un essai gratuit.

Une fois que vous avez terminé les tâches décrites dans ce document, vous pouvez éviter de continuer à payer des frais en supprimant les ressources que vous avez créées. Pour en savoir plus, consultez la section Effectuer un nettoyage.

Avant de commencer

  1. Dans Google Cloud Console, sur la page de sélection du projet, sélectionnez ou créez un projet Google Cloud.

    Accéder au sélecteur de projet

  2. Vérifiez que la facturation est activée pour votre projet Google Cloud.

  3. Dans la console Google Cloud, activez Cloud Shell.

    Activer Cloud Shell

Préparer l'environnement

  1. Dans Cloud Shell, définissez le projet Google Cloud que vous souhaitez utiliser pour ce tutoriel :

    gcloud config set project PROJECT_ID
    

    Remplacez PROJECT_ID par l'ID de votre projet Google Cloud.

  2. Activez les API Artifact Registry et GKE :

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

Créer le cluster GKE

  1. Dans Cloud Shell, créez un cluster GKE pour exécuter vos services 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
    

    Ce tutoriel utilise la zone us-central1-f. Vous pouvez définir une zone ou une région différente.

  2. Vérifiez que le contexte kubectl a été configuré en répertoriant les nœuds de votre cluster :

    kubectl get nodes --output name
    

    Le résultat ressemble à ceci :

    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
    

Créer le dépôt Artifact Registry

  1. Dans Cloud Shell, créez un dépôt pour stocker les images de conteneurs :

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

    Créez le dépôt dans la même région que le cluster GKE afin d'optimiser la latence et la bande passante réseau lorsque les nœuds extraient des images de conteneurs.

  2. Attribuez le rôle Lecteur Artifact Registry au dépôt au compte de service Google utilisé par les VM de nœuds du cluster 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. Ajoutez une entrée d'assistant d'identification pour le nom d'hôte du dépôt au fichier de configuration Docker dans votre répertoire d'accueil Cloud Shell :

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

    L'entrée de l'assistant d'identification permet aux outils d'image de conteneur exécutés dans Cloud Shell de s'authentifier auprès de l'emplacement du dépôt Artifact Registry pour extraire et stocker des images.

Déployer les services gRPC

Pour acheminer le trafic vers plusieurs services gRPC derrière un équilibreur de charge, vous devez déployer deux exemples de services gRPC : echo-grpc et reverse-grpc. Ces deux services exposent une méthode unaire qui prend une chaîne dans le champ de requête content. echo-grpc répond avec le contenu non modifié, alors que reverse-grpc renvoie la chaîne de contenu inversée.

  1. Dans Cloud Shell, clonez le dépôt contenant les services gRPC, puis basculez vers le répertoire du dépôt :

    git clone https://github.com/GoogleCloudPlatform/grpc-gke-nlb-tutorial.git ~/grpc-gke-nlb-tutorial
    
    cd ~/grpc-gke-nlb-tutorial
    
  2. Créez un certificat TLS autosigné et une clé privée :

    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. Créez un secret Kubernetes nommé envoy-certs contenant le certificat TLS autosigné et la clé privée :

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

    Envoy utilise ce certificat TLS et cette clé privée lorsqu'il met fin aux connexions TLS.

  4. Créez les images de conteneur pour les exemples d'applications echo-grpc et reverse-grpc, transférez les images vers Artifact Registry, puis déployez les applications sur le cluster GKE à l'aide de Skaffold :

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

    Skaffold est un outil Open Source de Google qui automatise les workflows pour le développement, la compilation, le transfert et le déploiement d'applications en tant que conteneurs.

  5. Déployez Envoy sur le cluster GKE à l'aide de Skaffold :

    skaffold run \
        --digest-source=none \
        --module=envoy \
        --skip-tests
    
  6. Vérifiez que deux pods sont prêts pour chaque déploiement :

    kubectl get deployments
    

    La sortie doit ressembler à ce qui suit. La valeur de READY doit être 2/2 pour tous les déploiements.

    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. Vérifiez que echo-grpc, envoy et reverse-grpc correspondent à des services Kubernetes :

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

    La sortie doit ressembler à ce qui suit. echo-grpc et reverse-grpc doivent tous deux être définis comme suit : TYPE=ClusterIP et 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
    

Tester les services gRPC

Vous allez utiliser l'outil de ligne de commande grpcurl pour tester les services.

  1. Dans Cloud Shell, installez grpcurl :

    go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
    
  2. Obtenez l'adresse IP externe du service Kubernetes envoy et stockez-la dans une variable d'environnement :

    EXTERNAL_IP=$(kubectl get service envoy \
        --output=jsonpath='{.status.loadBalancer.ingress[0].ip}')
    
  3. Envoyez une requête à l'exemple d'application 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
    

    Le résultat ressemble à ceci :

    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
    

    L'en-tête de réponse hostname indique le nom du pod echo-grpc ayant traité la requête. Si vous répétez cette commande plusieurs fois, vous devriez voir deux valeurs différentes pour l'en-tête de réponse hostname, correspondant aux noms des pods echo-grpc.

  4. Vérifiez que vous obtenez le même comportement avec le service gRPC Reverse :

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

    Le résultat ressemble à ceci :

    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
    

Configuration d'Envoy

Pour mieux comprendre la configuration d'Envoy, vous pouvez consulter le fichier de configuration envoy/k8s/envoy.yaml dans le dépôt Git.

La section route_config spécifie la manière dont les requêtes entrantes sont acheminées vers les exemples d'application echo-grpc et 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

Les exemples d'applications sont définis en tant que clusters 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: {}

Les champs type: STRICT_DNS et lb_policy: ROUND_ROBIN de la définition de cluster spécifient qu'Envoy effectue des recherches DNS du nom d'hôte spécifié dans le champ address et équilibre la charge sur les adresses IP dans la réponse à la résolution DNS La réponse contient plusieurs adresses IP, car les objets de service Kubernetes qui définissent les exemples d'applications spécifient des services sans interface graphique.

Le champ http2_protocol_options indique qu'Envoy utilise le protocole HTTP/2 à destination des exemples d'applications.

Le champ grpc_health_check de la section health_checks indique qu'Envoy utilise le protocole de vérification d'état gRPC pour déterminer l'état des exemples d'applications.

Dépannage

Si vous rencontrez des problèmes avec ce tutoriel, nous vous recommandons de consulter les documents ci-dessous :

Vous pouvez également explorer l'interface d'administration Envoy pour diagnostiquer les problèmes liés à la configuration Envoy.

  1. Pour ouvrir l'interface d'administration, exécutez la commande suivante pour configurer le transfert de port de Cloud Shell vers le port admin de l'un des pods Envoy :

    kubectl port-forward \
        $(kubectl get pods -o name | grep envoy | head -n1) 8080:8090
    
  2. Attendez que le résultat ci-dessous s'affiche dans la console :

    Forwarding from 127.0.0.1:8080 -> 8090
    
  3. Cliquez sur le bouton Aperçu Web dans Cloud Shell et sélectionnez Preview on port 8080 (Prévisualiser sur le port 8080). Une nouvelle fenêtre de navigateur s'ouvre et affiche l'interface d'administration.

    Interface d'administration Envoy avec la fonction de prévisualisation sélectionnée

  4. Lorsque vous avez terminé, revenez à Cloud Shell et appuyez sur Control+C pour mettre fin au transfert de port.

Autres moyens d'acheminer le trafic gRPC

Vous pouvez modifier cette solution de différentes manières pour l'adapter à votre environnement.

Autres équilibreurs de charge de couche d'application

Certaines des fonctionnalités de couche d'application fournies par Envoy peuvent également être fournies par d'autres solutions d'équilibrage de charge :

  • Vous pouvez utiliser un équilibreur de charge d'application externe global ou régional au lieu d'un équilibreur de charge réseau passthrough externe et d'Envoy autogéré. L'utilisation d'un équilibreur de charge d'application externe offre plusieurs avantages par rapport à un équilibreur de charge réseau passthrough externe, tels que la fonctionnalité de gestion avancée du trafic, les certificats TLS gérés et l'intégration à d'autres produits Google Cloud tels que Cloud CDN, Google Cloud Armor et IAP.

    Nous vous recommandons d'utiliser un équilibreur de charge d'application externe global ou régional si les fonctionnalités de gestion du trafic qu'il propose répondent à vos cas d'utilisation et si vous n'avez pas besoin d'assistance pour l'authentification basée sur un certificat client, également appelée authentification TLS mutuelle (mTLS). Pour en savoir plus, consultez les documents suivants :

  • Si vous utilisez Anthos Service Mesh ou Istio, vous pouvez utiliser leurs fonctionnalités pour acheminer et équilibrer le trafic gRPC. La passerelle d'entrée d'Anthos Service Mesh et d'Istio est déployée en tant qu'équilibreur de charge réseau passthrough externe avec un backend Envoy, semblable à l'architecture déployée dans ce tutoriel. La principale différence est qu'Envoy est configuré via les objets de routage de trafic d'Istio.

    Pour rendre les exemples de services de ce tutoriel routables dans le maillage de services Anthos Service Mesh ou Istio, vous devez supprimer la ligne clusterIP: None des fichiers manifestes des services Kubernetes (echo-service.yaml etreverse-service.yaml). Cela signifie que vous devez utiliser les fonctionnalités de détection de services et d'équilibrage de charge d'Anthos Service Mesh ou d'Istio au lieu des fonctionnalités similaires dans Envoy.

    Si vous utilisez déjà Anthos Service Mesh ou Istio, nous vous recommandons d'utiliser la passerelle d'entrée pour accéder à vos services gRPC.

  • Vous pouvez utiliser NGINX à la place d'Envoy, soit en tant que déploiement, soit via le contrôleur d'entrée NGINX pour Kubernetes. Envoy est utilisé dans ce tutoriel, car il offre des fonctionnalités gRPC plus avancées, telles que la compatibilité avec le protocole de vérification de l'état gRPC.

Connectivité du réseau VPC interne

Si vous souhaitez exposer les services en dehors de votre cluster GKE mais exclusivement à l'intérieur de votre réseau VPC, vous pouvez utiliser un équilibreur de charge réseau passthrough interne ou un équilibreur de charge d'application interne.

Pour utiliser un équilibreur de charge réseau passthrough interne à la place d'un équilibreur de charge réseau passthrough externe, ajoutez l'annotation cloud.google.com/load-balancer-type: "Internal" au fichier manifeste envoy-service.yaml.

Pour utiliser un équilibreur de charge d'application interne, consultez la documentation sur la Configuration d'Ingress pour les équilibreurs de charge d'application internes.

Effectuer un nettoyage

Une fois le tutoriel terminé, vous pouvez procéder au nettoyage des ressources que vous avez créées afin qu'elles ne soient plus comptabilisées dans votre quota et qu'elles ne vous soient plus facturées. Dans les sections suivantes, nous allons voir comment supprimer ou désactiver ces ressources.

Supprimer le projet

  1. Dans la console Google Cloud, accédez à la page Gérer les ressources.

    Accéder à la page Gérer les ressources

  2. Dans la liste des projets, sélectionnez le projet que vous souhaitez supprimer, puis cliquez sur Supprimer.
  3. Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez sur Arrêter pour supprimer le projet.

Supprimer les ressources

Si vous souhaitez conserver le projet Google Cloud que vous avez utilisé dans ce tutoriel, supprimez les différentes ressources :

  1. Dans Cloud Shell, supprimez le clone du dépôt Git local :

    cd ; rm -rf ~/grpc-gke-nlb-tutorial
    
  2. Supprimez le cluster GKE :

    gcloud container clusters delete envoy-grpc-tutorial \
        --zone us-central1-f --async --quiet
    
  3. Supprimer le dépôt dans Artifact Registry:

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

Étapes suivantes