Conception d’applications de charges de travail stratégiques sur Azure

Lorsque vous concevez une application, les exigences d’application fonctionnelles et non fonctionnelles sont essentielles. Cette zone de conception décrit les modèles d’architecture et les stratégies de mise à l’échelle qui peuvent aider à rendre votre application résiliente aux défaillances.

Important

Cet article fait partie de la série de charges de travail stratégiques Azure Well-Architected Framework. Si vous n’êtes pas familiarisé avec cette série, nous vous recommandons de commencer par Qu’est-ce qu’une charge de travail stratégique ?.

Architecture d’unité d’échelle

Tous les aspects fonctionnels d’une solution doivent être capables de mettre à l’échelle pour répondre aux changements de la demande, idéalement la mise à l’échelle automatique en réponse au chargement. Nous vous recommandons d’utiliser une architecture d’unité d’échelle pour optimiser l’extensibilité de bout en bout via la compartimentation, et également pour normaliser le processus d’ajout et de suppression de capacité. Une unité d’échelle est une unité logique ou une fonction qui peut être mise à l’échelle indépendamment. Une unité peut être composée de composants de code, de plateformes d’hébergement d’applications, de tampons de déploiement qui couvrent les composants associés et même des abonnements pour prendre en charge les exigences multilocataires.

Nous vous recommandons cette approche, car elle traite les limites d’échelle des ressources individuelles et de l’ensemble de l’application. Il permet de déployer des scénarios complexes de déploiement et de mise à jour, car une unité d’échelle peut être déployée en tant qu’unité. En outre, vous pouvez tester et valider des versions spécifiques des composants d’une unité avant de diriger le trafic utilisateur vers celui-ci.

Supposons que votre application stratégique soit un catalogue de produits en ligne. Il dispose d’un flux utilisateur pour traiter les commentaires et les évaluations des produits. Le flux utilise des API pour récupérer et publier des commentaires et des évaluations, ainsi que des composants de prise en charge tels qu’un point de terminaison OAuth, un magasin de données et des files d’attente de messages. Les points de terminaison d’API sans état représentent des unités fonctionnelles granulaires qui doivent s’adapter aux modifications à la demande. La plateforme d’application sous-jacente doit également être en mesure de mettre à l’échelle en conséquence. Pour éviter les goulots d’étranglement des performances, les composants et dépendances en aval doivent également être mis à l’échelle dans un degré approprié. Elles peuvent être mises à l’échelle indépendamment, en tant qu’unités d’échelle distinctes ou ensemble, dans le cadre d’une seule unité logique.

Exemple d’unités d’échelle

L’image suivante montre les étendues possibles pour les unités d’échelle. Les étendues vont des pods de microservice aux nœuds de cluster et aux tampons de déploiement régional.

Diagramme montrant plusieurs étendues pour les unités d’échelle.

Considérations sur la conception

  • Portée. L’étendue d’une unité d’échelle, la relation entre les unités d’échelle et leurs composants doit être définie en fonction d’un modèle de capacité. Prenez en compte les exigences non fonctionnelles pour les performances.

  • Limites de mise à l’échelle. Les limites et quotas d’échelle d’abonnement Azure peuvent avoir une incidence sur la conception des applications, les choix technologiques et la définition d’unités d’échelle. Les unités d’échelle peuvent vous aider à contourner les limites d’échelle d’un service. Par exemple, si un cluster AKS dans une unité ne peut avoir que 1 000 nœuds, vous pouvez utiliser deux unités pour augmenter cette limite à 2 000 nœuds.

  • Charge attendue. Utilisez le nombre de demandes pour chaque flux utilisateur, le taux de requêtes maximal attendu (demandes par seconde) et les modèles de trafic quotidien/hebdomadaire/saisonnier pour informer les exigences de mise à l’échelle principales. Facteur également dans les modèles de croissance attendus pour le trafic et le volume de données.

  • Performances dégradées acceptables. Déterminez si un service détérioré avec des temps de réponse élevés est acceptable sous charge. Lorsque vous modélisez la capacité requise, les performances requises de la solution sous charge sont un facteur essentiel.

  • Exigences non fonctionnelles. Les scénarios techniques et métier ont des considérations distinctes pour la résilience, la disponibilité, la latence, la capacité et l’observabilité. Analysez ces exigences dans le contexte des flux d’utilisateurs de bout en bout clés. Vous aurez une flexibilité relative dans la conception, la prise de décision et les choix technologiques au niveau du flux utilisateur.

