BLOG | NGINX

MRA, partie 6 : Mise en œuvre du modèle de disjoncteur avec NGINX Plus

NGINX-Partie-de-F5-horiz-black-type-RGB
Miniature de Chris Stetson
Chris Stetson
Publié le 10 novembre 2016

Note de l'auteurCe billet de blog est le sixième d'une série :

  1. Présentation de l'architecture de référence des microservices de NGINX
  2. MRA, partie 2 : Le modèle proxy
  3. MRA, partie 3 : Le modèle de maillage du routeur
  4. MRA, partie 4 : Le modèle en tissu
  5. MRA, partie 5 : Adaptation de l'application à douze facteurs pour les microservices
  6. MRA, partie 6 : Implémentation du modèle de disjoncteur avec NGINX Plus (ce post)

Les six blogs, ainsi qu'un blog sur les interfaces Web pour les applications de microservices<.htmla>, ont été rassemblés dans un livre électronique gratuit .

Consultez également ces autres ressources NGINX sur les microservices :

 

La conception d’applications de microservices a entraîné un changement radical dans la manière dont les applications fonctionnent. Dans une architecture de microservices, une « application » est désormais un ensemble de services qui s’appuient les uns sur les autres pour effectuer des tâches et fournir des fonctionnalités. Dans les applications complexes, le graphique de services peut être assez profond et avoir de multiples interdépendances entre les différents services.

Par exemple, un service utilisateur peut faire partie intégrante de nombreux autres services qui s’appuient sur les données fournies par le service. Dans ce scénario, une défaillance du service utilisateur peut provoquer une cascade de défaillances dans l’ensemble de l’application.

Le modèle Circuit Breaker , un terme popularisé par Martin Fowler, gagne en popularité parmi les architectes de microservices en tant que modèle de conception d’application permettant d’éviter les pannes de service en cascade. L’idée du modèle Circuit Breaker est de surveiller vos services d’application et le trafic circulant entre eux afin d’éviter les pannes et, lorsque des pannes se produisent, de minimiser l’impact de ces pannes sur vos applications.

Pour les microservices, le modèle Circuit Breaker est particulièrement utile, car il offre une résilience ascendante. Si elle est mise en œuvre correctement, elle peut aider à éviter les pannes en cascade en assurant la continuité du service même lorsque les services ne sont pas disponibles. Le modèle Circuit Breaker a été adopté par Netflix comme un composant essentiel de sa philosophie de conception d'applications.

N’évitez pas l’échec, acceptez-le

L’un des principes clés de la conception des applications modernes est que des échecs surviendront. Le gâteau à plusieurs couches sur lequel s’appuient les applications modernes – des machines virtuelles hébergées dans le cloud aux conteneurs en passant par les bibliothèques d’applications et les réseaux dynamiques – signifie que les pièces mobiles de toute application sont légion. Vous devez supposer qu’une ou plusieurs parties de votre application échoueront d’une manière ou d’une autre à un moment donné. Prévoir les échecs et mettre en place des mécanismes pour atténuer leurs effets contribue grandement à rendre votre application plus résiliente.

L’un des objectifs les plus critiques du modèle de disjoncteur est d’essayer d’empêcher la défaillance en premier lieu. Pour certains types de conditions d’erreur, comme le manque de mémoire, il est possible de reconnaître qu’une défaillance est imminente et de prendre des mesures pour l’éviter. Cela se produit généralement lorsque le service signale qu'il n'est pas sain, puis le disjoncteur donne au service une chance de récupérer en limitant le nombre de requêtes ou en les redirigeant complètement. Une fois le service rétabli, il est également prudent pour le disjoncteur d'augmenter lentement les demandes adressées au service afin de ne pas le submerger immédiatement et de risquer qu'il redevienne défectueux.

Dans l' architecture de référence des microservices NGINX , nous avons un service appelé resizer . Lorsqu'une grande photo est téléchargée sur le système, le redimensionneur la décompresse, corrige sa rotation, la réduit, puis la réduit à nouveau, enregistrant l'image originale corrigée et les deux images redimensionnées dans un magasin d'objets. La nature de ces processus fait du redimensionnement la partie la plus gourmande en ressources processeur et en mémoire de l’application.

