É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.

Reportez-vous à la section Équilibrage de charge natif en conteneurs pour en savoir plus sur les avantages, les exigences et les limites de l'équilibrage de charge natif en conteneurs.

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 en version 1.17 et ultérieure, et sous certaines conditions, l'équilibrage de charge natif en conteneurs est exploité par défaut et ne nécessite pas d'ajouter explicitement une annotation de service cloud.google.com/neg: '{"ingress": true}'.

Créer un cluster de VPC natif

Pour utiliser l'équilibrage de charge natif en conteneurs, vous devez créer un cluster avec des adresses IP d'alias activées.

Par exemple, la commande suivante crée un cluster, neg-demo-cluster, avec un sous-réseau provisionné automatiquement dans la zone us-central1-a :

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

Créer un déploiement

Déployez ensuite une charge de travail sur le cluster.

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, accessible selon la version de GKE que vous exploitez. Consultez la section Disponibilité des pods pour en savoir plus et connaître les exigences concernant la version de GKE. Envisagez de mettre à niveau votre cluster pour pouvoir utiliser le 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: k8s.gcr.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: k8s.gcr.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 simplement 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 en exécutant la commande suivante :

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:
  - 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 en exécutant la commande suivante :

kubectl apply -f neg-demo-svc.yaml

Créer une entrée pour le service

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

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: neg-demo-ing
spec:
  backend:
    serviceName: neg-demo-svc # Name of the Service targeted by the Ingress
    servicePort: 80 # Should match the port used by the Service

Enregistrez ce fichier manifeste sous le nom neg-demo-ing.yaml, puis créez l'entrée en exécutant la commande suivante :

kubectl apply -f neg-demo-ing.yaml

La création de cette entrée entraîne celle d'un équilibreur de charge HTTP(S) 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'entrée

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.

Pour retrouver l'état de l'entrée, exécutez la commande suivante :

kubectl describe ingress neg-demo-ing

Dans le résultat de la commande, recherchez 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 le fonctionnement de 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 HTTP(S) 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.

Commencez par obtenir la liste des services de backend qui s'exécutent dans votre projet :

gcloud compute backend-services list

Copiez le nom du backend, qui comprend le nom du service (par exemple neg-demo-svc). Obtenez ensuite l'état de santé du service de backend :

gcloud compute backend-services get-health backend-service-name --global

Vérifier le fonctionnement de l'entrée

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.

La commande suivante permet d'effectuer un scaling du déploiement neg-demo-app (passage d'une instance à deux instances) :

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

Un délai de quelques minutes peut être nécessaire. Pour vérifier que le déploiement est terminé, exécutez la commande suivante :

kubectl get deployment neg-demo-app

Dans le résultat de la commande, vérifiez que deux instances dupliquées sont bien disponibles :

NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
neg-demo-app   2         2         2            2           26m

Exécutez ensuite la commande suivante pour compter 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

ip-address est l'adresse IP de l'entrée. Vous pouvez obtenir l'adresse IP de l'entrée à l'aide de la commande kubectl describe ingress neg-demo-ing.

Dans le résultat de la commande, vérifiez que le nombre de réponses distinctes est identique au nombre d'instances dupliquées. Cela indique que tous les pods backend diffusent du trafic :

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

Nettoyer

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 au menu "Google Kubernetes Engine" dans Cloud Console.

    Accéder au menu Google Kubernetes Engine

  2. Sélectionnez neg-demo-cluster.

  3. 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 des clusters de VPC natif à l'aide d'adresses IP d'alias.

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.

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.

Pour plus d'informations, reportez-vous à la documentation sur l'arrêt des pods et à ce billet de blog sur les bonnes pratiques en matière d'arrêt des pods (en anglais).

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 backend concerné inclut le nom et l'espace de noms du service Google Kubernetes Engine 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 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

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

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 my-pod -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 my-pod -o yaml

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

Ré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 Google Kubernetes Engine, l'équilibrage de charge natif en conteneurs présente les problèmes connus suivants :

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

Google Kubernetes Engine 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, où neg est le nom du NEG :

gcloud compute network-endpoint-groups delete neg

Aligner des 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 concernant 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.

Étapes suivantes