Meilleures pratiques pour les conteneurs Kubernetes : vérifications de disponibilité

Meilleures pratiques pour les conteneurs Kubernetes : vérifications de disponibilité

TL;DR

  • Pour atteindre une haute observabilitĂ© des conteneurs et des microservices, les journaux et les mĂ©triques primaires ne suffisent pas.
  • Pour une rĂ©cupĂ©ration plus rapide et une meilleure rĂ©silience, les applications doivent appliquer le Principe de Haute ObservabilitĂ© (HOP, High Observability Principle).
  • Au niveau de l'application, le HOP nĂ©cessite : une journalisation appropriĂ©e, une surveillance rigoureuse, des vĂ©rifications de disponibilitĂ© et des traces de performance/transitions.
  • En tant qu'Ă©lĂ©ment du HOP, utilisez les vĂ©rifications readinessProbe et livenessProbe Kubernetes.

Qu'est-ce qu'un ModÚle de vérification de disponibilité ?

Lors de la conception d'une application critique et hautement disponible, il est trĂšs important de penser Ă  un aspect tel que la rĂ©silience. Une application est considĂ©rĂ©e comme rĂ©siliente si elle se remet rapidement aprĂšs une dĂ©faillance. Une application cloud typique utilise une architecture de microservices - chaque composant Ă©tant placĂ© dans un conteneur sĂ©parĂ©. Pour s'assurer que l'application sur k8s est hautement disponible, il est nĂ©cessaire de suivre certains modĂšles lors de la conception d'un cluster. Parmi eux se trouve le ModĂšle de vĂ©rification de disponibilitĂ©. Il dĂ©finit comment l'application informe k8s de sa disponibilitĂ©. Il ne s'agit pas seulement d'une information sur le fait que le pod fonctionne, mais aussi sur la façon dont il traite et rĂ©pond aux requĂȘtes. Plus Kubernetes en sait sur la disponibilitĂ© d'un pod, plus il prend des dĂ©cisions intelligentes quant Ă  l'acheminement du trafic et Ă  l'Ă©quilibrage de la charge. Ainsi, le Principe de Haute ObservabilitĂ© permet Ă  l'application de rĂ©pondre en temps voulu aux requĂȘtes.

Principe de Haute Observabilité (HOP)

Le Principe de Haute ObservabilitĂ© est l'un des principes de conception des applications conteneurisĂ©esDans une architecture microservices, les services ne se soucient pas de la maniĂšre dont leur requĂȘte est traitĂ©e (et c'est la bonne approche), mais il est important de savoir comment obtenir des rĂ©ponses des services rĂ©cepteurs. Par exemple, pour l'authentification d'un utilisateur, un conteneur envoie une requĂȘte HTTP Ă  un autre, en attendant une rĂ©ponse dans un certain format : c'est tout. Une requĂȘte peut ĂȘtre traitĂ©e en PythonJS et la rĂ©ponse peut ĂȘtre fournie en Python Flask. Les conteneurs sont comme des boĂźtes noires avec un contenu cachĂ©. Cependant, le principe de la santĂ© opĂ©rationnelle exige que chaque service rĂ©vĂšle plusieurs points de terminaison API, montrant son niveau de fonctionnement ainsi que son Ă©tat de prĂ©paration et de tolĂ©rance aux pannes. Ces indicateurs sont demandĂ©s par Kubernetes afin de planifier les prochaines Ă©tapes en matiĂšre de routage et de rĂ©partition des charges.

Une application cloud bien conçue journalise ses événements principaux en utilisant les flux d'entrée-sortie standard STDERR et STDOUT. Ensuite, un service auxiliaire, comme filebeat, logstash ou fluentd, transmet les journaux à un systÚme de surveillance centralisé (comme Prometheus) et à un systÚme de collecte de journaux (ensemble de logiciels ELK). Le schéma ci-dessous illustre comment l'application cloud fonctionne selon le ModÚle de vérification de la disponibilité et le Principe de haute observabilité.

