Équilibrage de charge natif en conteneurs via Ingress


Cette page explique comment utiliser l'équilibrage de charge natif en conteneurs dans Google Kubernetes Engine (GKE). L'équilibrage de charge natif en conteneurs permet aux équilibreurs de charge de cibler directement les pods Kubernetes et de répartir le trafic de manière homogène entre ceux-ci.

Pour en savoir plus sur les avantages, les exigences et les limites de l'équilibrage de charge natif en conteneurs, consultez la page Équilibrage de charge natif en conteneurs.

Avant de commencer

Avant de commencer, effectuez les tâches suivantes :

  • Activez l'API Google Kubernetes Engine.
  • Activer l'API Google Kubernetes Engine
  • Si vous souhaitez utiliser Google Cloud CLI pour cette tâche, installez puis initialisez gcloud CLI. Si vous avez déjà installé gcloud CLI, assurez-vous de disposer de la dernière version en exécutant la commande gcloud components update.

Utiliser l'équilibrage de charge natif en conteneurs

Les sections suivantes présentent les étapes de configuration de l'équilibrage de charge natif en conteneurs sur GKE. Notez que, pour les clusters GKE exécutant la version 1.17 ou ultérieure, et sous certaines conditions, l'équilibrage de charge natif en conteneurs est utilisé par défaut et ne nécessite pas d'annotation de service cloud.google.com/neg: '{"ingress": true}' explicite.

Créer un cluster de VPC natif

Pour utiliser l'équilibrage de charge natif en conteneurs, des adresses IP d'alias doivent être activées sur votre cluster GKE.

Par exemple, la commande suivante crée un cluster GKE, neg-demo-cluster, avec un sous-réseau provisionné automatiquement :

  • En mode Autopilot, les adresses IP d'alias sont activées par défaut :

    gcloud container clusters create-auto neg-demo-cluster \
        --location=COMPUTE_LOCATION
    

    Remplacez COMPUTE_LOCATION par l'emplacement Compute Engine du cluster.

  • En mode standard, activez les adresses IP d'alias lors de la création du cluster:

    gcloud container clusters create neg-demo-cluster \
        --enable-ip-alias \
        --create-subnetwork="" \
        --network=default \
        --zone=us-central1-a
    

Créer un déploiement

L'exemple de déploiement suivant, neg-demo-app, exécute une seule instance d'un serveur HTTP conteneurisé. Nous vous recommandons d'utiliser des charges de travail qui font appel au retour d'informations via la disponibilité des pods.

Utiliser le retour d'informations via la disponibilité des pods

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: neg-demo-app # Label for the Deployment
  name: neg-demo-app # Name of Deployment
spec:
  selector:
    matchLabels:
      run: neg-demo-app
  template: # Pod template
    metadata:
      labels:
        run: neg-demo-app # Labels Pods from this Deployment
    spec: # Pod specification; each Pod created by this Deployment has this specification
      containers:
      - image: registry.k8s.io/serve_hostname:v1.4 # Application to run in Deployment's Pods
        name: hostname # Container name
        ports:
        - containerPort: 9376
          protocol: TCP
  

Utiliser un délai codé en dur

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: neg-demo-app # Label for the Deployment
  name: neg-demo-app # Name of Deployment
spec:
  minReadySeconds: 60 # Number of seconds to wait after a Pod is created and its status is Ready
  selector:
    matchLabels:
      run: neg-demo-app
  template: # Pod template
    metadata:
      labels:
        run: neg-demo-app # Labels Pods from this Deployment
    spec: # Pod specification; each Pod created by this Deployment has this specification
      containers:
      - image: registry.k8s.io/serve_hostname:v1.4 # Application to run in Deployment's Pods
        name: hostname # Container name
      # Note: The following line is necessary only on clusters running GKE v1.11 and lower.
      # For details, see https://cloud.google.com/kubernetes-engine/docs/how-to/container-native-load-balancing#align_rollouts
        ports:
        - containerPort: 9376
          protocol: TCP
      terminationGracePeriodSeconds: 60 # Number of seconds to wait for connections to terminate before shutting down Pods
  

Dans ce déploiement, chaque conteneur exécute un serveur HTTP. En réponse, le serveur HTTP renvoie le nom d'hôte du serveur d'applications (le nom du pod sur lequel le serveur est exécuté).

