Conteneurs, microservices et maillages de services

Sur Internet empiler articles о mailles de service (service mesh), et en voici un autre. Hourra ! Mais pourquoi? Ensuite, je veux exprimer mon opinion que les maillages de services auraient été meilleurs il y a 10 ans, avant l'avènement des plateformes de conteneurs telles que Docker et Kubernetes. Je ne dis pas que mon point de vue est meilleur ou pire que les autres, mais comme les maillages de services sont des animaux assez complexes, plusieurs points de vue permettront de mieux les comprendre.

Je parlerai de la plate-forme dotCloud, qui reposait sur plus d'une centaine de microservices et supportait des milliers d'applications dans des conteneurs. J'expliquerai les défis que nous avons rencontrés lors de son développement et de son lancement, et comment les maillages de services pourraient (ou non) aider.

historique des nuages ​​de points

J'ai déjà écrit sur l'histoire de dotCloud et le choix de l'architecture de cette plateforme, mais je n'ai pas beaucoup parlé de la couche réseau. Si vous ne voulez pas lire dernier article à propos de dotCloud, voici l'essentiel : c'est une plate-forme PaaS en tant que service qui permet aux clients d'exécuter une large gamme d'applications (Java, PHP, Python...), avec le support d'une large gamme de services de données ( MongoDB, MySQL, Redis...) et un workflow comme Heroku : vous uploadez votre code sur la plateforme, elle construit des images de conteneurs et les déploie.

Je vais vous dire comment le trafic a été envoyé à la plateforme dotCloud. Non pas parce que c'était particulièrement cool (même si le système fonctionnait bien pour l'époque !), mais surtout parce qu'avec l'aide d'outils modernes, une telle conception peut être facilement mise en œuvre en peu de temps par une équipe modeste si elle a besoin d'un moyen de routage trafic entre un tas de microservices ou un tas d'applications. Ainsi, vous pouvez comparer les options : que se passe-t-il si vous développez tout vous-même ou utilisez un maillage de services existant. Choix standard : fabriquer soi-même ou acheter.

Routage du trafic pour les applications hébergées

Les applications sur dotCloud peuvent exposer les points de terminaison HTTP et TCP.

Points de terminaison HTTP ajouté dynamiquement à la configuration du cluster d'équilibreur de charge Mal de hanche. Ceci est similaire à ce que les ressources font aujourd'hui Entrée dans Kubernetes et un équilibreur de charge comme Traefik.

Les clients se connectent aux points de terminaison HTTP via les domaines appropriés, à condition que le nom de domaine pointe vers les équilibreurs de charge dotCloud. Rien de spécial.

Points de terminaison TCP associé à un numéro de port, qui est ensuite transmis à tous les conteneurs de cette pile via des variables d'environnement.

Les clients peuvent se connecter aux points de terminaison TCP en utilisant le nom d'hôte approprié (quelque chose comme gateway-X.dotcloud.com) et le numéro de port.

Ce nom d'hôte correspond au cluster de serveurs "nats" (non lié à NATS) qui acheminera les connexions TCP entrantes vers le bon conteneur (ou, dans le cas de services à charge équilibrée, vers les bons conteneurs).

Si vous êtes familier avec Kubernetes, cela vous rappellera probablement les services Port de nœud.

Il n'y avait pas d'équivalent de services sur la plateforme dotCloud ClusterIP: pour plus de simplicité, l'accès aux services s'est fait de la même manière depuis l'intérieur et l'extérieur de la plateforme.

Tout était organisé assez simplement : les implémentations originales des réseaux de routage HTTP et TCP n'étaient probablement que quelques centaines de lignes de Python. Des algorithmes simples (je dirais naïfs) qui ont été affinés avec la croissance de la plateforme et l'émergence d'exigences supplémentaires.

Une refactorisation approfondie du code existant n'a pas été nécessaire. En particulier, 12 applications factorielles peut utiliser directement l'adresse obtenue via les variables d'environnement.

En quoi est-ce différent d'un maillage de services moderne ?

Limité visibilité. Nous n'avions aucune métrique pour le maillage de routage TCP. En ce qui concerne le routage HTTP, les versions plus récentes ont des métriques HTTP détaillées avec des codes d'erreur et des temps de réponse, mais les maillages de services modernes vont encore plus loin, offrant une intégration avec des systèmes de collecte de métriques comme Prometheus, par exemple.

La visibilité est importante non seulement d'un point de vue opérationnel (pour aider à résoudre les problèmes), mais également lorsque de nouvelles fonctionnalités sont publiées. Parler de sécurité déploiement bleu-vert и déploiement de canaris.