Meilleures pratiques pour les conteneurs Kubernetes : vérifications de disponibilité

Comment appliquer le ModÚle de vérification de la disponibilité dans Kubernetes ?

Par dĂ©faut, k8s surveille l'Ă©tat des pods Ă  l'aide de l'un des contrĂŽleurs (DĂ©ploiements, ReplicaSets, DaemonSets, StatefulSets et autres, etc.). Lorsqu'un pod est constatĂ© comme Ă©tant hors service pour une raison quelconque, le contrĂŽleur tente de le redĂ©marrer ou de le reprogrammer sur un autre nƓud. Cependant, un pod peut signaler qu'il est en cours d'exĂ©cution et fonctionnel alors qu'en rĂ©alitĂ© il ne fonctionne pas. Prenons un exemple : votre application utilise Apache comme serveur web, et vous avez dĂ©ployĂ© le composant sur plusieurs pods du cluster. Étant donnĂ© que la bibliothĂšque a Ă©tĂ© mal configurĂ©e, toutes les requĂȘtes Ă  l'application renvoient le code 500 (erreur interne du serveur). Lors de la vĂ©rification de la livraison, l'Ă©tat des pods affiche un rĂ©sultat satisfaisant, alors que les clients ne sont pas de cet avis. Nous dĂ©crirons cette situation indĂ©sirable comme suit :

Meilleures pratiques pour les conteneurs Kubernetes : vérifications de disponibilité

Dans notre exemple, k8s effectue une vĂ©rification de la disponibilitĂ©. Dans ce type de vĂ©rification, kubelet surveille constamment l'Ă©tat du processus dans le conteneur. DĂšs qu'il dĂ©tecte que le processus est bloquĂ©, il le redĂ©marre. Si une erreur peut ĂȘtre corrigĂ©e par un simple redĂ©marrage de l'application, et que le programme est conçu pour se fermer en cas de toute erreur, alors pour respecter les NORMES et le ModĂšle de vĂ©rification de la fonctionnalitĂ©, il suffit de contrĂŽler la fonctionnalitĂ© du processus. Malheureusement, toutes les erreurs ne peuvent pas ĂȘtre rĂ©solues par un redĂ©marrage. Dans ce cas, k8s propose deux mĂ©thodes plus approfondies pour diagnostiquer les problĂšmes d'un pod : livenessProbe et readinessProbe.

LivenessProbe

Pendant ce temps, livenessProbe kubelet effectue trois types de vĂ©rifications : il dĂ©termine non seulement si le pod fonctionne, mais aussi s'il est prĂȘt Ă  recevoir et Ă  rĂ©pondre correctement aux requĂȘtes :

  • Établir une requĂȘte HTTP au pod. La rĂ©ponse doit contenir un code de rĂ©ponse HTTP compris entre 200 et 399. Ainsi, les codes 5xx et 4xx signalent que le pod a des problĂšmes, mĂȘme si le processus fonctionne.
  • Pour vĂ©rifier les pods avec des services non-HTTP (comme le serveur de messagerie Postfix), il est nĂ©cessaire d'Ă©tablir une connexion TCP.
  • ExĂ©cuter une commande arbitraire pour le pod (en interne). La vĂ©rification est considĂ©rĂ©e comme rĂ©ussie si le code de sortie de la commande est 0.

Voici un exemple de son fonctionnement. La dĂ©finition du pod suivant contient une application NodeJS qui renvoie une erreur 500 aux requĂȘtes HTTP. Pour s'assurer que le conteneur redĂ©marre aprĂšs avoir reçu cette erreur, nous utilisons le paramĂštre livenessProbe :

apiVersion: v1
kind: Pod
metadata:
 name: node500
spec:
 containers:
   - image: magalix/node500
     name: node500
     ports:
       - containerPort: 3000
         protocol: TCP
     livenessProbe:
       httpGet:
         path: /
         port: 3000
       initialDelaySeconds: 5

