Modifier

Partager via


Modèle d’application web moderne pour Java

Azure App Service
Azure Front Door
Cache Azure pour Redis

Cet article vous montre comment implémenter le modèle d’application web moderne. Le modèle d’application web moderne définit la façon dont vous devez moderniser les applications web dans le cloud et mettre en place une architecture orientée service. Le modèle d’application web moderne fournit une architecture, un code et des instructions de configuration préscriptifs qui s’alignent sur les principes du Framework Azure Well-Architected et s’appuient sur le modèle Reliable Web App.

Pourquoi utiliser le modèle d’application web moderne ?

Le modèle d’application web moderne permet d’optimiser les zones à forte demande de votre application web. Il fournit des conseils détaillés pour dissocier ces derniers, ce qui permet une mise à l’échelle indépendante pour l’optimisation des coûts. Cette approche vous permet d’allouer des ressources dédiées à des composants critiques, ce qui améliore les performances globales. Le découplage des services séparables peut améliorer la fiabilité en empêchant les ralentissements dans une partie de l’application d’affecter d’autres personnes et d’activer indépendamment le contrôle de version des composants d’application individuels.

Comment implémenter le modèle d’application web moderne (Modern Web App)

Cet article contient l’architecture, le code et les instructions de configuration pour implémenter le modèle d’application web moderne. Utilisez les liens suivants pour accéder aux conseils dont vous avez besoin :

  • Conseils d’architecture : Découvrez comment modulariser les composants d’application web et sélectionner les solutions PaaS (Platform as a Service) appropriées.
  • Conseils en matière de code : implémentez quatre modèles de conception pour optimiser les composants découplés : Figuier étrangleur, Nivellement de la charge basé sur une file d’attente, consommateurs concurrents et surveillance des points de terminaison d’intégrité.
  • Conseils en matière de configuration : configurez l’authentification, l’autorisation, la mise à l’échelle automatique et la conteneurisation pour les composants découplés.

Conseil

Logo GitHub Il existe une implémentation de référence (application d’exemple) du modèle d’application web moderne. Il représente l’état final de l’implémentation d’application web moderne. Il s’agit d’une application web de niveau production qui présente toutes les mises à jour du code, de l’architecture et de la configuration dont il est question dans cet article. Déployez et utilisez l’implémentation de référence pour guider votre implémentation du modèle d’application web moderne.

Conseils sur l’architecture

Le modèle d’application web moderne s’appuie sur le modèle d’application web fiable. Il nécessite quelques composants architecturaux supplémentaires à implémenter. Vous avez besoin d’une file d’attente de messages, d’une plateforme de conteneurs, d’un service de stockage et d’un registre de conteneurs (voir la figure 1).

Diagramme montrant l’architecture de référence du modèle d’application web moderne.Figure 1. Éléments architecturaux essentiels du modèle d’application web moderne.

Pour un objectif de niveau de service (SLO) supérieur, vous pouvez ajouter une deuxième région à votre architecture d’application web. Configurez votre équilibreur de charge pour acheminer le trafic vers la deuxième région pour prendre en charge une configuration active-active ou active-passive en fonction des besoins de votre entreprise. Les deux régions nécessitent les mêmes services, sauf qu’une région possède un réseau virtuel hub qui se connecte. Adoptez une topologie de réseau hub-and-spoke pour centraliser et partager des ressources, comme un pare-feu réseau. Accédez au référentiel de conteneurs via le réseau virtuel hub. Si vous avez des machines virtuelles, ajoutez un hôte bastion au réseau virtuel de type hub pour les gérer en toute sécurité (voir la figure 2).

Diagramme montrant l’architecture de modèle d’application web moderne avec la deuxième région et la topologie de réseau hub-and-spoke.Figure 2. Architecture de modèle d’application web moderne avec la deuxième région et la topologie de réseau hub-and-spoke.

Découpler l’architecture