Recommandations de conception

  • Définissez l’étendue d’une unité d’échelle et les limites qui déclenchent l’échelle de l’unité.

  • Vérifiez que tous les composants d’application peuvent être mis à l’échelle indépendamment ou dans le cadre d’une unité d’échelle qui inclut d’autres composants associés.

  • Définissez la relation entre les unités d’échelle, en fonction d’un modèle de capacité et des exigences non fonctionnelles.

  • Définissez un tampon de déploiement régional pour unifier l’approvisionnement, la gestion et le fonctionnement des ressources d’application régionales dans une unité d’échelle hétérogène mais interdépendante. À mesure que la charge augmente, des empreintes supplémentaires peuvent être déployées, dans la même région Azure ou différentes, pour mettre à l’échelle horizontalement la solution.

  • Utilisez un abonnement Azure comme unité d’échelle afin que les limites de mise à l’échelle au sein d’un seul abonnement ne limitent pas l’extensibilité. Cette approche s’applique aux scénarios d’application à grande échelle qui ont un volume de trafic important.

  • Modélisez la capacité requise autour des modèles de trafic identifiés pour vous assurer que la capacité suffisante est approvisionnée aux heures de pointe pour empêcher la dégradation du service. Vous pouvez également optimiser la capacité pendant les heures creuses.

  • Mesurez le temps nécessaire pour effectuer des opérations de scale-out et de scale-in pour vous assurer que les variations naturelles du trafic ne créent pas de niveau de dégradation inacceptable du service. Suivez les durées de l’opération de mise à l’échelle en tant que métrique opérationnelle.

Remarque

Lorsque vous déployez dans une zone d’atterrissage Azure, assurez-vous que l’abonnement à la zone d’atterrissage est dédié à l’application pour fournir une limite de gestion claire et pour éviter l’antimodèle voisin bruyant.

Diffusion mondiale

Il est impossible d’éviter une défaillance dans un environnement hautement distribué. Cette section fournit des stratégies pour atténuer de nombreux scénarios d’erreur. L’application doit pouvoir résister aux défaillances régionales et zonales. Il doit être déployé dans un modèle actif/actif afin que la charge soit distribuée entre toutes les régions.

Regardez cette vidéo pour obtenir une vue d’ensemble de la planification des défaillances dans les applications stratégiques et optimiser la résilience :

Considérations sur la conception

  • Redondance. Votre application doit être déployée dans plusieurs régions. En outre, dans une région, nous vous recommandons vivement d’utiliser des zones de disponibilité pour permettre la tolérance de panne au niveau du centre de données. Les zones de disponibilité ont un périmètre de latence de moins de 2 millisecondes entre les zones de disponibilité. Pour les charges de travail qui sont « bavardées » entre les zones, cette latence peut entraîner une pénalité de performances pour le transfert de données interzone.

  • Modèle actif/actif. Une stratégie de déploiement active/active est recommandée, car elle optimise la disponibilité et fournit un contrat de niveau de service composite (SLA). Toutefois, il peut présenter des défis liés à la synchronisation et à la cohérence des données pour de nombreux scénarios d’application. Relevez les défis au niveau d’une plateforme de données tout en tenant compte des compromis liés à l’augmentation des coûts et des efforts d’ingénierie.

    Un déploiement actif/actif sur plusieurs fournisseurs de cloud permet d’atténuer potentiellement la dépendance vis-à-vis des ressources globales au sein d’un seul fournisseur de cloud. Toutefois, une stratégie de déploiement multicloud active/active introduit une grande quantité de complexité autour de CI/CD. En outre, compte tenu des différences entre les spécifications et les fonctionnalités des ressources entre les fournisseurs de cloud, vous avez besoin d’empreintes de déploiement spécialisées pour chaque cloud.

  • Distribution géographique. La charge de travail peut avoir des exigences de conformité pour la résidence des données géographiques, la protection des données et la rétention des données. Déterminez s’il existe des régions spécifiques où les données doivent résider ou où les ressources doivent être déployées.

  • Origine de la demande. La proximité géographique et la densité des utilisateurs ou des systèmes dépendants doivent informer les décisions de conception relatives à la distribution mondiale.

  • Connectivité. La façon dont la charge de travail est accessible par les utilisateurs ou les systèmes externes influence votre conception. Déterminez si l’application est disponible via l’Internet public ou les réseaux privés qui utilisent des circuits VPN ou Azure ExpressRoute.

