Guide de dépannage visuel pour Kubernetes

Remarque perev. : Cet article fait partie du matériel disponible gratuitement du projet learnk8s , qui vous apprend à travailler avec les sociétés Kubernetes et les administrateurs individuels. Dans ce document, Daniele Polencic, le chef de projet, partage une instruction claire sur les mesures à prendre en cas de problèmes généraux pour les applications s'exécutant dans le cluster K8s.



TL; DR: voici un diagramme qui vous aidera à déboguer le déploiement dans Kubernetes:



Organigramme pour rechercher et corriger les erreurs dans un cluster. Dans l'original (en anglais), il est disponible en PDF et en image .

Lors du déploiement d'une application sur Kubernetes, vous devez généralement définir trois composants:

  • Le déploiement est une recette pour créer des copies d'une application appelée pods;
  • Service - un équilibreur de charge interne qui répartit le trafic entre les pods;
  • Entrée - une description de la façon dont le trafic sera acheminé du monde extérieur vers le service.

Voici un bref résumé graphique:

1) Dans Kubernetes, les applications reçoivent le trafic du monde extérieur via deux couches d'équilibreurs de charge: interne et externe.



2) L'équilibreur interne est appelé Service, externe - Ingress.



3) Le déploiement crée des pods et les surveille (ils ne sont pas créés manuellement).



Supposons que vous souhaitiez déployer une application simple à la Hello World . La configuration YAML pour cela ressemblera à ceci:

apiVersion: apps/v1 kind: Deployment # <<< metadata: name: my-deployment labels: track: canary spec: selector: matchLabels: any-name: my-app template: metadata: labels: any-name: my-app spec: containers: - name: cont1 image: learnk8s/app:1.0.0 ports: - containerPort: 8080 --- apiVersion: v1 kind: Service # <<< metadata: name: my-service spec: ports: - port: 80 targetPort: 8080 selector: name: app --- apiVersion: networking.k8s.io/v1beta1 kind: Ingress # <<< metadata: name: my-ingress spec: rules: - http: paths: - backend: serviceName: app servicePort: 80 path: / 

La définition est assez longue et il est facile de se tromper sur la façon dont les composants sont liés les uns aux autres.

Par exemple:

  • Quand faut-il utiliser le port 80 et quand - 8080?
  • Dois-je créer un nouveau port pour chaque service afin qu'ils n'entrent pas en conflit?
  • Les noms d'étiquette sont-ils importants? Devraient-ils être les mêmes partout?

Avant de nous concentrer sur le débogage, rappelons comment les trois composants sont liés les uns aux autres. Commençons par le déploiement et le service.

Déploiement de connexion'a et Service'a


Vous serez surpris, mais les déploiements et les services ne sont en aucun cas connectés. Au lieu de cela, le service pointe directement vers des pods contournant le déploiement.

Ainsi, nous sommes intéressés par la façon dont les pods et les services sont liés les uns aux autres. Trois choses à retenir:

  1. Un selector service doit correspondre à au moins une étiquette de pod.
  2. targetPort doit correspondre au containerPort conteneur à l'intérieur du pod.
  3. port service port peut être n'importe quoi. Différents services peuvent utiliser le même port car ils ont des adresses IP différentes.

Le diagramme suivant représente tout ce qui précède sous forme graphique:

1) Imaginez que le service dirige le trafic vers un certain pod:



2) Lors de la création d'un pod, vous devez spécifier containerPort pour chaque conteneur dans les pods:



3) Lors de la création du service, vous devez spécifier le port et targetPort . Mais lequel se connecte au conteneur?



4) Via targetPort . Il doit correspondre à containerPort .



5) Supposons que le port 3000 soit ouvert dans le conteneur, puis la valeur targetPort devrait être la même.



Dans le fichier YAML, les étiquettes et les ports / targetPort doivent correspondre:

 apiVersion: apps/v1 kind: Deployment metadata: name: my-deployment labels: track: canary spec: selector: matchLabels: any-name: my-app template: metadata: labels: # <<< any-name: my-app # <<< spec: containers: - name: cont1 image: learnk8s/app:1.0.0 ports: - containerPort: 8080 # <<< --- apiVersion: v1 kind: Service metadata: name: my-service spec: ports: - port: 80 targetPort: 8080 # <<< selector: # <<< any-name: my-app # <<< 

