La journalisation et le suivi des requêtes sont les meilleures pratiques. Rapport Yandex

Yandex.Market a une grande architecture de microservices. La demande de navigateur de la page principale du marché donne lieu à des dizaines de demandes intégrées dans différents services (backends) qui sont développées par différentes personnes. Dans un tel système, il peut être difficile de comprendre pour quelle raison la demande est tombée ou a pris beaucoup de temps à traiter.


Anatoly Ostrovsky megatolyaexplique comment son équipe a résolu ce problème et partage des pratiques spécifiques au marché, mais généralement pertinentes pour tout excellent service. Son rapport est basé sur sa propre expérience de déploiement d'un nouveau marché dans un temps assez court. Pendant plusieurs années, Tolya a dirigé l'équipe de développement d'interfaces sur le marché, et maintenant il s'est orienté vers les véhicules sans pilote.

- Tous nos marchés sont construits selon des principes généraux. Il s'agit d'un grand système unique. Mais si nous parlons de l'interface, du point de vue de l'utilisateur, les applications sont complètement différentes.



Dans le même temps, nos frontends vont à de nombreux backends. Parfois, ces backends sont similaires les uns aux autres (différentes instances de la même application). Et parfois, ils sont uniques au service (facturation spéciale). La structure d'un tel système peut être considérée comme une architecture de microservice classique.

Il y a toujours un problème dans un grand service - il est difficile de comprendre ce qui s'y passe exactement. Ou, par exemple, ce qui se passe au moment où une erreur de paiement se produit avec l'utilisateur. Supposons que cela se soit passé chez lui hier, et aujourd'hui, nous devons comprendre ce qui s'est passé.



Le backend 2 peut «graver» soit pour un produit spécifique, soit à un moment précis, soit pour des utilisateurs spécifiques. Nous devons être en mesure de répondre à toutes les situations.



Nous avons de nombreux backends et, comme je l'ai dit, ils peuvent marcher seuls. Si cela est présenté sous la forme d'un graphique, cela se révélera plutôt déroutant. Dans la vraie vie, il peut y avoir des centaines de microservices. Imaginez combien de connexions il y aura entre eux.

Il y a plusieurs étapes pour plonger dans ce sujet. Je vais parler brièvement de chacun d'eux.



Tout d'abord, convenez avec vos collègues du backend d'un système de marquage de demande commun - il est plus facile de les trouver plus tard. Ensuite, vous devez pouvoir reproduire rapidement le problème. Supposons qu'une erreur de paiement se soit produite - essayez de comprendre rapidement comment elle s'est produite et dans quel backend. Stockez les journaux non seulement dans des fichiers, mais également dans la base de données afin de pouvoir effectuer des agrégations. Et, bien sûr, une partie importante du processus concerne les graphiques et la surveillance. Ensuite, dans l'ordre.

Système d'identification des demandes unifié




C'est l'un des outils les plus simples pour comprendre ce qui se passe avec le service. Convenez avec vos collègues que, par exemple, votre frontend génère une sorte de demande d'identification (la variable requestId dans l'image), puis la jette dans tous les endopoints des backends. Et le backend lui-même ne réinvente rien. Il prend le requestId qui est arrivé et le transmet plus loin dans les requêtes à ses backends. En même temps, il peut spécifier son préfixe, de sorte que parmi le requestId identique, il serait possible de trouver ce backend particulier.



Ainsi, lorsque vous avalez vos journaux, puis, en vous assurant que vos journaux indiquent, par exemple, que le backend est de cinq cents, il peut y avoir deux options. Vous allez soit donner cet identifiant de demande à vos collègues et ils le regarderont dans leurs journaux, ou vous verrez par vous-même.



Tous nos journaux sont marqués avec un tel identifiant afin non seulement de comprendre ce qui s'est passé et à quel moment, mais aussi de garder le contexte de cette demande. Il est asynchrone, il peut par la suite ajouter quelque chose au journal. Et si vous mordez à la demande, rien de bon n'en sortira.

boucle


Pour reproduire le problème, nous utilisons l'utilitaire cURL. Il s'agit d'un utilitaire de console qui effectue des requêtes réseau - http et https. cURL prend en charge de nombreux autres protocoles, mais lors du développement Web, il est plus facile de supposer qu'il s'agit d'un outil pour travailler avec les requêtes http.



