La série .NET Aspire se poursuit avec les intégrations. Nous ajouterons des intégrations SQL Server et Redis et montrerons comment Aspire le gère pour nous.
Il s’agit de la quatrième partie d’une série exploratoire en six parties sur .NET Aspire.
Bonjour encore, amis.
Dans les trois premiers versements de cette série .net Aspire, nous avons fait ce qui suit:
- Dit Salut à .net Aspire et a vu comment son approche d’opinion supprime une tonne de douleur native du nuage.
- Tiré le tableau de bord du développeur Pour regarder les traces, les journaux et les mesures sans jongler avec les fenêtres terminales.
- Rencontré Service par défaut et a appris comment une méthode d’extension unique répartit la télémétrie, les contrôles de santé et la résilience dans tous les projets.
Aujourd’hui, nous transformerons l’infrastructure en code C #. Nous allons saisir mon conteneur SQL Server piraté à partir de Docker Compose, le déposer dans Aspire et accélérer les lectures avec un cache Redis. Les deux ressources tournent, câblent et s’allument dans le tableau de bord. Aucune gymnastique portuaire n’est requise.
Assez de parler! Apporter le code.
Ajouter un serveur SQL conteneurisé à .net Aspire
Mon conteneur SQL Server fonctionne dans Docker Compose mais c’est un citoyen de deuxième classe. En l’invitant à aspirer, nous obtenons:
- Une chaîne d’outils partagée: Nous en utilisons un
AppHost
un profil de lancement et un pipeline CI. - Pas extra
docker compose up
étape: Frapper F5 (oudotnet run
) et SQL commence par tout le reste. - Sondes de santé automatiques, découvertes et secrets: Le service par défaut injecte les chaînes de connexion et les points de terminaison OTLP pour nous.
- Observabilité native: Les journaux, les traces et les métriques se déroulent directement vers le tableau de bord du développeur.
Ajouter SQL Server en tant que ressource Aspire
Pour commencer cette fête, naviguez vers notre AppHost
Projet et mise à jour Program.cs
. Après avoir examiné le code, nous le parcourons en détail.
var builder = DistributedApplication.CreateBuilder(args);
var password = builder.AddParameter("password", secret: true);
var server = builder.AddSqlServer("server", password, 1433)
.WithDataVolume("guitar-data")
.WithLifetime(ContainerLifetime.Persistent);
var db = server.AddDatabase("guitardb");
var api = builder.AddProject<Projects.Api>("api")
.WithReference(db)
.WaitFor(db);
builder.AddProject<Projects.Frontend>("frontend")
.WithReference(api)
.WaitFor(api)
.WithExternalHttpEndpoints();
builder.Build().Run();
Capture en toute sécurité des secrets
Dans cet exemple:
var password = builder.AddParameter("password", secret: true);
AddParameter
Crée une ressource de paramètre. Lorsque nous définissons le secret
paramètre true
nous disons à Aspire de traiter la valeur comme un secret et facilite la remplacement avec une variable d’environnement ou des secrets d’utilisateurs pendant le développement local.
Dans notre secrets.json
(ou appsettings.json
), Je l’ai lu en utilisant la structure suivante:
{
"Parameters": {
"password": "Dave#123"
}
}
Évidemment, Cela ne convient que pour le développement local. Lors du déploiement sur des serveurs, vous voudrez un meilleur modèle de sécurité comme Azure Key Vault. Dans ce cas, vous pouvez mettre à jour votre code à ce qui suit:
var keyVault = builder.AddAzureKeyVault("key‑vault");
builder.Configuration.AddAzureKeyVaultSecrets("key‑vault");
var sqlPassword = builder.AddParameter("sql-password", secret: true);
Tournez un serveur SQL avec stockage
Pour cet exemple:
var server = builder.AddSqlServer("server", password, 1433)
.WithDataVolume("guitar-data")
.WithLifetime(ContainerLifetime.Persistent);
Que vient-il de se passer?
AddSqlServer
tire le fonctionnairemcr.microsoft.com/mssql/server:2022-latest
image, setsSA_PASSWORD
à notre mot de passe et le mappe au port 1433.WithDataVolume("guitar-data")
Monte un volume Docker nommé pour que notre conteneur survit des reconstructions.- Enfin,
WithLifetime(ContainerLifetime.Persistent)
dit à Aspire de réutiliser la même instance entre les courses. Cela évite les temps de démarrage lents et devoir exécuter des migrations à chaque fois.
Ajout de la base de données
Ce liner…
var db = server.AddDatabase("guitardb");
… Le sucre syntaxtique est-il pour écrire un CREATE DATABASE guitardb
Commande lorsque nous lançons pour la première fois, et génère également une ressource de chaîne de connexion fortement tapée. Cette chaîne de connexion peut désormais être référencée par n’importe quel projet qui en a besoin.
Nous avons ajouté la base de données, mais elle est vide et nous devons la semer. En commençant par Aspire 9.2, vous pouvez utiliser le WithCreationScript
méthode.
Avec un script (ou un ensemble de scripts) en main, assurez-vous de le copier dans le AppHost
Répertoire de sortie en l’ajoutant à votre fichier de projet:
<ItemGroup>
<None Include="..\Api\data\createdb.sql" Link="createdb.sql">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
Nous pouvons maintenant mettre à jour les apphost Program.cs
Code de base de données à ce qui suit.
var password = builder.AddParameter("password", secret: true);
var server = builder.AddSqlServer("server", password, 1433)
.WithDataVolume("guitar-data")
.WithLifetime(ContainerLifetime.Persistent);
var scriptPath = Path.Join(Path.GetDirectoryName(typeof(Program).Assembly.Location), "createdb.sql");
var db = server.AddDatabase("guitardb").WithCreationScript(File.ReadAllText(scriptPath));
Ce n’est qu’une des nombreuses façons de voir les données. Pour en savoir plus, Lire les documents Microsoft.
Enregistrez l’API avec la base de données
Enfin, ce code enregistre l’API à la base de données.
var api = builder.AddProject<Projects.Api>("api")
.WithReference(db)
.WaitFor(db);
Le WithReference(db)
Injecte deux choses dans le conteneur:
- La chaîne de connexion comme
ConnectionStrings__guitardb
- L’adresse de la santé de la santé du conteneur SQL afin que votre application sache quand la base de données est en place
Le WaitFor(db)
Ligne signifie que Aspire ne sera même pas docker run
L’API jusqu’à SQL Server le signale comme saine.
Mettez à jour l’API d’inventaire
Parce que nous comptons déjà sur les valeurs par défaut du service, l’API peut ramasser builder.Configuration.GetConnectionString("db")
automatiquement. Le seul code que nous devons toucher est de s’assurer que EF Core est enregistré pour utiliser la base de données dans le projet API Program.cs
:
builder.Services.AddSqlServer<GuitarStoreContext>("guitardb");
Cette seule ligne permet au AppHost
Projet pour faire tourner le conteneur SQL Server et injecter sa chaîne de connexion dans l’API au lancement. Le service par défaut est ensuite de la journalisation, du traçage, des métriques et de la résilience. En conséquence, le AddSqlServer<GuitarStoreContext>("guitardb")
L’appel saisit la chaîne de connexion injectée, enregistre le DbContext
et se branche sur notre observabilité hors de la boîte.
Examiner le tableau de bord
Lorsque nous lançons l’application, nous voyons maintenant la base de données opérationnelle à côté de notre application.
Si nous passons en revue les détails de la ressource API, nous voyons également la chaîne de connexion injectée.
Notre télémétrie s’allume: si nous naviguons vers nos traces, nous pouvons voir une demande de bout en bout.
Notre base de données est officiellement hors de Docker Compose et dans Aspire! Nous pouvons maintenant supprimer l’ancien docker-compose.yml
et utilisez SQL Server aux côtés du reste de notre projet avec une seule commande F5 (ou dotnet run
).
Nous pouvons maintenant passer à notre intégration Redis pour accélérer nos lectures.
Ajout de redis pour la mise en cache rapide de la foudre
Notre /guitars
Le point de terminaison est lourd en lecture. Pour le rendre rapide à la foudre (et pour éviter lestiné à la base de données), nous ajouterons Redis en tant que couche de cache et permettons d’aspirer les choses.
Pour filmer Redis dans AppHost
nous allons l’ajouter juste après l’extrait SQL.
var cache = builder.AddRedis("cache")
.WithRedisInsight()
.WithLifetime(ContainerLifetime.Persistent);
Que se passe-t-il ici? Nous tirons un conteneur redis de docker.io/library/redis
Cela aide à éviter les installations locales ou les cordes de port locales. Nous obtenons également un PING
sonde de santé qui est visible à partir du /health
Point de terminaison et tableau de bord du développeur.
Ensuite, la chaîne de connexion ConnectionStrings:cache
est injecté automatiquement dans tout projet qui appelle WithReference(cache)
.
Facultativement, nous pouvons également inclure notre interface utilisateur d’administration Redis préférée, en tant que supports Aspire Redis insight ou Redis commandant. J’ai ajouté Redis Insight. Quand je le fais, Aspire s’empare docker.io/redis/redisinsight
pour me permettre de travailler facilement avec mon instance redis.
Tout comme avec notre conteneur SQL Server, nous ajoutons .WithLifetime(ContainerLifetime.Persistent)
Pour dire Aspire, « Ne démolir pas le conteneur quand j’arrête de déboguer. » Le résultat est des start-ups beaucoup plus rapides et un état durable entre les sessions – le grand pour le développement quotidien, les démos ou les antécédents de longue durée. Si jamais vous avez besoin d’une ardoise propre, vous pouvez toujours tailler le conteneur manuellement ou ramener la durée de vie à la valeur par défaut ContainerLifetime.Transient
mode.
Voici mon tout Program.cs
dans AppHost
:
var builder = DistributedApplication.CreateBuilder(args);
var password = builder.AddParameter("password", secret: true);
var server = builder.AddSqlServer("server", password, 1433)
.WithDataVolume("guitar-data")
.WithLifetime(ContainerLifetime.Persistent);
var db = server.AddDatabase("guitardb");
var cache = builder.AddRedis("cache")
.WithRedisInsight()
.WithLifetime(ContainerLifetime.Persistent);
var api = builder.AddProject<Projects.Api>("api")
.WithReference(db)
.WithReference(cache)
.WaitFor(db);
builder.AddProject<Projects.Frontend>("frontend")
.WithReference(api)
.WaitFor(api)
.WithExternalHttpEndpoints();
builder.Build().Run();
Mettez à jour notre API pour utiliser Redis
Comment mettre à jour l’API pour utiliser Redis? Sauprouillons une mise en cache minimale sur le GET /guitars
point de terminaison. Après avoir installé le StackExchange.Redis
Emballage, nous pouvons ajouter un décorateur pour utiliser la mise en cache de sortie.
app.UseOutputCache();
app.MapGet("/guitars",
[OutputCache(Duration = 30)] async (GuitarStoreContext db) =>
await db.Guitars
.OrderBy(g => g.Brand).ThenBy(g => g.Model)
.Select(g => new { g.Id, g.Brand, g.Model, g.Series, g.Price, g.SalePrice })
.ToListAsync());
Le middleware de mise en cache de sortie peut désormais intercepter les demandes après que Aspire ait enregistré le magasin Redis. Pour les tests, nous utilisons un TTL de 30 secondes, mais une fois que nous sommes en ligne, nous pouvons l’ajuster par la configuration.
Exécutons maintenant la solution et ouvrons le tableau de bord. Nos ressources continuent de se développer.
Si nous naviguons sur notre URL Redis Insight, nous pouvons voir le cache!
Lorsque nous licencions certains appels, nous voyons maintenant une demande de bout en bout terminer en moins de 3,43 ms. Pas mal!
Avec SQL Server et Redis dans Play, notre tableau de bord montre désormais un graphique de dépendance pointant vers les nœuds SQL et Redis.
Les développeurs sont heureux
Avant Aspire (et Docker Containeurs), pouvez-vous imaginer tout le travail, un nouveau développeur accès à un environnement local avec plusieurs API, la journalisation, un serveur SQL et une instance Redis?
Maintenant, voici tout ce que nous devons faire:
git clone https://github.com/davesguitarshop
cd inventory
dotnet run --project GuitarShop.AppHost
Avec une seule commande, nous restaurons les packages NuGet, créons des projets, démarrons nos conteneurs SQL Server et Redis, lancez notre tableau de bord et ouvrez un onglet de navigateur à https://localhost:5001
. Pas d’installations SQL, non docker compose up
Et pas de «quels ports devons-nous utiliser à nouveau?» Quel monde.
Un aperçu: arriver au nuage
Bien que le bonheur du développeur soit énorme, le véritable gain survient lorsque nous poussons vers le cloud – aux applications de conteneurs Azure, par exemple. Parce que la même chose AppHost
La définition voyage avec nous, le pipeline n’a besoin que:
az containerapp up \
--name guitar-shop \
--resource-group guitar-shop-rg \
--subscription guitar-shop-prd
En conséquence, l’extension de l’application Azure Container d’Aspire sera mapée:
AddSqlServer
– Azure SQL (ou apporter votre propre image)AddRedis
– Azure Cache pour redis (ou votre propre image)- Problèmes de santé – Points de terminaison de la vivacité et de préparation de style Kubernetes
- OTLP – Azure Monitor / AppInSights
Nous aurons beaucoup plus dans la partie 6 – assis à l’écoute!
Envelopper (et quelle est la prochaine)
Nous venons de remplacer un énorme docker-compose.yml
une gymnastique à demi-radieuse à l’environnement et un réadme plein de «Run This Before This» commandes avec seulement quelques lignes de C # – toutes découvrables de Intellisense. N’êtes-vous pas diverti?
Les principaux plats à retenir:
- Les intégrations sont des ressources, pas des tâches. Après avoir déclaré l’intention avec
AddSqlServer
etAddRedis
Aspire gère les images, les ports et la santé. - Les chaînes de connexion s’écoulent automatiquement.
- L’observabilité est intégrée. Chaque requête, hit du cache et exception apparaissent dans le tableau de bord avec zéro code supplémentaire.
- La configuration locale des développeurs est triviale. Les nouveaux développeurs exécutent une commande et commencent le codage.
- Portabilité. La même définition AppHost se déplace sur Azure Container Apps, AKS ou Docker Swarm avec un minimum de réglages.
Dans la partie 5, nous allons plonger dans la découverte et l’orchestration des services – comment Aspire permet aux services de se parler sans URL du codage dur et comment les politiques Polly maintiennent les choses résilientes lorsque le réseau se fâche.
Jusque-là, codage heureux et à bientôt!
Source link