Qu'en est-il de la track: canary en haut de la section Déploiement? Doit-elle correspondre?

Cette étiquette fait référence au déploiement et n'est pas utilisée par le service pour acheminer le trafic. En d'autres termes, il peut être supprimé ou attribué une valeur différente.

Qu'en est-il du sélecteur matchLabels ?

Il doit toujours correspondre aux étiquettes de Pod , car il est utilisé par Deployment pour suivre les pods.

Supposons que vous ayez effectué les modifications correctes. Comment les vérifier?

Vous pouvez vérifier l'étiquette du pod à l'aide de la commande suivante:

 kubectl get pods --show-labels 

Ou, si les pods appartiennent à plusieurs applications:

 kubectl get pods --selector any-name=my-app --show-labels 

any-name=my-app est l'étiquette any-name: my-app .

Y a-t-il des difficultés?

Vous pouvez vous connecter au pod! Pour ce faire, utilisez la commande port-forward dans kubectl. Il vous permet de vous connecter au service et de vérifier la connexion.

 kubectl port-forward service/<service name> 3000:80 

Ici:

  • service/<service name> - nom du service; dans notre cas, c'est my-service ;
  • 3000 - le port que vous souhaitez ouvrir sur l'ordinateur;
  • 80 - port spécifié dans le champ port du service.

Si vous pouvez établir une connexion, les paramètres sont corrects.

Si la connexion n'a pas pu être établie, il y a un problème avec les étiquettes ou les ports ne correspondent pas.

Connexion du service et de l'entrée


L'étape suivante pour fournir l'accès à l'application est liée à la configuration d'Ingress. Ingress doit savoir comment trouver le service, puis trouver les pods et diriger le trafic vers eux. Ingress trouve le service souhaité par son nom et son port ouvert.

Dans la description d'Ingress and Service, deux paramètres doivent correspondre:

  1. servicePort dans Ingress doit correspondre au paramètre de port dans Service;
  2. serviceName dans Ingress doit correspondre au champ de name dans Service.

Le schéma suivant résume la connexion des ports:

1) Comme vous le savez déjà, le service écoute sur un certain port :



2) Ingress a un paramètre appelé servicePort :



3) Ce paramètre ( servicePort ) doit toujours correspondre au port dans la définition de service:



4) Si le port 80 est spécifié dans Service, alors servicePort doit également être 80:



En pratique, vous devez faire attention aux lignes suivantes:

 apiVersion: v1 kind: Service metadata: name: my-service # <<< spec: ports: - port: 80 # <<< targetPort: 8080 selector: any-name: my-app --- apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: my-ingress spec: rules: - http: paths: - backend: serviceName: my-service # <<< servicePort: 80 # <<< path: / 

Comment vérifier si Ingress fonctionne?

Vous pouvez utiliser la méthode avec kubectl port-forward , mais au lieu du service, vous devez vous connecter au contrôleur Ingress.

Vous devez d'abord trouver le nom du pod avec le contrôleur Ingress:

 kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS kube-system coredns-5644d7b6d9-jn7cq 1/1 Running kube-system etcd-minikube 1/1 Running kube-system kube-apiserver-minikube 1/1 Running kube-system kube-controller-manager-minikube 1/1 Running kube-system kube-proxy-zvf2h 1/1 Running kube-system kube-scheduler-minikube 1/1 Running kube-system nginx-ingress-controller-6fc5bcc 1/1 Running 

Localisez le module Ingress (il peut faire référence à un autre espace de noms) et exécutez la commande describe pour connaître les numéros de port:

 kubectl describe pod nginx-ingress-controller-6fc5bcc \ --namespace kube-system \ | grep Ports Ports: 80/TCP, 443/TCP, 18080/TCP 

Enfin, connectez-vous au pod:

 kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system 

Désormais, chaque fois que vous envoyez une demande au port 3000 sur l'ordinateur, elle sera redirigée vers le port 80 du pod avec le contrôleur Ingress. En allant sur http: // localhost: 3000 , vous devriez voir la page créée par l'application.

Résumé du port


