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

Ce tutoriel explique comment exposer plusieurs services gRPC déployés sur Google Kubernetes Engine (GKE) avec une seule adresse IP externe à l'aide de l'équilibrage de charge réseau 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 que service d'équilibrage de charge réseau 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 SSL/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 l'équilibrage de charge réseau 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 GKE. 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.

Coûts

Ce tutoriel utilise 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é ce tutoriel, vous pouvez éviter de continuer à payer des frais en supprimant les ressources que vous avez créées. Consultez la page Effectuer un nettoyage pour en savoir plus.

Avant de commencer

  1. Connectez-vous à votre compte Google.

    Si vous n'en possédez pas déjà un, vous devez en créer un.

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

    Accéder à la page de sélection du projet

  3. Vérifiez que la facturation est activée pour votre projet Google Cloud. Découvrez comment vérifier que la facturation est activée pour votre projet.

  4. Activer les API Cloud Build, Container Registry, and Container Analysis.

    Activer les API

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

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

  • Équilibrage de charge des connexions entrantes vers les nœuds de calcul du pool. Le trafic est transféré vers le service Kubernetes envoy, qui est exposé sur tous les nœuds de calcul 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 de calcul du cluster

Envoy effectue les tâches suivantes :

  • Désactivation des connexions SSL/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 à l'aide de l'équilibrage de charge réseau

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

Initialiser l'environnement

Dans cette section, vous allez définir les variables d'environnement qui seront utilisées ultérieurement dans le tutoriel.

  1. Ouvrez Cloud Shell.

    Accéder à Cloud Shell

    Utilisez Cloud Shell pour exécuter toutes les commandes de ce tutoriel.

  2. Dans Cloud Shell, affichez l'ID de projet actuel :

    gcloud config list --format 'value(core.project)'
    
  3. Si la commande ne renvoie pas l'ID du projet que vous avez sélectionné, configurez Cloud Shell pour utiliser votre projet, en remplaçant project-id par le nom de votre projet :

    gcloud config set project project-id
    
  4. Définissez des variables d'environnement pour la région et la zone que vous souhaitez utiliser pour ce tutoriel :

    REGION=us-central1
    ZONE=$REGION-b
    

    Ce tutoriel utilise la région us-central1 et la zone us-central1-b. Toutefois, vous pouvez modifier la région et la zone selon vos besoins.

Créer le cluster GKE

  1. Créez un cluster GKE pour exécuter vos services gRPC :

    gcloud container clusters create grpc-cluster --zone $ZONE
    
  2. Vérifiez que le contexte kubectl a été configuré en répertoriant les nœuds de calcul dans votre cluster :

    kubectl get nodes -o name
    

    Le résultat ressemble à ceci :

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

Déployer les services gRPC

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

  1. Clonez le dépôt contenant les services gRPC et basculez vers le répertoire de travail :

    git clone https://github.com/GoogleCloudPlatform/grpc-gke-nlb-tutorial
    cd grpc-gke-nlb-tutorial
    
  2. À l'aide de Cloud Build, créez les images de conteneur pour les services gRPC Echo et Reverse, et stockez-les dans 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. Vérifiez que les images existent dans Container Registry :

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

    Le résultat ressemble à ceci :

    NAME
    gcr.io/grpc-gke-nlb-tutorial/echo-grpc
    gcr.io/grpc-gke-nlb-tutorial/reverse-grpc
    
  4. Créez des déploiements Kubernetes pour echo-grpc et 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. Vérifiez qu'il y a deux pods disponibles pour chaque déploiement :

    kubectl get deployments
    

    La sortie doit ressembler à ce qui suit. La valeur de DESIRED, CURRENT, UP-TO-DATE et AVAILABLE doit être de 2 pour les deux déploiements.

    NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    echo-grpc      2         2         2            2           1m
    reverse-grpc   2         2         2            2           1m
    
  6. Créez des services Kubernetes sans adresse IP de cluster pour echo-grpc et reverse-grpc. Ces commandes créent des enregistrements DNS A dans le service DNS du cluster, mais n'attribuent pas d'adresses IP virtuelles.

    kubectl apply -f k8s/echo-service.yaml
    kubectl apply -f k8s/reverse-service.yaml
    
  7. Vérifiez que echo-grpc et reverse-grpc existent en tant que services Kubernetes :

    kubectl get services
    

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

Configurer l'équilibrage de charge réseau

  1. Créez un service Kubernetes de type LoadBalancer dans votre cluster :

    kubectl apply -f k8s/envoy-service.yaml
    

    Cette commande provisionne les ressources requises pour l'équilibrage de charge réseau et attribue une adresse IP publique éphémère. L'attribution de l'adresse IP publique peut prendre quelques minutes.

  2. Exécutez la commande suivante et attendez que la valeur de EXTERNAL-IP pour le service envoy passe de <pending> à une adresse IP publique :

    kubectl get services envoy --watch
    
  3. Appuyez sur Control+C pour ne plus attendre.

Créer un certificat SSL/TLS autosigné

