Images prêtes pour la production pour k8s

Cette histoire concerne la façon dont nous utilisons les conteneurs dans les épiceries, en particulier sous Kubernetes. L'article est consacré à la collecte de métriques et de journaux à partir de conteneurs, ainsi qu'à une génération d'images.

image

Nous venons de la société fintech Exness, qui développe des services de trading en ligne et des produits fintech pour B2B et B2C. Il existe de nombreuses équipes différentes dans notre R&D, dans le département de développement 100+ employés.

Nous représentons l'équipe responsable de la plateforme de collecte et d'exécution de code par nos développeurs. En particulier, nous sommes responsables de la collecte, du stockage et de la fourniture de métriques, de journaux et d'événements à partir des applications. Actuellement, nous exploitons environ trois mille conteneurs Docker dans l'environnement du produit, prenons en charge notre stockage de données volumineuses 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 immédiatement. 



Pourquoi certains conteneurs tiennent-ils debout tandis que d'autres tombent? Quel conteneur était à blâmer? En effet, à l'extérieur les conteneurs sont les mêmes, mais à l'intérieur chacun a son propre Neo.



Nos développeurs sont des gars lettrés. Ils font de bons services qui font profiter l'entreprise. Mais il y a fakapy lorsque les conteneurs avec des applications vont au hasard. Un conteneur consomme trop de CPU, l'autre consomme du réseau, le troisième consomme des opérations d'E / S et le quatrième ne sait généralement pas ce qu'il fait avec les sockets. Tout cela tombe et le navire coule. 

Agents


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



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

Dans notre cas, les agents doivent fournir des journaux dans un format standard, balisés et avec trot. Ils devraient également nous fournir des mesures standardisées qui sont extensibles en termes d'applications commerciales.

Les agents désignent également les utilitaires d'exploitation et de maintenance, capables de 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 un CI / CD simple comprenant des fichiers Docker. Sinon, le navire s'effondrera, car les conteneurs commenceront à être livrés sur des rails "courbes".

Processus d'assemblage et image du périphérique cible


Pour que tout soit standardisé et gérable, vous devez respecter un processus d'assemblage standard. Par conséquent, nous avons décidé de collecter les conteneurs par conteneurs - une telle récursivité.



Ici, les conteneurs sont représentés par des contours solides. En même temps, ils ont décidé d'y mettre des distributions pour que "la vie ne semble pas framboise". Pourquoi cela a été fait, nous décrirons ci-dessous.
 
Le résultat est un outil de construction - un conteneur d'une certaine version, qui fait référence à certaines versions de distributions et à certaines versions de scripts.

Comment l'utilisons-nous? Nous avons un Docker Hub dans lequel se trouve le conteneur. Nous le reflétons à l'intérieur de notre système afin de nous débarrasser des dépendances externes. Le conteneur résultant est marqué en jaune. Nous créons un modèle pour installer dans le conteneur toutes les distributions et scripts dont nous avons besoin. Après cela, nous collectons une image prête à fonctionner: les développeurs y mettent le code et quelques dépendances spéciales. 

Pourquoi cette approche est-elle bonne? 

  • Premièrement, le contrôle complet des versions des outils de construction - construire des versions de conteneurs, de scripts et de distributions. 
  • Deuxièmement, nous avons atteint la standardisation: de la même manière, nous créons des modèles, des images intermédiaires et prêtes à fonctionner. 
  • Troisièmement, les conteneurs nous assurent la portabilité. Aujourd'hui, nous utilisons Gitlab, et demain nous passerons à TeamCity ou Jenkins et de la même manière nous pourrons lancer nos conteneurs. 
  • Quatrièmement, minimiser les dépendances. Ce n'est pas un hasard si nous mettons des distributions dans le conteneur, car cela nous permet de ne pas les télécharger à chaque fois depuis Internet. 
  • Cinquièmement, la vitesse d'assemblage a augmenté - la disponibilité de copies locales d'images vous permet de ne pas perdre de temps à télécharger, car il existe une image locale. 

En d'autres termes, nous avons réalisé un processus d'assemblage contrôlé et flexible. Nous utilisons les mêmes outils pour construire tous les conteneurs avec versionnage complet. 

Comment fonctionne notre procédure de construction




L'assemblage est lancé avec une seule commande, le processus s'effectue dans l'image (surlignée en rouge). Le développeur a 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, la journalisation et d'autres agents, remplace le point d'entrée, etc. 