Rappelons-nous à nouveau quels ports et étiquettes doivent correspondre:

  1. Le sélecteur dans la définition de service doit correspondre à l'étiquette du pod;
  2. targetPort dans la définition de service doit correspondre au containerPort conteneur à l'intérieur du pod;
  3. port dans la définition de Service peut être n'importe quoi. Différents services peuvent utiliser le même port car ils ont des adresses IP différentes;
  4. servicePort Ingress doit correspondre au port dans la définition de service;
  5. Le nom du service doit correspondre au champ serviceName dans Ingress.

Hélas, il ne suffit pas de savoir structurer correctement votre configuration YAML.

Que se passe-t-il en cas de problème?

Peut-être que le pod ne démarre pas ou qu'il plante.

3 étapes pour résoudre les échecs d'applications dans Kubernetes


Avant de déboguer un déploiement, vous devez avoir une bonne compréhension du fonctionnement de Kubernetes.

Puisqu'il y a trois composants dans chaque application téléchargée sur K8, ils doivent être débogués dans un certain ordre, en commençant par le bas.

  1. Vous devez d'abord vous assurer que les modules fonctionnent, puis ...
  2. Vérifiez si le service fournit du trafic aux pods, puis ...
  3. Vérifiez si Ingress est correctement configuré.

Présentation visuelle:

1) Commencez la recherche des problèmes par le bas. Vérifiez d'abord que les modules ont les états Ready et Running :



2) Si les pods sont Ready , vous devez savoir si le service répartit le trafic entre les pods:



3) Enfin, vous devez analyser la connexion entre le service et Ingress:



1. Diagnostic des pods


Dans la plupart des cas, le problème vient du pod. Assurez-vous que les modules sont Ready et en Running . Vous pouvez le vérifier en utilisant la commande:

 kubectl get pods NAME READY STATUS RESTARTS AGE app1 0/1 ImagePullBackOff 0 47h app2 0/1 Error 0 47h app3-76f9fcd46b-xbv4k 1/1 Running 1 47h 

Dans la sortie de la commande ci-dessus, le dernier module est répertorié comme en Running d' Running et Ready , mais pas pour les deux autres.

Comment comprendre ce qui a mal tourné?

Il existe quatre commandes utiles pour diagnostiquer les modules:

  1. kubectl logs < pod'> vous permet d'extraire les journaux des conteneurs du pod;
  2. kubectl describe pod < pod'> vous permet d'afficher une liste des événements associés au pod;
  3. kubectl get pod < pod'> vous permet d'obtenir la configuration YAML du kubectl get pod < pod'> stockée dans Kubernetes;
  4. kubectl exec -ti < pod'> bash vous permet d'exécuter un shell de commande interactif dans l'un des conteneurs de pod

Lequel choisir?

Le fait est qu'il n'y a pas d'équipe universelle. Une combinaison de ces éléments doit être utilisée.

Problèmes courants de pod


Il existe deux principaux types d'erreurs de module: les erreurs de démarrage et les erreurs d'exécution.

Erreurs de démarrage:

  • ImagePullBackoff
  • ImageInspectError
  • ErrImagePull
  • ErrImageNeverPull
  • RegistryUnavailable
  • InvalidImageName

Erreurs d'exécution:

  • CrashLoopBackOff
  • RunContainerError
  • KillContainerError
  • VerifyNonRootError
  • RunInitContainerError
  • CreatePodSandboxError
  • ConfigPodSandboxError
  • KillPodSandboxError
  • SetupNetworkError
  • TeardownNetworkError

Certaines erreurs sont plus courantes que d'autres. Voici quelques erreurs courantes et comment les corriger.

ImagePullBackOff


Cette erreur apparaît lorsque Kubernetes ne parvient pas à obtenir une image pour l'un des conteneurs de pod. Voici les trois raisons les plus courantes à cela:

  1. Le nom de l'image n'est pas spécifié correctement - par exemple, vous y avez fait une erreur ou l'image n'existe pas;
  2. Une balise inexistante pour l'image est spécifiée;
  3. L'image est stockée dans un registre privé et Kubernetes n'est pas autorisé à y accéder.

Les deux premières raisons sont faciles à éliminer - il suffit de corriger le nom et la balise de l'image. Dans ce dernier cas, vous devez saisir les informations d'identification du registre privé dans Secret et ajouter des liens vers celui-ci dans les pods. La documentation de Kubernetes a un exemple de comment cela peut être fait.