Cela ne diffĂšre en rien de toute autre dĂ©finition de pod, mais nous ajoutons l'objet .spec.containers.livenessProbe. Le paramĂštre httpGet prend le chemin sur lequel il envoie la requĂȘte HTTP GET (dans notre exemple, c'est /, mais dans des scĂ©narios de production, cela pourrait ĂȘtre quelque chose comme /api/v1/status). De plus, livenessProbe accepte un paramĂštre initialDelaySeconds, qui indique aux opĂ©rations de vĂ©rification d'attendre un certain nombre de secondes. Ce dĂ©lai est nĂ©cessaire car le conteneur a besoin de temps pour se lancer, et lors d'un redĂ©marrage, il sera indisponible pendant un certain temps.

Pour appliquer cette configuration au cluster, utilisez :

kubectl apply -f pod.yaml

AprÚs quelques secondes, vous pouvez vérifier le contenu du pod à l'aide de la commande suivante :

kubectl describe pods node500

À la fin de la sortie, recherchez voilà ce que.

Comme vous pouvez le voir, l'livenessProbe a initiĂ© une requĂȘte HTTP GET, le conteneur a retournĂ© une erreur 500 (comme prĂ©vu), kubelet l'a redĂ©marrĂ©.

Si vous ĂȘtes curieux de savoir comment l'application NideJS a Ă©tĂ© programmĂ©e, voici le fichier app.js et le Dockerfile qui ont Ă©tĂ© utilisĂ©s :

app.js

var http = require('http');

var server = http.createServer(function(req, res) {
    res.writeHead(500, { "Content-type": "text/plain" });
    res.end("Nous avons rencontré une erreurn");
});

server.listen(3000, function() {
    console.log('Le serveur fonctionne sur le port 3000')
})

Dockerfile

FROM node
COPY app.js /
EXPOSE 3000
ENTRYPOINT [ "node","/app.js" ]

Il est important de noter que : l'livenessProbe redĂ©marrera le conteneur uniquement en cas de dĂ©faillance. Si le redĂ©marrage ne corrige pas l'erreur empĂȘchant le fonctionnement du conteneur, kubelet ne pourra pas prendre de mesures pour rĂ©soudre la panne.

readinessProbe

readinessProbe fonctionne de maniĂšre similaire aux livenessProbes (requĂȘtes GET, communications TCP et exĂ©cution de commandes), Ă  l'exception des actions de dĂ©pannage. Un conteneur dans lequel une dĂ©faillance est signalĂ©e n'est pas redĂ©marrĂ©, mais isolĂ© du trafic entrant. Imaginez qu'un des conteneurs effectue de nombreux calculs ou subit une charge importante, provoquant une augmentation du temps de rĂ©ponse aux requĂȘtes. Dans le cas de l'livenessProbe, un contrĂŽle de la disponibilitĂ© de la rĂ©ponse se dĂ©clenche (via le paramĂštre de vĂ©rification timeoutSeconds), aprĂšs quoi kubelet redĂ©marre le conteneur. Lors du dĂ©marrage, le conteneur commence Ă  effectuer des tĂąches gourmandes en ressources, et il est redĂ©marrĂ© Ă  nouveau. Cela peut ĂȘtre critique pour les applications oĂč la rĂ©activitĂ© est importante. Par exemple, une machine en route attend une rĂ©ponse du serveur, le dĂ©lai de rĂ©ponse s'allonge – et la machine se retrouve en panne.

Écrivons une dĂ©finition de readinessProbe, qui Ă©tablira le temps de rĂ©ponse Ă  la requĂȘte GET Ă  un maximum de deux secondes, alors que l'application rĂ©pondra Ă  la requĂȘte GET aprĂšs 5 secondes. Le fichier pod.yaml devrait ressembler Ă  ceci :

apiVersion: v1
kind: Pod
metadata:
 name: nodedelayed
