Azure DevOps – Build d’un site ASP.NET Core et déploiement

Azure DevOps – Build d’un site ASP.NET Core et déploiement

ASP.NET Core est une révolution. Certes ce n’est pas une technologie toute nouvelle car elle fait suite à plus de 15ans d’ASP.Net, mais c’est une révolution pour le développeur .NET que je suis depuis la première version de Visual Studio .NET en 2001.

Pourquoi ? Et bien parce que plus besoin de IIS, plus besoin du module WarmUp pour que le site charge rapidement, plus besoin de Windows et bien d’autres choses encore 😉 En gros, cela me paraît être le meilleur d’ASP.NET MVC 4.5 avec encore plus de puissance, de simplicité, de productivité, et hébergé sur Linux !

Si vous découvrez .NET Core depuis peu, comprenons-nous bien, après la définition de .NET Standard, Microsoft s’est dit que ce serait bien de « réécrire .NET » (les guillemets ne sont pas de trop) pour apprendre des évolutions de la plateforme sur les 15 dernières années, en profiter pour faire du propre et surtout, rendre une bonne partie du framework compatible Mac et Linux. Oui, c’est pour moi une petite révolution.


Dans ce tutoriel je m’attarderai uniquement sur la notion de build et déploiement par et pour Linux. J’entends par là le build par un agent Azure Pipelines hébergé dans un container Docker et le déploiement (+ hébergement) pour un serveur Linux.

Création de l’application web et push

Pour l’exemple, rien d’exotique, on créé un site ASP.NET Core MVC v2.2, sans authentification. Pas besoin d’ajouter de code, le template par défaut suffit. Toujours par simplicité, je reste sur la branche master de mon versionning Azure DevOps, je lance le projet dans un navigateur pour vérifier que tout est OK et je pousse le projet.

Notre site est créé, versionné dans Azure DevOps, il faut désormais en faire une build et la déployer.

Build du site

Pour se faire on reste dans Azure DevOps, dans Pipelines, puis Builds, on va créer un nouveau pipeline en utilisant l’éditeur classique.

Le template que l’on va utiliser est bien entendu celui pour ASP.NET Core

Une fois appliqué, on constate 5 étapes de build :

  • La restauration des dépendances (packages NuGet)
  • Le build du code
  • Le test du code
  • La publication du code généré
  • Et la publication des artéfacts

Dans notre cas, je supprime l’étape de test du code n’ayant pas créé de projet de test unitaire associé au site web et je désactive également la publication des artéfacts, n’en ayant pas l’utilité.

Je créer une nouvelle tâche à mon pipeline, Copy files over SSH, pour copier le résultat de ma publication afin d’être hébergé sur le serveur désiré.

Pour copier les fichiers sur mon serveur, il faut que l’agent Azure Pipelines y ait accès.

Dans notre cas, j’ai déclaré un accès SSH dans Azure DevOps intitulé « Monserveur ». J’indique ensuite à la tâche où se trouve les fichiers de publication, et où je souhaite les copier.

Copie des fichiers depuis le repo staging de l’agent vers /data/web

Je lance le build et tout s’exécute sans accroc. Arrivé ici, j’ai fait une publication de mon code et je l’ai entreposé sur mon serveur.

Code YAML du pipeline

Déploiement n°1 : Site porté par un service linux

Cette première option de déploiement est la plus utilisée lorsque l’on déploie plusieurs applications web sur un serveur. Dans ce cas, un service = une application.

Attention dans ce cas, j’estime que le runtime ASPNET.Core v2.2 est déjà installé sur le serveur Linux. Procédure d’intallation officielle ici pour CentOS.

On va créer avec l’éditeur nano, un fichier de service pour l’hébergement de notre site

$ nano /etc/systemd/system/web-app.service

[Unit]
Description=Mon application web

[Service]
WorkingDirectory=/data/web
ExecStart=/usr/bin/dotnet /data/web/BuildAndDeployWeb.dll
Restart=always
Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=dotnet-example
User=root
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
Environment=ASPNETCORE_URLS=http://*:80

[Install]
WantedBy=multi-user.targeta

Celui-ci indique où se trouvent nos fichiers (WorkingDirectory), la commande a exécuter (ExecStart) et quelques variables d’environnement spécifiques à .NET Core dont l’adresse et le port du listener, ici *:80.

Pour que le service fonctionne immédiatement, on autorise l’exécution du fichier d’entrée par la commande

$ chmod a+x /etc/systemd/system/wep-app.service

On recharge systemd pour la bonne prise en compte du nouveau service

$ systemctl --system daemon-reload

Et on exécute notre service

$ systemctl start wep-app.service

On vérifie le bon fonctionnement du service par deux procédés, le status du service par la commande :

$ systemctl status web-app.service

et l’accès au site web par l’IP ou le nom d’hôte du serveur

Notre site web est hébergé dans un service Linux et fonctionne, toutefois il n’est pas automatisé avec Azure DevOps. L’idéal est qu’à chaque déploiement de l’application web, le service soit arrêté avant la copie des nouveaux fichiers de build et redémarré après.

On ajoute dans ce cas deux tâches SSH permettant ces actions :