Lorsque de nombreuses images sont redimensionnées simultanément, le redimensionneur peut manquer de mémoire et, dans certains scénarios, échouer complètement. Pour éviter les problèmes, nous avons placé un disjoncteur entre les instances du service de redimensionnement et le service de téléchargement qui leur fournit des images. Le téléchargeur interroge régulièrement les instances de redimensionnement pour connaître leur état de santé. La requête déclenche le redimensionneur pour évaluer s'il a utilisé plus de 80 % de la mémoire disponible, entre autres contrôles de santé, et répond au téléchargeur avec son état de santé.

Si une instance de redimensionnement indique qu'elle n'est pas saine, le téléchargeur achemine les requêtes vers d'autres instances – comme illustré dans la Figure 1 – mais continue de vérifier si cette instance de redimensionnement a récupéré. Lorsque l’instance de redimensionnement indique qu’elle est à nouveau saine, elle est remise dans le pool à charge équilibrée et le téléchargeur augmente progressivement le trafic jusqu’à la pleine capacité de l’instance. Cette conception empêche les instances du redimensionneur d'échouer complètement, empêche le travail d'être commencé mais non terminé, évite une attente excessive pour les utilisateurs dont les processus auraient autrement échoué et aide le système à gérer le plus efficacement possible le flux de demandes qui lui est envoyé.

Le modèle de disjoncteur coupe le trafic vers les instances non saines. Un disjoncteur et NGINX fonctionnent bien ensemble.
Figure 1. Les contrôles de santé actifs empêchent les appels vers une instance de microservice défectueuse

Le modèle de disjoncteur améliore la cohérence

L’un des avantages de l’implémentation du disjoncteur au niveau NGINX est qu’il crée une couche universelle, cohérente et hautement flexible pour la gestion des disjoncteurs dans votre application de microservices. Cette universalité et cette cohérence signifient que vous n’avez pas à gérer et à construire autour des nuances et des incohérences des bibliothèques de disjoncteurs pour chaque langage.

Vous bénéficiez de nombreux avantages en conservant la plupart des fonctionnalités de disjoncteur hors du code de chaque service et en les implémentant plutôt dans NGINX Plus :

  • Le disjoncteur pour un service écrit, par exemple, en Java, est le même que pour un service écrit en PHP – et le disjoncteur lui-même peut être écrit dans un autre langage, selon les besoins
  • Vous évitez d'avoir à réimplémenter la fonctionnalité de disjoncteur dans l'ensemble des langages et des bibliothèques de support utilisés par chacun de vos services
  • Chaque service qui n'a pas besoin d'inclure le code du disjoncteur est ainsi simplifié ; il s'exécute plus rapidement et est plus facile à écrire, à déboguer, à exécuter et à entretenir.
  • Le code de support de chaque service est simplifié ; le mélange de bibliothèques et de systèmes utilisés peut refléter uniquement la fonctionnalité principale du service
  • Le code du disjoncteur est simplifié ; existant à un seul endroit, il peut être réduit à l'essentiel, sans qu'il soit nécessaire de s'adapter aux contextes locaux
  • Le code du disjoncteur peut tirer parti des fonctionnalités de NGINX Plus telles que la mise en cache, ce qui le rend beaucoup plus puissant
  • Vous pouvez affiner votre code de disjoncteur de niveau NGINX Plus, puis le réutiliser dans d'autres applications et sur des plates-formes de déploiement, telles que sur site, sur différentes plates-formes cloud et dans des environnements mixtes.

Il est toutefois important de noter que les disjoncteurs ne peuvent pas être implémentés uniquement dans NGINX Plus. Un véritable disjoncteur nécessite que le service fournisse un contrôle de santé introspectif et actif sur un URI désigné (généralement /health ). Le bilan de santé doit être adapté aux besoins de ce service spécifique.

Lors du développement du contrôle de santé, vous devez comprendre le profil de défaillance du service et les types de conditions pouvant entraîner une défaillance, comme une défaillance de connexion à la base de données, une condition de mémoire insuffisante, un manque d'espace disque ou une surcharge du processeur. Ces conditions sont évaluées lors du processus de contrôle de santé, qui fournit ensuite un statut binaire de sain ou de malsain.

Le modèle de disjoncteur offre une flexibilité