Efficacité du routage est également limité. Dans le maillage de routage dotCloud, tout le trafic devait passer par un cluster de nœuds de routage dédiés. Cela signifiait potentiellement franchir plusieurs limites AZ (zone de disponibilité) et une augmentation significative de la latence. Je me souviens d'un code de dépannage qui effectuait plus d'une centaine de requêtes SQL par page et ouvrait une nouvelle connexion au serveur SQL pour chaque requête. Lorsqu'elle est exécutée localement, la page se charge instantanément, mais sur dotCloud, le chargement prend quelques secondes car chaque connexion TCP (et requête SQL ultérieure) prend des dizaines de millisecondes. Dans ce cas particulier, les connexions persistantes ont résolu le problème.

Les maillages de services modernes sont plus efficaces pour gérer ces problèmes. Tout d'abord, ils vérifient que les connexions sont routées à la source. La suite logique est la même : клиент → меш → сервис, mais maintenant le maillage fonctionne localement et non sur des nœuds distants, donc la connexion клиент → меш est local et très rapide (microsecondes au lieu de millisecondes).

Les maillages de services modernes implémentent également des algorithmes d'équilibrage de charge plus intelligents. En surveillant la santé des backends, ils peuvent envoyer plus de trafic vers des backends plus rapides, ce qui se traduit par de meilleures performances globales.

sécurité est aussi meilleur. Le maillage de routage dotCloud fonctionnait entièrement sur EC2 Classic et ne chiffrait pas le trafic (en supposant que si quelqu'un réussissait à mettre un renifleur sur le trafic réseau EC2, vous auriez déjà de gros problèmes). Les maillages de services modernes protègent de manière transparente tout notre trafic, par exemple avec une authentification TLS mutuelle et un cryptage ultérieur.

Routage du trafic pour les services de plateforme

D'accord, nous avons discuté du trafic entre les applications, mais qu'en est-il de la plate-forme dotCloud elle-même ?

La plate-forme elle-même était composée d'une centaine de microservices chargés de diverses fonctions. Certains acceptaient les demandes des autres, et certains étaient des travailleurs de fond qui se connectaient à d'autres services mais n'acceptaient pas les connexions eux-mêmes. Dans les deux cas, chaque service doit connaître les points de terminaison des adresses auxquelles il doit se connecter.

De nombreux services de haut niveau peuvent utiliser le maillage de routage décrit ci-dessus. En fait, bon nombre des plus de XNUMX microservices dotCloud ont été déployés en tant qu'applications régulières sur la plate-forme dotCloud elle-même. Mais un petit nombre de services de bas niveau (en particulier, ceux qui implémentent ce maillage de routage) avaient besoin de quelque chose de plus simple, avec moins de dépendances (car ils ne pouvaient pas compter sur eux-mêmes pour fonctionner - le bon vieux problème de la poule et de l'œuf).

Ces services essentiels de bas niveau ont été déployés en exécutant des conteneurs directement sur quelques nœuds clés. Dans le même temps, les services de plate-forme standard n'étaient pas impliqués : éditeur de liens, ordonnanceur et exécuteur. Si vous voulez comparer avec les plates-formes de conteneurs modernes, c'est comme lancer un plan de contrôle avec docker run directement sur les nœuds, au lieu de déléguer la tâche à Kubernetes. C'est assez similaire dans le concept modules statiques (pods), qui utilise Kubeadm ou bootkube lors du démarrage d'un cluster autonome.

Ces services étaient exposés de manière simple et grossière : un fichier YAML listait leurs noms et adresses ; et chaque client devait prendre une copie de ce fichier YAML pour le déploiement.

D'une part, cela est extrêmement fiable, car il ne nécessite pas le support d'un magasin clé/valeur externe, tel que Zookeeper (rappelez-vous, etcd ou Consul n'existaient pas à l'époque). D'un autre côté, cela a rendu difficile le déplacement des services. Chaque fois qu'un déplacement était effectué, tous les clients devaient obtenir un fichier YAML mis à jour (et éventuellement le recharger). Pas très confortable !

Par la suite, nous avons commencé à implémenter un nouveau schéma, où chaque client se connectait à un serveur proxy local. Au lieu de l'adresse et du port, il suffit de connaître le numéro de port du service et de se connecter via localhost. Le proxy local gère cette connexion et la transmet au serveur réel. Désormais, lors du déplacement du backend vers une autre machine ou de la mise à l'échelle, au lieu de mettre à jour tous les clients, seuls tous ces proxys locaux doivent être mis à jour ; et un redémarrage n'est plus nécessaire.