Pour vous familiariser avec l'équipe cURL, vous pouvez vous rendre sur n'importe quel site, puis aller sur Réseau et copier toute demande sous forme de cURL. Le résultat est une si grande ligne:



si vous essayez de le comprendre, alors il n'y a rien à craindre. Essayons de le démonter.



Voici une demande à market.yandex.ru.



Un User-Agent a été ajouté ici, ce qui prend déjà beaucoup de place.



En fait, le reste est constitué de cookies. Il y en a beaucoup dans le code Yandex. Sous forme sérialisée, ils ont un look très redoutable. En fait, il n'y a rien d'autre à part eux.

À quoi sert cURL? Si vous l'avez copié sur vous-même et l'avez exécuté, vous avez vu la même page market.yandex.ru que moi - seul l'ordinateur sur lequel il s'exécutait serait différent. Bien sûr, certains effets secondaires pourraient donner des différences dans les adresses IP, mais en général, ce seraient les mêmes demandes. Nous reproduirions le même scénario.



Afin de ne pas inventer de telles requêtes cURL à chaque fois, vous pouvez utiliser le format-curl npm-package.



Il prend tous les paramètres de requête que la fonction prend habituellement - c'est-à-dire, dans ce cas, uniquement les en-têtes et l'URL. Mais il sait aussi interroger, corps, etc. Et la sortie est juste une chaîne avec une requête cURL.



Par conséquent, tous nos journaux dans l'environnement de développement contiennent également des demandes cURL.



Nous avons même enregistré les demandes cURL de backend directement dans le navigateur pour voir immédiatement comment nous allons à nos backends sans regarder la console du navigateur.



Veuillez noter que les demandes cURL impliquent le transfert de cookies de session - c'est mauvais. Si vous m'avez rejeté votre demande cURL sur market.yandex.ru, je pourrais entrer dans votre marché et tout autre service Yandex sous votre identifiant. Par conséquent, nous ne stockons ces demandes nulle part et nous les enregistrons uniquement dans des bancs de test pour nous-mêmes - ces données ne peuvent pas être divulguées.

Clickhouse


Ensuite, je vais parler des journaux structurés. Ici, je garderai à l'esprit la base de données ClickHouse spécifique, mais vous pouvez en choisir une. ClickHouse est un SGBD en colonnes, il est plus pratique de sélectionner parmi une énorme quantité de données et de prendre de gros morceaux de données. C'est bien parce que vous pouvez y sauvegarder une grande partie du journal et ensuite, par exemple, faire une sorte d'agrégation d'un milliard d'enregistrements.



Dans ce cas, l'exemple de sélection ClickHouse est du SQL brut. Ici, nous montrons le nombre de demandes de codes d'état pour aujourd'hui.



En conséquence, nous aurons 180 mille deux cent sept cinq cents, et les codes de statut restants, par exemple, ne nous intéressent pas. Mais comment pouvons-nous utiliser cela de façon intéressante?



Nous pouvons dire que le rapport de deux cents au rapport du nombre total de réponses est l'indicateur de niveau de service, qui répond à la question de savoir comment notre application fonctionne en termes de codes de statut. Bien que simple, il parle déjà de quelque chose.



Sur la base de notre indicateur, nous pouvons proposer le premier SLI, c'est-à-dire, par exemple, que 99% de nos demandes devraient être OK. Et ici, nous pouvons comparer que nous avons effectué notre SLI. S'ils ne l'avaient pas terminé, nous aurions pu essayer de distinguer soit certaines dernières demandes qui auraient été cinq cents, soit tout simplement des éléments critiques.



Par exemple, les erreurs de paiement sont critiques pour nous, mais dans ce cas, elles retourneront zéro - c'est une chance :)



Comment vous assurer que vos journaux sont sous cette forme pratique et peuvent être pris via SQL?



C'est un sujet pour un gros rapport séparé, et tout dépend de votre infrastructure. Mais il semble qu'il y ait deux façons. Premièrement: soumettez les métadonnées directement à l'exécution, directement à la base de données. Nous le faisons différemment, dans la deuxième manière: nous suivons le fichier journal et soumettons des morceaux soit dans la base de données ou dans un endroit intermédiaire.

Cela fonctionne pour nous plutôt en plusieurs couches - nous envoyons des journaux d'une instance spécifique à un serveur de stockage distant de ces journaux.

Demande de trace


Il n'y a pas de concept de «traçage des requêtes». Ce terme a été inventé par Google.