Pour obtenir des recommandations de conception et des choix de configuration au niveau de la plateforme, consultez Plateforme d’application : Distribution globale.

Architecture basée sur des événements faiblement couplée

Le couplage permet une communication interservice via des interfaces bien définies. Un couplage libre permet à un composant d’application de fonctionner indépendamment. Un style d’architecture de microservices est conforme aux exigences stratégiques. Il facilite la haute disponibilité en empêchant les défaillances en cascade.

Pour le couplage libre, nous vous recommandons vivement d’incorporer la conception pilotée par les événements. Le traitement asynchrone des messages via un intermédiaire peut générer une résilience.

Diagramme illustrant la communication asynchrone pilotée par les événements.

Dans certains scénarios, les applications peuvent combiner des couplages lâches et serrés, en fonction des objectifs métier.

Considérations sur la conception

  • Dépendances d’exécution. Les services faiblement couplés ne doivent pas être contraints d’utiliser la même plateforme de calcul, le langage de programmation, le runtime ou le système d’exploitation.

  • Mise à l’échelle. Les services doivent pouvoir être mis à l’échelle indépendamment. Optimisez l’utilisation des ressources d’infrastructure et de plateforme.

  • Tolérance de panne. Les échecs doivent être gérés séparément et ne doivent pas affecter les transactions clientes.

  • Intégrité transactionnelle. Tenez compte de l’effet de la création et de la persistance des données qui se produisent dans des services distincts.

  • Traçage distribué. Le suivi de bout en bout peut nécessiter une orchestration complexe.

Recommandations de conception

  • Aligner les limites de microservice avec les flux utilisateur critiques.

  • Utilisez la communication asynchrone pilotée par les événements dans la mesure du possible pour prendre en charge une mise à l’échelle durable et des performances optimales.

  • Utilisez des modèles tels que outbox et session transactionnelle pour garantir la cohérence afin que chaque message soit traité correctement.

Exemple : approche pilotée par les événements

L’implémentation de référence de Mission-Critical Online utilise des microservices pour traiter une transaction métier unique. Il applique des opérations d’écriture de manière asynchrone avec un répartiteur de messages et un worker. Les opérations de lecture sont synchrones, avec le résultat directement retourné à l’appelant.

Diagramme montrant la communication pilotée par les événements.

Modèles de résilience et gestion des erreurs dans le code d’application

Une application stratégique doit être conçue pour être résiliente afin qu’elle traite autant de scénarios d’échec que possible. Cette résilience optimise la disponibilité et la fiabilité du service. L’application doit avoir des fonctionnalités d’auto-réparation, que vous pouvez implémenter à l’aide de modèles de conception tels que Retries avec backoff et disjoncteur.

Pour les défaillances non temporaires que vous ne pouvez pas atténuer entièrement dans la logique de l’application, le modèle d’intégrité et les wrappers opérationnels doivent prendre des mesures correctives. Le code d’application doit incorporer une instrumentation et une journalisation appropriées pour informer le modèle d’intégrité et faciliter la résolution des problèmes ou l’analyse de la cause racine ultérieure en fonction des besoins. Vous devez implémenter le suivi distribué pour fournir à l’appelant un message d’erreur complet qui inclut un ID de corrélation lorsqu’un échec se produit.