Enregistrez ce fichier manifeste sous le nom neg-demo-app.yaml, puis créez le déploiement :

kubectl apply -f neg-demo-app.yaml

Créer un service pour un équilibreur de charge natif en conteneurs

Après avoir créé un déploiement, vous devez regrouper ses pods dans un service.

L'exemple de service suivant, neg-demo-svc, cible le déploiement que vous avez créé dans la section précédente :

apiVersion: v1
kind: Service
metadata:
  name: neg-demo-svc # Name of Service
  annotations:
    cloud.google.com/neg: '{"ingress": true}' # Creates a NEG after an Ingress is created
spec: # Service's specification
  type: ClusterIP
  selector:
    run: neg-demo-app # Selects Pods labelled run: neg-demo-app
  ports:
  - name: http
    port: 80 # Service's port
    protocol: TCP
    targetPort: 9376

L'annotation du service, cloud.google.com/neg: '{"ingress": true}', active l'équilibrage de charge natif en conteneurs. Cependant, l'équilibreur de charge n'est pas créé tant que vous n'avez pas créé une entrée pour le service.

Enregistrez ce fichier manifeste sous le nom neg-demo-svc.yaml, puis créez le service :

kubectl apply -f neg-demo-svc.yaml

Créer un objet Ingress pour le service

L'exemple d'entrée suivant, neg-demo-ing, cible le service que vous avez créé :

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: neg-demo-ing
spec:
  defaultBackend:
    service:
      name: neg-demo-svc # Name of the Service targeted by the Ingress
      port:
        number: 80 # Should match the port used by the Service

Enregistrez ce fichier manifeste sous le nom neg-demo-ing.yaml, puis créez l'objet Ingress :

kubectl apply -f neg-demo-ing.yaml

La création de cette entrée entraîne celle d'un équilibreur de charge d'application au sein du projet ainsi que celle d'un groupe de points de terminaison réseau (NEG, Network Endpoint Group) dans chacune des zones où le cluster est exécuté. Les points de terminaison des NEG et ceux du service sont synchronisés.

Vérifier l'objet Ingress

Après avoir déployé une charge de travail, regroupé ses pods dans un service et créé une entrée pour le service, vous devez vérifier que l'entrée a correctement provisionné l'équilibreur de charge natif en conteneurs.

Récupérez l'état de l'objet Ingress :

kubectl describe ingress neg-demo-ing

Le résultat inclut les événements ADD et CREATE :

Events:
Type     Reason   Age                From                     Message
----     ------   ----               ----                     -------
Normal   ADD      16m                loadbalancer-controller  default/neg-demo-ing
Normal   Service  4s                 loadbalancer-controller  default backend set to neg-demo-svc:32524
Normal   CREATE   2s                 loadbalancer-controller  ip: 192.0.2.0

Tester l'équilibreur de charge

Dans les sections suivantes, nous expliquons comment tester le fonctionnement d'un équilibreur de charge natif en conteneurs.

Accéder à l'adresse IP de l'entrée

Attendez plusieurs minutes que l'équilibreur de charge d'application soit configuré.

Vous pouvez vérifier le fonctionnement de l'équilibreur de charge natif en conteneurs en accédant à l'adresse IP de l'entrée.

Pour obtenir l'adresse IP de l'entrée, exécutez la commande suivante :

kubectl get ingress neg-demo-ing

Dans le résultat de la commande, l'adresse IP de l'entrée est affichée dans la colonne ADDRESS. Accédez à l'adresse IP dans un navigateur Web.

Vérifier l'état de santé du service de backend

Vous pouvez également vérifier l'état de santé du service de backend de l'équilibreur de charge.

  1. Obtenez la liste des services de backend qui s'exécutent dans votre projet :

    gcloud compute backend-services list
    

    Enregistrez le nom du service de backend qui inclut le nom du Service (par exemple neg-demo-svc).

  2. Obtenez l'état de santé du service de backend :

    gcloud compute backend-services get-health BACKEND_SERVICE_NAME --global
    

    Remplacez BACKEND_SERVICE_NAME par le nom du service de backend.

Tester l'objet Ingress