(Il était également prévu d'encapsuler le trafic dans les connexions TLS et d'installer un autre serveur proxy côté réception, ainsi que de vérifier les certificats TLS sans la participation du service récepteur, qui est configuré pour accepter les connexions uniquement sur localhost. Plus à ce sujet plus tard).

Ceci est très similaire à pile intelligente d'Airbnb, mais la différence significative est que SmartStack est implémenté et déployé en production, tandis que le système de routage interne de dotCloud a été bloqué lorsque dotCloud s'est transformé en Docker.

Personnellement, je considère SmartStack comme l'un des prédécesseurs de systèmes comme Istio, Linkerd et Consul Connect car ils suivent tous le même schéma :

  • Exécutez un proxy sur chaque nœud.
  • Les clients se connectent au proxy.
  • Le plan de contrôle met à jour la configuration du proxy lorsque les backends changent.
  • … Profit!

Mise en œuvre d'un maillage de services moderne

Si nous devons mettre en œuvre une grille similaire aujourd'hui, nous pouvons utiliser des principes similaires. Par exemple, configurez une zone DNS interne en mappant les noms de service aux adresses dans l'espace 127.0.0.0/8. Exécutez ensuite HAProxy sur chaque nœud de cluster, en acceptant les connexions sur chaque adresse de service (sur ce sous-réseau 127.0.0.0/8) et rediriger/équilibrer la charge vers les backends appropriés. La configuration HAProxy peut être gérée confier, vous permettant de stocker des informations backend dans etcd ou Consul et de transmettre automatiquement la configuration mise à jour à HAProxy si nécessaire.

C'est ainsi qu'Istio fonctionne ! Mais avec quelques différences :

  • Les usages Mandataire émissaire au lieu de HAProxy.
  • Enregistre la configuration du backend via l'API Kubernetes au lieu d'etcd ou de Consul.
  • Les services se voient attribuer des adresses sur le sous-réseau interne (adresses Kubernetes ClusterIP) au lieu de 127.0.0.0/8.
  • Possède un composant supplémentaire (Citadel) pour ajouter une authentification mutuelle TLS entre le client et les serveurs.
  • Prend en charge de nouvelles fonctionnalités telles que la coupure de circuit, le traçage distribué, le déploiement Canary, etc.

Jetons un coup d'œil à certaines des différences.

Mandataire émissaire