Nous avons longuement réfléchi à l'opportunité de définir un superviseur. Finalement, ils ont décidé que nous avions besoin de lui. Choisissez S6. Le superviseur assure le contrôle du conteneur: il vous permet de vous y connecter en cas de chute du processus principal et assure un contrôle manuel du conteneur sans le recréer. Les journaux et les mesures sont des processus qui s'exécutent à l'intérieur d'un 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 de l'entretien ménager, du traitement du signal et d'autres tâches.

Puisque nous utilisons différents systèmes d'orchestration, après assemblage et lancement, le conteneur doit comprendre dans quel environnement il se trouve et agir sur la situation. Par exemple:
Cela nous permet de collecter une image et de la lancer 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.

 

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



la charge utile est exécutée sous le superviseur S6. Faites attention au collecteur et aux événements - ce sont nos agents responsables des journaux et des métriques. Kubernetes n'en a pas, mais Docker en a. Pourquoi? 

Si vous regardez la spécification du "foyer" (ci-après - pod Kubernetes), nous verrons que le conteneur d'événements est exécuté dans le foyer, dans lequel il existe un conteneur collecteur distinct 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 foyer, dans un seul espace de processus et / ou de réseau. Présentez réellement vos agents et exécutez certaines fonctions. Et si le même conteneur est lancé dans Docker, il recevra toutes les mêmes fonctionnalités en sortie, c'est-à-dire qu'il sera en mesure de fournir des journaux et des métriques, car les agents seront lancés à l'intérieur. 

Mesures et journaux


La livraison des métriques et des journaux est une tâche difficile. Il y a plusieurs aspects à sa décision.
L'infrastructure est créée pour répondre à la charge utile et non à la livraison en masse de journaux. Autrement dit, ce processus doit être effectué avec des exigences minimales pour les ressources de conteneur. Nous nous efforçons d'aider nos développeurs: "Prenez le conteneur Docker Hub, lancez-le et nous pouvons livrer les journaux." 

Le deuxième aspect est la limitation du volume des journaux. Si, dans plusieurs conteneurs, il y a une situation de hausse du volume des journaux (l'application affiche la trace de pile dans une boucle), la charge sur le processeur, les canaux de communication, le système de traitement des journaux augmente, ce qui affecte le fonctionnement de l'hôte dans son ensemble et d'autres conteneurs sur l'hôte, parfois cela conduit à "Fall" de l'hôte. 

Le troisième aspect - vous devez prendre en charge autant de méthodes de collecte de métriques que possible. De la lecture de fichiers et de l'interrogation du point de terminaison Prometheus à l'utilisation de protocoles d'application spécifiques.

Et le dernier aspect - vous devez 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 sorties (plugins de sortie). Nous l'avons finalisé et nous allons maintenant dire comment il est utilisé avec Kubernetes comme exemple. 



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

  • Pour ce faire, il utilise les annotations du foyer et, selon son contenu, crée, disons, le point final du Prométhée; 
  • Sur la base des spécifications du foyer et des paramètres spécifiques des conteneurs, il décide de la manière de livrer les journaux.

Nous collectons les journaux via l'API Docker: il suffit aux développeurs de les mettre dans stdout ou stderr, puis Collector le découvrira. Les journaux sont collectés par morceaux avec un certain retard afin d'éviter une éventuelle congestion de l'hôte. 

Les métriques sont collectées sur les instances de charge de travail (processus) dans des conteneurs. Tout est étiqueté: espace de noms, sous, etc., puis converti au format Prometheus - et devient disponible pour la collecte (à l'exception des journaux). En outre, nous envoyons des journaux, des mesures et des événements à Kafka et plus loin:

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

De même, tout fonctionne dans AWS, seulement nous remplaçons Graylog de Kafka par Cloudwatch. Nous y envoyons des journaux, et tout se passe très facilement: il est immédiatement clair à qui appartiennent le cluster et le conteneur. 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 de Kubernetes avec des pods, le schéma est un peu plus compliqué, mais il fonctionne sur les mêmes principes.



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

Finalement


Nous avons créé une solution complète pour assembler et lancer des images en fonctionnement, avec des options de collecte et de livraison de journaux et de mesures:

  • Développer une approche standardisée pour l'assemblage d'images, sur la base de celui-ci développé des modèles CI;
  • Les agents de collecte de données sont nos extensions de Telegraf. Nous les avons bien gérés en production;
  • Nous utilisons le webhook de mutation pour implémenter des conteneurs avec des agents dans les 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 attendu;
  • Création d'une configuration de gestion de conteneurs entièrement dynamique. 

Co-auteur: Ilya Prudnikov

All Articles