Images prêtes pour la production pour les k8

Cette histoire raconte comment nous utilisons les conteneurs dans un environnement de production, en particulier Kubernetes. L'article est consacré à la collecte de métriques et de journaux à partir de conteneurs, ainsi qu'à la création d'images.

Images prêtes pour la production pour les k8

Nous appartenons à la société fintech Exness, qui développe des services de commerce en ligne et des produits fintech pour le B2B et le B2C. Notre R&D compte de nombreuses équipes différentes, le département développement compte plus de 100 employés.

Nous représentons l'équipe responsable de la plate-forme permettant à nos développeurs de collecter et d'exécuter du code. En particulier, nous sommes responsables de la collecte, du stockage et du reporting des métriques, des journaux et des événements des applications. Nous exploitons actuellement environ trois mille conteneurs Docker dans un environnement de production, entretenons notre stockage Big Data de 50 To et fournissons des solutions architecturales construites autour de notre infrastructure : Kubernetes, Rancher et divers fournisseurs de cloud public. 

Notre motivation

Qu'est-ce qui brûle ? Personne ne peut répondre. Où est le foyer ? C'est difficile à comprendre. Quand a-t-il pris feu ? Vous pouvez le découvrir, mais pas tout de suite. 

Images prêtes pour la production pour les k8

Pourquoi certains conteneurs restent debout alors que d’autres sont tombés ? Quel conteneur était en cause ? Après tout, l’extérieur des conteneurs est le même, mais à l’intérieur chacun a son propre Neo.

Images prêtes pour la production pour les k8

Nos développeurs sont des gars compétents. Ils rendent de bons services qui rapportent des bénéfices à l'entreprise. Mais il y a des échecs lorsque des conteneurs contenant des applications s'égarent. Un conteneur consomme trop de CPU, un autre consomme le réseau, un troisième consomme des opérations d'E/S et le quatrième ne sait absolument pas ce qu'il fait avec les sockets. Tout s'effondre et le navire coule. 

Agents

Pour comprendre ce qui se passe à l’intérieur, nous avons décidé de placer les agents directement dans des conteneurs.

Images prêtes pour la production pour les k8

Ces agents sont des programmes de retenue qui maintiennent les conteneurs dans un état tel qu'ils ne se brisent pas. Les agents sont standardisés, ce qui permet une approche standardisée de la maintenance des conteneurs. 

Dans notre cas, les agents doivent fournir des journaux dans un format standard, balisés et limités. Ils devraient également nous fournir des métriques standardisées extensibles du point de vue des applications métier.

Par agents, on entend également des utilitaires d'exploitation et de maintenance pouvant fonctionner dans différents systèmes d'orchestration prenant en charge différentes images (Debian, Alpine, Centos, etc.).

Enfin, les agents doivent prendre en charge des CI/CD simples incluant des fichiers Docker. Sinon, le navire s'effondrera, car les conteneurs commenceront à être livrés le long de rails « tordus ».

Processus de construction et périphérique d'image cible

Pour que tout reste standardisé et gérable, une sorte de processus de construction standard doit être suivi. Par conséquent, nous avons décidé de collecter conteneurs par conteneurs - c'est la récursion.

Images prêtes pour la production pour les k8

Ici, les conteneurs sont représentés par des contours pleins. En même temps, ils ont décidé d’y mettre des kits de distribution pour que « la vie ne ressemble pas à des framboises ». Pourquoi cela a été fait, nous l'expliquerons ci-dessous.
 
Le résultat est un outil de construction : un conteneur spécifique à une version qui fait référence à des versions de distribution spécifiques et à des versions de script spécifiques.

Comment l’utilisons-nous ? Nous avons un Docker Hub qui contient un conteneur. Nous le reflétons dans notre système pour nous débarrasser des dépendances externes. Le résultat est un conteneur marqué en jaune. Nous créons un modèle pour installer toutes les distributions et scripts dont nous avons besoin dans le conteneur. Après cela, nous assemblons une image prête à l'emploi : les développeurs y mettent du code et certaines de leurs propres dépendances spéciales. 