CrashLoopBackOff


Kubenetes générera une erreur CrashLoopBackOff si le conteneur ne peut pas démarrer. Cela se produit généralement lorsque:

  1. Il y a une erreur dans l'application qui l'empêche de démarrer;
  2. Le conteneur n'est pas configuré correctement ;
  3. Le test de vivacité a échoué trop de fois.

Vous devez essayer d'accéder aux journaux du conteneur pour découvrir la raison de son échec. Si l'accès aux journaux est difficile, car le conteneur redémarre trop rapidement, vous pouvez utiliser la commande suivante:

 kubectl logs <pod-name> --previous 

Il affiche les messages d'erreur d'une réincarnation de conteneur précédente.

RunContainerError


Cette erreur se produit lorsque le conteneur ne démarre pas. Il correspond à l'instant précédant le lancement de l'application. Habituellement, sa cause est une configuration incorrecte, par exemple:

  • Tenter de monter un volume inexistant, tel que ConfigMap ou Secrets;
  • essayez de monter un volume en lecture seule en lecture-écriture.

La commande kubectl describe pod <pod-name> est bien adaptée pour analyser de telles erreurs.

Gousses en attente


Après la création, le pod reste dans l'état Pending .

Pourquoi cela se produit-il?

Voici les raisons possibles (je suppose que le planificateur fonctionne bien):

  1. Le cluster ne dispose pas de suffisamment de ressources, telles que la puissance de traitement et la mémoire, pour exécuter le module.
  2. L'objet ResourceQuota est installé dans l'espace de noms correspondant et la création d'un pod entraînera l'espace de noms au-delà du quota.
  3. Le pod est lié à Pending PersistentVolumeClaim .

Dans ce cas, il est recommandé d'utiliser la commande kubectl describe et de vérifier la section Events :

 kubectl describe pod <pod name> 

En cas d'erreurs liées à ResourceQuotas , il est recommandé d'afficher les journaux de cluster à l'aide de la commande

 kubectl get events --sort-by=.metadata.creationTimestamp 

Pods non prêts


Si le module est répertorié comme Running d' Running , mais n'est pas à l'état Ready , la sonde de préparation échoue.

Lorsque cela se produit, le pod ne se connecte pas au service et le trafic n'y circule pas. Le test de préparation a échoué en raison de problèmes d'application. Dans ce cas, pour trouver l'erreur, vous devez analyser la section Events dans la sortie de la commande kubectl describe .

2. Diagnostic des services


Si les modules sont répertoriés comme Running d' Running et Ready , mais qu'il n'y a toujours pas de réponse de l'application, vous devez vérifier les paramètres de service.

Les services sont impliqués dans l'acheminement du trafic vers les pods en fonction de leurs étiquettes. Par conséquent, la première chose à faire est de vérifier combien de pods fonctionnent avec le service. Pour ce faire, vous pouvez vérifier les points de terminaison du service:

 kubectl describe service <service-name> | grep Endpoints 

Le point de terminaison est une paire de valeurs de la forme <IP-:> , et au moins une telle paire doit être présente dans la sortie (c'est-à-dire qu'au moins un pod fonctionne avec le service).

Si la section Endpoins vide, deux options sont possibles:

  1. il n'y a pas de pods avec la bonne étiquette (astuce: vérifiez si l'espace de noms est sélectionné correctement);
  2. Il y a une erreur dans les étiquettes de service dans le sélecteur.

Si vous voyez une liste de points de terminaison, mais ne pouvez toujours pas accéder à l'application, le coupable probable est l'erreur dans targetPort dans la description du service.

Comment vérifier le bon fonctionnement du service?

Quel que soit le type de service, vous pouvez utiliser la commande kubectl port-forward pour vous y connecter:

 kubectl port-forward service/<service-name> 3000:80 

Ici:

  • <service-name> - le nom du service;
  • 3000 - le port que vous ouvrez sur l'ordinateur;
  • 80 - port côté service.

3. Diagnostics d'entrée


Si vous lisez jusqu'à cet endroit, alors:

  • les pods sont répertoriés comme en Running d' Running et Ready ;
  • le service distribue avec succès le trafic entre les pods.