Pour implémenter le modèle d’application web moderne, vous devez découpler l’architecture existante de l’application web. Découpler l’architecture implique de décomposer une application monolithique en services plus petits, indépendants, chacun responsable d’une fonctionnalité ou d’une fonctionnalité spécifique. Ce processus implique l’évaluation de l’application web actuelle, la modification de l’architecture et pour finir l’extraction du code de l’application web dans une plateforme de conteneurs. L’objectif est d’identifier et d’extraire systématiquement les services d’application qui bénéficient le plus d’être découplés. Pour découpler votre architecture, suivez ces recommandations :

  • Identifiez les limites de service Appliquer des principes de conception pilotés par le domaine pour identifier les contextes délimités au sein de votre application monolithique. Chaque limite de contexte représente une limite logique et peut être candidate à un service distinct. Les services qui représentent des fonctions métier distinctes et qui ont moins de dépendances sont de bons candidats au découplage.

  • Évaluer les avantages du service. Concentrez-vous sur les services qui bénéficient le plus d’une mise à l’échelle indépendante. Par exemple, une dépendance externe telle qu’un fournisseur de services de messagerie dans une application métier peut nécessiter une isolation plus grande contre l’échec. Tenez compte des services qui subissent des mises à jour ou des modifications fréquentes. Le découplage de ces services permet un déploiement indépendant et réduit le risque d’affecter d’autres parties de l’application.

  • Évaluer la faisabilité technique. Examinez l’architecture existante pour identifier les contraintes techniques et les dépendances susceptibles d’affecter le processus de découplage. Planifiez la façon dont les données sont gérées et partagées entre les services. Les services découplés doivent gérer leurs propres données et réduire au minimum l’accès direct à la base de données au-delà des limites des services.

  • Déployer des services Azure. Sélectionnez et déployez les services Azure dont vous avez besoin pour prendre en charge le service d’application web que vous souhaitez extraire. Consultez la section Sélectionnez les services Azure appropriés pour obtenir des conseils.

  • Découpler un service d’application web. Définissez des interfaces et des API claires pour permettre l’interaction des services de l’application web récemment extraits avec d’autres parties du système. Concevez une stratégie de gestion des données qui permet à chaque service de gérer ses propres données tout en garantissant la cohérence et l’intégrité. Pour connaître les stratégies de mise en œuvre spécifiques et les modèles de conception à utiliser au cours de ce processus d’extraction, reportez-vous à la section Instructions en matière de code.

  • Utiliser un stockage indépendant pour les services découplés. Chaque service découplé doit avoir ses propres magasins de données pour faciliter le contrôle de version et le déploiement. Par exemple, l’implémentation de référence sépare le service de messagerie de l’application web et élimine la nécessité pour le service d’accéder à la base de données. Au lieu de cela, le service communique l’état de remise par e-mail à l’application web via un message Azure Service Bus, et l’application web enregistre une note dans sa base de données.

  • Implémenter des pipelines de déploiement distincts pour chaque service découplé. Les pipelines de déploiement distincts permettent la mise à jour de chaque service à son propre rythme. Si différentes équipes ou organisations au sein de votre entreprise disposent de services différents, les pipelines de déploiement distincts permettent à chacune d’elles de contrôler ses propres déploiements. Utilisez des outils d’intégration continue et de livraison continue (CI/CD) tels que Jenkins, GitHub Actions ou Azure Pipelines pour configurer ces pipelines.

  • Vérifier les contrôles de sécurité. Veillez à ce que vos contrôles de sécurité soient mis à jour pour tenir compte de la nouvelle architecture, y compris les règles de pare-feu et les contrôles d’accès.

Sélectionner les services Azure appropriés

Pour chaque service Azure de votre architecture, consultez le guide de service Azure approprié dans Well-Architected Framework. Pour le modèle d’application web moderne, il vous faut un système de messagerie pour prendre en charge la messagerie asynchrone, une plateforme d’application qui prend en charge la conteneurisation et un référentiel d’images conteneur.

  • Choisir une file d’attente de messages. Une file d’attente de messages est un élément important des architectures orientées service. Elle découple les expéditeurs et les récepteurs de messages pour activer une messagerie asynchrone. Utilisez les conseils sur le choix d’un service de messagerie Azure pour en sélectionner un qui prend en charge vos besoins en matière de conception. Azure dispose de trois services de messagerie : Azure Event Grid, Azure Event Hubs et Service Bus. Commencez par Service Bus comme choix par défaut et utilisez les deux autres options si Service Bus ne répond pas à vos besoins.

    Service Cas d’usage
    Service Bus Choisissez Service Bus pour une livraison fiable, ordonnée et éventuellement transactionnelle de messages à valeur élevée dans les applications d’entreprise.
    Event Grid Choisissez Event Grid lorsque vous devez gérer efficacement un grand nombre d’événements discrets. Event Grid est évolutif pour les applications pilotées par les événements où de nombreux événements petits et indépendants (comme les modifications d’état des ressources) doivent être routés vers les abonnés dans un modèle d’abonnement à faible latence et à publication.
    Event Hubs Choisissez Event Hubs pour l’ingestion de données massives et à haut débit, telles que la télémétrie, les journaux ou l’analytique en temps réel. Event Hubs est optimisé pour les scénarios de streaming où les données en bloc doivent être ingérées et traitées en continu.
  • Implémenter un service de conteneur. Pour les parties de votre application que vous souhaitez conteneuriser, il vous faut une plateforme d’application qui prend en charge les conteneurs. Utilisez les conseils de la section Choisir un service de conteneur Azure pour vous aider à prendre votre décision. Azure a trois principaux services de conteneur : Azure Container Apps, Azure Kubernetes Service (AKS) et Azure App Service. Commencez par Container Apps comme choix par défaut et utilisez les deux autres options si Container Apps ne répond pas à vos besoins.

    Service Cas d’usage
    Applications de conteneur Choisissez Container Apps si vous avez besoin d’une plateforme serverless qui met automatiquement à l’échelle et gère les conteneurs dans les applications pilotées par les événements.
    AKS Choisissez AKS si vous voulez disposer d’un contrôle précis sur les configurations Kubernetes et de fonctionnalités avancées pour la mise à l’échelle, la mise en réseau et la sécurité.
    Web Apps pour conteneurs Choisissez Web App pour conteneurs sur App Service pour l’expérience PaaS la plus simple.
  • Implémenter un référentiel de conteneurs. Lors de l’utilisation d’un service de calcul basé sur un conteneur, il est nécessaire d’avoir un référentiel pour stocker les images conteneur. Vous pouvez utiliser un registre de conteneurs public comme Docker Hub ou un registre managé comme Azure Container Registry. Consultez les conseils de la section Présentation des registres de conteneurs dans Azure pour vous aider à prendre votre décision.

Conseils sur le code

