Exploitation des conteneurs et des orchestrateurs
Conseil
Ce contenu est un extrait du livre électronique, Cloud Native .NET apps for Azure (Architecture d’applications .NET natives cloud pour Azure), disponible dans la documentation .NET ou au format PDF à télécharger gratuitement pour le lire hors connexion.
Les conteneurs et les orchestrateurs sont conçus pour résoudre les problèmes courants liés aux approches de déploiement monolithiques.
Défis liés aux déploiements monolithiques
Traditionnellement, la plupart des applications sont déployées en tant qu’unités uniques. Ces applications sont appelées monolithes. Cette approche générale du déploiement d’applications en tant qu’unités uniques, même si elles sont composées de plusieurs modules ou assemblys, est appelée architecture monolithique, comme illustré dans la figure 3-1.
Figure 3-1. Architecture monolithique.
Bien qu’elles aient l’avantage de la simplicité, les architectures monolithiques sont confrontées à de nombreux défis :
Déploiement
En outre, elles nécessitent un redémarrage de l’application, ce qui peut avoir un impact temporaire sur la disponibilité si des techniques de temps d’arrêt zéro ne sont pas appliquées lors du déploiement.
Mise à l'échelle
Une application monolithique est entièrement hébergée sur une seule instance de machine, nécessitant souvent du matériel haute capacité. Si une partie du monolithe nécessite une mise à l’échelle, une autre copie de l’application entière doit être déployée sur un autre ordinateur. Avec un monolithe, vous ne pouvez pas mettre à l’échelle les composants d’application individuellement ; c’est tout ou rien. La mise à l’échelle des composants qui ne nécessitent pas de mise à l’échelle entraîne une utilisation inefficace et coûteuse des ressources.
Environnement
Les applications monolithiques sont généralement déployées dans un environnement d’hébergement avec un système d’exploitation, un runtime et des dépendances de bibliothèque préinstallés. Cet environnement peut ne pas correspondre à celui sur lequel l’application a été développée ou testée. Les incohérences entre les environnements d’application sont une source courante de problèmes pour les déploiements monolithiques.
Couplage
Une application monolithique risque de rencontrer un couplage élevé entre ses composants fonctionnels. Sans limites dures, les changements du système entraînent souvent des effets secondaires inattendus et coûteux. Les nouvelles fonctionnalités et les correctifs deviennent difficiles, fastidieux et coûteux à implémenter. Les mises à jour nécessitent des tests approfondis. Le couplage rend également difficile la refactorisation des composants ou l’échange avec d’autres implémentations. Même lors de la construction avec une stricte séparation des préoccupations, l’érosion architecturale s’installe à mesure que la base de code monolithique se détériore avec une liste interminable de « cas spéciaux ».
Verrouillage de la plateforme
Une application monolithique est construite avec une pile technologique unique. Tout en offrant une certaine uniformité, cet engagement peut devenir un obstacle à l’innovation. Les nouvelles fonctionnalités et les nouveaux composants seront construits à l’aide de la pile actuelle de l’application, même lorsque des technologies plus modernes pourraient constituer un meilleur choix. Un risque à long terme est que votre pile technologique devienne dépassée et obsolète. La réarchitecture d’une application entière sur une nouvelle plateforme plus moderne est au mieux coûteuse et risquée.
Quels sont les avantages des conteneurs et des orchestrateurs ?
Nous avons introduit les conteneurs dans le chapitre 1. Nous avons souligné comment la Cloud Native Computing Foundation (CNCF) classe la conteneurisation comme la première étape de sa Feuille de route vers le cloud natif pour les entreprises qui commencent leur voyage vers le cloud natif. Dans cette section, nous abordons les avantages des conteneurs.
Docker est la plateforme de gestion des conteneurs la plus populaire. Elle fonctionne avec des conteneurs sur Linux ou Windows. Les conteneurs fournissent des environnements d’application distincts, mais reproductibles, qui s’exécutent de la même façon sur n’importe quel système. Cet aspect les rend parfaits pour le développement et l’hébergement de services cloud natifs. Les conteneurs sont isolés les uns des autres. Deux conteneurs sur le même matériel hôte peuvent avoir différentes versions de logiciels, sans provoquer de conflits.
Les conteneurs sont définis par des fichiers texte simples qui deviennent des artefacts de projet et sont archivés dans le contrôle de code source. Alors que les serveurs complets et les machines virtuelles nécessitent un effort manuel pour être mis à jour, les conteneurs sont facilement contrôlés par version. Les applications créées pour s’exécuter dans des conteneurs peuvent être développées, testées et déployées à l’aide d’outils automatisés dans le cadre d’un pipeline de build.
Les conteneurs sont immuables. Une fois que vous avez défini un conteneur, vous pouvez le recréer et l’exécuter exactement de la même façon. Cette immuabilité se prête à la conception basée sur les composants. Si certaines parties d’une application évoluent différemment des autres, pourquoi redéployer l’ensemble de l’application quand vous pouvez simplement déployer les parties qui changent le plus fréquemment ? Différentes fonctionnalités et préoccupations croisées d’une application peuvent être divisées en unités distinctes. La figure 3-2 montre comment une application monolithique peut tirer parti des conteneurs et des microservices en déléguant certaines fonctionnalités. Les fonctionnalités restantes de l’application elle-même ont également été conteneurisées.
Figure 3-2. Décomposition d’une application monolithique pour adopter des microservices.
Chaque service natif cloud est créé et déployé dans un conteneur distinct. Chacun peut être mis à jour si nécessaire. Les services individuels peuvent être hébergés sur des nœuds avec des ressources appropriées pour chaque service. L’environnement dans lequel chaque service s’exécute est immuable, partagé entre les environnements de développement, de test et de production, et facilement versionné. Le couplage entre différentes zones de l’application se produit explicitement en tant qu’appels ou messages entre les services, et non sous forme de dépendances au moment de la compilation dans le monolithe. Vous pouvez également choisir la technologie qui convient le mieux à une fonctionnalité donnée sans avoir à apporter de modifications au reste de l’application.
Les services en conteneur nécessitent une gestion automatisée. Il n’est pas possible d’administrer manuellement un grand ensemble de conteneurs déployés indépendamment. Par exemple, considérez les tâches suivantes :
- Comment les instances de conteneur seront-elles approvisionnées sur un cluster de nombreuses machines ?
- Une fois déployés, comment les conteneurs vont-ils se découvrir et communiquer entre eux ?
- Comment les conteneurs peuvent-ils être mis à l’échelle à la demande ?
- Comment surveiller l’intégrité de chaque conteneur ?
- Comment protéger un conteneur contre les défaillances matérielles et logicielles ?
- Comment mettre à niveau des conteneurs pour une application disponible sans temps d’arrêt ?
Les orchestrateurs de conteneurs traitent et automatisent ces problèmes et d’autres.
Dans l’éco-système natif cloud, Kubernetes est devenu l’orchestrateur de conteneur de choix. Il s’agit d’une plateforme open source gérée par la Cloud Native Computing Foundation (CNCF). Kubernetes automatise le déploiement, la mise à l’échelle et les problèmes opérationnels des charges de travail en conteneur sur un cluster de machines. Toutefois, l’installation et la gestion de Kubernetes sont notoirement complexes.
Une approche beaucoup plus efficace consiste à tirer parti de Kubernetes en tant que service managé d’un fournisseur cloud. Le cloud Azure propose une plateforme Kubernetes entièrement managée appelée Azure Kubernetes Service (AKS). AKS fait abstraction de la complexité et de la surcharge opérationnelle de la gestion de Kubernetes. Vous consommez Kubernetes en tant que service cloud ; Microsoft prend la responsabilité de la gestion et de la prise en charge. AKS s’intègre également étroitement à d’autres services et outils de développement Azure.
AKS est une technologie basée sur les clusters. Un pool de machines virtuelles fédérées, ou de nœuds, est déployé dans le cloud Azure. Ensemble, ils forment un environnement hautement disponible, ou cluster. Le cluster apparaît sous la forme d’une entité unique transparente pour votre application native cloud. Sous le capot, AKS déploie vos services conteneurisés sur ces nœuds en suivant une stratégie prédéfinie qui distribue uniformément la charge.
Quels sont les avantages de la mise à l’échelle ?
Les services basés sur des conteneurs peuvent tirer parti des avantages de mise à l’échelle fournis par des outils d’orchestration comme Kubernetes. Par conception, les conteneurs ne se connaissent qu’eux-mêmes. Une fois que vous avez plusieurs conteneurs qui doivent fonctionner ensemble, vous devez les organiser à un niveau supérieur. Organiser un grand nombre de conteneurs et leurs dépendances partagées, comme la configuration réseau, est là où les outils d’orchestration entrent en jeu pour vous sauver la mise ! Kubernetes crée une couche d’abstraction sur les groupes de conteneurs et les organise en pods. Les pods s’exécutent sur des machines Worker appelées nœuds. Cette structure organisée est appelée cluster. La figure 3-3 montre les différents composants d’un cluster Kubernetes.
Figure 3-3. Composants d’un cluster Kubernetes.
La mise à l’échelle des charges de travail conteneurisées est une fonctionnalité clé des orchestrateurs de conteneurs. AKS prend en charge la mise à l’échelle automatique entre deux dimensions : les instances de conteneur et les nœuds de calcul. Ensemble, ils permettent à AKS de répondre rapidement et efficacement aux pics de demande et d’ajouter des ressources supplémentaires. Nous discutons de la mise à l’échelle dans AKS plus loin dans ce chapitre.
Différence entre le développement déclaratif et le développement impératif
Kubernetes prend en charge la configuration déclarative et impérative. L’approche impérative implique d’exécuter différentes commandes qui indiquent à Kubernetes ce qu’il faut faire lors de chaque étape. Exécutez cette image. Supprimez ce pod. Exposez ce port. Avec l’approche déclarative, vous créez un fichier de configuration, appelé manifeste, pour décrire ce que vous voulez plutôt que ce qu’il faut faire. Kubernetes lit le manifeste et transforme votre état final souhaité en état final réel.
Les commandes impératives sont idéales pour l’apprentissage et l’expérimentation interactive. Toutefois, vous souhaitez créer de manière déclarative des fichiers manifeste Kubernetes pour adopter une approche d’infrastructure en tant que code, ce qui offre des déploiements fiables et reproductibles. Le fichier manifeste devient un artefact de projet et est utilisé dans votre pipeline CI/CD pour automatiser les déploiements Kubernetes.
Si vous avez déjà configuré votre cluster à l’aide de commandes impératives, vous pouvez exporter un manifeste déclaratif à l’aide de kubectl get svc SERVICENAME -o yaml > service.yaml
. Cette commande produit un manifeste similaire à celui indiqué ci-dessous :
apiVersion: v1
kind: Service
metadata:
creationTimestamp: "2019-09-13T13:58:47Z"
labels:
component: apiserver
provider: kubernetes
name: kubernetes
namespace: default
resourceVersion: "153"
selfLink: /api/v1/namespaces/default/services/kubernetes
uid: 9b1fac62-d62e-11e9-8968-00155d38010d
spec:
clusterIP: 10.96.0.1
ports:
- name: https
port: 443
protocol: TCP
targetPort: 6443
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
Lorsque vous utilisez la configuration déclarative, vous pouvez afficher un aperçu des modifications qui seront apportées avant de les valider à l’aide de kubectl diff -f FOLDERNAME
sur le dossier où se trouvent vos fichiers de configuration. Une fois que vous êtes sûr de vouloir appliquer les modifications, exécutez kubectl apply -f FOLDERNAME
. Ajoutez -R
pour traiter de manière récursive une hiérarchie de dossiers.
Vous pouvez également utiliser la configuration déclarative avec d’autres fonctionnalités Kubernetes, dont l’une est les déploiements. Les déploiements déclaratifs aident à gérer les versions, les mises à jour et la mise à l’échelle. Ils demandent au contrôleur de déploiement Kubernetes de déployer de nouvelles modifications, de mettre à l’échelle la charge ou de revenir à une révision précédente. Si un cluster est instable, un déploiement déclaratif retourne automatiquement le cluster à un état souhaité. Par exemple, si un nœud doit se bloquer, le mécanisme de déploiement redéploie un remplacement pour obtenir votre état souhaité
L’utilisation de la configuration déclarative permet à l’infrastructure d’être représentée en tant que code qui peut être archivé et versionné en même temps que le code de l’application. Cela fournit un contrôle de modification amélioré et une meilleure prise en charge du déploiement continu à l’aide d’un pipeline de build et de déploiement.
Quels scénarios sont idéaux pour les conteneurs et les orchestrateurs ?
Les scénarios suivants sont idéaux pour l’utilisation de conteneurs et d’orchestrateurs.
Applications nécessitant une haute disponibilité et une scalabilité
Les applications individuelles qui ont des exigences de disponibilité et de scalabilité élevées sont des candidats idéaux pour les architectures natives cloud utilisant des microservices, des conteneurs et des orchestrateurs. Elles peuvent être développées dans des conteneurs, testées dans des environnements versionnés et déployées en production sans temps d’arrêt. L’utilisation de clusters Kubernetes garantit que ces applications peuvent également être mises à l’échelle à la demande et récupérer automatiquement à partir des défaillances de nœud.
Grand nombre d’applications
Les organisations qui déploient et gèrent un grand nombre d’applications bénéficient des conteneurs et orchestrateurs. L’effort initial de configuration des environnements conteneurisés et des clusters Kubernetes est essentiellement un coût fixe. Le déploiement, la maintenance et la mise à jour d’applications individuelles ont un coût qui varie selon le nombre d’applications. Au-delà de quelques applications, la complexité de la maintenance manuelle des applications personnalisées dépasse le coût d’implémentation d’une solution utilisant des conteneurs et orchestrateurs.
Quand éviter d’utiliser des conteneurs et des orchestrateurs ?
Si vous ne parvenez pas à créer votre application en suivant les principes de l’application Twelve-Factor, vous devez envisager d’éviter les conteneurs et les orchestrateurs. Dans ces cas, envisagez une plateforme d’hébergement basée sur des machines virtuelles, ou éventuellement un système hybride. Avec cela, vous pouvez toujours déplacer certains éléments de fonctionnalité dans des conteneurs distincts, ou même des fonctions serverless.
Ressources de développement
Cette section présente une courte liste de ressources de développement qui peuvent vous aider à commencer à utiliser des conteneurs et des orchestrateurs pour votre prochaine application. Si vous recherchez des conseils sur la conception de votre application d’architecture de microservices native dans le cloud, lisez le compagnon de ce livre, Microservices .NET : Architecture pour les applications .NET en conteneurs.
Développement Kubernetes local
Les déploiements Kubernetes offrent une grande valeur dans les environnements de production, mais peuvent également s’exécuter localement sur votre machine de développement. Bien que vous puissiez travailler indépendamment sur des microservices individuels, il peut arriver que vous deviez exécuter l’ensemble du système localement, tout comme il s’exécutera lors du déploiement en production. Il existe plusieurs outils qui peuvent vous aider pour cela : Minikube et Docker Desktop. Visual Studio fournit également des outils pour le développement Docker.
Minikube
Qu’est-ce que Minikube ? Le projet Minikube indique « Minikube implémente un cluster Kubernetes local sur macOS, Linux et Windows ». Ses principaux objectifs sont « d’être le meilleur outil pour le développement d’applications Kubernetes local et de prendre en charge toutes les fonctionnalités Kubernetes qui conviennent ». L’installation de Minikube est distincte de Docker, mais Minikube prend en charge différents hyperviseurs que Docker Desktop. Les fonctionnalités Kubernetes suivantes sont actuellement prises en charge par Minikube :
- DNS
- NodePorts
- ConfigMaps et secrets
- Tableaux de bord
- Runtimes de conteneur : Docker, rkt, CRI-O et containerd
- Activation de l’interface réseau du conteneur (CNI)
- Entrée
Après avoir installé Minikube, vous pouvez rapidement commencer à l’utiliser en exécutant la commande minikube start
, qui télécharge une image et démarre le cluster Kubernetes local. Une fois le cluster démarré, vous interagissez avec celui-ci à l’aide des commandes kubectl
Kubernetes standard.
Docker Desktop
Vous pouvez également utiliser Kubernetes directement à partir de Docker Desktop sur Windows. Il s’agit de votre seule option si vous utilisez des conteneurs Windows, et c’est également un excellent choix pour les conteneurs non Windows. La figure 3-4 montre comment activer la prise en charge de Kubernetes locale lors de l’exécution de Docker Desktop.
Figure 3-4. Configuration de Kubernetes dans Docker Desktop.
Docker Desktop est l’outil le plus populaire pour configurer et exécuter des applications conteneurisées localement. Lorsque vous utilisez Docker Desktop, vous pouvez développer localement sur le même ensemble d’images conteneur Docker que vous allez déployer en production. Docker Desktop est conçu pour « générer, tester et expédier » des applications conteneurisées localement. Il prend en charge les conteneurs Linux et Windows. Une fois que vous envoyez vos images à un registre d’images, comme Azure Container Registry ou Docker Hub, AKS peut les extraire et les déployer en production.
Outils Docker de Visual Studio
Visual Studio prend en charge le développement Docker pour les applications web. Lorsque vous créez une application ASP.NET Core, vous avez la possibilité de la configurer avec la prise en charge de Docker, comme illustré dans la figure 3-5.
Figure 3-5. Visual Studio active la prise en charge de Docker
Lorsque cette option est sélectionnée, le projet est créé avec un Dockerfile
dans sa racine, qui peut être utilisé pour générer et héberger l’application dans un conteneur Docker. Un exemple de fichier Dockerfile est illustré dans la figure 3-6.
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["eShopWeb/eShopWeb.csproj", "eShopWeb/"]
RUN dotnet restore "eShopWeb/eShopWeb.csproj"
COPY . .
WORKDIR "/src/eShopWeb"
RUN dotnet build "eShopWeb.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "eShopWeb.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "eShopWeb.dll"]
Figure 3-6. Fichier Dockerfile généré par Visual Studio
Une fois la prise en charge ajoutée, vous pouvez exécuter votre application dans un conteneur Docker dans Visual Studio. La figure 3-7 montre les différentes options d’exécution disponibles à partir d’un nouveau projet ASP.NET Core créé avec la prise en charge de Docker ajoutée.
Figure 3-7. Options d’exécution de Docker de Visual Studio
En outre, à tout moment, vous pouvez ajouter la prise en charge de Docker à une application ASP.NET Core existante. Dans l’Explorateur de solutions Visual Studio, cliquez avec le bouton droit sur le projet et sélectionnez Ajouter>Prise en charge de Docker, comme illustré dans la figure 3-8.
Figure 3-8. Ajout de la prise en charge de Docker à Visual Studio
Outils Docker de Visual Studio Code
De nombreuses extensions sont disponibles pour Visual Studio Code pour prendre en charge le développement Docker.
Microsoft fournit l’extension Docker pour Visual Studio Code. Cette extension simplifie le processus d’ajout de la prise en charge des conteneurs aux applications. Elle crée des fichiers requis, génère des images Docker et vous permet de déboguer votre application à l’intérieur d’un conteneur. L’extension propose un explorateur visuel qui facilite l’exécution d’actions sur des conteneurs et des images comme le démarrage, l’arrêt, l’inspection, la suppression et bien plus encore. L’extension prend également en charge Docker Compose pour gérer plusieurs conteneurs en cours d’exécution en tant qu’unité unique.