Meilleures pratiques pour les conteneurs Kubernetes : bilans de santé

Meilleures pratiques pour les conteneurs Kubernetes : bilans de santé

TL; DR

  • Pour obtenir une observabilité élevée des conteneurs et des microservices, les journaux et les métriques primaires ne suffisent pas.
  • Pour une récupération plus rapide et une résilience accrue, les applications doivent appliquer le principe de haute observabilité (HOP).
  • Au niveau de l'application, NOP nécessite : une journalisation appropriée, une surveillance étroite, des contrôles d'intégrité et un traçage des performances/transitions.
  • Utiliser les chèques comme élément de NOR état de préparationSonde и vivacitéSonde Kubernetes.

Qu'est-ce qu'un modèle de bilan de santé ?

Lors de la conception d’une application critique et hautement disponible, il est très important de penser à un aspect tel que la tolérance aux pannes. Une application est considérée comme tolérante aux pannes si elle se remet rapidement d'une panne. Une application cloud typique utilise une architecture de microservices, dans laquelle chaque composant est placé dans un conteneur distinct. Et afin de vous assurer que l'application sur k8s est hautement disponible lorsque vous concevez un cluster, vous devez suivre certains modèles. Parmi eux se trouve le modèle de bilan de santé. Il définit comment l'application communique aux k8 qu'elle est saine. Il ne s'agit pas seulement d'informations indiquant si le pod est en cours d'exécution, mais également de la manière dont il reçoit et répond aux demandes. Plus Kubernetes en sait sur l’état du pod, plus il prend des décisions judicieuses concernant le routage du trafic et l’équilibrage de charge. Ainsi, le principe de haute observabilité permet à l'application de répondre aux demandes dans les meilleurs délais.

Principe de haute observabilité (HOP)

Le principe de haute observabilité est l'un des principes de conception d'applications conteneurisées. Dans une architecture de microservices, les services ne se soucient pas de la manière dont leur demande est traitée (et à juste titre), mais ce qui compte, c'est la manière dont ils reçoivent les réponses des services récepteurs. Par exemple, pour authentifier un utilisateur, un conteneur envoie une requête HTTP à un autre, attendant une réponse dans un certain format - c'est tout. PythonJS peut également traiter la demande et Python Flask peut répondre. Les conteneurs sont comme des boîtes noires dont le contenu est caché les uns aux autres. Cependant, le principe NOP exige que chaque service expose plusieurs points de terminaison d'API qui indiquent son état de santé, ainsi que son état de préparation et de tolérance aux pannes. Kubernetes demande ces indicateurs afin de réfléchir aux prochaines étapes de routage et d'équilibrage de charge.

Une application cloud bien conçue enregistre ses principaux événements à l'aide des flux d'E/S standard STDERR et STDOUT. Vient ensuite un service auxiliaire, par exemple filebeat, logstash ou fluentd, qui transmet les logs à un système de surveillance centralisé (par exemple Prometheus) et à un système de collecte de logs (suite logicielle ELK). Le diagramme ci-dessous montre comment fonctionne une application cloud selon le modèle de test de santé et le principe de haute observabilité.

Meilleures pratiques pour les conteneurs Kubernetes : bilans de santé

Comment appliquer le modèle de contrôle de santé dans Kubernetes ?

Prêt à l'emploi, k8s surveille l'état des pods à l'aide de l'un des contrôleurs (Déploiements, Ensembles de répliques, Ensembles de démons, Ensembles avec état etc.). Ayant découvert que le pod est tombé pour une raison quelconque, le contrôleur tente de le redémarrer ou de le déplacer vers un autre nœud. Cependant, un pod peut signaler qu'il est opérationnel, mais il ne fonctionne pas lui-même. Donnons un exemple : votre application utilise Apache comme serveur web, vous avez installé le composant sur plusieurs pods du cluster. La bibliothèque étant mal configurée, toutes les requêtes adressées à l'application répondent avec le code 500 (erreur interne du serveur). Lors du contrôle de livraison, la vérification de l'état des pods donne un résultat positif, mais les clients pensent différemment. Nous décrirons cette situation indésirable comme suit :

Meilleures pratiques pour les conteneurs Kubernetes : bilans de santé

Dans notre exemple, k8s fait vérification de la fonctionnalité. Dans ce type de vérification, le kubelet vérifie en permanence l'état du processus dans le conteneur. Une fois qu’il aura compris que le processus s’est arrêté, il le relancera. Si l'erreur peut être résolue en redémarrant simplement l'application et que le programme est conçu pour s'arrêter en cas d'erreur, alors une vérification de l'état du processus est tout ce dont vous avez besoin pour suivre le NOP et le modèle de test d'intégrité. Le seul regret est que toutes les erreurs ne sont pas éliminées en redémarrant. Dans ce cas, k8s propose 2 méthodes plus approfondies pour identifier les problèmes avec le pod : vivacitéSonde и état de préparationSonde.

LivenessProbe

Pendant la vivacitéSonde kubelet effectue 3 types de vérifications : détermine non seulement si le pod est en cours d'exécution, mais également s'il est prêt à recevoir et à répondre adéquatement aux requêtes :

  • Configurez une requête HTTP vers le 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 rencontre des problèmes, même si le processus est en cours d'exécution.
  • Pour tester des pods avec des services non HTTP (par exemple, le serveur de messagerie Postfix), vous devez établir une connexion TCP.
  • Exécutez une commande arbitraire pour un pod (en interne). La vérification est considérée comme réussie si le code de fin de commande est 0.