Un autre moyen vous permet de vérifier que l'équilibreur de charge fonctionne : effectuer un scaling de l'exemple de déploiement, envoyer des requêtes test à l'entrée et vérifier que le bon nombre d'instances dupliquées répond.

  1. Effectuez le scaling du déploiement neg-demo-app d'une instance à deux instances :

    kubectl scale deployment neg-demo-app --replicas 2
    

    Cette commande peut prendre plusieurs minutes.

  2. Vérifiez que le déploiement est terminé :

    kubectl get deployment neg-demo-app
    

    Le résultat doit inclure deux instances dupliquées disponibles :

    NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    neg-demo-app   2         2         2            2           26m
    
  3. Récupérez l'adresse IP de l'objet Ingress :

    kubectl describe ingress neg-demo-ing
    

    Si cette commande renvoie une erreur 404, attendez quelques minutes que l'équilibreur de charge démarre, puis réessayez.

  4. Comptez le nombre de réponses distinctes de l'équilibreur de charge :

    for i in `seq 1 100`; do \
      curl --connect-timeout 1 -s IP_ADDRESS && echo; \
    done  | sort | uniq -c
    

    Remplacez IP_ADDRESS par l'adresse IP d'entrée.

    Le résultat ressemble à ce qui suit :

    44 neg-demo-app-7f7dfd7bc6-dcn95
    56 neg-demo-app-7f7dfd7bc6-jrmzf
    

    Dans ce résultat, le nombre de réponses distinctes est identique au nombre d'instances dupliquées, ce qui indique que tous les pods backend diffusent du trafic.

Effectuer un nettoyage

Une fois les tâches de cette page terminées, procédez comme suit pour supprimer les ressources afin d'éviter que des frais inutiles ne vous soient facturés sur votre compte :

Supprimer le cluster

gcloud

gcloud container clusters delete neg-demo-cluster

Console

  1. Accédez à la page Google Kubernetes Engine dans Google Cloud Console.

    Accéder à Google Kubernetes Engine

  2. Sélectionnez neg-demo-cluster, puis cliquez sur Supprimer.

  3. Lorsque vous êtes invité à confirmer votre choix, cliquez sur Supprimer.

Dépannage

Utilisez les techniques ci-dessous pour valider votre configuration réseau. Dans les sections suivantes, nous expliquons comment résoudre des problèmes spécifiques liés à l'équilibrage de charge natif en conteneurs.

  • Consultez la documentation sur l'équilibrage de charge pour savoir comment répertorier vos groupes de points de terminaison réseau.

  • Vous pouvez trouver le nom et les zones du NEG correspondant à un service donné dans l'annotation neg-status de ce service. Vous pouvez obtenir les spécifications du service avec la commande :

    kubectl get svc SVC_NAME -o yaml
    

    L'annotation metadata:annotations:cloud.google.com/neg-status contient le nom du NEG correspondant au service et les zones de ce groupe.

  • Vous pouvez vérifier l'état du service de backend correspondant à un NEG à l'aide de la commande suivante :

    gcloud compute backend-services --project PROJECT_NAME \
        get-health BACKEND_SERVICE_NAME --global
    

    Le service de backend porte le même nom que le NEG associé.

  • Pour imprimer les journaux d'événements d'un service, utilisez la commande :

    kubectl describe svc SERVICE_NAME
    

    La chaîne de caractères du nom du service inclut le nom et l'espace de noms du service GKE correspondant.

Impossible de créer un cluster avec des adresses IP d'alias

Symptômes

Lorsque vous tentez de créer un cluster à l'aide d'adresses IP d'alias, vous pouvez rencontrer le message d'erreur suivant :

ResponseError: code=400, message=IP aliases cannot be used with a legacy network.
Causes probables

Cette erreur se produit lorsque vous tentez de créer un cluster doté d'adresses IP d'alias qui utilise également un réseau ancien.

Solution

Assurez-vous de ne pas créer de cluster utilisant simultanément des adresses IP d'alias et un ancien réseau. Pour en savoir plus sur l'utilisation d'adresses IP d'alias, consultez la page Créer un cluster de VPC natif.

Le trafic n'atteint pas les points de terminaison

Symptômes
Erreurs 502 ou connexions refusées
.
Causes probables

Les nouveaux points de terminaison sont généralement accessibles une fois qu'ils sont reliés à l'équilibreur de charge, à condition qu'ils répondent aux vérifications d'état. Vous pouvez rencontrer des erreurs 502 ou des connexions refusées si le trafic ne peut pas atteindre les points de terminaison.