Les outils comme Application Insights peuvent vous aider à interroger, mettre en corrélation et visualiser les traces d’application.

Considérations sur la conception

  • Configurations appropriées. Il n’est pas rare que les problèmes temporaires provoquent des défaillances en cascade. Par exemple, une nouvelle tentative sans interruption appropriée aggrave le problème lorsqu’un service est limité. Vous pouvez espacer les retards de nouvelle tentative de manière linéaire ou les augmenter de façon exponentielle pour se retirer par le biais de retards croissants.

  • Points de terminaison d’intégrité. Vous pouvez exposer des vérifications fonctionnelles dans le code d’application à l’aide de points de terminaison d’intégrité que les solutions externes peuvent interroger pour récupérer l’état d’intégrité des composants d’application.

Recommandations de conception

Voici quelques modèles courants d’ingénierie logicielle pour les applications résilientes :

Modèle Résumé
Nivellement de la charge basé sur une file d’attente Introduit une mémoire tampon entre les consommateurs et les ressources demandées pour garantir des niveaux de charge cohérents. À mesure que les demandes de consommateur sont mises en file d’attente, un processus de travail les gère sur la ressource demandée à un rythme défini par le worker et par la capacité de la ressource demandée à traiter les demandes. Si les consommateurs attendent des réponses à leurs demandes, vous devez implémenter un mécanisme de réponse distinct. Appliquez un ordre hiérarchisé afin que les activités les plus importantes soient effectuées en premier.
Disjoncteur Fournit la stabilité en attendant la récupération ou en rejetant rapidement les demandes plutôt que de bloquer en attendant un service ou une ressource distant indisponible. Ce modèle gère également les erreurs qui peuvent prendre un certain temps de récupération lorsqu’une connexion est établie à un service distant ou à une ressource.
Cloisonnement Tente de partitionner des instances de service en groupes en fonction des exigences de charge et de disponibilité, en isolant les défaillances pour maintenir la fonctionnalité de service.
Saga Gère la cohérence des données entre les microservices qui ont des magasins de données indépendants en garantissant que les services se mettent à jour les uns les autres via des canaux d’événements ou de messages définis. Chaque service effectue des transactions locales pour mettre à jour son propre état et publie un événement pour déclencher la prochaine transaction locale dans la saga. Si une mise à jour de service échoue, la saga exécute des transactions de compensation pour contrer les étapes de mise à jour de service précédentes. Les étapes de mise à jour de service individuelles peuvent elles-mêmes implémenter des modèles de résilience, tels que la nouvelle tentative.
Surveillance de point de terminaison d’intégrité Implémente des vérifications fonctionnelles dans une application auxquelles les outils externes peuvent accéder via des points de terminaison exposés à intervalles réguliers. Vous pouvez interpréter les réponses des points de terminaison à l’aide de métriques opérationnelles clés pour informer l’intégrité de l’application et déclencher des réponses opérationnelles, comme déclencher une alerte ou effectuer un déploiement de restauration de compensation.
Réessayer Gère les défaillances temporaires de manière élégante et transparente.
- Annuler si l’erreur est peu susceptible d’être temporaire et est peu susceptible de réussir si l’opération est à nouveau tentée.
- Réessayez si l’erreur est inhabituelle ou rare et que l’opération est susceptible de réussir en cas de tentative immédiate.
- Réessayez après un délai si l’erreur est due à une condition qui peut nécessiter un court délai de récupération, comme la connectivité réseau ou les défaillances à charge élevée. Appliquez une stratégie d’interruption appropriée à mesure que les retards de nouvelle tentative augmentent.
Limitation Contrôle la consommation des ressources utilisées par les composants d’application, en les protégeant de leur sur-utilisation. Lorsqu’une ressource atteint un seuil de charge, elle reporte les opérations de priorité inférieure et dégrade les fonctionnalités non essentielles afin que les fonctionnalités essentielles puissent continuer jusqu’à ce que les ressources suffisantes soient disponibles pour revenir à l’opération normale.

