Mittelstand Radar : Signaux d'achat du Mittelstand allemand — réservez dès maintenant votre première édition du rapport.Rejoindre la liste d'attente
Plusieurs cargos en mer photographiés du dessus, naviguant dans différentes directions
Retour au blog
dotnetaspiredeployment

aspire deploy - que se passe-t-il réellement ?

Sven HennessenDevOps

Aspire 13 a considérablement élargi les plateformes de déploiement prises en charge : Docker Compose, Kubernetes, AKS et Azure App Service deviennent des cibles complètes, avec Helm sous le capot.

Dans Aspire 9, il n'existait qu'un seul chemin de déploiement pleinement pris en charge : Azure Container Apps via azd. À partir d'Aspire 13, cela a changé en profondeur. Cet article décrit ce que fait réellement aspire deploy pour chaque cible prise en charge, et où se situent les limites actuelles.

Le socle commun : PipelineStep

Quelle que soit la cible de déploiement, chaque aspire deploy passe par la même architecture pipeline. Chaque ressource de l'AppHost apporte des étapes qui déclarent leurs dépendances entre elles. Aspire construit un graphe d'exécution à partir de cela et exécute en parallèle les étapes indépendantes. Ce qui était auparavant une série séquentielle d'appels azd devient maintenant un processus visible et inspectable :

aspire deploy --list-steps   # show steps without executing
aspire deploy                # run deployment
aspire destroy               # tear down deployment

Azure Container Apps

Le chemin établi depuis Aspire 9 a été conservé et passe maintenant par le pipeline. La nouveauté est que l'Azure Container Registry n'est plus implicitement lié à l'environnement Container Apps, il est provisionné comme ressource autonome. Cela permet de le réutiliser entre environnements et de pousser des images en parallèle pendant le provisionnement.

var registry = builder.AddAzureContainerRegistry("acr");
var env = builder.AddAzureContainerAppEnvironment("prod")
    .WithAcrPullIdentity(registry);

Les Container App Jobs, pour les workloads batch et les processus de fond, sont stables à partir de 13.4 :

builder.AddProject<Projects.Worker>("worker")
    .PublishAsAzureContainerAppJob();

Azure App Service

Azure App Service a été ajouté comme cible de déploiement pleinement prise en charge dès 13.0. Le public cible : les équipes qui veulent continuer à utiliser leur infrastructure App Service existante sans migrer vers Container Apps.

builder.AddAzureAppServiceEnvironment("staging");

À partir de 13.1, les deployment slots ont été ajoutés. Les déploiements sans interruption peuvent être configurés directement depuis l'AppHost : Aspire déploie sur le slot de staging puis effectue automatiquement le swap vers la production.

builder.AddProject<Projects.Api>("api")
    .WithDeploymentSlot("staging");

L'Aspire Dashboard est automatiquement intégré comme connexion Application Insights pour les déploiements App Service.

Docker Compose

Docker Compose a suivi un chemin de maturité clair tout au long de la série 13.x :

  • 13.1 : première prise en charge complète du déploiement, aspire deploy fonctionnait de bout en bout
  • 13.2 : sortie du statut preview, stable

L'AppHost décrit comme d'habitude la structure de l'application. Aspire génère un docker-compose.yaml au moment du publish et exécute docker compose up lors du déploiement. aspire destroy arrête ensuite la stack.

builder.AddDockerComposeEnvironment("local");

Docker Compose est donc le seul chemin de déploiement pleinement pris en charge sans dépendance cloud, pertinent pour les scénarios on-premises, les environnements clients sans accès Azure ou des setups de staging simples.

Kubernetes, générique

Depuis Aspire 13.3, aspire deploy prend en charge les clusters Kubernetes génériques de bout en bout. Aspire génère des charts Helm à partir de l'AppHost et exécute helm install ou helm upgrade contre le cluster configuré.

builder.AddKubernetesEnvironment("k8s")
    .WithHelm(helm => helm.ReleaseName("myapp").Namespace("production"));

aspire destroy exécute helm uninstall et supprime les namespaces. Les charts générés contiennent des fichiers template individuels par ressource, Deployments, Services, ConfigMaps, Secrets et PersistentVolumeClaims.

Le routage et TLS peuvent être modélisés directement dans l'AppHost à partir de 13.3 :

builder.AddIngress("api-ingress")
    .WithPath("/api", api)
    .WithTls();

Aspire génère les ressources Kubernetes Ingress correspondantes. Lorsque WithTls() est utilisé sans hostname explicite, Aspire découvre automatiquement le FQDN attribué par le cluster.

À partir de 13.4, cert-manager avec Let's Encrypt est disponible via une API typée :

env.AddCertManager()
   .WithLetsEncrypt(staging: false);