Les erreurs 502 et les connexions refusées peuvent également être causées par un conteneur qui ne gère pas le signal SIGTERM. Si un conteneur ne gère pas explicitement le signal SIGTERM, il s'interrompt immédiatement et cesse de traiter les requêtes. L'équilibreur de charge continue d'envoyer du trafic entrant à destination du conteneur arrêté, ce qui entraîne des erreurs.

L'équilibreur de charge natif du conteneur ne comporte qu'un seul point de terminaison backend. Lors d'une mise à jour progressive, l'ancien point de terminaison est automatiquement déprogrammé avant la programmation du nouveau point de terminaison.

Le ou les pods de backend sont déployés dans une nouvelle zone pour la première fois après le provisionnement d'un équilibreur de charge natif en conteneurs. L'infrastructure de l'équilibreur de charge est programmée dans une zone lorsqu'elle comporte au moins un point de terminaison. Lorsqu'un nouveau point de terminaison est ajouté à une zone, l'infrastructure de l'équilibreur de charge est programmée et entraîne des interruptions de service.

Solution

Configurez les conteneurs afin qu'ils puissent gérer le signal SIGTERM et continuer à répondre aux requêtes tout au long du délai de grâce pour la résiliation (par défaut, 30 secondes). Configurez les pods afin qu'ils se mettent à échouer aux vérifications d'état lorsqu'ils reçoivent un signal SIGTERM. Cela indique à l'équilibreur de charge qu'il doit arrêter d'envoyer du trafic vers le pod lorsque la déprogrammation du point de terminaison est en cours.

Si votre application ne se ferme pas correctement et cesse de répondre aux requêtes lors de la réception d'un SIGTERM, le hook d'arrêt préalable peut servir à gérer SIGTERM et continuer à diffuser le trafic pendant la déprogrammation du point de terminaison.

lifecycle:
  preStop:
    exec:
      # if SIGTERM triggers a quick exit; keep serving traffic instead
      command: ["sleep","60"]

Consultez la documentation sur l'arrêt d'un pod.

Si le backend de votre équilibreur de charge ne comporte qu'une seule instance, veuillez configurer la stratégie de déploiement pour éviter de supprimer la seule instance avant que la nouvelle instance ne soit complètement programmée. Pour les pods d'application gérés par la charge de travail Deployment, cette opération peut être effectuée en configurant la stratégie de déploiement avec le paramètre maxUnavailable égal à 0.

strategy:
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

Pour résoudre les cas où le trafic n'atteint pas les points de terminaison, vérifiez que les règles de pare-feu autorisent le trafic TCP entrant vers vos points de terminaison sur les plages 130.211.0.0/22 et 35.191.0.0/16. Pour en savoir plus, consultez la page Ajouter des vérifications d'état dans la documentation Cloud Load Balancing.

Affichez les services de backend dans votre projet. La chaîne de caractères du nom du service de backend concerné inclut le nom et l'espace de noms du service GKE correspondant :

gcloud compute backend-services list

Retrouvez l'état de santé du service de backend :

gcloud compute backend-services get-health BACKEND_SERVICE_NAME

Si aucun des services de backend n'est opérationnel, il se peut que votre pare-feu, votre entrée ou votre service soient mal configurés.

Si certains services de backend ne sont pas opérationnels pendant une courte période, cela peut s'expliquer par la latence de la programmation réseau.

Si certains services de backend ne figurent pas dans la liste, cela peut être dû à la latence de la programmation. Vous pouvez vous en assurer en exécutant la commande suivante, où NEG_NAME est le nom du service de backend (les NEG et les services de backend portent le même nom) :

gcloud compute network-endpoint-groups list-network-endpoints NEG_NAME

Vérifiez que tous les points de terminaison prévus se trouvent dans le NEG.

Si vous avez un petit nombre de backends (par exemple, 1 Pod) sélectionnés par un équilibreur de charge natif en conteneurs, envisagez d'augmenter le nombre d'instances dupliquées et de répartir les pods de backend sur toutes les zones couvertes par le cluster GKE. Cela garantit que l'infrastructure de l'équilibreur de charge sous-jacent est entièrement programmée. Sinon, envisagez de limiter les pods de backend à une seule zone.

Si vous configurez une règle de réseau pour le point de terminaison, assurez-vous que le trafic entrant provenant du sous-réseau proxy réservé est autorisé.

Blocage du déploiement