Qu'est-ce qui est bien dans cette approche ? 

  • Tout d’abord, un contrôle complet des versions des outils de build : build des versions de conteneur, de script et de distribution. 
  • Deuxièmement, nous avons atteint la standardisation : nous créons de la même manière des modèles, des images intermédiaires et prêtes à l'emploi. 
  • Troisièmement, les conteneurs nous offrent la portabilité. Aujourd'hui nous utilisons Gitlab, et demain nous passerons à TeamCity ou Jenkins et nous pourrons exécuter nos conteneurs de la même manière. 
  • Quatrièmement, minimiser les dépendances. Ce n'est pas un hasard si nous avons mis les kits de distribution dans le conteneur, car cela nous permet d'éviter de les télécharger à chaque fois depuis Internet. 
  • Cinquièmement, la vitesse de construction a augmenté - la présence de copies locales d'images vous permet d'éviter de perdre du temps en téléchargement, puisqu'il existe une image locale. 

En d’autres termes, nous avons obtenu un processus d’assemblage contrôlé et flexible. Nous utilisons les mêmes outils pour créer des conteneurs entièrement versionnés. 

Comment fonctionne notre procédure de construction

Images prêtes pour la production pour les k8

L'assembly est lancé avec une seule commande, le processus est exécuté dans l'image (surlignée en rouge). Le développeur dispose d'un fichier Docker (surligné en jaune), nous le rendons en remplaçant les variables par des valeurs. Et en cours de route, nous ajoutons des en-têtes et des pieds de page : ce sont nos agents. 

L'en-tête ajoute des distributions à partir des images correspondantes. Et le pied de page installe nos services à l'intérieur, configure le lancement de la charge de travail, de la journalisation et d'autres agents, remplace le point d'entrée, etc. 

Images prêtes pour la production pour les k8

Nous avons longtemps réfléchi à l'opportunité d'installer un superviseur. Finalement, nous avons décidé que nous avions besoin de lui. Nous avons choisi S6. Le superviseur assure la gestion du conteneur : permet de s'y connecter en cas de crash du processus principal et assure la gestion manuelle du conteneur sans le recréer. Les journaux et les métriques sont des processus exécutés dans le conteneur. Ils doivent également être contrôlés d’une manière ou d’une autre, et nous le faisons avec l’aide d’un superviseur. Enfin, le S6 s'occupe du ménage, du traitement du signal et d'autres tâches.

Puisque nous utilisons différents systèmes d'orchestration, après avoir été construit et exécuté, le conteneur doit comprendre dans quel environnement il se trouve et agir en fonction de la situation. Par exemple:
Cela nous permet de créer une image et de l'exécuter dans différents systèmes d'orchestration, et elle sera lancée en tenant compte des spécificités de ce système d'orchestration.

 Images prêtes pour la production pour les k8

Pour le même conteneur, nous obtenons différentes arborescences de processus dans Docker et Kubernetes :

Images prêtes pour la production pour les k8

La charge utile est exécutée sous la supervision de S6. Faites attention au collecteur et aux événements : ce sont nos agents responsables des journaux et des métriques. Kubernetes ne les a pas, mais Docker les a. Pourquoi? 

Si nous regardons la spécification du « pod » (ci-après – pod Kubernetes), nous verrons que le conteneur d'événements est exécuté dans un pod, qui possède un conteneur collecteur séparé qui remplit la fonction de collecte de métriques et de journaux. Nous pouvons utiliser les capacités de Kubernetes : exécuter des conteneurs dans un seul pod, dans un seul processus et/ou espace réseau. Présentez réellement vos agents et remplissez certaines fonctions. Et si le même conteneur est lancé dans Docker, il recevra toutes les mêmes capacités en sortie, c'est-à-dire qu'il pourra fournir des journaux et des métriques, puisque les agents seront lancés en interne. 

Métriques et journaux

Fournir des métriques et des journaux est une tâche complexe. Sa décision comporte plusieurs aspects.
L'infrastructure est créée pour l'exécution de la charge utile et non pour la livraison massive de journaux. Autrement dit, ce processus doit être effectué avec des besoins minimaux en ressources de conteneur. Nous nous efforçons d'aider nos développeurs : « Obtenez un conteneur Docker Hub, exécutez-le et nous pourrons livrer les journaux. » 

