Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Les cycles de mise en production plus rapides constituent un avantage majeur des architectures de microservices. Sans un processus d’intégration continue fiable et de livraison continue (CI/CD), vous perdez l’agilité que fournissent les microservices. Cet article décrit les défis ci/CD courants dans les architectures de microservices et recommande des approches pour créer, valider, sécuriser et déployer des services indépendamment.
Qu’est-ce que le CI/CD ?
CI/CD fait référence à plusieurs processus connexes : intégration continue, livraison continue et déploiement continu.
Intégration continue (CI) : Les modifications de code sont fréquemment fusionnées dans la branche principale. Les processus de génération et de test automatisés garantissent que le code dans la branche principale est toujours de qualité de production.
Livraison continue (CD) : Les modifications de code qui passent le processus CI sont automatiquement publiées dans un environnement de type production. Le déploiement dans l’environnement de production dynamique peut nécessiter une approbation manuelle, mais il est sinon automatisé. L’objectif est que votre code est toujours prêt à être déployé en production.
Déploiement continu : Les modifications de code qui passent les deux étapes précédentes sont automatiquement déployées en production.
Tenez compte des objectifs suivants d’un processus CI/CD robuste pour une architecture de microservices :
Chaque équipe peut créer et déployer les services qu’elle possède indépendamment, sans affecter ni perturber d’autres équipes.
Avant qu’une nouvelle version d’un service soit déployée en production, elle est déployée dans des environnements de développement/test et d’assurance qualité pour la validation. Les seuils de qualité sont appliqués à chaque étape.
Une nouvelle version d'un service peut être déployée aux côtés de la version précédente.
Des stratégies de contrôle d’accès suffisantes sont en place. Les pipelines s’authentifient pour Azure avec des informations d’identification fédérées et de courte durée au lieu de secrets de longue durée.
Pour les charges de travail conteneurisées, vous pouvez faire confiance aux images de conteneur déployées en production. Cette confiance est établie par le biais d’images signées, d’attestations de documents logiciels (SBOM) et d’analyse des vulnérabilités appliquées dans le pipeline.
Pourquoi un pipeline CI/CD robuste importe
Dans une application monolithique traditionnelle, une unique chaîne de génération produit l’exécutable de l’application. Tout le travail de développement alimente ce pipeline. Si l’équipe trouve un bogue de haute priorité, le correctif doit être intégré, testé et publié, ce qui peut retarder la publication de nouvelles fonctionnalités. Vous pouvez réduire ces problèmes en utilisant des modules et des branches de fonctionnalités bien factornés pour limiter l’effet des modifications de code. Mais à mesure que l’application gagne en complexité et que de nouvelles fonctionnalités sont ajoutées, le processus de mise en production d’un monolithe tend à devenir plus compliqué et présente davantage de risques d’échec.
Selon la philosophie des microservices, il ne devrait jamais y avoir un long cycle de livraison où chaque équipe doit faire la queue. L’équipe qui génère le service A peut publier une mise à jour quand elle choisit et n’a pas à attendre que les modifications apportées au service B soient fusionnées, testées et déployées.
Pour atteindre une vitesse de mise en production élevée, votre pipeline de mise en production doit être automatisé et hautement fiable pour réduire les risques. Si vous déployez en production une ou plusieurs fois par jour, les régressions ou interruptions de service doivent être rares. Parallèlement, si vous déployez une mise à jour défectueuse, vous devez disposer d’un moyen fiable pour revenir rapidement à une version précédente d’un service ou pour passer rapidement à une version ultérieure stable.
Challenges
De nombreuses petites bases de code indépendantes : Chaque équipe est responsable de la création de son propre service, avec son propre pipeline de build. Dans certaines organisations, les équipes peuvent utiliser des référentiels de code distincts. Des référentiels distincts peuvent répartir les connaissances sur la façon de créer le système entre les équipes. Par conséquent, personne dans l’organisation ne sait comment déployer l’ensemble de l’application.
Atténuation: Disposer d’un pipeline unifié et automatisé ou au moins d’une infrastructure de pipeline commune pour créer et déployer des services afin que ces connaissances ne soient pas masquées au sein de chaque équipe. Les modèles de pipeline réutilisables, tels que GitHub Actions flux de travail réutilisables ou Azure Pipelines, permettent de normaliser la génération, le test, l’analyse et le déploiement des étapes sur chaque service.
Plusieurs langages et frameworks : Chaque équipe utilise sa propre combinaison de technologies. Il peut donc être difficile de créer un processus de génération unique qui fonctionne dans la charge de travail. Le processus de génération doit être suffisamment flexible pour que chaque équipe puisse l’adapter à son langage ou infrastructure choisi.
Atténuation: Conteneurisez le processus de génération pour chaque service afin que le système de build ne doit exécuter que les conteneurs. Les plateformes telles que GitHub Actions, Azure Pipelines et Azure Container Registry tâches peuvent générer et publier des images conteneur de manière cohérente, quel que soit le langage source.
Tests d’intégration et de charge : Les équipes publient des mises à jour à leur propre rythme, il peut donc être difficile de concevoir des tests robustes de bout en bout, en particulier lorsque les services dépendent d’autres services. L’exécution d’un cluster de production complet peut être coûteuse. Il est donc peu probable que chaque équipe exécute son propre cluster complet à l’échelle de production uniquement pour les tests.
Mitigation : Utilisez des environnements d’aperçu éphémères, tels que des espaces de noms Kubernetes par pull request ou des environnements Azure Container Apps créés à la demande. Utilisez des tests de contrat afin de détecter tôt les problèmes d’intégration sans nécessiter une réplique complète de la production.
Gestion des versions : Chaque équipe doit pouvoir déployer une mise à jour en production. Cette exigence ne signifie pas que chaque membre de l’équipe dispose des autorisations de déploiement. Un rôle de gestionnaire de mise en production centralisé peut réduire la vitesse de déploiement.
Atténuation: Plus votre processus CI/CD est automatisé et fiable, moins vous avez besoin d’une autorité centrale. Vous pouvez toujours avoir des stratégies différentes pour publier des mises à jour de fonctionnalités majeures par rapport aux correctifs de bogues mineurs. Une approche décentralisée ne signifie pas une gouvernance nulle. Imposez des approbations à l’aide de Azure Pipelines environments and approvals ou de GitHub Actions deployment environments and required reviewers, et codifiez la stratégie au niveau du cluster à l’aide de Azure Policy for Azure Kubernetes Service (AKS) ou de OPA Gatekeeper.
Mises à jour du service : Lorsque vous mettez à jour un service vers une nouvelle version, la mise à jour ne doit pas entraîner l’échec d’autres services qui dépendent de celui-ci.
Atténuation : Utilisez des techniques de déploiement comme les déploiements blue-green ou canary pour les modifications sans rupture de compatibilité. Pour les modifications d'API majeures, déployez la nouvelle version en parallèle avec la version précédente. Avec cette approche, les services qui consomment l’API précédente peuvent être mis à jour et testés pour la nouvelle API. Pour plus d’informations, consultez Update Services.
Gestion des identités et des secrets des pipelines : Les secrets à long terme des principaux de service stockés au sein des pipelines constituent une source fréquente de compromission et de surcharge opérationnelle. Les secrets du principal de service expirent, peuvent fuiter et nécessiter une rotation sur de nombreux pipelines de microservice indépendants.
Atténuation : Authentifiez les pipelines auprès d’Azure à l’aide de la fédération d’identités de charge de travail, qui utilise OpenID Connect (OIDC), afin qu’aucun secret client ne soit stocké dans le pipeline. Pour plus d’informations, consultez Identités de charge de travail pour Azure Pipelines et Configurer OpenID Connect dans Azure pour GitHub Actions. Stockez les secrets restants dans Azure Key Vault et référencez-les au moment de l’exécution.
Sécurité de la chaîne d’approvisionnement : Tout ce que vous déployez en production doit être traçable jusqu’au code et aux dépendances à partir desquels il a été construit. Les microservices augmentent le nombre d’images, de registres et de pipelines, ce qui augmente votre surface d’attaque de chaîne d’approvisionnement.
Mitigation : Signez les images de conteneur à l’aide de Notation et Key Vault, et vérifiez les signatures lors de l’admission à l’aide de AKS image integrity ou Ratify. Générez un SBOM en tant qu’artefact de build. Analysez le code, les dépendances et les pipelines à l’aide de Microsoft Defender for Cloud DevOps Security et de GitHub Advanced Security. Analysez les images runtime à l’aide de Microsoft Defender pour conteneurs. Exiger que toutes les analyses passent avant qu’une mise en production puisse continuer.
Monorepo et multirepo
Avant de créer un flux de travail CI/CD, vous devez savoir comment la base de code est structurée et gérée, notamment :
- Que les équipes travaillent dans des référentiels distincts ou dans un monorepo.
- Votre stratégie de branchement.
- Qui peut envoyer du code en production et savoir si un gestionnaire de versions existe.
Les équipes utilisent largement les deux approches en production. Votre choix dépend de la topologie d’équipe, de la maturité des outils et de la quantité de code partagée entre les services.
| Monorepo | Dépôts multiples | |
|---|---|---|
| Avantages | - Partage de code - Plus facile à normaliser le code et les outils - Plus facile de remanier le code - Facilité de repérage (une vue unifiée du code) |
- Responsabilité clairement définie pour chaque équipe - Potentiellement moins de conflits de fusion - Permet d’appliquer le découplage de microservice |
| Défis | - Les modifications apportées au code partagé peuvent affecter plusieurs microservices - Plus de potentiel pour les conflits de fusion - Les outils doivent être mis à l’échelle vers une base de code volumineuse - Contrôle d’accès - Processus de déploiement plus complexe |
- Plus difficile de partager du code - Plus difficile d’appliquer les normes de codage - Gestion des dépendances - Base de code diffuse, mauvaise détectabilité - Absence d’infrastructure partagée |
Quel que soit le modèle que vous choisissez, utilisez des déclencheurs délimités par le chemin d’accès dans vos pipelines, tels que les filtres path dans GitHub Actions ou trigger dans Azure Pipelines. Les déclencheurs basés sur le chemin permettent de s’assurer que seuls les microservices affectés sont reconstruits et redéployés à chaque commit.
Mettre à jour les services
Il existe différentes stratégies de mise à jour d’un service déjà en production, notamment la mise à jour propagée, le déploiement bleu-vert et la version canary. Ces modèles sont souvent coordonnés par le biais d’un flux de travail GitOps. Pour plus d’informations, consultez GitOps et livraison progressive.
Mises à jour continues
Dans une mise à jour propagée, vous déployez de nouvelles instances d’un service et les nouvelles instances commencent à recevoir des demandes immédiatement. À mesure que les nouvelles instances deviennent prêtes, les instances précédentes sont supprimées.
Exemple dans Kubernetes : Dans Kubernetes, les mises à jour propagées sont le comportement par défaut lorsque vous mettez à jour la spécification de pod pour un déploiement. Le contrôleur de déploiement crée un ReplicaSet pour les pods mis à jour. Ensuite, il augmente le nombre de réplicas du nouveau ReplicaSet tout en réduisant celui du ReplicaSet précédent afin de maintenir le nombre de réplicas souhaité. Il ne supprime pas les pods précédents tant que les nouveaux pods ne sont pas prêts. Kubernetes conserve un historique de la mise à jour. Vous pouvez donc restaurer une mise à jour si nécessaire.
Exemple dans Container Apps : Container Apps utilise des révisions pour gérer les mises à jour propagées. Lorsque vous déployez une nouvelle révision, Container Apps peut passer progressivement du trafic de la révision précédente à la nouvelle révision à l’aide de règles de fractionnement du trafic. Si la nouvelle révision rencontre des problèmes, vous pouvez restaurer le trafic en redirigeant le trafic vers la révision précédente. Vous pouvez configurer plusieurs révisions actives simultanément et contrôler le pourcentage de trafic reçu par chaque révision.
L’un des défis des mises à jour progressives est que, pendant la mise à jour, des versions anciennes et nouvelles coexistent, s’exécutent et reçoivent du trafic. Pendant cette période, le système peut acheminer n’importe quelle requête vers l’une ou l’autre version.
Pour les ruptures d'API, une bonne pratique consiste à prendre en charge les deux versions en parallèle, jusqu’à ce que tous les clients de la version précédente soient mis à jour. Pour plus d’informations, consultez gestion des versions d’API.
Déploiement bleu-vert
Dans un déploiement bleu-vert, vous déployez la nouvelle version en même temps que la version précédente. Après avoir validé la nouvelle version, vous basculez tout le trafic à la fois de la version précédente vers la nouvelle version. Après la transition, vous surveillez l'application pour détecter des problèmes. En cas de problème, vous pouvez rediriger le trafic vers la version précédente. S’il n’y a aucun problème, vous pouvez supprimer la version précédente.
Avec une application monolithique ou multiniveau plus traditionnelle, le déploiement bleu-vert signifie généralement que vous créez deux environnements identiques. Vous déployez la nouvelle version dans un environnement intermédiaire, puis redirigez le trafic client vers cet environnement, par exemple en échangeant une adresse IP virtuelle. Dans une architecture de microservices, les mises à jour se produisent au niveau du microservice. Vous déployez donc généralement la mise à jour dans le même environnement et utilisez un mécanisme de découverte de service pour changer le trafic.
Exemple dans Kubernetes : Dans Kubernetes, vous n’avez pas besoin de créer un cluster distinct pour effectuer des déploiements bleu-vert. Au lieu de cela, vous pouvez tirer parti des sélecteurs. Créez une ressource de déploiement avec une nouvelle spécification de pod et un autre ensemble d’étiquettes. Créez ce déploiement, mais ne supprimez pas le déploiement précédent ou modifiez le service qui pointe vers celui-ci. Une fois les nouveaux pods démarrés, vous pouvez mettre à jour le sélecteur du service afin qu’il corresponde au nouveau déploiement.
L’un des inconvénients du déploiement bleu-vert est que pendant la mise à jour, vous exécutez deux fois plus de pods pour le service (actuel et suivant). Si les pods utilisent des ressources de processeur ou de mémoire substantielles, vous devrez peut-être effectuer un scale-out temporaire du cluster pour répondre à la demande de ressources plus élevée.
Version de Canary
Dans une version canary, vous déployez une version mise à jour sur un petit sous-ensemble de clients, puis surveillez le comportement du nouveau service avant de le déployer sur tous les clients. Avec cette approche, vous pouvez déployer progressivement de manière contrôlée, surveiller les données réelles et identifier les problèmes avant qu’ils n’affectent tous les clients.
Un déploiement canari est plus complexe à gérer qu’un déploiement bleu-vert ou qu’une mise à jour progressive, car vous devez acheminer dynamiquement les requêtes vers différentes versions du service.
Exemple dans Kubernetes : Dans Kubernetes, vous pouvez configurer un service pour couvrir deux jeux de réplicas (un pour chaque version) et ajuster le nombre de réplicas manuellement. Toutefois, cette approche est peu granulaire en raison de la façon dont Kubernetes répartit la charge entre les pods. Par exemple, si vous avez un total de 10 réplicas, vous ne pouvez déplacer le trafic que par incréments de 10%. Si vous utilisez un maillage de service, vous pouvez utiliser les règles de routage de maillage de service pour implémenter une stratégie de mise en production canary plus sophistiquée.
Exemple dans Container Apps : Dans Container Apps, vous pouvez utiliser le fractionnement du trafic pour envoyer un pourcentage défini de trafic à une nouvelle révision (comme 10% jusqu’à v2 90% reste activé v1) et déplacer les poids à mesure que la confiance augmente, sans maillage de service externe requis.
Livraison progressive et GitOps
Pour les équipes qui exploitent de nombreux microservices sur Kubernetes, un modèle gitOps basé sur pull complète les exemples push précédents. L’état de cluster souhaité réside dans Git et un opérateur in-cluster rapproche le cluster de cet état. CI génère, teste, analyse, signe et envoie (push) l’image. CD réconcilie le cluster avec le manifeste. Cette séparation vous offre une piste d’audit et facilite la reprise après sinistre (DR). Cela évite également au runner CI de détenir des identifiants d’accès directs au cluster.
Étapes suivantes
- Parcours d’apprentissage : Définir et implémenter l’intégration continue
- Formation : Développement pour Enterprise DevOps
- architecture de microservices