Voici quelques autres recommandations :

  • Utilisez des kits SDK fournis par le fournisseur, comme les kits de développement logiciel (SDK) Azure, pour vous connecter aux services dépendants. Utilisez des fonctionnalités de résilience intégrées au lieu d’implémenter des fonctionnalités personnalisées.

  • Appliquez une stratégie d’interruption appropriée lors de la nouvelle tentative d’appels de dépendance ayant échoué pour éviter un scénario DDoS auto-infligé.

  • Définissez des critères d’ingénierie courants pour toutes les équipes de microservices d’application afin de favoriser la cohérence et la vitesse dans l’utilisation des modèles de résilience au niveau de l’application.

  • Implémentez des modèles de résilience à l’aide de packages standardisés éprouvés, tels que Polly pour C# ou Sentinel pour Java.

  • Utilisez des ID de corrélation pour tous les événements de trace et les messages de journalisation pour les lier à une demande donnée. Retournez les ID de corrélation à l’appelant pour tous les appels, et pas seulement pour les demandes en échec.

  • Utilisez la journalisation structurée pour tous les messages de journal. Sélectionnez un récepteur de données opérationnelles unifié pour les traces d’application, les métriques et les journaux pour permettre aux opérateurs de déboguer facilement des problèmes. Pour plus d’informations, consultez Collecter, agréger et stocker des données de surveillance pour les applications cloud.

  • Assurez-vous que les données opérationnelles sont utilisées avec les exigences métier pour informer un modèle d’intégrité d’application.

Sélection du langage de programmation

Il est important de sélectionner les langages et frameworks de programmation appropriés. Ces décisions sont souvent pilotées par les ensembles de compétences ou les technologies standardisées au sein de l’organisation. Toutefois, il est essentiel d’évaluer les performances, la résilience et les fonctionnalités globales de différents langages et infrastructures.

Considérations sur la conception

  • Fonctionnalités du kit de développement. Il existe des différences dans les fonctionnalités offertes par les sdk de service Azure dans différents langages. Ces différences peuvent influencer votre choix d’un service Azure ou d’un langage de programmation. Par exemple, si Azure Cosmos DB est une option réalisable, Go n’est peut-être pas un langage de développement approprié, car il n’existe pas de KIT de développement logiciel (SDK) tiers.

  • Mises à jour des fonctionnalités. Réfléchissez à la fréquence à laquelle le SDK est mis à jour avec de nouvelles fonctionnalités pour la langue sélectionnée. Les kits SDK couramment utilisés, tels que les bibliothèques .NET et Java, sont fréquemment mis à jour. D’autres kits SDK ou kits SDK pour d’autres langages peuvent être mis à jour moins fréquemment.

  • Plusieurs langages de programmation ou frameworks. Vous pouvez utiliser plusieurs technologies pour prendre en charge différentes charges de travail composites. Toutefois, vous devez éviter l’éparse car il introduit la complexité de la gestion et les défis opérationnels.

  • Option de calcul. Les logiciels hérités ou propriétaires peuvent ne pas s’exécuter dans les services PaaS. En outre, vous ne pourrez peut-être pas inclure des logiciels hérités ou propriétaires dans des conteneurs.

Recommandations de conception

  • Évaluez toutes les sdk Azure pertinents pour les fonctionnalités dont vous avez besoin et vos langages de programmation choisis. Vérifiez l’alignement avec les exigences non fonctionnelles.

  • Optimisez la sélection des langages de programmation et des frameworks au niveau du microservice. Utilisez plusieurs technologies selon les besoins.

  • Hiérarchiser le Kit de développement logiciel (SDK) .NET pour optimiser la fiabilité et les performances. Les kits SDK .NET Azure fournissent généralement davantage de fonctionnalités et sont fréquemment mis à jour.

Étape suivante

Passez en revue les considérations relatives à la plateforme d’application.