Le deuxième aspect est de limiter le volume des logs. Si une augmentation du volume de journaux se produit dans plusieurs conteneurs (l'application génère une trace de pile en boucle), la charge sur le processeur, les canaux de communication et le système de traitement des journaux augmente, ce qui affecte le fonctionnement de l'hôte en tant que système. des conteneurs entiers et autres sur l'hôte, cela conduit parfois à une "chute" de l'hôte. 

Le troisième aspect est qu’il est nécessaire de prendre en charge autant de méthodes de collecte de métriques que possible dès le départ. De la lecture de fichiers et de l'interrogation du point de terminaison Prometheus à l'utilisation de protocoles spécifiques à l'application.

Et le dernier aspect est de minimiser la consommation de ressources.

Nous avons choisi une solution Go open source appelée Telegraf. Il s'agit d'un connecteur universel qui prend en charge plus de 140 types de canaux d'entrée (plugins d'entrée) et 30 types de canaux de sortie (plugins de sortie). Nous l'avons finalisé et nous allons maintenant vous expliquer comment nous l'utilisons en utilisant Kubernetes comme exemple. 

Images prêtes pour la production pour les k8

Supposons qu'un développeur déploie une charge de travail et que Kubernetes reçoive une demande de création d'un pod. À ce stade, un conteneur appelé Collector est automatiquement créé pour chaque pod (nous utilisons un webhook de mutation). Le collectionneur est notre agent. Au départ, ce conteneur se configure pour fonctionner avec Prometheus et le système de collecte de journaux.

  • Pour ce faire, il utilise des annotations de pod et, en fonction de son contenu, crée, par exemple, un point final Prometheus ; 
  • En fonction des spécifications du pod et des paramètres spécifiques du conteneur, il décide comment livrer les journaux.

Nous collectons les logs via l'API Docker : les développeurs n'ont qu'à les mettre dans stdout ou stderr, et Collector s'en chargera. Les journaux sont collectés en morceaux avec un certain retard pour éviter une éventuelle surcharge de l'hôte. 

Les métriques sont collectées sur les instances de charge de travail (processus) dans des conteneurs. Tout est balisé : espace de noms, sous, etc., puis converti au format Prometheus - et devient disponible pour la collecte (sauf pour les journaux). Nous envoyons également des journaux, des métriques et des événements à Kafka et plus encore :

  • Les journaux sont disponibles dans Graylog (pour l'analyse visuelle) ;
  • Les journaux, les métriques et les événements sont envoyés à Clickhouse pour un stockage à long terme.

Tout fonctionne exactement de la même manière dans AWS, sauf que nous remplaçons Graylog par Kafka par Cloudwatch. Nous y envoyons les journaux, et tout s'avère très pratique : il est immédiatement clair à quel cluster et conteneur ils appartiennent. Il en va de même pour Google Stackdriver. Autrement dit, notre système fonctionne à la fois sur site avec Kafka et dans le cloud. 

Si nous n’avons pas Kubernetes avec des pods, le schéma est un peu plus compliqué, mais il fonctionne sur les mêmes principes.

Images prêtes pour la production pour les k8

Les mêmes processus sont exécutés à l'intérieur du conteneur, ils sont orchestrés à l'aide de S6. Tous les mêmes processus s’exécutent dans le même conteneur.

En conséquence,

Nous avons créé une solution complète pour créer et lancer des images, avec des options de collecte et de livraison de journaux et de métriques :

  • Nous avons développé une approche standardisée pour l'assemblage d'images et, sur cette base, nous avons développé des modèles CI ;
  • Les agents de collecte de données sont nos extensions Telegraf. Nous les avons bien testés en production ;
  • Nous utilisons un webhook de mutation pour implémenter des conteneurs avec des agents dans des pods ; 
  • Intégré à l'écosystème Kubernetes/Rancher ;
  • Nous pouvons exécuter les mêmes conteneurs dans différents systèmes d'orchestration et obtenir le résultat que nous attendons ;
  • Création d'une configuration de gestion de conteneurs complètement dynamique. 

Coauteur: Ilya Prudnikov

Source: habr.com

Ajouter un commentaire