Symptômes
Le déploiement d'une mise à jour se bloque et le nombre d'instances dupliquées à jour ne correspond pas au nombre visé
.
Causes probables

Les vérifications d'état du déploiement échouent. Il est possible que l'image du conteneur soit incorrecte ou que la vérification d'état soit mal configurée. Le remplacement progressif des pods attend jusqu'à ce qu'un pod qui vient d'être lancé passe sa porte de disponibilité de pod. Cela se produit uniquement si le pod répond aux vérifications d'état de l'équilibreur de charge. Si le pod ne répond pas ou si la vérification d'état est mal configurée, les conditions de la porte de disponibilité ne peuvent pas être remplies et le déploiement ne peut pas se poursuivre.

Si vous utilisez kubectl 1.13 ou une version ultérieure, vous pouvez vérifier l'état des portes de disponibilité d'un pod à l'aide de la commande suivante :

kubectl get pod POD_NAME -o wide

Vérifiez le contenu de la colonne READINESS GATES.

Cette colonne n'existe pas dans les versions 1.12 et antérieures de kubectl. Un pod identifié comme étant dans l'état READY (prêt) peut néanmoins présenter une porte de disponibilité en échec. Pour le vérifier, exécutez la commande suivante :

kubectl get pod POD_NAME -o yaml

Les portes de disponibilité et leur état sont répertoriés dans le résultat.

Solution

Vérifiez que l'image du conteneur référencée dans la spécification de déploiement de pod fonctionne correctement et qu'elle est en mesure de répondre aux vérifications d'état. Assurez-vous que les vérifications d'état sont correctement configurées.

Problèmes connus

Sur GKE, l'équilibrage de charge natif en conteneurs présente les problèmes connus suivants :

Récupération de mémoire incomplète

GKE effectue la récupération de mémoire sur les équilibreurs de charge natifs en conteneurs toutes les deux minutes. Si un cluster est supprimé avant la suppression complète des équilibreurs de charge, vous devez supprimer manuellement les NEG de l'équilibreur de charge.

Affichez les NEG de votre projet en exécutant la commande suivante :

gcloud compute network-endpoint-groups list

Dans le résultat de la commande, recherchez les NEG concernés.

Pour supprimer un NEG, exécutez la commande suivante, en remplaçant NEG_NAME par le nom du NEG :

gcloud compute network-endpoint-groups delete NEG_NAME

Aligner les déploiements de charges de travail avec la propagation des points de terminaison

Lorsque vous déployez une charge de travail sur votre cluster ou lorsque vous mettez à jour une charge de travail existante, l'équilibreur de charge natif en conteneurs peut nécessiter plus de temps pour propager les nouveaux points de terminaison que pour terminer le déploiement de la charge de travail. L'exemple de déploiement que vous créez dans ce guide utilise deux champs pour s'aligner sur la propagation des points de terminaison : terminationGracePeriodSeconds et minReadySeconds.

terminationGracePeriodSeconds permet au pod de s'arrêter normalement en attendant l'arrêt des connexions après sa suppression programmée.

minReadySeconds ajoute une période de latence après la création d'un pod. Vous devez spécifier le nombre minimal de secondes pendant lesquelles un nouveau pod doit se trouver à l'état Ready sans connaître aucun plantage de conteneur pour être considéré comme disponible.

Vous devez configurer les valeurs minReadySeconds et terminationGracePeriodSeconds de vos charges de travail sur 60 secondes ou plus pour vous assurer que le service n'est pas interrompu en raison de déploiements de charges de travail.

terminationGracePeriodSeconds est disponible dans toutes les spécifications de pod, et minReadySeconds est disponible pour les déploiements et les DaemonSets.

Pour savoir comment configurer avec précision des déploiements, consultez la section sur RollingUpdateStrategy.

La contrainte initialDelaySeconds du champ readinessProbe du pod n'est pas respectée

Vous pourriez vous attendre à ce que la contrainte initialDelaySeconds configurée dans le champ readinessProbe du pod soit respectée par l'équilibreur de charge natif en conteneurs. Cependant, readinessProbe est mis en œuvre par kubelet, et la configuration initialDelaySeconds contrôle la vérification d'état du kubelet, et non de l'équilibreur de charge natif en conteneurs. L'équilibrage de charge natif en conteneurs dispose de sa propre vérification d'état.

Étape suivante