Code YAML du pipeline pour ce déploiement

Déploiement n°2 : Site porté par un container Docker (Docker CLI)

Le déploiement par service fonctionne mais n’est pas idéal pour un déploiement par Azure DevOps. La mise en place initiale du service est une action manuelle et toute évolution passe par la mise à jour du fichier de service.

Cette seconde méthode de déploiement et les deux qui suivront passent par Docker pour l’hébergement de l’application web. Je vous invite tout d’abord à vous rapprocher de l’image Docker officielle pour le runtime ASP.NET Core v2.2.

En reprenant les paramètres utilisés dans la première option de déploiement, la commande Docker CLI donne le résultat suivant :

$ docker run -it \
-p 80:80 \
-w /app \
-v /data/web:/app
--name app-web \
-e "ASPNETCORE_URLS=http://*:80"
-e "ASPNETCORE_ENVIRONMENT=Production"
-e "DOTNET_PRINT_TELEMETRY_MESSAGE=false"
mcr.microsoft.com/dotnet/core/aspnet:2.2 dotnet BuildAndDeployWeb.dll

Dans l’ordre des paramètres de commande :

  • Le port 80 est reporté
  • Le working directory dans le container est dans le chemin /app
  • Le lien entre le stockage physique des fichier et le stockage dans le container est fait
  • Le container est nommé app-web
  • La variable d’environnement spécifie à Kestrel d’héberger l’application sur le port 80
  • L’environnement ASP.Net Core utilisé est celui de « Production »
  • On n’affiche pas les données de télémétrie

Pour le déploiement via Azure DevOps, même problème que pour le premier déploiement par service Linux, il nous faut arrêter le container au préalable et le relancer dans une tâche SSH, puis le recréer après la copie.

Le problème encore une fois ici est qu’il faut avoir créé au moins une fois le container pour procéder à ce déploiement. On tout aussi procéder avec moins de délicatesse et ignorer les message d’erreur si le container n’existe pas. La régularité et le suivi ne sont plus au rendez-vous à ce moment là…

Pour les amateurs de Docker Compose, la procédure est semblable; on peut incorporer directement le fichier docker-compose.yml à la racine du site ou dans un dossier parent et on execute la commande docker-compose up -d pour interpréter la définition du fichier.

Code YAML du pipeline pour ce déploiement

Déploiement n°3 : Site porté par un container Docker (Repo + Dockerfile + Image)

Autant vous dire que c’est (à mon avis) la solution par excellence de nos jours qui défini véritablement de Continuous Delivery de l’acronyme CI/CD.

Le principe ici est plus dense, plus long à mettre en place, mais il ne bouge plus ensuite et est fortement intégré à votre projet Visual Studio.

De retour dans l’IDE, commencer par ajouter l’intrégration de Docker à votre projet par un clic droit, Add > Docker Support puis Linux en TargetOS.

Un fichier Dockerfile (sans extension) est créé dans votre projet et….. voilà 🙂

Votre projet est ainsi prêt à suivre le processus de build en une image Docker basée elle-même sur l’image mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim de Microsoft.

  • 1er bloc : l’image de base s’appuie sur aspnet:2.2-stretch-slim et expose les ports 80 + 443
  • 2ème bloc : on build le code depuis l’image sdk:2.2-stretch
  • 3ème bloc : on publie le code
  • 4ème bloc : on ajoute le code publié à l’image de base et on indique la commande d’exécution au démarrage du container

On commit et push le code pour prendre en compte le fichier Dockerfile dans le versionning.

De retour désormais dans Azure DevOps, notre pipeline de build et déploiement va radicalement changer. Plus besoin d’effectuer le build et la publication du code puisque ces étapes sont comprises dans la génération de l’image Docker.

Le mieux est de créer un template de pipeline vide pour commencer. On ajoute trois tâches Docker une pour executer le build depuis le fichier Dockerfile, une seconde pour s’authentifier sur le repo Docker et une troisième pour pousser l’image ainsi créer.

Il ne reste plus qu’à ajouter une tâche SSH pour faire un pull de l’image Docker nouvellement générée et référencé puis executer la commande de déploiement du container.

Personnellement, j’aime pousser deux images à la fois, l’une taguée ‘latest’ (c’est celle que je redescends toujours) et une autre avec comme tag, l’incrément de mon build pour la garder comme historique. Ainsi en cas de retour arrière sur une mise en prod, j’ai juste à redéployer l’image avec le tag = numéro de build – 1.

Code YAML du pipeline pour ce déploiement

Autre solution ?

Bien sûr il y a d’autres solutions de déploiement, mais celles-ci me semblent être les plus courantes. Sans aucun doute vous les utilisez également et peut être autrement.

Pour un peu plus de qualité dans cette dernière méthode de déploiement, j’utilise la fonctionnalité Releases de Azure Pipeline pour ajouter une notion de validation/approbation par workflow au déploiement et ainsi laisser libre champs à un pôle qualité/recette pour valider un environnement de recette, et au chef de projet associé à la maitrise d’ouvrage, pour déclencher le déploiement en production par un simple clic.

Laisser un commentaire