Envoy Proxy a été écrit par Lyft [concurrent d'Uber sur le marché des taxis - env. par.]. Il est similaire à bien des égards à d'autres proxys (par exemple HAProxy, Nginx, Traefik...), mais Lyft a écrit le sien parce qu'il avait besoin de fonctionnalités que les autres proxys n'ont pas et il semblait plus judicieux d'en créer un nouveau plutôt que d'étendre un existant.

Envoy peut être utilisé seul. Si j'ai un service spécifique qui doit se connecter à d'autres services, je peux le configurer pour qu'il se connecte à Envoy, puis configurer et reconfigurer dynamiquement Envoy avec l'emplacement d'autres services, tout en bénéficiant de nombreux extras comme la visibilité. Au lieu d'une bibliothèque client personnalisée ou d'une injection dans le code de traçage des appels, nous envoyons le trafic à Envoy, et il collecte des métriques pour nous.

Mais Envoy est également capable de travailler comme plan de données (plan de données) pour le maillage de services. Cela signifie que maintenant pour ce service mesh, Envoy est configuré avion de contrôle (avion de contrôle).

Avion de contrôle

Dans le plan de contrôle, Istio s'appuie sur l'API Kubernetes. Ce n'est pas très différent de l'utilisation de confd, qui s'appuie sur etcd ou Consul pour rechercher un ensemble de clés dans le magasin de données. Istio examine un ensemble de ressources Kubernetes via l'API Kubernetes.

Entre ceci et ensuite: J'ai personnellement trouvé cela utile Description de l'API Kubernetesqui se lit :

Le serveur d'API Kubernetes est un "serveur stupide" qui offre le stockage, la gestion des versions, la validation, la mise à jour et la sémantique des ressources API.

Istio est conçu pour fonctionner avec Kubernetes ; et si vous souhaitez l'utiliser en dehors de Kubernetes, vous devez démarrer une instance du serveur d'API Kubernetes (et du service d'assistance etcd).

Adresses de service

Istio s'appuie sur les adresses ClusterIP que Kubernetes alloue, de sorte que les services Istio obtiennent une adresse interne (pas dans la plage 127.0.0.0/8).

Le trafic vers une adresse ClusterIP pour un service spécifique dans un cluster Kubernetes sans Istio est intercepté par kube-proxy et envoyé au back-end du proxy. Si vous êtes intéressé par les détails techniques, kube-proxy configure des règles iptables (ou des équilibreurs de charge IPVS, selon la façon dont il est configuré) pour réécrire les adresses IP de destination des connexions allant à l'adresse ClusterIP.

Une fois Istio installé sur un cluster Kubernetes, rien ne change tant qu'il n'est pas explicitement activé pour un consommateur donné, voire l'ensemble de l'espace de noms, en introduisant un conteneur sidecar aux modules personnalisés. Ce conteneur démarrera une instance Envoy et configurera un ensemble de règles iptables pour intercepter le trafic allant vers d'autres services et rediriger ce trafic vers Envoy.

Lorsqu'il est intégré au DNS Kubernetes, cela signifie que notre code peut se connecter par nom de service, et tout « fonctionne simplement ». En d'autres termes, notre code émet des requêtes telles que http://api/v1/users/4242alors api résoudre la demande de 10.97.105.48, les règles iptables interceptent les connexions à partir de 10.97.105.48 et les redirigent vers le proxy Envoy local, qui transmettra la demande au backend API réel. Phew!

Des fioritures supplémentaires

Istio fournit également un chiffrement et une authentification de bout en bout via mTLS (mutual TLS). Le composant appelé Citadelle.

Il y a aussi un composant Mixer, qu'Envoy peut demander chaque demande de prendre une décision spéciale concernant cette demande en fonction de divers facteurs tels que les en-têtes, le chargement du backend, etc... (ne vous inquiétez pas : il existe de nombreux outils pour que Mixer continue de fonctionner, et même s'il plante, Envoy continuera à fonctionner comme mandataire).

Et, bien sûr, nous avons mentionné la visibilité : Envoy collecte une énorme quantité de métriques tout en fournissant un traçage distribué. Dans une architecture de microservices, si une seule requête API doit passer par les microservices A, B, C et D, lors de la connexion, le traçage distribué ajoutera un identifiant unique à la requête et stockera cet identifiant via des sous-requêtes à tous ces microservices, permettant vous permet de capturer tous les appels liés, leurs retards, etc.

Développer ou acheter

Istio a la réputation d'être un système complexe. En revanche, construire le maillage de routage que j'ai décrit au début de cet article est relativement facile avec les outils existants. Alors, est-il judicieux de créer votre propre maillage de services à la place ?

Si on a des besoins modestes (on n'a pas besoin de visibilité, de coupe-circuit et autres subtilités), alors on réfléchit à développer son propre outil. Mais si nous utilisons Kubernetes, cela pourrait même ne pas être nécessaire car Kubernetes fournit déjà les outils de base pour la découverte de services et l'équilibrage de charge.

Mais si nous avons des exigences avancées, alors "acheter" un maillage de services semble être une bien meilleure option. (Il ne s'agit pas toujours d'un "achat" puisqu'Istio est open source, mais nous devons encore investir du temps d'ingénierie pour le comprendre, le déployer et le gérer.)

Que choisir : Istio, Linkerd ou Consul Connect ?

Jusqu'à présent, nous n'avons parlé que d'Istio, mais ce n'est pas le seul maillage de services. L'alternative populaire est Linker, mais il y a plus Connexion Consul.

Que choisir?

Pour être honnête, je ne sais pas. Pour le moment, je ne me considère pas assez compétent pour répondre à cette question. Il y a un peu intéressant articles avec une comparaison de ces outils et même repères.

Une approche prometteuse consiste à utiliser un outil comme supergloo. Il implémente une couche d'abstraction pour simplifier et unifier les API fournies par les maillages de services. Au lieu d'apprendre les API spécifiques (et, à mon avis, relativement complexes) de divers maillages de services, nous pouvons utiliser les constructions SuperGloo plus simples - et passer facilement de l'une à l'autre, comme si nous avions un format de configuration intermédiaire décrivant les interfaces HTTP et les backends capable de générer la configuration réelle pour Nginx, HAProxy, Traefik, Apache…

J'ai un peu joué avec Istio et SuperGloo, et dans le prochain article, je veux montrer comment ajouter Istio ou Linkerd à un cluster existant en utilisant SuperGloo, et comment ce dernier fera son travail, c'est-à-dire qu'il vous permet de passer de d'un maillage de services à un autre sans réécrire les configurations.

Source: habr.com

Ajouter un commentaire