Si vous recherchez sur Internet le «traçage des requêtes», vous verrez la commande traceroute. C'est peut-être similaire au suivi des requêtes.



Il y a même un programme console, et ici je l'ai exécuté pour le site bringly.ru (un service que nous avons développé au printemps dernier). Cela aide à comprendre quel type de machines et de serveurs la requête traverse avant d'arriver à l'équilibreur, qui répondra soit par la mise en page, soit par autre chose.



Voici notre équilibreur. Par traçage de requêtes, nous ne voulons pas dire cela, mais tout ce qui se passe à l'intérieur de notre équilibreur - comment la demande se déroule plus loin dans notre application node.js.



Nous voyons cela sous la forme d'une chronologie, où l'heure horizontale est affichée, et la séquence verticale des demandes. Dans ce cas, nous avons une demande à la fiche produit, où l'on peut voir qu'il s'agit d'une demande au backend de notre autorisation. Après sa décision, trois longues files d'attente sont allées - il s'agit d'une demande à notre backend principal pour un panier, une carte de produit et des produits similaires. Nous avons donc une trace.



Ici, vous voyez la même demande d'autorisation, puis le backend est parti. Dans ce cas, le backend ne se comporte pas de manière très optimale, car il a de nombreuses requêtes consécutives sur sa base de données. Probablement, une telle requête pourrait être optimisée.



Et voici un exemple de trace, lorsque nous ne sommes allés dans aucun backend, mais avons immédiatement donné le statut 500. Comment une telle trace nous est-elle utile? Nous n'avons pas à déranger nos collègues. Nous avons l'identifiant de cette demande, nous pouvons donc regarder nous-mêmes dans les journaux et comprendre ce qui se passe.



Voici la situation inverse. Backend a dit que quelque chose n'allait pas et a en même temps écrit dans les méta-informations ce qui s'est exactement passé - une sorte de trace de pile est apparue.



Comment se faire le même outil?

La chose la plus importante ici est la base de données. Si vous avez le plus simple «INSERT INTO» dans la base de données de certaines actions avec le service, plus tard, vous pouvez au moins utiliser SQL pour trouver les événements nécessaires. Et si nécessaire, vous pouvez créer une interface pour cela.

Graphiques


C'est un sujet très intéressant, je ne m'y attarderai pas en détail aujourd'hui, bien sûr, :)



Parlons de la journalisation. Nous avons beaucoup de graphiques, nous les regardons lorsque nous déployons quelque chose - et dans les temps, il y a un tel bruit.



Les graphiques aident à voir visuellement que quelque chose ne va pas. Et puis vous devez toujours regarder les journaux et comprendre ce qui ne va pas avec eux. Dans ce cas, la montée subite immédiatement après la libération signifie au moins qu'une telle libération doit être annulée immédiatement.

surveillance


Une partie encore plus importante et un degré plus élevé d'immersion dans ce sujet est la surveillance. Par surveillance, je comprends, d'une part, la surveillance automatique des graphiques et, d'autre part, les règles automatisées de surveillance de quelque chose.



Nous surveillons le rapport de cinq cents au nombre total de réponses par minute. Nous surveillons également quatre cents, la présence d'une charge sur le service, en vérifiant le bouton de contrôle de santé, qui tire le bouton ping de chacun des backends, etc.



De plus, nous avons des tableaux de bord de surveillance que nous incluons sur les écrans à proximité des postes de travail. On voit donc immédiatement lequel de la surveillance "rougir". Par exemple, ici c'est l'un des principaux, où le frontend et notre backend principal sont visibles. Ici, vous pouvez voir que certains contrôles s'allument sur le backend. Cela signifie qu'à ce moment, la personne responsable de ce service recevra un message sur Telegram, ou peut-être même l'appellera - cela dépend des paramètres de surveillance.

Sommaire


Un seul requestId vous aidera à trouver plus facilement les problèmes dans un service composé de plusieurs applications. La cURL correcte vous permettra de reproduire plus précisément les problèmes et de voir comment vous-même, par exemple, envoyer des données au backend. Les journaux structurés vous permettront de créer SLI, et ils sont plus pratiques à utiliser que les journaux de texte standard. Et n'oubliez pas de suivre les graphiques et d'effectuer un suivi.

Je recommande de lire le livre Site Reliability Engineering de Google si vous êtes intéressé par les infrastructures.

All Articles