Pour réussir à découpler et à extraire un service indépendant, vous devez mettre à jour votre code d’application web avec les modèles de conception suivants : Figuier étrangleur, Nivellement de charge basé sur la file d’attente, Consommateurs concurrents, Surveillance du point de terminaison d’intégrité et Nouvelle tentative.

Diagramme montrant le rôle des modèles de conception dans l’architecture d’un modèle d’application web moderne.Figure 3. Rôle des modèles de conception.

  1. Modèle Figuier étrangleur : le modèle Figuier étrangleur migre de façon incrémentielle les fonctionnalités d’une application monolithique vers le service découplé. Implémentez ce modèle dans l’application web principale pour migrer progressivement les fonctionnalités vers des services indépendants en dirigeant le trafic en fonction des points de terminaison.

  2. Modèle de niveau de charge basé sur la file d’attente : le modèle de nivellement de charge basé sur la file d’attente gère le flux de messages entre le producteur et le consommateur à l’aide d’une file d’attente en tant que mémoire tampon. Implémentez ce modèle sur la partie producteur du service découplé pour gérer le flux de messages de manière asynchrone à l’aide d’une file d’attente.

  3. Modèle Consommateurs concurrents : le modèle Consommateurs concurrents permet à plusieurs instances du service découplé de lire indépendamment la même file d’attente de messages et de se faire concurrence pour traiter ces derniers. Implémentez ce modèle dans le service découplé pour distribuer des tâches entre plusieurs instances.

  4. Modèle de surveillance des points de terminaison d’intégrité : le modèle de surveillance des points de terminaison d’intégrité expose les points de terminaison pour surveiller l’état et l’intégrité des différentes parties de l’application web. (4a) Implémentez ce modèle dans l’application web principale. (4b) Implémentez-le également dans le service découplé pour surveiller l’intégrité des points de terminaison.

  5. Modèle Nouvelle tentative : : le modèle Nouvelle tentative permet de gérer les défaillances transitoires en relançant les opérations susceptibles d’échouer de manière intermittente. (5a) Implémentez ce modèle sur tous les appels sortants vers d’autres services Azure dans l’application web principale, tels que les appels à la file d’attente de messages et aux points de terminaison privés. (5b) Implémentez également ce modèle dans le service découplé pour gérer les erreurs temporaires dans les appels aux points de terminaison privés.

Chaque modèle de conception offre des avantages qui s’alignent sur un ou plusieurs piliers du Well-Architected Framework (consultez le tableau suivant).

Modèle de conception Emplacement de l’implémentation Fiabilité (RE) Sécurité (SE) Optimisation des coûts (CO) Excellence opérationnelle (OE) Efficacité des performances (PE) Prise en charge des principes de l’infrastructure bien architecte
Modèle Figuier étrangleur Application web principale RE :08
CO :07
CO :08
OE :06
OE :11
Modèle de nivellement de charge basé sur une file d’attente Producteur de services découplés RE :06
RE :07
CO :12
PE :05
Modèle Consommateurs concurrents Service découplé RE :05
RE :07
CO :05
CO :07
PE :05
PE :07
Modèle de supervision des points de terminaison d’intégrité Application web principale & service découplé RE :07
RE :10
OE :07
PE :05
Modèle de nouvelle tentative Application web principale & service découplé RE :07

Implémenter le modèle Figuier étrangleur

Utilisez le modèle de fig Strangler pour migrer progressivement les fonctionnalités de la base de code monolithique vers de nouveaux services indépendants. Extrayez de nouveaux services à partir de la base de code monolithique existante et modernisez les parties critiques de l’application web. Pour implémenter le modèle Figuier étrangleur, suivez les recommandations ci-dessous :

  • Configurez une couche de routage. Dans la base de code d’application web monolithique, implémentez une couche de routage qui dirige le trafic en fonction des points de terminaison. Utilisez la logique de routage personnalisée si nécessaire pour gérer des règles métier spécifiques pour diriger le trafic. Par exemple, si vous avez un /users point de terminaison dans votre application monolithique et que vous avez déplacé cette fonctionnalité vers le service découplé, la couche de routage dirige toutes les requêtes vers /users le nouveau service.

  • Gérer le déploiement des fonctionnalités. Implémentez des indicateurs de fonctionnalité et un déploiement intermédiaire pour déployer progressivement les services découplés. Le routage d’application monolithique existant doit contrôler le nombre de demandes reçues par les services découplés. Commencez par un petit pourcentage de demandes et augmentez l’utilisation au fur et à mesure que vous acquérez de la confiance dans sa stabilité et ses performances.

    Par exemple, l’implémentation de référence extrait la fonctionnalité de remise des e-mails dans un service autonome, qui peut être introduite progressivement pour gérer une plus grande partie des demandes d’envoi de courriers électroniques contenant des guides de support Contoso. Comme le nouveau service prouve sa fiabilité et ses performances, il peut éventuellement assumer l’ensemble des responsabilités de messagerie à partir du monolithe, en effectuant la transition.

  • Utiliser un service de façade (si nécessaire). Un service de façade est utile lorsqu’une demande unique doit interagir avec plusieurs services ou lorsque vous souhaitez cacher au client la complexité du système sous-jacent. Toutefois, si le service découplé n’a pas d’API publiques, un service de façade peut ne pas être nécessaire.

    Dans la base de code de l’application web monolithique, implémentez un service de façade pour acheminer les requêtes vers le serveur principal approprié (monolithe ou microservice). Dans le nouveau service découplé, il faut s’assurer que le nouveau service peut traiter les demandes de manière indépendante lorsqu’on y accède par la façade.

Implémenter le modèle de nivellement de la charge basé sur une file d’attente

Implémentez le modèle de nivellement de charge basé sur la file d’attente sur la partie producteur du service découplé pour gérer de manière asynchrone les tâches qui n’ont pas besoin de réponses immédiates. Ce modèle améliore la réactivité et l’évolutivité globales du système en utilisant une file d’attente pour gérer la distribution de la charge de travail. Il permet au service découplé de traiter les demandes à un rythme constant. Pour implémenter efficacement ce modèle, suivez ces recommandations :

  • Utiliser une mise en file d’attente des messages non bloquante. Assurez-vous que le processus qui envoie des messages à la file d’attente ne bloque pas les autres processus en attendant que le service découplé gère les messages dans la file d’attente. Si le processus nécessite le résultat de l’opération de service découplé, implémentez une autre façon de gérer la situation en attendant que l’opération mise en file d’attente se termine. Par exemple, dans Spring Boot, vous pouvez utiliser la StreamBridge classe pour publier de manière asynchrone des messages dans la file d’attente sans bloquer le thread appelant (voir l’exemple de code) :

    private final StreamBridge streamBridge;
    
    public SupportGuideQueueSender(StreamBridge streamBridge) {
        this.streamBridge = streamBridge;
    }
    
    // Asynchronously publish a message without blocking the calling thread
    @Override
    public void send(String to, String guideUrl, Long requestId) {
        EmailRequest emailRequest = EmailRequest.newBuilder()
                .setRequestId(requestId)
                .setEmailAddress(to)
                .setUrlToManual(guideUrl)
                .build();
    
        log.info("EmailRequest: {}", emailRequest);
    
        var message = emailRequest.toByteArray();
        streamBridge.send(EMAIL_REQUEST_QUEUE, message);
    
        log.info("Message sent to the queue");
    }
    

    Cet exemple Java utilise StreamBridge pour envoyer des messages de manière asynchrone. Cette approche garantit que l’application principale reste réactive et peut gérer d’autres tâches simultanément, tandis que le service découplé traite les demandes mises en file d’attente à un débit raisonnable.

  • Implémenter la nouvelle tentative et la suppression de messages. Implémentez un mécanisme pour effectuer une nouvelle tentative de traitement des messages mis en file d’attente qui ne peuvent pas être traités correctement. Si les échecs persistent, ces messages doivent être supprimés de la file d’attente. Par exemple, Service Bus dispose de fonctionnalités intégrées de nouvelle tentative et de file d’attente de lettres mortes.

  • Configurer le traitement des messages idempotents. La logique qui traite les messages de la file d’attente doit être idempotente pour gérer les cas où un message peut être traité plusieurs fois. Dans Spring Boot, vous pouvez utiliser @StreamListener ou @KafkaListener utiliser un identificateur de message unique pour empêcher le traitement en double. Vous pouvez également organiser le processus métier pour fonctionner dans une approche fonctionnelle avec Spring Cloud Stream, où la consume méthode est définie de manière à produire le même résultat lorsqu’elle s’exécute à plusieurs reprises. Lisez Spring Cloud Stream avec Service Bus pour obtenir une liste plus poussée des paramètres qui gèrent le comportement de consommation des messages.

  • Gérer les modifications apportées à l’expérience. Le traitement asynchrone peut empêcher l’achèvement immédiat des tâches. Les utilisateurs doivent être informés du fait que leur tâche est encore en cours de traitement afin de définir des attentes adéquates et d’éviter toute confusion. Utilisez des signaux visuels ou des messages pour indiquer qu’une tâche est en cours. Offrez aux utilisateurs la possibilité de recevoir des notifications lorsque leur tâche est effectuée, par exemple un e-mail ou une notification Push.

Implémenter le modèle Consommateurs concurrents

Implémentez le modèle Consommateurs concurrents dans le service découplé pour gérer les tâches entrantes à partir de la file d’attente de messages. Ce modèle implique la distribution de tâches entre plusieurs instances de services découplés. Ces services traitent les messages de la file d’attente, améliorent l’équilibrage de charge et renforcent la capacité du système pour gérer les requêtes simultanées. Le modèle Consommateurs concurrents est efficace dans les cas suivants :

  • La séquence de traitement des messages n’est pas cruciale.
  • La file d’attente n’est pas affectée par les messages malformés.
  • L’opération de traitement est idempotente, ce qui signifie qu’elle peut être appliquée plusieurs fois sans modifier le résultat au-delà de l’application initiale.

Pour implémenter le modèle Consommateurs concurrents, suivez ces recommandations :

  • Gérez les messages simultanés. Lorsque vous recevez des messages à partir d’une file d’attente, assurez-vous que votre système est mis à l’échelle de manière prévisible en configurant l’accès concurrentiel pour qu’il corresponde à la conception de votre système. Vos résultats de test de charge vous aident à déterminer le nombre approprié de messages simultanés à gérer et vous pouvez commencer à partir d’un pour mesurer l’exécution du système.

  • Désactivez la prérécupération. Désactivez la prérécupération des messages afin que les consommateurs récupèrent uniquement les messages lorsqu’ils sont prêts.

  • Utilisez des modes de traitement des messages fiables. Utilisez un mode de traitement fiable, comme PeekLock (ou son équivalent), qui effectue automatiquement une nouvelle tentative de traitement des messages qui échouent. Ce mode améliore la fiabilité par rapport aux méthodes de suppression prioritaire. Si un collaborateur ne parvient pas à gérer un message, un autre doit être en mesure de le traiter sans erreurs, même si le message est traité plusieurs fois.

  • Implémentez la gestion des erreurs Acheminer les messages mal formés ou non traitables vers une file d’attente de lettres mortes distincte. Cette conception permet d’éviter les traitements répétitifs. Par exemple, vous pouvez détecter les exceptions pendant le traitement du message et déplacer le message problématique vers la file d’attente distincte. Pour Service Bus, les messages sont déplacés vers la file d’attente du leter mort après un nombre spécifié de tentatives de remise ou lors d’un rejet explicite par l’application.

  • Gérez les messages désordonnés. Concevez des consommateurs pour traiter les messages qui arrivent dans le désordre. L’existence de plusieurs consommateurs parallèles signifie qu’ils sont susceptibles de traiter les messages dans le désordre.

  • Mise à l’échelle en fonction de la longueur de la file d’attente. Les services qui consomment des messages provenant d’une file d’attente doivent s’adapter automatiquement à la longueur de cette dernière. La mise à l’échelle automatique basée sur la mise à l’échelle permet un traitement efficace des pics de messages entrants.

  • Utilisez la file d’attente de réponses de messages. Si le système requiert des notifications pour le traitement post-message, configurez une file d’attente de réponse ou de réponse dédiée. Cette configuration sépare la messagerie opérationnelle des processus de notification.

  • Utilisez des service sans état. Envisagez d’utiliser des services sans état pour traiter les demandes à partir d’une file d’attente. Cela facilite la mise à l’échelle et l’utilisation efficace des ressources.

  • Configurez la journalisation. Intégrez la journalisation et la gestion des exceptions spécifiques dans le flux de travail de traitement des messages. Concentrez-vous sur la capture d’erreurs de sérialisation et la mise en place de ces messages problématiques vers un mécanisme de lettres mortes. Ces journaux fournissent des informations précieuses pour la résolution des problèmes.

Par exemple, l’implémentation de référence utilise le modèle Consommateurs concurrents sur un service sans état exécuté dans Container Apps pour traiter les demandes de remise par e-mail à partir d’une file d’attente Service Bus.

Le processeur enregistre les détails du traitement des messages, ce qui facilite la résolution des problèmes et la surveillance. Il capture les erreurs de désérialisation et fournit des insights nécessaires lors du débogage du processus. Le service est mis à l’échelle au niveau du conteneur, ce qui permet une gestion efficace des pics de messages en fonction de la longueur de la file d’attente (voir le code suivant).

@Configuration
public class EmailProcessor {

    private static final Logger log = LoggerFactory.getLogger(EmailProcessor.class);

    @Bean
    Function<byte[], byte[]> consume() {
        return message -> {

            log.info("New message received");

            try {
                EmailRequest emailRequest = EmailRequest.parseFrom(message);
                log.info("EmailRequest: {}", emailRequest);

                EmailResponse emailResponse = EmailResponse.newBuilder()
                        .setEmailAddress(emailRequest.getEmailAddress())
                        .setUrlToManual(emailRequest.getUrlToManual())
                        .setRequestId(emailRequest.getRequestId())
                        .setMessage("Email sent to " + emailRequest.getEmailAddress() + " with URL to manual " + emailRequest.getUrlToManual())
                        .setStatus(Status.SUCCESS)
                        .build();

                return emailResponse.toByteArray();

            } catch (InvalidProtocolBufferException e) {
                throw new RuntimeException("Error parsing email request message", e);
            }
        };
    }
}

Implémenter le modèle Surveillance de point de terminaison d’intégrité

Implémentez le modèle Surveillance des point de terminaison d’intégrité dans le code principal de l’application et le code de service découplé pour surveiller l’intégrité des points de terminaison d’application. Les orchestrateurs tels qu’AKS ou Container Apps peuvent interroger ces points de terminaison pour vérifier l’intégrité du service et redémarrer des instances non saines. Spring Boot fournit une prise en charge intégrée des contrôles d’intégrité avec Spring Boot Actuator, qui peut exposer des points de terminaison de contrôle d’intégrité pour les dépendances clés telles que les bases de données, les répartiteurs de messages et les systèmes de stockage. Pour implémenter le modèle de Surveillance de point de terminaison d’intégrité, suivez ces recommandations :

  • Implémentez des contrôles d’intégrité. Utilisez l’actionneur Spring Boot pour fournir des points de terminaison de contrôle d’intégrité. Spring Boot Actuator expose un point de terminaison /actuator/health qui inclut des indicateurs d’intégrité intégrés et des vérifications personnalisées pour différentes dépendances. Pour activer le point de terminaison d’intégrité, ajoutez la spring-boot-starter-actuator dépendance dans votre pom.xml ou build.gradle fichier.

    <!-- Add Spring Boot Actuator dependency -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    

    Configurez le point de terminaison d’intégrité dans application.properties l’implémentation de référence : txt management.endpoints.web.exposure.include=metrics,health,info,retry,retryevents

  • Validez les dépendances. Spring Boot Actuator inclut des indicateurs d’intégrité pour différentes dépendances telles que les bases de données, les répartiteurs de messages (RabbitMQ ou Kafka) et les services de stockage. Pour valider la disponibilité des services Azure tels que Stockage Blob Azure ou Service Bus, utilisez des plug-ins de communauté tels que les intégrations Azure Spring Apps ou Micrometer, qui fournissent des indicateurs d’intégrité pour ces services. Si des vérifications personnalisées sont nécessaires, vous pouvez les implémenter en créant un haricot personnalisé HealthIndicator .

    import org.springframework.boot.actuate.health.Health;
    import org.springframework.boot.actuate.health.HealthIndicator;
    import org.springframework.stereotype.Component;
    
    @Component
    public class CustomAzureServiceBusHealthIndicator implements HealthIndicator {
        @Override
        public Health health() {
            // Implement your health check logic here (e.g., ping Service Bus)
            boolean isServiceBusHealthy = checkServiceBusHealth();
            return isServiceBusHealthy ? Health.up().build() : Health.down().build();
        }
    
        private boolean checkServiceBusHealth() {
            // Implement health check logic (pinging or connecting to the service)
            return true; // Placeholder, implement actual logic
        }
    }
    
  • Configurer des ressources Azure. Configurez la ressource Azure pour utiliser les URL de contrôle d’intégrité de l’application pour confirmer la durée de vie et la préparation. Par exemple, vous pouvez utiliser Terraform pour utiliser les URL de contrôle d’intégrité pour confirmer la durée de vie et la préparation des applications déployées sur Container Apps. Pour plus d’informations, consultez Sondes d’intégrité dans Container Apps.

Implémenter le modèle Nouvelle tentative

Le modèle Nouvelle tentative permet aux applications de récupérer à partir d’erreurs temporaires. Le modèle Nouvelle tentative est au cœur du modèle d’application web fiable, votre application web devrait donc déjà l’utiliser. Appliquez le modèle Nouvelle tentative aux demandes réalisées aux systèmes de messagerie et à celles émises par les services découplés que vous extrayez de l’application web. Pour implémenter le modèle Nouvelle tentative, suivez ces recommandations :

  • Configurer les options de nouvelles tentatives. Lors de l’intégration à une file d’attente de messages, veillez à configurer le client responsable des interactions avec la file d’attente avec les paramètres de nouvelle tentative appropriés. Spécifiez des paramètres commet le nombre maximal de nouvelles tentatives, le délai entre elles et le délai maximal.

  • Utilisez un backoff exponentiel. Implémentez la stratégie d’interruption exponentielle pour les nouvelles tentatives. Cela implique d’augmenter le temps entre chaque nouvelle tentative de manière exponentielle, pour réduire la charge sur le système pendant les périodes de taux d’échec élevés.

  • Utilisez la fonctionnalité de nouvelle tentative du Kit de développement logiciel (SDK). Pour les services avec des kits SDK spécialisés, tels que Service Bus ou Stockage Blob, utilisez les mécanismes de nouvelle tentative intégrés. Les mécanismes de nouvelle tentative intégrés sont optimisés pour les cas d’utilisation classiques du service et peuvent gérer les nouvelles tentatives de manière plus efficace avec moins une configuration moins poussée de votre part.

  • Adoptez des bibliothèques de résilience standard pour les clients HTTP. Pour les clients HTTP, vous pouvez utiliser Resilience4* avec RestTemplate ou WebClient de Spring pour gérer les nouvelles tentatives dans les communications HTTP. RestTemplate de Spring peut être encapsulé avec la logique de nouvelle tentative de Resilience4j pour gérer efficacement les erreurs HTTP temporaires.

  • Gérez le verrouillage des messages. Pour les systèmes basés sur des messages, implémentez des stratégies de gestion des messages qui prennent en charge les nouvelles tentatives sans perte de données, notamment l’utilisation de modes « peek-lock » lorsqu’ils sont disponibles. Assurez-vous que les messages qui échouent sont correctement relancés et placés dans une file d’attente de lettres mortes en cas d’échecs répétés.

Conseils sur la configuration

Les sections suivantes contiennent des conseils sur l’implémentation des mises à jour de la configuration. Chaque section s’aligne sur un ou plusieurs piliers de Well-Architected Framework.

Configuration Fiabilité (RE) Sécurité (SE) Optimisation des coûts (CO) Excellence opérationnelle (OE) Efficacité des performances (PE) Prise en charge des principes de l’infrastructure bien architecte
Configurer l’authentification et l’autorisation SE :05
OE :10
Implémenter une mise à l’échelle automatique indépendante RE :06
CO :12
PE :05
Conteneuriser le déploiement d’un service CO :13
PE :09
PE :03

Configurer l’authentification et l’autorisation

Pour configurer l’authentification et l’autorisation sur de nouveaux services Azure (identités de charge de travail) que vous ajoutez à l’application web, suivez ces recommandations :

  • Utilisez des identités managées pour chaque nouveau service. Chaque service indépendant doit avoir sa propre identité et utiliser des identités managées pour l’authentification de service à service. Les identités managées éliminent le besoin de gérer les informations d’identification dans votre code et réduisent le risque de fuite d’informations d’identification. Cela vous permet d’éviter de placer des informations sensibles telles que des chaîne de connexion dans vos fichiers de code ou de configuration.

  • Octroyez des privilèges minimum à chaque nouveau service. Attribuez uniquement les autorisations nécessaires à chaque nouvelle identité du service. Par exemple, si une identité doit uniquement envoyer (push) à un registre de conteneurs, ne lui accordez pas d’autorisations d’extraction. Passez en revue ces autorisations régulièrement et ajustez-les si nécessaire. Utilisez différentes identités pour différents rôles, tels que le déploiement et l’application. Cela limite les dommages potentiels si une identité est compromise.

  • Adoptez l’infrastructure en tant que code (IaC). Utilisez Bicep ou des outils IaC similaires comme Terraform pour définir et gérer vos ressources cloud. IaC garantit une application cohérente des configurations de sécurité dans vos déploiements et vous permet de contrôler la version de votre configuration d’infrastructure.

Pour configurer l’authentification et l’autorisation sur les utilisateurs (identités utilisateur), suivez ces recommandations :

  • Accordez aux utilisateurs des privilèges minimum. Comme pour les services, assurez-vous que les utilisateurs disposent uniquement des autorisations dont ils ont besoin pour effectuer leurs tâches. Examinez et ajustez régulièrement ces autorisations.

  • Effectuez des audits de sécurité réguliers. Examinez et auditez régulièrement votre configuration de sécurité. Recherchez les mauvaises configurations ou les autorisations inutiles et remédiez-y immédiatement.

L’implémentation de référence utilise IaC pour affecter des identités managées à des services ajoutés et des rôles spécifiques à chaque identité. Il définit les rôles et l’accès aux autorisations pour le déploiement en définissant des rôles pour l’envoi (push) et l’extraction (voir le code suivant).

resource "azurerm_role_assignment" "container_app_acr_pull" {
  principal_id         = var.aca_identity_principal_id
  role_definition_name = "AcrPull"
  scope                = azurerm_container_registry.acr.id
}

resource "azurerm_user_assigned_identity" "container_registry_user_assigned_identity" {
  name                = "ContainerRegistryUserAssignedIdentity"
  resource_group_name = var.resource_group
  location            = var.location
}

resource "azurerm_role_assignment" "container_registry_user_assigned_identity_acr_pull" {
  scope                = azurerm_container_registry.acr.id
  role_definition_name = "AcrPull"
  principal_id         = azurerm_user_assigned_identity.container_registry_user_assigned_identity.principal_id
}


# For demo purposes, allow current user access to the container registry
# Note: when running as a service principal, this is also needed
resource "azurerm_role_assignment" "acr_contributor_user_role_assignement" {
  scope                = azurerm_container_registry.acr.id
  role_definition_name = "Contributor"
  principal_id         = data.azuread_client_config.current.object_id
}

Configurez la mise à l’échelle automatique indépendante

Le modèle d’application web moderne commence par fractionner l’architecture monolithique et introduit le découplage des services. Le découplage d’une architecture d’application web vous permet de mettre à l’échelle des services découplés indépendamment. La mise à l’échelle des services Azure pour prendre en charge un service d’application web indépendant, plutôt qu’une application web entière, optimise les coûts de mise à l’échelle tout en répondant aux demandes. Pour la mise à l’échelle automatique de conteneurs, suivez ces recommandations :

  • Utilisez des service sans état. Vérifiez que vos services sont sans état. Si votre application web contient un état de session in-process, externalisez-la vers un cache distribué tel que Redis ou une base de données comme SQL Server.

  • Configurer des règles de mise à l’échelle automatique. Utilisez les configurations de mise à l’échelle automatique qui offrent le contrôle le plus économique sur vos services. Pour les services conteneurisés, la mise à l’échelle basée sur les événements, telle que Kubernetes Event-Driven Autoscaler (KEDA) fournit souvent un contrôle granulaire, ce qui vous permet de mettre à l’échelle en fonction des métriques d’événement. Container Apps et AKS prennent en charge KEDA. Pour les services qui ne prennent pas en charge KEDA, tels qu’App Service, utilisez les fonctionnalités de mise à l’échelle automatique fournies par la plateforme elle-même. Ces fonctionnalités incluent souvent la mise à l’échelle en fonction des règles basées sur des métriques ou du trafic HTTP.

  • Configurez le nombre minimal de réplicas. Pour éviter un démarrage à froid, configurez les paramètres de mise à l’échelle automatique pour conserver un minimum d’un réplica. On parle de démarrage à froid lorsqu’on initialise un service à partir d’un état arrêté, ce qui entraîne souvent une réponse différée. Si la réduction des coûts constitue une priorité et que vous pouvez tolérer les retards d’un démarrage à froid, définissez le nombre minimal de réplicas sur 0 lors de la configuration de la mise à l’échelle automatique.

  • Configurez une période de recharge. Appliquez une période de recharge appropriée pour introduire un délai entre les événements de mise à l’échelle. L’objectif est d’éviter des activités de mise à l’échelle excessives déclenchées par des pics de charge temporaires.

  • Configurez la mise à l’échelle basée sur la file d’attente. Si votre application utilise une file d’attente de messages comme Service Bus, configurez vos paramètres de mise à l’échelle automatique pour qu’ils s’adaptent en fonction de la longueur de la file d’attente avec des messages de demande. Le scaler vise à maintenir un réplica du service pour chaque message N dans la file d’attente (arrondi).

Par exemple, l’implémentation de référence utilise le scaler KEDA Service Bus pour mettre automatiquement à l’échelle l’application conteneur en fonction de la longueur de la file d’attente Service Bus. La règle de mise à l’échelle, nommée service-bus-queue-length-rule, ajuste le nombre de réplicas de service en fonction du nombre de messages dans la file d’attente Service Bus spécifiée. Le messageCount paramètre est défini sur 10, ce qui signifie que le scaler ajoute un réplica pour chaque 10 messages de la file d’attente. Les réplicas maximum (max_replicas) sont définis sur 10 et les réplicas minimaux sont implicitement 0, sauf en cas de substitution, ce qui permet au service de passer à zéro lorsqu’il n’y a pas de messages dans la file d’attente. Le chaîne de connexion de la file d’attente Service Bus est stocké en toute sécurité en tant que secret dans Azure, nommé azure-servicebus-connection-string, utilisé pour authentifier le scaler auprès de Service Bus.

    max_replicas = 10
    min_replicas = 1

    custom_scale_rule {
      name             = "service-bus-queue-length-rule"
      custom_rule_type = "azure-servicebus"
      metadata = {
        messageCount = 10
        namespace    = var.servicebus_namespace
        queueName    = var.email_request_queue_name
      }
      authentication {
        secret_name       = "azure-servicebus-connection-string"
        trigger_parameter = "connection"
      }
    }

Conteneuriser le déploiement d’un service

La conteneurisation implique que toutes les dépendances nécessaires au fonctionnement de l’application sont encapsulées dans une image légère qui peut être déployée de manière fiable sur un large éventail d’hôtes. Pour conteneuriser le déploiement, suivez ces recommandations :

  • Identifiez les limites de domaine. Commencez par identifier les limites de domaine au sein de votre application monolithique. Cela permet de déterminer quelles parties de l’application vous pouvez extraire dans des services distincts.

  • Créez des images Docker. Lors de la création d’images Docker pour vos services Java, utilisez des images de base OpenJDK officielles. Ces images contiennent uniquement l’ensemble minimal de packages nécessaires à l’exécution de Java, ce qui réduit à la fois la taille du package et la surface d’attaque.

  • Utilisez Dockerfiles multiphases. Utilisez des fichiers Dockerfiles à plusieurs étapes pour séparer les ressources au moment de la génération de l’image conteneur runtime. Vous pouvez ainsi gardez vos images de production petites et sécurisées. Vous pouvez également utiliser un serveur de build préconfiguré et copier le fichier jar dans l’image conteneur.

  • Exécutez en tant qu’utilisateur non-root. Exécutez vos conteneurs Java en tant qu’utilisateur non-root (par le biais du nom d’utilisateur ou de l’UID, $APP_UID) pour s’aligner sur le principe du privilège minimum. Cela limite les effets potentiels d’un conteneur compromis.

  • Écouter sur le port 8080. Lors de l’exécution en tant qu’utilisateur non-racine, configurez votre application pour écouter sur le port 8080. Il s’agit d’une convention commune pour les utilisateurs non-racine.

  • Encapsuler les dépendances. Vérifiez que toutes les dépendances nécessaires au fonctionnement de l’application sont encapsulées dans l’image conteneur Docker. L’encapsulation permet le déploiement fiable de l’application sur un large éventail d’hôtes.

  • Choisir les images de base appropriées. L’image de base que vous choisissez dépend de votre environnement de déploiement. Si vous effectuez un déploiement sur Container Apps, par exemple, vous devez utiliser des images Docker Linux.

L’implémentation de référence illustre un processus de génération Docker pour le conteneurisation d’une application Java. Ce fichier Dockerfile utilise une build à étape unique avec l’image de base OpenJDK (mcr.microsoft.com/openjdk/jdk:17-ubuntu), qui fournit l’environnement d’exécution Java nécessaire.

Le fichier Dockerfile comprend les étapes suivantes :

  1. Déclaration de volume : un volume temporaire (/tmp) est défini, ce qui permet un stockage de fichiers temporaire distinct du système de fichiers principal du conteneur.
  2. Copie d’artefacts : le fichier JAR de l’application (email-processor.jar) est copié dans le conteneur, ainsi que l’agent Application Insights (applicationinsights-agent.jar) pour la surveillance.
  3. Définition du point d’entrée : le conteneur est configuré pour exécuter l’application avec l’agent Application Insights activé, à l’aide de java -javaagent la surveillance de l’application pendant l’exécution.

Ce fichier Dockerfile conserve l’image maigre en incluant uniquement les dépendances d’exécution, adaptées aux environnements de déploiement tels que Container Apps, qui prennent en charge les conteneurs Linux.

# Use OpenJDK 17 base image on Ubuntu as the foundation
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu

# Define a volume to allow temporary files to be stored separately from the container's main file system
VOLUME /tmp

# Copy the packaged JAR file into the container
COPY target/email-processor.jar app.jar

# Copy the Application Insights agent for monitoring
COPY target/agent/applicationinsights-agent.jar applicationinsights-agent.jar

# Set the entry point to run the application with the Application Insights agent
ENTRYPOINT ["java", "-javaagent:applicationinsights-agent.jar", "-jar", "/app.jar"]

Déployer l’implémentation de référence

Déployez l’implémentation de référence du modèle d’application web moderne pour Java. Le référentiel contient des instructions pour le développement et le déploiement en production. Après le déploiement, vous pouvez simuler et observer les modèles de conception.

Diagramme montrant l’architecture de l’implémentation de référence.Figure 3 : Architecture de l’implémentation de référence. Téléchargez un fichier Visio de cette architecture.