Cependant, vous ne pouvez toujours pas «tendre la main» à l'application.

Cela signifie que, très probablement, le contrôleur Ingress est mal configuré. Le contrôleur Ingress étant un composant tiers du cluster, il existe différentes méthodes de débogage en fonction de son type.

Mais avant de recourir à des outils spéciaux pour configurer Ingress, vous pouvez faire quelque chose de très simple. Ingress utilise serviceName et servicePort pour se connecter au service. Vous devez vérifier qu'ils sont correctement configurés. Vous pouvez le faire en utilisant la commande:

 kubectl describe ingress <ingress-name> 

Si la colonne Backend est vide, le risque d'erreur de configuration est élevé. Si les backends sont en place, mais qu'il n'y a toujours pas accès à l'application, le problème peut être lié à:

  • Entrer les paramètres d'accessibilité à partir d'Internet public;
  • paramètres d'accessibilité du cluster à partir d'Internet public.

Vous pouvez identifier les problèmes d'infrastructure en vous connectant directement au module Ingress. Pour ce faire, recherchez d'abord le pod du contrôleur Ingress (il peut se trouver dans un espace de noms différent):

 kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS kube-system coredns-5644d7b6d9-jn7cq 1/1 Running kube-system etcd-minikube 1/1 Running kube-system kube-apiserver-minikube 1/1 Running kube-system kube-controller-manager-minikube 1/1 Running kube-system kube-proxy-zvf2h 1/1 Running kube-system kube-scheduler-minikube 1/1 Running kube-system nginx-ingress-controller-6fc5bcc 1/1 Running 

Utilisez la commande describe pour définir le port:

 kubectl describe pod nginx-ingress-controller-6fc5bcc --namespace kube-system \ | grep Ports 

Enfin, connectez-vous au pod:

 kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system 

Désormais, toutes les demandes de port 3000 sur l'ordinateur seront redirigées vers le port 80 pod.

Ça marche maintenant?

  • Si c'est le cas, le problème est lié à l'infrastructure. Il est nécessaire de savoir exactement comment le trafic est acheminé vers le cluster.
  • Sinon, le problème vient du contrôleur Ingress.

Si vous ne pouvez pas faire fonctionner le contrôleur Ingress, vous devrez le déboguer.

Il existe de nombreuses variétés de contrôleurs Ingress. Les plus populaires sont Nginx, HAProxy, Traefik, etc. (pour plus d'informations sur les solutions existantes, consultez notre revue - environ la traduction). Vous devez utiliser le guide de dépannage dans la documentation du contrôleur correspondant. Étant donné qu'Ingress Nginx est le contrôleur Ingress le plus populaire, nous avons inclus quelques conseils sur la résolution des problèmes connexes dans cet article.

Débogage d'un contrôleur Ingress Nginx



Le projet Ingress-nginx a un plugin officiel pour kubectl . La commande kubectl ingress-nginx peut être utilisée pour:

  • analyse des journaux, backends, certificats, etc.;
  • connexion à Ingress;
  • étudier la configuration actuelle.

Les trois équipes suivantes vous y aideront:

  • kubectl ingress-nginx lint - vérifie nginx.conf ;
  • kubectl ingress-nginx backend - examine le backend (similaire à kubectl describe ingress <ingress-name> );
  • kubectl ingress-nginx logs - vérifie les journaux.

Veuillez noter que dans certains cas, il peut être nécessaire de spécifier l'espace de noms correct pour le contrôleur Ingress à l'aide de l' --namespace <name> .

Résumé


Le diagnostic de Kubernetes peut être une tâche ardue si vous ne savez pas par où commencer. Le problème doit toujours être abordé selon le principe ascendant: commencer par les pods, puis aller au service et à Ingress. Les méthodes de débogage décrites dans l'article peuvent être appliquées à d'autres objets, tels que:

  • Jobs inactifs et CronJobs;
  • StatefulSets et DaemonSets.

Merci à Gergely Risko , Daniel Weibel et Charles Christyraj pour leurs précieux commentaires et ajouts.

PS du traducteur


Lisez aussi dans notre blog:

Source: https://habr.com/ru/post/fr484954/


All Articles