Article écrit par Mohamed Ali Mdalla
Introduction
Une application native cloud est une application web contenant plusieurs petits microservices, utilisant les Message Queues pour communiquer ou des caches afin d’optimiser les performances. Ce type d’applications sont très utilisées, cependant elles sont difficiles à créer et à configurer dans un environnement local ou en production : chaque microservice doit pouvoir communiquer avec les autres microservices.
A partir de la version 8 du .NET, Microsoft présente la nouvelle stack .NET ASPIRE qui facilite la création d’applications natives cloud en fournissant des composants et des outils pour le codage, la configuration et la supervision des applications.
Qu’est-ce que .NET ASPIRE
En mai 2024, durant Microsoft Build, Microsoft a annoncé officiellement .NET Aspire. Il s’agit d’une stack “Opinionated” permettant de construire des applications cloud distribuées, observables et prête pour la production :
- Opinionated : Une pile logicielle avec des conventions et pratiques prédéfinies, guidant fortement la structure et l’implémentation des systèmes;
- Prêt pour le Cloud : Conçu pour exploiter pleinement le cloud, avec des caractéristiques comme évolutivité, résilience, flexibilité et sécurité;
- observable : Une pile qui permet la supervision détaillée sur son état, comportement et performance via la surveillance, journalisation, traçage, et alertes;
- Prêt pour la production : Prêt et configuré pour gérer les exigences d’une application en production;
- Applications distribuées : Systèmes fonctionnant sur plusieurs dispositifs, collaborant comme un système unique, offrant plus de flexibilité et de scalabilité par rapport aux systèmes traditionnels.
.NET Aspire est fourni sous forme d’une collection de packages NuGet qui gèrent des problématiques spécifiques au cloud natif.
Avantages
Orchestration
Les microservices sont souvent faiblement couplés afin d’augmenter la flexibilité d’application déployée. Cependant la configuration devient plus complexe: par exemple l’identification du point de terminaison où un microservice est disponible peut être difficile.
Pour faire face à ce défi, Microsoft a fourni des fonctionnalités d’orchestration dans .NET Aspire permettant la spécification des projets .NET, des conteneurs, des exécutables et des ressources cloud qui composent l’application. Ces fonctionnalités aident les microservices à détecter automatiquement les points de terminaison pour tous les composants de l’application : cette approche s’appelle découverte de service (Service discovery).
Lors de la création d’une solution .NET Aspire, l’orchestration est implémentée dans un nouveau projet nommé .AppHost: ce projet doit être le projet de démarrage de la solution.
Composants (.NET Aspire components)
Les microservices ont besoin de services de stockage complexes pour assurer leur fonctionnement. Les composants .NET Aspire sont des packages NuGet pour un grand nombre de services fréquemment utilisés :
- Composants liés à PostgreSQL, SQL Database, Azure Cosmos DB et MongoDB de stockage de données;
- Composants de mise en cache, y compris un pour Redis pour optimiser les performances;
- Composants de messagerie tels que ceux pour RabbitMQ et Azure Service Bus pour faciliter la communication fiable entre les microservices indépendamment l’état du réseau ou du trafic.
Dans .NET Aspire, il est simple d’implémenter ces différents services de stockage dans chaque microservice: chaque composant est un package NuGet qui implémente une interface standard vers un service de stockage. Cette interface assure la connexion à ses services de manière cohérente et transparente.
Outillage (tooling)
.NET Aspire a été ajouté aux outils disponibles dans Visual Studio pour les développeurs afin de faciliter son utilisation. On peut citer les exemples suivants :
- De nouveaux modèles de projets permettant la création des solutions .NET Aspire en quelques clics dans un Assistant;
- Une interface web qui s’affiche à chaque démarrage de la solution via Visual Studio : il s’agit d’un tableau de bord présentant tous les microservices et services de stockage pour l’application et montrant également les outils de performances et de surveillance;
- Des éléments de menus ajoutés s’affichent permettant d’ajouter des composants .NET Aspire, d’inscrire un projet, ou d’effectuer des tâches diverses.
Dans le cas ou l’on utiliserait pas visual studio, des commandes ajoutées au CLI dotnet facilitent l’interaction avec les projets .NET Aspire.
Comment créer un projet .NET Aspire
Prérequis à installer
Pour pouvoir créer un projet .NET Aspire, il est indispensable d’installer les éléments suivants :
- Installez .NET 8;
- Visual Studio 2022;
- Docker Desktop ou Podman;
- .NET Aspire workload dans Visual Studio.
Modèles de projet .NET Aspire
Microsoft propose cinq modèles de projets .NET Aspire que l’on peut créer à partir de visual studio ou en utilisant dotnet CLI :
- .NET Aspire Starter Application : un modèle de projet pour créer une application .NET Aspire avec un front-end en Blazor, back-end en web API. On peut également utiliser Redis pour la mise en cache. Il inclut les projets AspireAppDemo.AppHost et AspireAppDemo.ServiceDefaults. Ces projets sont déjà configurés afin de découvrir et se familiariser avec .NET Aspire;
- .NET Aspire Application : un modèle de projet qui inclut uniquement les projets AspireAppDemo.AppHost et AspireAppDemo.ServiceDefaults. Il est idéale pour commencer une solution de zéro, ajouter les élements de mon application et faire soi-même les configurations;
- .NET Aspire App Host : un modèle de projet pour créer une application .NET Aspire Host jouant le rôle d’un orchestrateur entre les élements d’application cloud native;
- .NET Aspire Service Default : Modèle qui contient uniquement le projet de services par défaut;
- .NET Aspire Test Project : comme son l’indique, il s’agit d’un projet contenant des tests d’intégration liés au projet .NET Aspire AppHost.
.NET Aspire Starter Application
Création
Pour créer un projet Starter de .NET Aspire via visual studio, il faut sélectionner .NET Aspire Starter Application comme indique le schéma ci-dessous :
Ensuite on peut choisir d’utiliser Redis pour la mise en cache. On peut également créer le même projet en utilisant dotnet CLI en saisissant la commande suivante :
dotnet new aspire-starter
Solution
Le projet obtenu est structuré de la manière suivante :
Les projets AppHost et ServiceDefaults forment ensemble le noyau d’une application crée avec .NET Aspire. Les éléments d’application sont une application Blazor WebAssembly et un ApiService qui retourne des données de prévision.
Projet AppHost
Le projet AppHost agit en tant qu’orchestrateur entre les différents éléments d’application : un front-end en Blazor, back-end en web API. Le projet AppHost est également le point d’entrée de l’application, pour cela il doit être sélectionné en tant que projet de démarrage.
Le fichier Program.vs du projet contient le code suivant :
var builder = DistributedApplication.CreateBuilder(args);
var apiService = builder.AddProject<Projects.AspireAppDemo_ApiService>("apiservice");
builder.AddProject<Projects.AspireAppDemo_Web>("webfrontend")
.WithExternalHttpEndpoints()
.WithReference(apiService);
builder.Build().Run();
On initialise d’abord un constructeur Builder pour une application distribuée. Ensuite on ajoute deux projets : un projet ApiService nommé “apiService” et un projet Web nommé “webfrontend” à l’orchestrateur. Le projet “webfrontend” fait référence au service API “apiService” ajoutés précédemment. Enfin on exécute l’application.
Dans le projet “webfrontend”, on met le code suivant dans le fichier Program.cs :
builder.Services.AddHttpClient<WeatherApiClient>(client =>
{
client.BaseAddress = new("https+http://apiservice");
});
Le projet “webfrontend” peut communiquer via des requêtes HTTP à http://apiservice sans se soucier des ports. Cela diminue énormément les configurations à faire pour faire fonctionner le système : c’est l’approche de découverte de service (Service discovery). Cette approche permet aux développeurs d’utiliser des noms logiques à la places des ports HTTP, adresse IP…
Remarque : on doit utiliser le même nom “apiservice” initialiser dans le projet AppHost dans le projet “webfrontend”.
Projet ServiceDefaults
Le projet ServiceDefaults contient la configuration par défaut de l’application : toutes les configurations présentes dans ce projet sont réutilisées dans tous les autres projets de la solution. Il s’agit de la configuration de la découverte de services, la télémétrie et le health check endpoints.
Exécution d’application
Lors du lancement du projet .NET Aspire, un tableau de bord apparait au développeur : C’est une interface interactive fournissant des informations sur vos projets. Ce tableau sert d’outil indisponsable pour le débogage des applications distribuées, présentant une vue unifiée de vos services aux côtés de leurs journaux, métriques et traces.
Pour accéder aux projets d’application, il suffit de cliquer sur le lien situé dans la colonne “Points de terminaison”.
Le développeur peut également voir les journaux et les traces de tous les projets. Remarque : Les traces sont un outil nécessaire permettant de diagnostiquer les soucis dans les systèmes distribués.
Ajouter un projet ApiService dans .NET Aspire Started Application
Lorsqu’on ajoute un nouveau projet, visual studio propose une case à cocher afin de donner la possibilité au développeur d’inclure le projet dans le projet d’orchestration AppHost comme suivant :
Cette ligne de code s’ajoute dans le fichier Program.cs du projet AppHost :
builder.AddProject<Projects.ApiService2>("apiservice2");
Cela Ajoute le projet ApiService2 et le nomme “apiservice2” afin de pouvoir le référencer.
Pour tester la communication entre la nouvelle API ajoutée et le projet front-end existant, Le projet “webfrontend” doit faire référence au service API “apiService2” de la manière suivantes :
var apiService = builder.AddProject<Projects.AspireAppDemo_ApiService>("apiservice");
var apiService2 = builder.AddProject<Projects.ApiService2>("apiservice2");
builder.AddProject<Projects.AspireAppDemo_Web>("webfrontend")
.WithExternalHttpEndpoints()
.WithReference(apiService)
.WithReference(apiService2);
Ensuite on ajoute la nouvelle API dans le fichier Program.cs du projet Blazor de la manière suivante :
builder.Services.AddHttpClient<MessageApiClient>(client =>
{
client.BaseAddress = new Uri("https+http://apiservice2");
});
Quand on exécute .NET Aspire, on remarque qu’on a un nouveau projet dans le tableau des éléments d’application de la manière suivantes :
Après avoir modifié le projet front-end, on affiche dans la page ci-dessous un tableau récupéré de l’API apiservice et un message de l’API apiservice2 :
Comment mettre en place .NET Aspire dans un projet existant
On va utiliser une application composée d’une API “DotNetAspireDemo.API” en ASP.NET Core, et un front-end “DotNetAspireDemo.Front” en Angular.
Ajouter un support d’orchestration .NET Aspire
Pour ajouter un support d’orchestration .NET Aspire à ce projet, il suffit de cliquer à droite sur le projet DotNetAspireDemo.API, sélectionner le sous-menu “Ajouter” et choisir “Support orchestrateur .NET Aspire”.
Par la suite, dans la solution, les modifications suivantes se produisent :
- Un projet orchestrateur AppHost est ajouté : Il gère la découverte de services et la gestion des chaînes de connexion;
- Le projet AppHost devient le projet de démarrage par défaut de la solution : il devient le point d’entrée de votre application;
- Un projet ServiceDefaults est ajouté contenant la configuration OpenTelemetry, les points de terminaison par défaut et l’activation du Service Discovery;
- La dépendances sur le projet API inscrit dans l’orchestration est ajoutées au projet AppHost;
var builder = DistributedApplication.CreateBuilder(args);
builder.AddProject<Projects.DotNetAspireDemo_API>("dotnetaspiredemo-api");
builder.Build().Run();
- Le tableau de bord .NET Aspire est ajouté à la solution permettant d’accéder à tous les points de terminaison de projet de la solution;
- Le tableau de bord ajoute des journaux, des traces et des métriques liés aux projets de la solution
Si on ajoute une orchestration à un projet .NET web, on doit apporter des modifications sur le fichier Program.cs de ce dernier de la manière suivante :
builder.AddServiceDefaults();
// Add services to the container.
var app = builder.Build();
app.MapDefaultEndpoints();
// Configure the HTTP request pipeline.
C’est le cas dans notre exemple avec le projet API.
Intégrer le projet Angular dans .NET Aspire
D’abord il faut ajouter le package NuGet Aspire.Hosting.NodeJs dans le projet Host. Ce package contient des méthodes d’extension et des définitions de ressources pour un .NET Aspire AppHost afin de configurer un projet Node.js.
Ensuite on modifie le code du fichier Program.cs du projet Host de la manière suivante :
var builder = DistributedApplication.CreateBuilder(args);
var dotnetaspiredemoApi = builder.AddProject<Projects.DotNetAspireDemo_API>("dotnetaspiredemo-api").WithExternalHttpEndpoints();
builder.AddNpmApp("angular-front", "../DotNetAspireDemo.Front")
.WithReference(dotnetaspiredemoApi)
.WithHttpEndpoint(env: "PORT")
.WithExternalHttpEndpoints();
builder.Build().Run();
Cela déclare les ressources de l’application cliente, à l’aide du AddNpmApp du package Aspire.Hosting.NodeJs, en tant qu’application npm. Le projet “angular-front” fait référence au service API “dotnetaspiredemoApi” ajoutés précédemment.
Il y a plusieurs modifications à faire dans le projet Angular :
- Ajouter un fichier proxy.conf.js contenant la configuration liée aux requêtes HTTP du client Angular vers le service “dotnetaspiredemo-api” : toutes requêtes HTTP commençant par /api cible l’URL dans la variable d’environnement services__weatherapi__http__0. Cette variable d’environnement est définie par .NET Aspire AppHost et est utilisée pour résoudre le point de terminaison du service “dotnetaspiredemo-api”.
module.exports = {
"/api": {
target:
process.env["services__dotnetaspiredemo-api__https__0"] ||
process.env["services__dotnetaspiredemo-api__http__0"],
secure: process.env["NODE_ENV"] !== "development",
pathRewrite: {
"^/api": "",
},
},
};
- Modifier le fichier angular.json : ajouter “proxyConfig” dans la propriété “options” de section “serve” afin de référencier le fichier proxy.conf.js.
"architect": {
"build": {
//...
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "DotNetAspireDemo.Front:build:production"
},
"development": {
"buildTarget": "DotNetAspireDemo.Front:build:development"
}
},
"defaultConfiguration": "development",
"options": {
"proxyConfig": "proxy.conf.js"
}
}
- Mettre à jour le fichier package.json : configurer Angular pour qu’il s’exécute sur un port différent du port par défaut. Ceci est réalisé en utilisant le “PORT” variable d’environnement et le run-script-os (package npm) pour définir le port.
{
"name": "dot-net-aspire-demo.front",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "run-script-os",
"start:win32": "ng serve --port %PORT%",
"start:default": "ng serve --port $PORT",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"serve:ssr:DotNetAspireDemo.Front": "node dist/dot-net-aspire-demo.front/server/server.mjs"
},
"private": true,
"dependencies": {
//...
},
"devDependencies": {
//...
"run-script-os": "^1.1.6"
}
}
Exécution
Lors de l’exécution d’application .NET Aspire, on a un tableau de bord contenant les éléments d’application : une application “dotnetaspiredemo-api” en ASP.NET Core et une application “angular-front” en Angular.
Pour consulter l’application client Angular, il faut accéder au point de terminaison Angular dans le .NET Aspire tableau de bord. L’image suivante représente l’application client Angular:
Remarque : Il est tout à fait possible d’exécuter l’application client Angular, de consulter sa console via .NET Aspire. Cependant, contrairement aux applications en .NET, la surveillance de la télémétrie d’application Angular n’est pas implémentée automatiquement. Cela implique d’utiliser d’autres outils tels que Application Insights pour la mise en place de cette télémétrie.
Conclusion
.NET ASPIRE est conçu afin de faciliter la création d’applications cloud natives et aider les développeurs à être plus productifs.
Il peut évoluer d’un seul projet ou service avec sa dépendance de base de données jusqu’à un système distribué complet avec tous les services et dépendances fonctionnant ensemble.
Sources
Orchestrate Node.js apps in .NET Aspire
Introducing .NET Aspire: Simplifying Cloud-Native Development with .NET 8