Toujours en 13.4, des charts Helm externes peuvent être intégrés comme étapes de déploiement, de sorte que les ressources Aspire et les charts standards de l'écosystème s'exécutent dans un pipeline unique :

builder.AddHelmChart("nginx-ingress", "ingress-nginx/ingress-nginx", "4.10.0");

Azure Kubernetes Service, AKS

AKS a reçu sa propre intégration d'hébergement en 13.3, Aspire.Hosting.Azure.Kubernetes, combinant provisionnement du cluster et déploiement Helm dans un seul pipeline :

builder.AddAzureKubernetesEnvironment("prod-aks")
    .WithHelm();

Aspire génère des templates Bicep pour le cluster puis exécute l'étape de déploiement Helm, sans configuration Terraform séparée ni création manuelle de cluster. Les node pools, niveaux de SKU et clusters privés sont configurables.

À partir de 13.4, Azure Application Gateway for Containers, AGC, est intégrable directement. Aspire attribue automatiquement le rôle Network Contributor nécessaire à l'identité du contrôleur :

env.AddLoadBalancer("agc");

Kubernetes : les pièges en pratique

PublishAsDockerFile et le problème des args manquants

Un piège silencieux affecte les services Python et JavaScript : appeler .PublishAsDockerFile(configure => { }) sur une app Uvicorn provoque la disparition des args générés dans le manifest Helm, sans erreur, sans warning. Le conteneur démarre alors avec le mauvais entrypoint.

L'équipe Aspire a marqué ce problème avec le label silent-failure : le comportement est intentionnel, le callback signifie "je prends le contrôle complet", mais la conséquence n'est pas documentée. Toute personne utilisant PublishAsDockerFile avec un callback vide doit définir explicitement les args :

builder.AddUvicornApp("app", "./app", "main:app")
    .WithUv()
    .PublishAsDockerFile(configure =>
    {
        configure.WithArgs("main:app", "--host", "0.0.0.0", "--port", "8000");
    });

Tant que cela n'est pas corrigé (microsoft/aspire#16874), vérifiez toujours les fichiers générés templates/*/deployment.yaml pour la présence de blocs args après publication.

Sidecars et PersistentVolumes

Les sidecar containers ne sont actuellement pas correctement traduits en manifests Kubernetes : ils finissent comme pods séparés, rendant les volumes partagés impossibles. Toute personne définissant des sidecars dans l'AppHost et déployant sur Kubernetes n'obtient ni erreur, ni manifest correct.

WithPersistentVolume existe à partir de 13.4, mais est marqué [Experimental("ASPIRECOMPUTE002")]. Les workloads stateful, bases de données, message brokers, sont donc possibles sur Kubernetes en principe, avec la réserve que la surface API n'est pas encore stable. Effet secondaire pratique : Aspire convertit automatiquement un Deployment en StatefulSet dès qu'un PVC est attaché. C'est correct, mais cela peut casser des helm upgrade existants si le workload était auparavant déployé comme Deployment.

Le problème des deux déploiements

Une limite structurelle affecte les services qui produisent au premier démarrage des données de bootstrap dont leurs consommateurs ont besoin, Keycloak, Vault, MinIO, Grafana. En local via aspire run, cela fonctionne de manière transparente grâce à ResourceReadyEvent. En mode déploiement, il n'existe pas de mécanisme équivalent.

Le contournement actuel est un pattern en deux déploiements : le premier aspire deploy démarre le service de bootstrap, puis une étape pipeline personnalisée appelle l'API de provisionnement et enregistre les valeurs dans IDeploymentStateManager. Le aspire deploy suivant lit l'état sauvegardé et injecte les valeurs dans les services dépendants. Cela fonctionne, mais ce n'est pas idempotent au premier run et ce n'est documenté officiellement nulle part.

La feature request sous-jacente (microsoft/aspire#18097) décrit un concept AddDeferredValue qui résoudrait précisément ce problème. En juin 2026, elle reste ouverte.

Ce qui manque encore

AWS, GCP et les environnements on-premises complètement auto-gérés au-delà de Docker Compose n'ont toujours pas d'intégrations Aspire officielles. Le modèle pipeline est extensible via WithPipelineStepFactory, mais les fournisseurs personnalisés doivent être implémentés entièrement à la main. Pour Kubernetes générique, aspire deploy ne provisionne pas le cluster, celui-ci doit déjà exister.

Besoin d'aide ?

Vous voulez exécuter aspire deploy dans votre environnement de production, sur Kubernetes, AKS ou Docker Compose, mais vous ne savez pas par où commencer ni à quels pièges vous attendre ? Nous pouvons vous aider. Contactez-nous via notre page de contact et nous mettrons en place ensemble votre workflow de déploiement avec Aspire 13.