Lorsque vous implémentez le modèle de disjoncteur au niveau NGINX, comme décrit ici, il appartient à NGINX Plus de gérer la situation lorsqu’une instance de service indique qu’elle n’est pas saine. Il existe un certain nombre d'options.

La première option consiste à rediriger les requêtes vers d’autres instances saines et à continuer d’interroger l’instance non saine pour voir si elle récupère. La deuxième option consiste à fournir des réponses en cache aux clients qui demandent le service, en maintenant la stabilité même si le service n'est pas disponible. Cette solution fonctionne bien avec les services orientés lecture, comme un service de contenu.

Une autre option consiste à fournir des sources de données alternatives. Par exemple, l’un de nos clients dispose d’un serveur publicitaire personnalisé qui utilise les données de profil pour diffuser des publicités ciblées à ses utilisateurs. Si le serveur d'annonces personnalisées est en panne, la demande de l'utilisateur est redirigée vers un serveur de sauvegarde qui fournit un ensemble générique d'annonces adaptées à tout le monde. Cette approche de source de données alternative peut être assez puissante.

Enfin, si vous avez une compréhension très claire du profil de défaillance d’un service, vous pouvez atténuer la défaillance en ajoutant une limitation de débit au disjoncteur. Les demandes ne sont autorisées à être traitées que dans la mesure où le service peut les traiter. Cela crée un tampon dans le disjoncteur afin qu'il puisse absorber les pics de trafic.

La limitation du débit peut être particulièrement efficace dans un scénario d'équilibrage de charge centralisé comme le modèle de maillage de routeur , où le trafic d'application est acheminé via un nombre limité d'équilibreurs de charge qui peuvent avoir une bonne compréhension de l'utilisation totale du trafic sur le site.

Implémentation du modèle de disjoncteur dans NGINX Plus

Comme nous l’avons décrit ci-dessus, le modèle de disjoncteur peut empêcher une panne avant qu’elle ne se produise en réduisant le trafic vers un service défectueux ou en acheminant les demandes loin de celui-ci. Cela nécessite un contrôle de santé actif connecté à un moniteur de santé introspectif sur chaque service. Malheureusement, un contrôle de santé passif ne suffit pas, car il vérifie uniquement les défaillances. À ce stade, il est déjà trop tard pour prendre des mesures préventives. C'est pour cette raison que NGINX Open Source ne peut pas implémenter le modèle de disjoncteur – il ne prend en charge que les contrôles de santé passifs.

NGINX Plus dispose toutefois d’un système de contrôle de santé actif robuste avec de nombreuses options pour vérifier et répondre aux problèmes de santé. L’examen de la mise en œuvre de certains types de services pour l’architecture de référence des microservices fournit de bons exemples d’options et de cas d’utilisation pour la mise en œuvre du disjoncteur.

Commençons par le service de téléchargement qui se connecte au redimensionneur. Le téléchargeur place les images dans un magasin d'objets, puis demande au redimensionneur d'ouvrir une image, de la corriger et de la redimensionner. Il s’agit d’une opération gourmande en calcul et en mémoire. Le téléchargeur doit surveiller l’état du redimensionneur et éviter de le surcharger, car le redimensionneur peut littéralement tuer l’hôte sur lequel il s’exécute.

La première chose à faire est de créer un bloc d’emplacement spécifiquement pour le contrôle de santé du redimensionneur. Ce bloc est un emplacement interne , ce qui signifie qu'il n'est pas accessible avec une requête à l'URL standard du serveur ( http://example.com/health-check-resizer ). Il s’agit plutôt d’un espace réservé aux informations de contrôle de santé. La directive health_check envoie un contrôle de santé à l'URI /health toutes les trois secondes et utilise les tests définis dans le bloc de correspondance appelé conditions pour vérifier l'état de l'instance de service. Une instance de service est marquée comme non saine lorsqu'elle manque une seule vérification. Les directives proxy_* envoient la vérification de l'état au groupe en amont du redimensionneur , en utilisant TLS 1.2 sur HTTP 1.1 avec les en-têtes HTTP indiqués définis sur null.

emplacement /health-check-resizer { interne ;
health_check uri=/health match=conditions fails=1 interval=3s;

proxy_pass https://resizer ;
proxy_ssl_session_reuse on ;
proxy_ssl_protocols TLSv1.2 ;
proxy_http_version 1.1 ;
proxy_set_header Connexion "" ;
proxy_set_header Accept-Encoding "" ;
}