Un exemple de la façon dont cela fonctionne. La définition de pod suivante contient une application NodeJS qui génère une erreur 500 sur les requêtes HTTP. Pour nous assurer que le conteneur est redémarré lors de la réception d'une telle 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

Ce n'est pas différent de toute autre définition de pod, mais nous ajoutons un objet .spec.containers.livenessProbe. Paramètre httpGet accepte le chemin vers lequel la requête HTTP GET est envoyée (dans notre exemple, il s'agit de /, mais dans les scénarios de combat, il peut y avoir quelque chose comme /api/v1/status). Un autre livenessProbe accepte un paramètre initialDelaySeconds, qui demande à l'opération de vérification d'attendre un nombre de secondes spécifié. Le délai est nécessaire car le conteneur a besoin de temps pour démarrer et, une fois redémarré, il sera indisponible pendant un certain temps.

Pour appliquer ce paramètre à un 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 c'est ce que.

Comme vous pouvez le voir, livenessProbe a lancé une requête HTTP GET, le conteneur a généré une erreur 500 (ce pour quoi il a été programmé) et le kubelet l'a redémarré.

Si vous vous demandez comment l'application NideJS a été programmée, voici le 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("We have run into an errorn");
});

server.listen(3000, function() {
    console.log('Server is running at 3000')
})

Dockerfile

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

Il est important de noter ceci : livenessProbe ne redémarrera le conteneur qu'en cas d'échec. Si un redémarrage ne corrige pas l’erreur qui empêche le conteneur de s’exécuter, le kubelet ne pourra pas prendre d’action pour corriger le problème.

état de préparationSonde

readinessProbe fonctionne de la même manière que livenessProbes (requêtes GET, communications TCP et exécution de commandes), à l'exception des actions de dépannage. Le conteneur dans lequel la panne est détectée n'est pas redémarré, mais est isolé du trafic entrant. Imaginez que l'un des conteneurs effectue de nombreux calculs ou soit soumis à une forte charge, ce qui entraîne une augmentation des temps de réponse. Dans le cas de livenessProbe, le contrôle de disponibilité de la réponse est déclenché (via le paramètre timeoutSeconds check), après quoi le kubelet redémarre le conteneur. Une fois démarré, le conteneur commence à effectuer des tâches gourmandes en ressources et est redémarré. Cela peut être critique pour les applications qui nécessitent une vitesse de réponse. Par exemple, une voiture sur la route attend une réponse du serveur, la réponse est retardée - et la voiture a un accident.

Écrivons une définition redinessProbe qui définira le temps de réponse de la requête GET à pas plus de deux secondes, et 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 un pod avec kubectl :

kubectl apply -f pod.yaml

Attendons quelques secondes, puis voyons comment fonctionne readinessProbe :

kubectl describe pods nodedelayed

À la fin du résultat, vous pouvez voir que certains événements sont similaires celui-ci.

Comme vous pouvez le voir, kubectl n'a pas redémarré le pod lorsque le temps de vérification dépassait 2 secondes. Au lieu de cela, il a annulé la demande. Les communications entrantes sont redirigées vers d’autres modules fonctionnels.

Notez que maintenant que le pod est déchargé, kubectl lui achemine à nouveau les requêtes : les réponses aux requêtes GET ne sont plus retardées.

À titre de comparaison, vous trouverez ci-dessous le fichier app.js modifié :

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('Server is running at 3000')
})

TL; DR
Avant l’avènement des applications cloud, les journaux constituaient le principal moyen de surveillance et de vérification de l’état des applications. Cependant, il n’y avait aucun moyen de prendre des mesures correctives. Les journaux sont encore utiles aujourd'hui : ils doivent être collectés et envoyés à un système de collecte de journaux pour analyser les situations d'urgence et prendre des décisions. [Tout cela pourrait être fait sans applications cloud utilisant monit, par exemple, mais avec k8s, c'est devenu beaucoup plus facile :) – ndlr. ]

Aujourd’hui, les corrections doivent être effectuées quasiment en temps réel, les candidatures ne doivent donc plus être des boîtes noires. Non, ils doivent afficher des points de terminaison qui permettent aux systèmes de surveillance d'interroger et de collecter des données précieuses sur l'état des processus afin qu'ils puissent répondre instantanément si nécessaire. C'est ce qu'on appelle le modèle de conception de test de performance, qui suit le principe de haute observabilité (HOP).

Kubernetes propose 2 types de vérifications de l'état par défaut : readinessProbe et livenessProbe. Les deux utilisent les mêmes types de contrôles (requêtes HTTP GET, communications TCP et exécution de commandes). Ils diffèrent dans les décisions qu'ils prennent en réponse aux problèmes dans les pods. livenessProbe redémarre le conteneur dans l'espoir que l'erreur ne se reproduise plus, et readinessProbe isole le pod du trafic entrant jusqu'à ce que la cause du problème soit résolue.

Une conception appropriée des applications doit inclure les deux types de vérification et garantir qu'elles collectent suffisamment de données, en particulier lorsqu'une exception est levée. Il doit également afficher les points de terminaison d'API nécessaires qui fournissent au système de surveillance (Prometheus) des mesures de santé importantes.

Source: habr.com

Ajouter un commentaire