spec:
 containers:
   - image: afakharany/node_delayed
     name: nodedelayed
     ports:
       - containerPort: 3000
         protocol: TCP
     readinessProbe:
       httpGet:
         path: /
         port: 3000
       timeoutSeconds: 2

Déployons le pod avec kubectl :

kubectl apply -f pod.yaml

Attendons quelques secondes, puis voyons comment l'livenessProbe a réagi :

kubectl describe pods nodedelayed

À la fin de la sortie, vous pouvez voir que certains Ă©vĂ©nements ressemblent Ă  cela.

Comme vous pouvez le voir, kubectl n'a pas redĂ©marrĂ© le pod lorsque le temps de vĂ©rification a dĂ©passĂ© 2 secondes. Au lieu de cela, il a annulĂ© la requĂȘte. Les connexions entrantes sont redirigĂ©es vers d'autres pods opĂ©rationnels.

Notez que maintenant qu'il n'y a plus de charge superflue sur le pod, kubectl dirige de nouveau les demandes vers celui-ci : les rĂ©ponses aux requĂȘtes GET ne sont plus retardĂ©es.

Pour comparaison : voici le fichier app.js modifié ci-dessous :

var http = require('http');

var server = http.createServer(function(req, res) {
   const sleep = (milliseconds) => {
       return new Promise(resolve => setTimeout(resolve, milliseconds))
   }
   sleep(5000).then(() => {
       res.writeHead(200, { "Content-type": "text/plain" });
       res.end("Hellon");
   })
});

server.listen(3000, function() {
   console.log('Le serveur fonctionne sur le port 3000')
})

TL;DR
Avant l'Ă©mergence des applications cloud, le principal moyen de surveillance et de vĂ©rification de l'Ă©tat des applications Ă©tait les journaux. Pourtant, il n'y avait pas d'outils pour entreprendre des mesures de dĂ©pannage. Les journaux sont toujours utiles aujourd'hui, ils doivent ĂȘtre collectĂ©s et envoyĂ©s Ă  un systĂšme de collecte de journaux pour l'analyse des situations d'urgence et la prise de dĂ©cisions. [cela pouvait Ă©galement ĂȘtre fait sans applications cloud en utilisant monit, par exemple, mais avec k8s, c'est devenu beaucoup plus facile 🙂 – note de l'Ă©diteur. ]

Aujourd'hui, les corrections doivent ĂȘtre apportĂ©es presque en temps rĂ©el, donc les applications ne doivent plus ĂȘtre des boĂźtes noires. Non, elles doivent afficher des points de terminaison qui permettent aux systĂšmes de surveillance de demander et de collecter des donnĂ©es prĂ©cieuses sur l'Ă©tat des processus, afin de rĂ©agir immĂ©diatement si nĂ©cessaire. Cela s'appelle le ModĂšle de conception de vĂ©rification de la disponibilitĂ©, qui suit le Principe de haute observabilitĂ© (HLO).

Kubernetes propose par dĂ©faut 2 types de vĂ©rification de la disponibilitĂ© : readinessProbe et livenessProbe. Les deux utilisent les mĂȘmes types de vĂ©rifications (requĂȘtes HTTP GET, connexions TCP et exĂ©cution de commandes). Ils diffĂšrent par les dĂ©cisions prises en rĂ©ponse Ă  des problĂšmes dans les pods. L'livenessProbe redĂ©marre le conteneur dans l'espoir que l'erreur ne se reproduise plus, tandis que readinessProbe isole le pod du trafic entrant – jusqu'Ă  ce que la cause du problĂšme soit rĂ©solue.

La conception correcte d'une application doit inclure les deux types de vérification, et elles doivent collecter suffisamment de données, notamment lorsqu'une situation exceptionnelle est créée. Elle doit également afficher les points de terminaison API nécessaires qui transmettent au systÚme de surveillance (comme Prometheus) des métriques importantes sur la disponibilité.

Source : habr.com

Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS đŸ”„ Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster