Modifier

Partager via


Modèle d’application web moderne pour Java

Azure App Service
Azure Service Bus

Cet article explique comment implémenter le modèle d’application web moderne. Le modèle d’application web moderne définit comment moderniser les applications web cloud et introduire une architecture orientée service. Le modèle fournit des instructions d’architecture, de code et de configuration prédéfinies qui s’alignent sur les principes des Azure Well-Architected Framework. Ce modèle s’appuie sur le modèle Reliable Web App.

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

Le modèle d’application web moderne vous permet d’optimiser les zones à forte demande de votre application web. Il fournit des instructions détaillées pour découpler ces zones afin d’activer la 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. Il permet également le contrôle de version indépendant des composants d’application individuels.

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

Cet article contient des conseils sur l’implémentation du modèle d’application web moderne. Utilisez les liens suivants pour accéder aux conseils spécifiques dont vous avez besoin :

  • Conseils sur l’architecture. Découvrez comment modulariser les composants d’application web et sélectionner les solutions PaaS (Platform as a Service) appropriées.
  • Guide du code. Implémentez quatre modèles de conception pour optimiser les composants découplés : Strangler Fig, Queue-Based Niveau de charge, Consommateurs concurrents et Surveillance des points de terminaison d’intégrité.
  • Guide 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 (exemple d’application) 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 propose toutes les mises à jour de code, d’architecture et de configuration décrites 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. 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, comme illustré dans le diagramme suivant :

Diagramme montrant l’architecture de base 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 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. Utilisez une topologie de réseau hub-and-spoke pour centraliser et partager des ressources, telles qu’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 hub pour les gérer avec une sécurité renforcée. Le diagramme suivant illustre cette architecture :

Diagramme montrant l’architecture de modèle d’application web moderne avec une deuxième région.

Dissocier l’architecture

Pour implémenter le modèle d’application web moderne, vous devez découpler l’architecture existante de l’application web. Le découplage de l’architecture implique de décomposer une application monolithique en services plus petits, indépendants, chacun responsable d’une fonctionnalité ou d’une fonction spécifique. Ce processus inclut l’évaluation de l’application web actuelle, la modification de l’architecture et, enfin, l’extraction du code de l’application web dans une plateforme de conteneur. 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 du service. Appliquez des principes de conception pilotés par le domaine pour identifier les contextes délimités au sein de votre application monolithique. Chaque contexte délimité représente une limite logique et est un candidat au découplage. Les services qui représentent des fonctions métier distinctes et qui ont moins de dépendances sont de bons candidats.

  • É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 gestion et le partage de donné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 envisagez d’extraire. Pour obtenir des conseils, consultez la Sélectionner les services Azure appropriés section de cet article.

  • Dissociez le service d’application web. Définissez des interfaces claires et des API que les services d’application web nouvellement extraits peuvent utiliser pour interagir 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, mais garantit la cohérence et l’intégrité. Pour obtenir des stratégies d’implémentation spécifiques et des modèles de conception à utiliser pendant ce processus d’extraction, consultez la section Guide du code.

  • Utiliser un stockage indépendant pour les services découplés. Pour simplifier le contrôle de version et le déploiement, assurez-vous que chaque service découplé possède ses propres magasins de données. 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é. Si vous implémentez des pipelines de déploiement distincts, chaque service peut être mis à jour selon sa propre planification. Si différentes équipes ou organisations au sein de votre entreprise possèdent différents services, l’utilisation de pipelines de déploiement distincts permet à chaque équipe 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 composant 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 et utilisez l’une des 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 dans lesquelles 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 de publication à faible latence.
    Event Hubs Choisissez Event Hubs pour l’ingestion massive et à haut débit de données, comme la télémétrie, les journaux ou l’analytique en temps réel. Event Hubs est optimisé pour les scénarios de diffusion en continu dans lesquels les données en bloc doivent être ingérées et traitées en continu.
  • Implémenter un service de conteneur. Pour les éléments de votre application que vous souhaitez conteneuriser, vous avez besoin d’une plateforme d’application qui prend en charge les conteneurs. Le Choisir un service de conteneur Azure conseils peuvent vous aider à en sélectionner un. Azure a trois principaux services de conteneur : Azure Container Apps, Azure Kubernetes Service (AKS) et Azure App Service. Commencez par Container Apps et utilisez l’une des 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é.
    Application web pour conteneurs Choisissez Web App pour conteneurs dans App Service pour l’expérience PaaS la plus simple.
  • Implémenter un référentiel de conteneurs. Lorsque vous utilisez un service de calcul basé sur un conteneur, vous devez disposer d’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. L'Présentation des registres de conteneurs dans Azure aide peut vous aider à en choisir un.

Conseils sur le code

Pour découpler et extraire un service indépendant, vous devez mettre à jour votre code d’application web avec les modèles de conception suivants : Strangler Fig, Queue-Based Load Leveling, Concurrent Consumers, Health Endpoint Monitoring et Retry. Le diagramme suivant montre les rôles de ces modèles :

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

  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 d’un service découplé de lire indépendamment à partir de la même file d’attente de messages et de traiter les messages. Implémentez ce modèle dans le service découplé pour distribuer des tâches entre plusieurs instances.

  4. modèle de supervision 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érents composants 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émenter ce modèle dans l’application web principale, sur tous les appels sortants vers d’autres services Azure, 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 des piliers de l’infrastructure Well-Architected. Le tableau suivant fournit des détails.

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 et service découplé RE :07
RE :10
OE :07
PE :05
Modèle de nouvelle tentative Application web principale et service découplé RE :07

Implémenter le modèle Figuier étrangleur

Utilisez le modèle Strangler Fig 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 point de terminaison /users dans votre application monolithique et que vous déplacez cette fonctionnalité vers le service découplé, la couche de routage dirige toutes les requêtes vers /users vers 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 requêtes et augmentez l’utilisation au fil du temps, car vous gagnez en confiance dans la stabilité et les performances du service.

    Par exemple, l’implémentation de référence extrait la fonctionnalité de remise par e-mail dans un service autonome. Le service peut être introduit progressivement pour gérer un pourcentage plus important des demandes d’envoi d’e-mails 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 seule requête doit interagir avec plusieurs services ou lorsque vous souhaitez masquer la complexité du système sous-jacent du client. 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). Assurez-vous que le nouveau service découplé peut gérer les requêtes indépendamment lorsqu’elle est accessible via 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 cohérent. 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 d’autres processus pendant qu’il attend 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 classe StreamBridge pour publier de manière asynchrone des messages dans la file d’attente sans bloquer le thread appelant :

    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 pendant que le service découplé traite les demandes en file d’attente à un débit gérable.

  • 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 dans lesquels 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. Pour obtenir la liste des paramètres qui gèrent le comportement de la consommation de messages, consultez Spring Cloud Stream avec Service Bus.

  • Gérer les modifications apportées à l’expérience utilisateur. Lorsque vous utilisez un traitement asynchrone, les tâches peuvent ne pas être effectuées immédiatement. Pour définir des attentes et éviter toute confusion, assurez-vous que les utilisateurs savent quand leurs tâches sont toujours traitées. 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. Le modèle améliore l’équilibrage de charge et augmente 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 après l’application initiale.

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

  • Gérez les messages simultanés. Lorsque les services reçoivent des messages d’une file d’attente, assurez-vous que votre système est mis à l’échelle de façon prévisible en configurant l’accès concurrentiel pour qu’il corresponde à la conception du système. Les résultats des tests de charge peuvent vous aider à déterminer le nombre approprié de messages simultanés à gérer. 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, tel que Peek-Lock, qui retente automatiquement les messages qui échouent. Ce mode offre plus de fiabilité que les méthodes de suppression première. 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 traités vers une file d’attente de lettres mortes distincte. Cette conception permet d’éviter les traitements répétitifs. Par exemple, vous pouvez intercepter des exceptions pendant le traitement des messages et déplacer des messages problématiques vers la file d’attente distincte. Avec 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. Lorsque vous avez plusieurs consommateurs parallèles, ils peuvent traiter les messages hors de commande.

  • Mise à l’échelle en fonction de la longueur de la file d’attente. Les services qui consomment des messages à partir d’une file d’attente doivent être mis à l’échelle automatiquement en fonction de la longueur de la file d’attente. La mise à l’échelle automatique basée sur la mise à l’échelle permet un traitement efficace des pics de messages entrants.

  • Utilisez une file d’attente de réponse aux messages. Si votre système nécessite 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 permet une mise à l’échelle facile et une 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.

L’implémentation de référence utilise le modèle Consommateurs concurrents sur un service sans état qui s’exécute dans Container Apps pour traiter les demandes de remise de courrier à partir d’une file d’attente Service Bus.

Le processeur enregistre les détails du traitement des messages pour faciliter la résolution des problèmes et la surveillance. Il capture les erreurs de désérialisation et fournit des insights qui peuvent être utiles pendant le débogage. Le service est mis à l’échelle au niveau du conteneur pour permettre une gestion efficace des pics de messages en fonction de la longueur de la file d’attente. Voici le code :

@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 Actuator fournit une prise en charge intégrée des vérifications d’intégrité. Il 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é. L’actionneur 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 dépendance spring-boot-starter-actuator dans votre fichier pom.xml ou build.gradle :

    <!-- 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 comme indiqué dans l’implémentation de référence :

        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 technologies telles qu’Azure Spring Apps ou Micrometer, qui fournissent des indicateurs d’intégrité pour ces services. Si vous avez besoin de vérifications personnalisées, vous pouvez les implémenter en créant une bean HealthIndicator personnalisée :

    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 (for example, 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 the 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 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 de nouvelle tentative permet aux applications de récupérer à partir d’erreurs temporaires. Ce modèle est central au modèle Reliable Web App. Votre application web doit donc déjà utiliser le modèle Nouvelle tentative. Appliquez le modèle Nouvelle tentative aux requêtes aux systèmes de messagerie et aux demandes émises par les services découplés que vous extrayez à partir de l’application web. Pour implémenter le modèle Nouvelle tentative, suivez ces recommandations :

  • Configurer les options de nouvelles tentatives. Veillez à configurer le client responsable des interactions avec la file d’attente de messages avec les paramètres de nouvelle tentative appropriés. Spécifiez des paramètres comme le nombre maximal de nouvelles tentatives, le délai entre les nouvelles tentatives et le délai maximal.

  • Utilisez un backoff exponentiel. Implémentez la stratégie d’interruption exponentielle pour les nouvelles tentatives. Cette stratégie implique d’augmenter le temps entre chaque nouvelle tentative de manière exponentielle, ce qui permet de 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 qui ont des kits sdk spécialisés, tels que Service Bus ou Stockage Blob, utilisez les mécanismes de nouvelle tentative intégrés. Ces mécanismes intégrés sont optimisés pour les cas d’usage classiques du service, peuvent gérer les nouvelles tentatives plus efficacement et nécessiter moins de configuration.

  • Utilisez des bibliothèques de résilience standard pour les clients HTTP. Pour les clients HTTP, vous pouvez utiliser Resilience4j avec RestTemplate ou WebClient de Spring pour gérer les nouvelles tentatives dans les communications HTTP. Vous pouvez encapsuler RestTemplate 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. Par exemple, utilisez des modes peek-lock quand 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 fournissent des conseils pour implémenter les mises à jour de configuration. Chaque section s’aligne sur un ou plusieurs des piliers de l’infrastructure Well-Architected.

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 les nouveaux services Azure (identités de charge de travail) que vous ajoutez à l’application web, suivez les recommandations suivantes :

  • 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. Ils vous aident à éviter d’inclure des informations sensibles telles que des chaînes 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.

  • Utilisez l’infrastructure en tant que code (IaC). Utilisez Bicep ou un outil IaC similaire 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 des configurations incorrectes et des autorisations inutiles et corrigez-les ou supprimez-les 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 les envois push et les extractions container Registry. Voici le code :

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 the current user to access 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 à décomposer l’architecture monolithique et introduit le découplage de service. 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, comme Kubernetes Event-Driven Autoscaler (KEDA), fournit souvent un contrôle granulaire 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 les démarrages à froid, configurez les paramètres de mise à l’échelle automatique pour conserver un minimum d’un réplica. Un démarrage à froid est l’initialisation d’un service à partir d’un état arrêté. Un démarrage à froid retarde souvent la réponse. Si la réduction des coûts est une priorité et que vous pouvez tolérer des retards de démarrage à froid, définissez le nombre minimal de réplicas sur 0 lorsque vous configurez 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 en fonction de la longueur de la file d’attente de messages de requête. Le scaler tente de conserver un réplica du service pour chaque N messages 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 paramètre messageCount est défini sur 10, ce qui configure le scaler pour ajouter un réplica pour chaque 10 messages de la file d’attente. Le nombre maximal de réplicas (max_replicas) est défini sur 10. Le nombre minimal de réplicas est implicitement 0, sauf s’il est substitué. Cette configuration permet au service de passer à zéro lorsqu’il n’y a pas de messages dans la file d’attente. La chaîne de connexion de la file d’attente Service Bus est stockée en tant que secret dans Azure, nommée azure-servicebus-connection-string, qui est utilisée pour authentifier le scaler auprès de Service Bus. Voici le code Terraform :

    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 est l’encapsulation de toutes les dépendances nécessaires par l’application 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 dans votre application monolithique. Cela vous aide à déterminer les parties de l’application que vous pouvez extraire dans des services distincts.

  • Créez des images Docker. Lorsque vous créez des images Docker pour vos services Java, utilisez des images de base OpenJDK officielles. Ces images contiennent uniquement l’ensemble minimal de packages que Java doit exécuter. L’utilisation de ces images réduit à la fois la taille du package et la surface d’attaque.

  • Utilisez Dockerfiles multiphases. Utilisez un fichier Dockerfile à plusieurs étapes pour séparer les ressources au moment de la génération de l’image conteneur du runtime. L’utilisation de ce type de fichier permet de maintenir 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 (via le nom d’utilisateur ou 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. Lorsque vous exécutez des conteneurs en tant qu’utilisateur non-root, configurez votre application pour écouter le port 8080. Il s’agit d’une convention commune pour les utilisateurs non-roots.

  • Encapsuler les dépendances. Vérifiez que toutes les dépendances dont l’application a besoin 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 déployez 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. Le 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 du volume. Un volume temporaire (/tmp) est défini. Ce volume fournit 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) utilisé 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é. Le code utilise java -javaagent pour surveiller l’application pendant l’exécution.

Le fichier Dockerfile conserve l’image petite en incluant uniquement les dépendances d’exécution. Il convient aux environnements de déploiement tels que Container Apps qui prennent en charge les conteneurs Linux.

# Use the 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 entrypoint 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 avoir déployé l’implémentation, vous pouvez simuler et observer des modèles de conception.

Le diagramme suivant illustre l’architecture de l’implémentation de référence :

Diagramme montrant l’architecture de l’implémentation de référence.

Télécharger un fichier Visio de cette architecture.