L’étape suivante consiste à créer le bloc de correspondance des conditions pour spécifier les réponses qui représentent des conditions saines et malsaines. La première vérification concerne le code d'état de la réponse : s'il est compris entre200 à travers399 , les tests passent à l’instruction d’évaluation suivante. La deuxième vérification est que le type de contenu est application/json . Enfin, la troisième vérification est une correspondance d'expression régulière avec la valeur des métriques deadlocks , Disk et Memory . S’ils sont tous en bonne santé, alors le service est considéré comme sain.

conditions de correspondance { statut 200-399 ;
header Content-Type ~ "application/json" ;
body ~ '{
"deadlocks":{"healthy":true},
"Disk":{"healthy":true},
"Memory":{"healthy":true}
}';
}

Le système de disjoncteur/vérification de l'état de santé NGINX Plus dispose également d'une fonction de démarrage lent. Le paramètre slow_start de la directive serveur pour le service de redimensionnement dans le bloc en amont indique à NGINX Plus de modérer le flux de trafic lorsqu'une instance de redimensionnement revient pour la première fois d'un état défectueux. Plutôt que de simplement écraser le service avec le même nombre de requêtes envoyées aux services sains, le trafic vers le service en cours de récupération est lentement augmenté jusqu'au débit normal sur la période indiquée par le paramètre slow_start – dans ce cas, 30 secondes. Le démarrage lent améliore les chances que le service revienne à sa pleine capacité tout en réduisant l’impact si cela ne se produit pas.

redimensionnement en amont { redimensionnement du serveur slow_start=30s;
zone backend 64k;
least_time last_byte;
keepalive 300;
}

La limitation des demandes gère et modère le flux de demandes vers le service. Si vous comprenez suffisamment bien le profil d’échec de l’application pour connaître le nombre de requêtes qu’elle peut gérer à un moment donné, alors la mise en œuvre de la limitation des requêtes peut être une véritable aubaine pour le processus. Cependant, cette fonctionnalité ne fonctionne que si NGINX Plus a pleinement connaissance du nombre total de connexions transmises au service. Pour cette raison, il est très utile d’implémenter le disjoncteur de limitation des demandes sur une instance NGINX Plus exécutée dans un conteneur avec le service lui-même, comme dans le modèle Fabric, ou dans un équilibreur de charge centralisé chargé de gérer tout le trafic dans un cluster.

L'extrait de code de configuration suivant définit une limite de débit sur les demandes à appliquer aux instances du service de redimensionnement dans leurs conteneurs. La directive limit_req_zone définit la limite de débit à 100 requêtes par seconde. La variable $server_addr est utilisée comme clé, ce qui signifie que toutes les requêtes dans le conteneur de redimensionnement sont comptabilisées dans la limite. Le nom de la zone est modéréeReqs et le délai de conservation du nombre de requêtes est de 1 minute. La directive limit_req permet à NGINX Plus de mettre en mémoire tampon des rafales allant jusqu'à 150 requêtes. Lorsque ce nombre est dépassé, les clients reçoivent le503 code d'erreur tel que spécifié par la directive limit_req_status , indiquant que le service n'est pas disponible.

http { # Livraison modérée
limit_req_zone $server_addr zone=moderateReqs:1m rate=100r/s;
# ...
server {
# ...
limit_req zone=moderateReqs burst=150;
limit_req_status 503;
# ...
}
}

Un autre avantage important de l’exécution du disjoncteur dans NGINX Plus est la possibilité d’intégrer la mise en cache et de conserver les données mises en cache de manière centralisée, pour une utilisation sur l’ensemble du système. Cela est particulièrement utile pour les services orientés lecture comme les serveurs de contenu où les données lues depuis le backend ne changent pas fréquemment.

proxy_cache_path /app/cache levels=1:2 keys_zone=oauth_cache:10m max_size=10m inactive=15s use_temp_path=off;
gestionnaire d'utilisateurs en amont {
gestionnaire d'utilisateurs du serveur ;
zone backend 64 ko ;
least_time last_byte ;
keepalive 300 ;
}

