Netramesh - solution de maillage de services légère

Alors que nous passons d’une application monolithique à une architecture de microservices, nous sommes confrontés à de nouveaux défis.

Dans une application monolithique, il est généralement assez simple de déterminer dans quelle partie du système l’erreur s’est produite. Très probablement, le problème réside dans le code du monolithe lui-même ou dans la base de données. Mais quand on commence à chercher un problème dans une architecture de microservices, tout n’est plus aussi évident. Nous devons trouver le chemin complet emprunté par la requête du début à la fin et le sélectionner parmi des centaines de microservices. De plus, beaucoup d’entre eux disposent également de leurs propres installations de stockage, ce qui peut également provoquer des erreurs logiques, ainsi que des problèmes de performances et de tolérance aux pannes.

Netramesh - solution de maillage de services légère

Je cherchais depuis longtemps un outil qui aiderait à faire face à de tels problèmes (j'ai écrit à ce sujet sur Habré : 1, 2), mais j'ai finalement créé ma propre solution open source. Dans cet article, je parle des avantages de l'approche de maillage de services et je partage un nouvel outil pour sa mise en œuvre.

Le traçage distribué est une solution courante au problème de la recherche d'erreurs dans les systèmes distribués. Mais que se passe-t-il si cette approche de collecte d'informations sur les interactions réseau n'a pas encore été mise en œuvre dans le système, ou, pire encore, dans une partie du système, elle fonctionne déjà correctement, mais en partie non, puisqu'elle n'a pas été ajoutée aux anciens services. ? Pour déterminer la cause exacte d’un problème, il est nécessaire d’avoir une image complète de ce qui se passe dans le système. Il est particulièrement important de comprendre quels microservices sont impliqués dans les chemins critiques pour l’entreprise.

Ici, l'approche du maillage de services peut nous venir en aide, qui traitera de tous les mécanismes de collecte d'informations sur le réseau à un niveau inférieur à celui des services eux-mêmes. Cette approche nous permet d'intercepter tout le trafic et de l'analyser à la volée. De plus, les applications n’ont même pas besoin d’en savoir quoi que ce soit.

Approche de maillage de services

L'idée principale de l'approche de maillage de services est d'ajouter une autre couche d'infrastructure sur le réseau, ce qui nous permettra de faire n'importe quoi avec l'interaction interservices. La plupart des implémentations fonctionnent comme suit : un conteneur side-car supplémentaire avec un proxy transparent est ajouté à chaque microservice, à travers lequel passe tout le trafic entrant et sortant du service. Et c'est ici même que nous pouvons procéder à l'équilibrage des clients, appliquer des politiques de sécurité, imposer des restrictions sur le nombre de requêtes et collecter des informations importantes sur l'interaction des services en production.

Netramesh - solution de maillage de services légère

Решения

Il existe déjà plusieurs implémentations de cette approche : Istio и linkerd2. Ils offrent de nombreuses fonctionnalités prêtes à l’emploi. Mais en même temps, cela implique une surcharge importante en termes de ressources. De plus, plus le cluster dans lequel un tel système fonctionne est grand, plus il faudra de ressources pour entretenir la nouvelle infrastructure. Chez Avito, nous exploitons des clusters Kubernetes qui contiennent des milliers d'instances de service (et leur nombre continue de croître rapidement). Dans sa mise en œuvre actuelle, Istio consomme environ 300 Mo de RAM par instance de service. En raison du grand nombre de possibilités, l'équilibrage transparent affecte également le temps de réponse global des services (jusqu'à 10 ms).

En conséquence, nous avons examiné exactement les capacités dont nous avions besoin à l’heure actuelle et avons décidé que la principale raison pour laquelle nous avons commencé à mettre en œuvre de telles solutions était la capacité de collecter de manière transparente des informations de traçage sur l’ensemble du système. Nous voulions également contrôler l'interaction des services et effectuer diverses manipulations avec les en-têtes transférés entre les services.

En conséquence, nous sommes arrivés à notre décision :  Netramesh.

Netramesh

Netramesh est une solution de maillage de services légère avec la capacité d'évoluer à l'infini, quel que soit le nombre de services dans le système.

Les principaux objectifs de la nouvelle solution étaient une faible consommation de ressources et des performances élevées. Parmi les fonctionnalités principales, nous avons immédiatement souhaité pouvoir envoyer de manière transparente des traçages à notre système Jaeger.

Aujourd'hui, la plupart des solutions cloud sont mises en œuvre à Golang. Et bien sûr, il y a des raisons à cela. Écrire des applications réseau dans Golang qui fonctionnent de manière asynchrone avec les E/S et s'adaptent à tous les cœurs selon les besoins est pratique et assez simple. Et, ce qui est également très important, les performances sont suffisantes pour résoudre ce problème. C'est pourquoi nous avons également choisi Golang.

Performance

Nous avons concentré nos efforts sur l’atteinte d’une productivité maximale. Pour une solution déployée à côté de chaque instance du service, une petite consommation de RAM et de temps CPU est requise. Et bien entendu, le délai de réponse doit également être réduit.

Voyons quels résultats nous avons obtenus.

RAM

Netramesh consomme ~ 10 Mo sans trafic et 50 Mo maximum avec une charge allant jusqu'à 10000 XNUMX RPS par instance.

Le proxy d'envoyé Istio consomme toujours environ 300 Mo dans nos clusters avec des milliers d'instances. Cela ne permet pas de l'étendre à l'ensemble du cluster.

Netramesh - solution de maillage de services légère

Netramesh - solution de maillage de services légère

Avec Netramesh, nous avons obtenu une réduction d'environ 10 fois de la consommation de mémoire.

Processeur

L'utilisation du processeur est relativement égale sous charge. Cela dépend du nombre de requêtes par unité de temps adressées au side-car. Valeurs à 3000 requêtes par seconde en pointe :

Netramesh - solution de maillage de services légère

Netramesh - solution de maillage de services légère

Il y a un autre point important : Netramesh - une solution sans plan de contrôle et sans charge ne consomme pas de temps CPU. Avec Istio, les side-cars mettent toujours à jour les points de terminaison du service. Du coup, on peut voir cette image sans charge :

Netramesh - solution de maillage de services légère

Nous utilisons HTTP/1 pour la communication entre les services. L'augmentation du temps de réponse d'Istio lors du proxy via Envoy pouvait atteindre 5 à 10 ms, ce qui est beaucoup pour des services prêts à répondre en une milliseconde. Avec Netramesh, ce temps a été réduit à 0.5-2 ms.

Évolutivité

La faible quantité de ressources consommées par chaque proxy permet de le placer à côté de chaque service. Netramesh a été intentionnellement créé sans composant de plan de contrôle pour simplement garder chaque side-car léger. Souvent, dans les solutions de maillage de services, le plan de contrôle distribue les informations de découverte de services à chaque side-car. Ces informations sont accompagnées d'informations sur les délais d'attente et les paramètres d'équilibrage. Tout cela vous permet de faire beaucoup de choses utiles, mais, malheureusement, cela gonfle la taille des side-cars.

Découverte de service

Netramesh - solution de maillage de services légère

Netramesh n'ajoute aucun mécanisme supplémentaire pour la découverte de services. Tout le trafic est transmis de manière transparente via le side-car Netra.

Netramesh prend en charge le protocole d'application HTTP/1. Pour le définir, une liste configurable de ports est utilisée. En règle générale, le système dispose de plusieurs ports via lesquels la communication HTTP s'effectue. Par exemple, pour l'interaction entre les services et les requêtes externes, nous utilisons 80, 8890, 8080. Dans ce cas, ils peuvent être définis à l'aide d'une variable d'environnement NETRA_HTTP_PORTS.

Si vous utilisez Kubernetes comme orchestrateur et son mécanisme d'entité de service pour la communication intra-cluster entre les services, le mécanisme reste exactement le même. Tout d'abord, le microservice obtient une adresse IP de service à l'aide de kube-dns et y ouvre une nouvelle connexion. Cette connexion est d'abord établie avec le side-car netra local et tous les paquets TCP arrivent initialement à netra. Ensuite, netra-sidecar établit une connexion avec la destination d'origine. NAT sur l'IP du pod sur le nœud reste exactement le même que sans netra.

Traçage distribué et transfert de contexte

Netramesh fournit la fonctionnalité nécessaire pour envoyer des étendues de traçage sur les interactions HTTP. Netra-sidecar analyse le protocole HTTP, mesure les délais de requête et extrait les informations nécessaires des en-têtes HTTP. En fin de compte, nous obtenons toutes les traces dans un seul système Jaeger. Pour une configuration fine, vous pouvez également utiliser les variables d'environnement fournies par la bibliothèque officielle bibliothèque Jaeger Go.

Netramesh - solution de maillage de services légère

Netramesh - solution de maillage de services légère

Mais il y a un problème. Jusqu'à ce que les services génèrent et envoient un en-tête Uber spécial, nous ne verrons pas les étendues de traçage connectées dans le système. Et c’est ce dont nous avons besoin pour trouver rapidement la cause des problèmes. Là encore, Netramesh a une solution. Les proxys lisent les en-têtes HTTP et, s'ils ne contiennent pas l'identifiant de trace Uber, en génèrent un. Netramesh stocke également des informations sur les requêtes entrantes et sortantes dans un side-car et les fait correspondre en les enrichissant avec les en-têtes de requêtes sortantes nécessaires. Tout ce que vous avez à faire dans les services est d'envoyer un seul en-tête X-Request-Id, qui peut être configuré à l'aide d'une variable d'environnement NETRA_HTTP_REQUEST_ID_HEADER_NAME. Pour contrôler la taille du contexte dans Netramesh, vous pouvez définir les variables d'environnement suivantes : NETRA_TRACING_CONTEXT_EXPIRATION_MILLISECONDS (la durée pendant laquelle le contexte sera stocké) et NETRA_TRACING_CONTEXT_CLEANUP_INTERVAL (fréquence de nettoyage du contexte).

Il est également possible de combiner plusieurs chemins sur votre système en les marquant avec un jeton de session spécial. Netra vous permet d'installer HTTP_HEADER_TAG_MAP pour transformer les en-têtes HTTP en balises span de traçage correspondantes. Cela peut être particulièrement utile pour les tests. Après avoir réussi le test fonctionnel, vous pouvez voir quelle partie du système a été affectée par le filtrage par la clé de session correspondante.

Détermination de la source de la demande

Pour déterminer d'où vient la demande, vous pouvez utiliser la fonctionnalité d'ajout automatique d'un en-tête avec la source. Utiliser une variable d'environnement NETRA_HTTP_X_SOURCE_HEADER_NAME Vous pouvez spécifier un nom d'en-tête qui sera automatiquement installé. En utilisant NETRA_HTTP_X_SOURCE_VALUE vous pouvez définir la valeur à laquelle l'en-tête X-Source sera défini pour toutes les requêtes sortantes.

Cela permet à la distribution de cet en-tête utile d'être répartie uniformément sur tout le réseau. Ensuite, vous pouvez l'utiliser dans les services et l'ajouter aux journaux et aux métriques.

Routage du trafic et composants internes de Netramesh

Netramesh se compose de deux composants principaux. Le premier, netra-init, définit des règles réseau pour intercepter le trafic. Il utilise règles de redirection iptables pour intercepter tout ou partie du trafic sur side-car, qui est le deuxième composant principal de Netramesh. Vous pouvez configurer les ports qui doivent être interceptés pour les sessions TCP entrantes et sortantes : INBOUND_INTERCEPT_PORTS, OUTBOUND_INTERCEPT_PORTS.

L'outil possède également une fonctionnalité intéressante : le routage probabiliste. Si vous utilisez Netramesh exclusivement pour collecter des étendues de traçage, dans un environnement de production, vous pouvez économiser des ressources et activer le routage probabiliste à l'aide de variables. NETRA_INBOUND_PROBABILITY и NETRA_OUTBOUND_PROBABILITY (de 0 à 1). La valeur par défaut est 1 (tout le trafic est intercepté).

Après une interception réussie, netra sidecar accepte la nouvelle connexion et utilise SO_ORIGINAL_DST option socket pour obtenir la destination d’origine. Netra ouvre ensuite une nouvelle connexion à l'adresse IP d'origine et établit une communication TCP bidirectionnelle entre les parties, écoutant tout le trafic qui transite. Si le port est défini comme HTTP, Netra essaie de l'analyser et de le tracer. Si l'analyse HTTP échoue, Netra revient à TCP et proxy les octets de manière transparente.

Construire un graphe de dépendances

Après avoir reçu une grande quantité d'informations de traçage dans Jaeger, je souhaite obtenir un graphique complet des interactions dans le système. Mais si votre système est très chargé et que des milliards d’étendues de traçage s’accumulent chaque jour, les agréger ne devient pas une tâche si simple. Il existe une manière officielle de procéder : dépendances spark. Cependant, la création d'un graphique complet prendra des heures et vous obligera à télécharger l'intégralité de l'ensemble de données de Jaeger pour les dernières XNUMX heures.

Si vous utilisez Elasticsearch pour stocker les étendues de traçage, vous pouvez utiliser un simple utilitaire Golang, qui créera le même graphique en quelques minutes en utilisant les fonctionnalités et capacités d'Elasticsearch.

Netramesh - solution de maillage de services légère

Comment utiliser Netramesh

Netra peut être facilement ajouté à n'importe quel service exécutant n'importe quel orchestrateur. Vous pouvez voir un exemple ici.

Pour le moment, Netra n'a pas la capacité d'implémenter automatiquement des side-cars dans les services, mais il existe des plans de mise en œuvre.

L'avenir de Netramesh

But principal Netramesh est d'atteindre des coûts de ressources minimes et des performances élevées, en fournissant des capacités de base pour l'observabilité et le contrôle de la communication interservices.

À l'avenir, Netramesh prendra en charge d'autres protocoles de couche application en plus de HTTP. Le routage L7 sera disponible dans un avenir proche.

Utilisez Netramesh si vous rencontrez des problèmes similaires et écrivez-nous avec vos questions et suggestions.

Source: habr.com

Ajouter un commentaire