Envoy utilise un certificat et une clé lorsqu'il met fin aux connexions SSL/TLS. Commencez par créer un certificat SSL/TLS autosigné.

  1. Créez une variable d'environnement pour stocker l'adresse IP publique du service Envoy que vous avez créé dans la section précédente :

    EXTERNAL_IP=$(kubectl get service envoy -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    
  2. Créez un certificat SSL/TLS autosigné et une clé :

    openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
        -keyout privkey.pem -out cert.pem -subj "/CN=$EXTERNAL_IP"
    
  3. Créez un secret TLS Kubernetes appelé envoy-certs qui contient le certificat SSL/TLS autosigné et la clé :

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

Déployer Envoy

  1. Créez un objet ConfigMap Kubernetes pour stocker le fichier de configuration Envoy (envoy.yaml) :

    kubectl apply -f k8s/envoy-configmap.yaml
    
  2. Créez un déploiement Kubernetes pour Envoy :

    kubectl apply -f k8s/envoy-deployment.yaml
    
  3. Vérifiez qu'il y a deux pods envoy en cours d'exécution :

    kubectl get deployment envoy
    

    Le résultat ressemble à ceci :

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

Vous êtes maintenant prêt à tester les services gRPC.

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 get github.com/fullstorydev/grpcurl
    go install github.com/fullstorydev/grpcurl/cmd/grpcurl
    
  2. Envoyez une requête au service gRPC Echo :

    grpcurl -d '{"content": "echo"}' -proto echo-grpc/api/echo.proto \
        -insecure -v $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, 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
    

    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.

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

    grpcurl -d '{"content": "reverse"}' -proto reverse-grpc/api/reverse.proto \
        -insecure -v $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, 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
    

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 configurer l'équilibrage de charge HTTP(S) à l'aide d'un objet Ingress Kubernetes et l'utiliser à la place de l'équilibrage de charge réseau et d'Envoy. L'utilisation de l'équilibrage de charge HTTP(S) offre plusieurs avantages par rapport à l'équilibrage de charge réseau, tels que les certificats SSL/TLS gérés et l'intégration à d'autres produits Google Cloud comme Cloud CDN et IAP.

    Nous vous recommandons d'utiliser l'équilibrage de charge HTTP(S) lorsque vous n'avez pas besoin des fonctionnalités suivantes :

    • Vérifications de l'état gRPC
    • Contrôle ultraprécis de l'algorithme d'équilibrage de charge
    • Exposition de plus de 50 services

    Pour en savoir plus sur le déploiement de l'équilibrage de charge HTTP(S) avec un exemple de service gRPC, consultez la documentation Google Kubernetes Engine sur l'objet Ingress et le tutoriel sur l'équilibrage de charge avec l'objet Ingress du service gRPC de GKE sur GitHub.

  • Si vous utilisez Istio, vous pouvez utiliser ses fonctionnalités pour acheminer et équilibrer le trafic gRPC. La passerelle d'entrée d'Istio est déployée en tant que service d'équilibrage de charge réseau avec un backend Envoy, semblable à l'architecture déployée dans ce tutoriel. La principale différence est que le proxy 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 Istio, vous devez supprimer la ligne clusterIP: None des fichiers manifestes des services Kubernetes (echo-service.yaml et reverse-service.yaml). Cela signifie que vous devez utiliser les fonctionnalités de découverte de services et d'équilibrage de charge d'Istio au lieu des fonctionnalités similaires dans Envoy. Si vous utilisez déjà Istio, nous vous recommandons d'utiliser la passerelle d'entrée pour définir le routage vers 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.

  • Vous pouvez utiliser Ambassador et Contour, qui fournissent les contrôleurs d’entrée Kubernetes et sont basés sur Envoy.

  • Vous pouvez utiliser Voyager, un contrôleur d'entrée Kubernetes basé sur HAProxy.

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 l'équilibrage de charge TCP/UDP interne à la place de l'équilibrage de charge réseau. Pour ce faire, ajoutez l'annotation cloud.google.com/load-balancer-type: "Internal" au fichier manifeste envoy-service.yaml.

Déployer Envoy plutôt que DaemonSet

Dans ce tutoriel, Envoy est configuré en tant que déploiement Kubernetes. Cette configuration signifie que le paramètre replica du fichier manifeste de déploiement détermine le nombre de pods Envoy. Si l'équilibreur de charge transmet les requêtes entrantes à un nœud de calcul qui n'exécute pas de pod Envoy, le proxy réseau Kubernetes transfère la requête à un nœud de calcul exécutant un pod Envoy.

DaemonSet constitue une alternative au déploiement pour Envoy. Avec DaemonSet, un pod Envoy est exécuté sur chaque nœud de calcul du cluster GKE. Cette alternative implique une utilisation plus intensive des ressources dans les clusters volumineux (pods Envoy plus nombreux), mais cela signifie également que les requêtes entrantes atteignent toujours un nœud de calcul exécutant un pod Envoy. Il en résulte une diminution du trafic réseau dans votre cluster et une latence moyenne plus faible, car les requêtes ne sont pas transférées entre les nœuds de calcul pour atteindre un pod Envoy.

Effectuer un nettoyage

Une fois que vous avez terminé ce tutoriel, vous pouvez procéder au nettoyage des ressources que vous avez créées sur Google Cloud 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 Cloud Console, 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 les images dans 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. Supprimez le cluster Google Kubernetes Engine :

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

Étapes suivantes