serveur {
écoute 443 ssl ;
emplacement /v1/utilisateurs {
proxy_pass http://user-manager ;
proxy_cache oauth_cache ;
proxy_cache_valid 200 30 s ;
proxy_cache_use_stale erreur timeout invalid_header mise à jour
http_500 http_502 http_503 http_504 ;
}
}

Comme le montre la figure 2, la mise en cache des données signifie que de nombreuses demandes de données client n’atteignent jamais les instances de microservice, libérant ainsi de la capacité pour les demandes qui n’ont pas été reçues auparavant.

NGINX Plus agissant comme un disjoncteur de microservices prend également en charge la mise en cache.
Figure 2. Bien que la mise en cache soit généralement utilisée pour accélérer les performances en empêchant les appels aux instances de microservices, elle sert également à assurer la continuité du service en cas de défaillance complète du service.

Cependant, avec un service où les données peuvent changer, par exemple un service de gestion d’utilisateurs, un cache doit être géré judicieusement. Sinon, vous pouvez vous retrouver avec un scénario dans lequel un utilisateur modifie son profil, mais voit d’anciennes données dans certains contextes car les données sont mises en cache. Un délai d’attente raisonnable et l’acceptation du principe de haute disponibilité avec cohérence éventuelle peuvent résoudre cette énigme.

L’une des fonctionnalités intéressantes du cache NGINX est qu’il peut continuer à diffuser les données mises en cache même si le service est complètement indisponible – dans l’extrait ci-dessus, si le service répond avec l’une des quatre erreurs les plus courantes500 -codes d'erreur de la série.

La mise en cache n’est pas la seule option pour répondre aux clients même si un serveur est en panne. Comme nous l'avons mentionné dans Le modèle de disjoncteur offre une flexibilité , l'un de nos clients avait besoin d'une solution résiliente au cas où son serveur de publicité personnalisé tomberait en panne, et les réponses mises en cache n'étaient pas une bonne solution. Au lieu de cela, ils voulaient un serveur publicitaire générique pour fournir des publicités généralisées jusqu'à ce que le serveur personnalisé soit à nouveau en ligne. Ceci est facilement réalisé en utilisant le paramètre de sauvegarde de la directive serveur . L'extrait suivant spécifie que lorsque tous les serveurs définis pour le domaine personal-ad-server ne sont pas disponibles, les serveurs définis pour le domaine generic-ad-server sont utilisés à la place.

serveur de publicité personnel en amont { serveur serveur de publicité personnel ;
serveur serveur de publicité générique sauvegarde ;
zone backend 64 ko ;
least_time last_byte ;
keepalive 300 ;
}

Et enfin, il est possible de demander à NGINX d’évaluer les codes de réponse d’un service et de les traiter individuellement. Dans l'extrait suivant, si un service renvoie un503 erreur, NGINX Plus envoie la demande à un service alternatif. Par exemple, si le redimensionneur dispose de cette fonctionnalité et que l'instance locale est surchargée ou cesse de fonctionner, les requêtes sont alors envoyées à une autre instance du redimensionneur.

emplacement / { error_page 503 = @fallback;
}

emplacement @fallback {
proxy_pass http://alternative-backend;
}

Conclusion

Le modèle de disjoncteur est un outil puissant pour fournir résilience et contrôle dans votre application de microservices. NGINX Plus fournit de nombreuses fonctionnalités et options pour implémenter le disjoncteur dans votre environnement. La clé de la mise en œuvre du modèle de disjoncteur est de comprendre le profil de défaillance du service que vous protégez, puis de choisir les options qui préviennent le mieux la défaillance, lorsque cela est possible, et qui atténuent le mieux les effets de la défaillance lorsqu'elle se produit.

Pour essayer NGINX Plus, démarrez votre essai gratuit de 30 jours dès aujourd'hui ou contactez-nous pour discuter de vos cas d'utilisation.


« Cet article de blog peut faire référence à des produits qui ne sont plus disponibles et/ou qui ne sont plus pris en charge. Pour obtenir les informations les plus récentes sur les produits et solutions F5 NGINX disponibles, explorez notre famille de produits NGINX . NGINX fait désormais partie de F5. Tous les liens NGINX.com précédents redirigeront vers un contenu NGINX similaire sur F5.com."