Onyx Solution – Une Mini-Formation sur l’Architecture Logiciel

Onyx Solution https://github.com/YannVasseur35/Onyx Onyx Solution Une solution Microsoft .net 7.0 / C# permettant l’apprentissage de concepts de software design, d’architecture, de clean code, de tests… Permet de partir sur de bonnes bases pour un petit/moyen projet, basé sur quelques années d’expériences dans le software design. Onyx est une pierre précieuse de couleur noire. J’ai donnée ce nom à ce projet d’apprentissage car il est court, facilement identifiable, abstrait, et j’aime bien donner des « noms de code » à un projet. On va prendre comme exemple une application de météo (qui est d’ailleurs l’exemple par défaut d’une application .net web) on aura donc Une API qui expose des données une base de données SQLite avec EntityFramwork (code first) Une clean architecture avec de nombreux tests + code coverage CI/CD avec une pipeline AzureDevops Introduction On a tous commencé par écrire un seul projet c# et tout mettre dedans, sans vraiment rien structurer. Alors oui, ça marche et honnêtement vu les ambitions de cette app méteo c’est ce que l’on devrait faire. Et puis comme tout bon projet, il évolue, en quelques années tout un tas de nouvelles fonctionnalités se sont ajoutés, plusieurs devs sont passés dessus, avec du code plus ou moins clair. De plus avec le temps, l’infrastructure a changé, on a fait évoluer la base de données, certain services sont directement sur le cloud et on a du faire face à de la montée en charge. On a du « scaler » l’application, recoder des parties, ajouter du cache etc…Et bien entendu rien n’est testé automatiquement. Bref la vie d’une application dans pas mal de boite en France et ailleurs dans le monde. Je développe avec .net depuis plus de 20 ans. Et j’ai vu un certain nombre d’application (un bon paquet à vrai dire) finir dans ce que l’on appelle un « monolith legacy », très très dure à maintenir. Ces applications existent encore à l’heure ou j’écris ces lignes. Parfois on en est au deuxième projet de « refonte ». Un cas typique est celui du projet ou l’on retrouve toute la logique métier dans les procédures SQL. Car c’est ce qui faisait vraiment gagner en pref à l’époque (et encore aujourd’hui). Mais seule la personne en charge de ces procédures stockées, souvent indéchiffrable et intestable, est en mesure de travailler dessus. Autre cas bancale, on a de la logique métier dans les pages web. Du coup on se retrouve à gérer à plusieurs endroits, du code qui fait plus ou moins la meme chose, au risque qu’un jour il ne fasse plus la même chose ! Heureusement aujourd’hui on a énormement plus de techniques, d’outils et de services qui permettent d’appréhender les projets autrement. Les entreprises comprennent les enjeux et les risques de garder trop longtemps du code legacy en production et s’engagent dans de gigantesque chantier de « renovation » voir même de ré-écriture complète. J’ai moi aussi contribuer à ces erreurs et je contribue aussi à leurs résolutions. Ce projet tente modestement de répondre à certaines problématiques couramment vues en entreprise. Je ne prétends pas tout résoudre mais apporter quelques réponses à des questions que moi même je me suis posé. Et à vrai dire, le meilleurs moyen d’y répondre pour moi est de tenter de le faire comprendre à quelqu’un d’autre. J’ai donc entrepris ce projet Onyx. Progression Vous avez deux façons de voir les choses : la rapide. Restez sur la branche master, la plus à jour et dernière en date. Vous avez tout le code source et tous les tests. Regardez le code et inspirez-en vous, prennez ce qui vous intéresse. l’apprentissage. Ce sera plus lent, mais j’explique tous les concepts pas à pas. On démarre gentillement et on progresse par étape (step). C’est une sorte de TP (travaux pratique) qu’on faisait à l’école. Pour suivre ce TP il faudra « naviguer » avec Git de branche en branche. C’est simple, toutes les branches s’appellent ‘step’ suvit d’un numéro. Chaque step aura sa propre doc (ex: step2.md). Suffit de s’y référer pour comprendre ce qu’y a été fait à chaque étape. Sommaire Step1 : Structure de solution Step2 : Premier endpoint Api Step3 : Test endpoint Api Rest Step4 : Application Service et Tests Step5 : DataService et Tests Step6 : Mapping et Tests Step7 : ORM (Entity Framework) et Tests Step8 : Entity Framework Step9 : Finalisation de la chaine de données Step10 : Code Coverage Step11 : CRUD : Implémentation et Tests Step12 : Tests d’intégration Step13 : CI/CD AzureDevops Pipeline Step14 : Pipeline avec Code Coverage Git Repo : https://dev.azure.com/ReactorLab/_git/Onyx Vous devrez connaitre quelques commandes git de base très simple pour « naviguer » dans les branches de ce repo. Pour changer de branche : git checkout step1 Si vous avez fait des modifications et que vous souhaitez changer de branche, git va vous obliger à faire un commit ou à vous débarraser de ces changements. Comme vous ne contribuez pas au projet, débarrassez vous de vos changements avec la commande stash, puis faites votre checkout git stash git checkout step2 Documentations Annexes Vous trouverez des informations complémentaires sur différents concept d’architecture ici (en anglais): Main Architecture Hexagonal Architecture DDD CQRS AND ES SOLID Tout est dans le répertoire docs.

Signoz et OpenTelemetry avec une app .net sous linux

Disclamer A l’heure ou j’écris cet article, le code source d’OpenTelemetry et Signoz sont en alpha/beta/pre-release.  Certaine lib sont en release officielle. J’édite au fur et à mesure certaine infos. Le code évolue et cet article aussi.  Il n’est certainement plus « à la page » lorsque vous lirez ces ligne.  Et de même, la doc officielle, celle de Microsoft et les ReadMe des repos ne sont pas tous mis à jours en temps et en heure.  C’est un sujet chaud pattate !!! Besoins Comme d’habitude, j’écris un article avec un vrai besoin et une application dans la vraie vie.  J’ai un serveur en prod et je souhaite le monitorer pour : observer son comportement (nombre de requêtes, % de CPU/RAM/Disk, periode d’activité/repos etc) analyser des problèmes, avoir accès aux logs sans ouvrir un terminal être alerté en cas de problème et pouvoir définir ces alertes avoir une vue d’ensemble avec des KPI de base   Contexte Depuis peu (qq années) on a la possibilité de faire tourner du .net sous linux. Ca change quoi allez vous me dire ?  Personnelement une question de coût. Un VPS (virual private server) sous linux c’est au moins deux fois moins cher que sous Windows (ça dépend avec quel fournisseur et les prix ont aussi évolué dans le temps). Mais aujourd’hui un VPS linux est utlra compétitif. Et j’ai pas de besoin cloud (même si j’y pense). Avec Windows on était habitué à installer des outils graphiques supplémentaires. Et meme de base on avait des outils de monitoring. Sous linux, c’est moins évident car on utilise principalement la ligne de commande. Et personnellement j’y retrouve pas mon compte. Même si on peut aussi mettre une interface graphique, c’est pas le but d’un serveur. Enfin, je voudrais pouvoir consulter ceci sans me loguer en ssh (depuis une interface web) et avoir des alertes email et sms serait top.  Actuellement je développe un nouveau produit (https://outquest.fr). Celui ci comporte un site web (game) et un site backoffice (qg). Le site officiel (outquest.fr) et le shop sont hébergés ailleurs (planethoster). J’ai une prod et un staging. (donc 4 site webs au final). J’ai commencé avec un serveur low cost à 2.99€/mois (Debian, 1 core,  2 Go Ram, 25Go SSD …) chez Ikoula. Franchement à ce prix la, ça marche bien pour faire tourner 4 sites webs.(avec que moi dessus hein !) Ces 4 sites web tournent sous 4 services kernel (autorestart) linux et géré par un serveur web frontal NGinx.  –> pour la config voir cet article J’ai de plus une CI/CD géré par azure devops (voir article Azure Devops) qui me permet via des pipelines de deployer automatiquement mon code sur les serveurs web respectif à chaque push git sur mes branches dev et master.  Ca a dla gueule ! Pour un mec tout seul (pour le moment) je sors la grosse artillerie. J’aurais pu partir direct avec un site web en prod et debuger dessus à l’ancienne. Mais j’aime bien apprendre à faire les choses bien :). Et justement, pour faire bien, la prochaine étape c’est le monitoring des serveurs (ya aussi les backups, je pense que j’en ferai un article à part).  Du coup j’ai voulu tester une solution qui me paraissait pas mal, un APM (Application Performance Management) : CheckMK.  Le souci c’est que j’ai vu trop gros (ce machin permet de gérer tout un parc informatique, plusieurs serveurs, node, kubernetes…) Et en l’installation, le machin à fait fondre mon serveur. Plus moyen d’y accéder pendant un temps et pas moyen de le redémarrer (j’ai contacté le support, ce qui m’a pris plusieurs jours, à ce prix la fallait pas attendre des miracles.) Le bon moment pour passer à un niveau supérieur.  A ce stade j’avais ma prod et mon staging sur la meme petite bécanne. Du coup je me dis qu’il faudra quand meme passer sur qq chose de plus sérieux pour la prod. J’ai donc opté pour un VPS OMGServ avec cette config :  Et une option payante supplémentaire de backup. (snapshots) CheckMK tourne déjà beaucoup mieux, mais comme je le disais c’est pas une solution pour moi. Ca install une bardé de trucs dont Apache2, qui fout la merde avec mon Nginx.  Après qq recherches, je suis tombé sur Signoz et OpenTelemetry. Et c’est ce qu’il me faut !   Signoz   Signoz est une interface web permettant de visualiser un ensemble de données sur votre serveur linux.  C’est une solution Open Source, récente et encore en dev. C’est un concurrent à DataDog (payant).  Elle permet de monitorer des applications et de fournir des éléments pertinents pour les devops.  Cette solution naît d’un constat. Les équipes techniques sont frustrées d’utiliser pleins d’outils différents provenants de sources de données elles aussi différentes pour voir leurs logs/metrics/traces …   De plus les outils existants sont « lourds », taillé pour de la grosse boite (faut bien vendre) avec des standards propriétaires et non générique.  Je n’ai pas de recul suffisant mais mon sentiment c’est que c’est compliqué et qu’un outil simple, uniformisé gratuit et open source manquait à ce niveau pour un niveau « intermediaire » (PME, TPME, indé) Un standard est en train d’émerger concernant toute ces données que l’on appelle OpenTelemetry. OpenTelemetry Encore un nouveau framework ? C’est quoi, une couche de plus ? OpenTelemetry c’est une collection d’outil, d’APIs et de SDKs permettant de mesurer, generer, collecter et exporter des données de télémetrie (metrics, logs et traces) afin de mieux analyser le comportement et les performances de nos applications.  Super mais qui porte le truc ? Est ce que c’est un projet qui va être adopté ? Oui, en tout cas par Microsoft : https://learn.microsoft.com/en-us/azure/azure-monitor/app/opentelemetry-overview Vu l’investissement de Microsoft pour les projets à succès Open Source, et vu le besoin, je pense qu’OpenTelemetry a de l’avenir.  Installer Signoz Il existe plusieurs façon d’installer Signoz, j’ai choisi pour ma part la version Standalone (https://signoz.io/docs/install/docker/) sur mon VPS linux Debian 11. Vous devrez télécharger le repo git : git clone -b main https://github.com/SigNoz/signoz.git puis vous rendre dans le répertoire suivant :…

Azure Devops

Qu’est ce qu’Azure Devops ? A quoi ça sert ? Comment l’utiliser ?  Je vais tenter d’expliquer par l’exemple et l’expérience ce que l’on peut faire avec Azure Devops Un bref historique Il est l’héritier de plusieurs anciens outils. Si je remonte loin on a VSS (visual source safe) qui était le svn de Microsoft, un gestionnaire de code source. Ensuite on a eu Team Foundation Server qui était un peu plus sérieux, on commençait à voir des outils annexes comme un board avec des work items. Git étant devenu une référence, Microsoft s’est adapté et a la intégré sur toutes ses plateformes. On a eu aussi VS Online (Visual Studio Online) et dans le même temps Microsoft rachetait HockeyApp qui deviendra App Center (spécialisé dans l’application mobile). C’est indépendant, mais j’imagine que demain tout ne fera qu’un. Et puis est né Azure Devops. Tout ça pour dire qu’il est pas arrivé comme ça. C’est le fruit d’années d’évolution et de savoir faire.   Azure Devops est gratuit.  Azure Devops est multi projets, multi utilisateurs, … voir ici pour le limites A quoi ça sert ? Alors qu’on soit bien clair, Azure Devops n’est pas indispenable… jusqu’à ce qu’on s’en serve… Azure Devops c’est une plateforme web qui regroupe plusieurs gros services : Board : un outil qui permet de créer des work items et de les organiser suivant des méthodes agile (scrum, kanban) Repos : un repo git, où l’on peut y voir les commits, pushes, branches, tags, pull requests Pipelines : orchestrateur automatique. Permet de lancer des « scripts » yaml et d’automatiser notre chaine de livraison continue.  Test Plans : La section test. Artefacts : ce que vous aller produire se retrouve ici.  Les deux derniers, j’en parlerai pas, je connais pas bien. Les 3 premiers sont des supers outils, ils sont liés.  En premier lieux on va utiliser Board pour s’organiser, créer des tâches et les répartir aux membres de l’équipe. Bien entendu il nous faut un repos Git. Ils nous permet de gérer notre code source. Enfin, quand le projet avance, on ajoute de l’automatisation pour que notre chaine de livraison continue fonctionne toute seule.  Comment l’utiliser ? Pour cela j’ai écris 9 articles qui se focalise sur la chaine de livraison/déploement continue CI/CD 1 – Première pipeline, On y apprend à faire un build tout simple : Azure Devops Pipeline (CI/CD) – First Build 2 – Deploiement via SSH Azure Devops Pipeline (CI/CD) – Deploy 3- Versioning Azure Devops Pipeline (CI/CD) – Versioning 4- Gestion des environnements Azure Devops Pipeline (CI/CD) – Environnement Azure Devops Pipeline (CI/CD) – Relancer les services 5- Git Flow Azure Devops – Git Flow intro 6- Pull Request Azure Devops – Pull Request Azure Devops – Pull Request (part 2) Voilà, j’espère que ça pourra servir à quelqu’un 🙂 

Azure Devops – Pull Request (part 2)

Cet article est la suite de https://reactor.fr/azure-devops-pull-request/ On avait créé des règles de branche sur la master : interdiction d’effacer cette branche pas de git push direct sur cette branche règle de Pull Request (light) Ceci nous permet de sécuriser la branche master. Faisons de même avec la branche develop. Rappels Git Flow On a vu la PR develop –> master.  Maintenant, on va ajouter une PR sur chaque commit vers la branche develop. Si on applique Git Flow, on aura une branche feature qui devra à un moment livrer son contenu sur la develop.  La PR intervient à ce moment.  Règle de branche develop sur Azure Devops On va donc fairre un clic sur le menu de la ligne représentant la branche develop dans Repos – Branches, puis on sélection "Branch Policies" Pour la branch dévelop, j’ai rajouté plus de contraintes que la master. En effet je veux qu’en premier lieu chaque commit sur la branch dévelop corresponde à un work item. Ensuite si il y a une intervention d’un reviewer, celle ci doit être résolut par un commentaire. Dans la pratique ça peut simplement être une question qui ne demande pas vraiment d’intervention. Mais ça peut aller jusqu’à ce que le reviewer demande un refacto voir refuse carrément le code (c’est rare…) Je limite aussi le type de merge afin d’avoir un historique lisible et en cohérence avec la branche master. Ensuite j’ajoute une règle de build. Je sélectionne une nouvelle Pipeline que je viens de juste de crée : pool: vmImage: ubuntu-latest variables: buildConfiguration: ‘Release’ projectBlazorWasmServer: ‘src/BlazorDemo/BlazorDemo/Server/BlazorDemo.Server.csproj’ steps: # Publish Blazor Demo Server – task: DotNetCoreCLI@2 displayName: Publish inputs: command: publish publishWebProjects: false projects: ‘$(projectBlazorWasmServer)’ arguments: ‘–configuration $(buildConfiguration) –output $(build.artifactstagingdirectory)’ zipAfterPublish: false modifyOutputPath: false La seule différence avec l’autre pipeline de build, c’est que je ne précise pas le trigger. En effet, je ne connais pas le nom de la branche feature. Donc par défault ce sera la source de la PR concernée. Mise en situation Mon tech lead m’a affecté une tâche. On va aller la créer pour la simuler, normalement elle est déjà existante : Je suis développeur, je dois donc créer une branche si elle n’existe pas déjà. Sut mon terminal je tape : git checkout -b feat/LoginPage Je développe, quand j’ai fini : git add . git commit -m « mon travail sur la page login » git push –set-upstream origin feat/LoginPage Je peux bien évidement avoir plusieurs commit et/ou avoir d’autre commit d’autre développeurs. D’ailleurs (si vous avez les droits) n’importe qui peut aller voir ce qu’il se passe sur cette branche depuis Azure Devops : La Pull Request feat -> develop Azure Devops sait qu’une branche feat/LoginPage est en cours et vous propose directement de choisir celle ci et de créer une pull request à partir de celle ci. Dans mon cas j’ai eu ceci : C’était pas voulu, mais j’ai eu la surprise de voir que j’ai un conflit de merge. Et c’est finalement un point intéressant à montrer.  J’ai pas récupéré la branche develop dans mon dev sur ma branche feature. Et honnetement ça arrive TOUT LE TEMPS.  Normal, qq a pu entre temps livrer qq chose sur la branche develop. Il faut donc s’assurer que ce nouveau code s’intègre au mien. Il faut alors lancer toute une série de commandes git: git checkout develop git pull git checkout feat/LoginPage git rebase develop git pull git push Explication : on change de branche pour aller sur develop. On récupère la dernière version de cette branche. On re bascule sur notre branche feat. On applique ces nouveautés (rebase permet de garder un historique chronlogique en mettant nos modifs après celle de develop). A ce stade on est iso en local. Faut faire un pull pour que ça lance un merge automatique (ca met au carré le repo local et le distant et prépare au push) Et enfin on push sur le repo distant.   Maintenant on retourne sur Azure devops : Ya plus de conflit ! 🙂 si on regarde de plus prêt Il ne reste plus qu’à associer notre PR à un Work Item : Ce qui résoud le problème : On peut valider le PR : Ceci effacera la branche feat/LoginPage Au final : Conclusion Pourquoi c’est génial ? C’est vrai qu’avant on publiait directement sur develop et tout allait bien. Et vous avez pas tort. Chacun travail comme il le souhaite.  Avec cette façon de faire, vous allez prendre un petit plus de temps à faire un "peu de paprasse". Mais on a un suivi très intéressant sur chaque étape de la construction de morceau d’application. Tout est lié, et tout le monde peu savoir ce qu’il se passe. Qui travaille sur quoi et quand. Attention, faut pas le voir comme un outil de flicage, mais plutot un outil d’orchestration dans une équipe logiciel.  L’élément que j’ai peu évoqué ici, et qui est relié à a chaque PR c’est le board. Le Board permet de suivre des work items de plusieurs types : Chacun s’organise comme il veut. On peut créer autant de work item que l’on veut et utiliser scrum ou kanban Le développeur se focalise sur sa feature et traite de bout en bout celle ci (jusqu’à la branche develop) Le tech lead suit tout ça. A chaque fin de sprint, ou quand il le souhaite, il peut faire une release. Le product owner est au courant des évolutions de son app.  Azure Devops est vraiment un super outil.  Et j’ai pas fait le tour de toutes ses possibilités.

Azure Devops – Pull Request

Dans mon dernier article, j’ai introduis Git Flow avec un exemple, la naissance d’une application.  A un moment, je raconte l’évolution de l’équipe de dev et de leur organisation autour du code source. On y parle de Pull Request.  La PR est une procédure de validation d’un ensemble de commits regroupés dans une branche feature (quand on applique Git Flow). On peut aussi appliquer une PR de la branche develop vers master. C’est ce qu’on appelle une "release".  Chacun ayant ses stratégies, je vous propose la mienne, qui va bien pour un petit/moyen projet :  Situation J’ai 2 branches principales : master. A considérer comme la branche release car c’est ce code qui part en prod. develop. Son code part en staging. Ca sert aux dev pour voir le résultat de leur travail sans influencer la prod. Quand j’estime que la branche develop a quelque chose d’intéressant, je pousse le code vers la master (merge).  Cette action est réalisé via une Pull Request depuis Azure Devops. Je ne fais aucune manipulation git. Tout ce passe sur Azure Devops. D’ailleurs j’ai interdit toute manipulation de la branche master en dehors d’une PR. Voyons voir comment faire cela : Règle de branche master Sur Azure Devops, il faut se rendre sur l’onglet "Repos" puis "Branches" : Cliquez sur le bouton à droite de la branche master, ce qui ouvre un menu. Cliquez sur Branch Policies. Nous y voila, si on lit la première ligne, elle nous dit que si une seule des options est cochée, alors la branche ne peut pas être effacée et toute modification devra passer par une PR. Quelque part, je pourrais m’arréter la car c’est tout ce dont j’ai besoin. Mais on va voir qu’il y a quelques options bien intéressantes : Require a minimum number of reviewers. J’ai une équipe de 1 dev, moi. donc je vais m’auto évalué, ça n’a pas de sens. Je n’ai pas coché cette case. Mais c’est intéressant dès lors qu’on valide par un second dev (lead tech pour la master).  – Check for linked work items. Cette option est intéressante dès lors que l’on travaille avec un backlog. Ce qui n’est pas mon cas pour ce projet "demo". Mais c’est conseillé. En effet, vous allez améliorer chaque PR avec un "work item" qui sera donné par votre product owner ou votre tech lead. C’est hyper quali de faire ça. Surtout intéressante depuis une branche feature vers la develop. Ici depuis la branche develop vers la master, on aurait plus un ensemble de feature. A voir avec les types de work item. Si ca se trouve ça peut coller.  Check for comment resolution option pas obligatoire. Si coché, cela force à valider tout commentaire dans le code. On le verra plus tard, mais si un reviewer constate un manquement, une erreur ou une objection dans le code, il le signifie par un "commentaire". Si celui ci n’est pas "résolut", alors on peut pas valider la PR. A tester avec votre équipe. La encore, c’est intéressant pour une PR vers la branche develop  Limit merge types cette option permet de laisser le choix lorsque la PR est validé, de choisir le type de merge que l’on souhaite appliquer à la branche cible. Pour mon cas, je ne laisse pas le choix, j’impose un squash merge. La encore, c’est les goûts et les couleurs. It’s up to you. Je pense qu’un article entier peut y être consacré. On en discute encore dans l’équipe où je bosse…  On va maintenant rajouter des règles de build : On ajoute une règle qui impose qu’un build s’execute correctement. Pour cela on doit construire une pipeline de build. Si la branche develop ne build pas, alors on ne fait pas le merge. Pour ma part, le build est un publish. Ce qui revient a peu prêt au même (un publish est un build + génération des fichiers statics web dans le cas d’une app Blazor WASM).  Ma première Pull Request On va tester tout ça. On va dans Repos – Pull request Cliquez sur New pull request : Il faut choisir sa source et sa destination, pour nous, develop –> master Notez que j’ai juste mis un titre. On peut ajouter des reviewers et bien sûr un work item. Tout ca on le verra dans une pull request de type feature –> develop. Cliquez sur Create. Voici ce qui se passe : Première chose, on a un élément requis qui doit se lancer. Celui ci c’est notre "règle de build" écrite juste avant.  Notez aussi qu’il y a un check "No merge conflict". Ce qui permet de vérifier en amont qu’on a pas foiré un merge/rebase malencontreux.  Si on attends qq secondes : On voit notre pipeline qui se lance et qui passe au vert si notre publish est ok.  En haut a droite on a un bouton approve qui va concerner des validations "humaines". Tout est vert. On a un pre-merge ok. Un build ok. Pas de reviewer, et pas d’autre règle (j’ai volontairement fait en sorte que ce soit pas trop compliqué pour cet exemple)  Si on clic sur Complete On a un message qui veut supprimer develop. On veut pas. Surtout pas. D’ailleurs on fera en sorte par la suite de rendre develop ineffacable.  Le squash commit est l’unique option, comme indiqué dans les règles de la branche master que l’on vient de définir. On peut si on veut rajouter un commentaire personalisé de commit.   On clique sur Complete merge, quand il est fini, on a cette info : Et si on vérifie nos pipeline, notre DEPLOY master to prod est lancée ! On vient d’automatiser notre branche master. Elle est sécurisée (personne hormis une PR peut la modifier). Elle est tracable et on peut échanger via une interface web sur une operation qui d’habitude se passe en ligne de commande dans un coin.  De plus en rajoutant une seconde pipeline, lorsque la PR est validé puis mergé, on a un deploy automatique vers la prod.…

Azure Devops Pipeline (CI/CD) – Relancer les services

Services Debian systemctl Suite à mon dernier article, j’ai été confronté à un problème que j’ai mis longtemps à identifier. Lorsque je mets à jour mon site web, mettons celui du staging avec une nouvelle publication, je modifie les fichiers à la racine du projet, c’est à dire var/etc/www/staging.blazordemo.reactor.fr/ dans mon cas. Pour ma part j’applique une politique du "j’effface tout" et "j’upload tout". Pas de remplacement de fichier. C’est plus long mais je suis certain qu’il y a pas un mauvais fichier en ligne.  Hormis des pb de cache ou de rafraichissement, mon site web est bien à jour.  Mais pas mon service Kernel. En tout cas sur linux Nginx, lorsque je rajoute par exemple un controler "api/plateform" coté serveur, si je ne relance pas mon service avec les commandes suivantes : sudo systemctl stop kestrel-staging.blazordemo.reactor.fr.service sudo systemctl start kestrel-staging.blazordemo.reactor.fr.service sudo systemctl status kestrel-staging.blazordemo.reactor.fr.service mon api « plateform » ne sera pas accessible. En tapant ces commandes, l’api est accessible. Il serait bon d’inclure cela dans la pipeline, histoire qu’on est rien à faire à la main à chaque run de pipeline si le code c# est modifié coté serveur. Pour cela, il suffit de rajouter ces lignes : – task: SSH@0 displayName: ‘Restart Kestrel Service’ inputs: sshEndpoint: ‘SSH to Ikoula’ runOptions: ‘commands’ commands: | sudo systemctl stop kestrel-staging.blazordemo.reactor.fr.service sudo systemctl start kestrel-staging.blazordemo.reactor.fr.service sudo systemctl status kestrel-staging.blazordemo.reactor.fr.service readyTimeout: ‘20000’ Ca donne ça dans les ‘logs’ des jobs de l’execution du pipeline :

Azure Devops Pipeline (CI/CD) – Environnement

Problème du dernier post Je me suis bien pris la tête sur le dernier billet au sujet d’un nouveau problème qui est apparu lorsque j’ai voulu diviser mon site web en 2, une partie staging et une partie prod. Ca pourrait tout aussi bien être "preprod", "recette" etc… Avant j’avais qu’un seul site web: blazordemo.reactor.fr. Celui ci a sa config Nginx et son service systemd qui fait tourner Kernel via un proxy sur le port 7777. (voir article ) J’en ai créé un nouveau "staging.blazordemo.reactor.fr".  L’idée est de faire comme les pros, un site web de "staging" et un de "prod".  dev : locale, tambouille de developeur, avec une base de données crado, des machins bricolés, des trucs à jours ou pas… staging:  distante, même environnement que la prod (meme serveur de base de données, même OS, même TOUT, sauf les données qui sont normalement copiés et anonymisés dans le meilleurs des mondes) prod: distante, le site qu’on update le vendredi soir 🙂 Donc j’ai crée "staging.blazordemo.reactor.fr". Sauf que j’ai pas modifié mon service qui fait tourner Kestrel via proxy sur port 7777.  Et j’ai mis un temps fou à m’en apercevoir. (j’ai été jusqu’a faire un gros delete de mon repertoire www. Nginx continuait à me servir un beau site web… même après un reboot. car le service était le meme…) Donc il me faut 2 services… Un de staging, l’autre de prod. Le tout sur un port proxy différent bien évidement.  Bien entendu, ce problème n’existe pas si le staging et la prod ne sont pas sur le meme serveur, ce qui est TRES recommandé. Cependant, je trouve l’exercice intéressant car il pousse dans les retranchements et force à de bonnes pratiques. Mon ancienne technique Dans le projet Blazor Server, dans Program.cs j’ajoutais ceci  Une ligne de compilation conditionnelle qui me changait le port si on était en release (ce que je considèrais comme mon unique prod).  Je pourrais ajouter une condition STAGING avec un port 7776 et m’en servir pour la version staging. A mon sens une directive de compilation n’a rien a voir avec les environnements, je veux dire c’est liés, mais c’est pas son but de définir du paramétrage de port…  Je pense qu’on peut faire mieux, avec les fichiers de conf appsettings.js. Créeons deux autres fichiers de config, un pour la prod et l’autre pour le staging. Normalement vous avez déjà par défaut un fichier appsettings.Development.json launchSettings.json Si vous travaillez avec Visual Studio ou VS Code vous devez savoir que vous avez des options de lancement de votre app qui sont configurable dans le fichier Properties/launchSettings.json Encore un fichier de config qui sème le doute… { « iisSettings »: { « windowsAuthentication »: false, « anonymousAuthentication »: true, « iisExpress »: { « applicationUrl »: « http://localhost:54778 », « sslPort »: 44382 } }, « profiles »: { « BlazorDemo.Server »: { « commandName »: « Project », « dotnetRunMessages »: true, « launchBrowser »: true, « inspectUri »: « {wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri} », « applicationUrl »: « https://localhost:7249;http://localhost:5249 », « environmentVariables »: { « ASPNETCORE_ENVIRONMENT »: « Development » } }, « IIS Express »: { « commandName »: « IISExpress », « launchBrowser »: true, « inspectUri »: « {wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri} », « environmentVariables »: { « ASPNETCORE_ENVIRONMENT »: « Development » } } } } Comme on peut le voir dans la section "profiles", je peux lancer mon app soit : Via IIS Express  Via la commande dotnet run La première est historique, je pense qu’on en parlera plus dans qq temps. Avant .net core on était obliger de lancer un server utilisant le .net Framework sous IIS qui est le serveur de Windows. La version Express est la version de dévelopement. Pour moi c’est voué à mourrir, mais certains ont des habitudes et Microsoft les forces pas à les changers.  Depuis .net Core (et donc .net 5 et 6), on peut executer nos app sur n’importe quel OS et serveur web comme apache ou nginx sous linux. On a un serveur web intermédiaire appelé Kestrel qui fait le job. D’ailleurs c’est lui que l’on utilise en dévelopement.  En executant une commande dotnet run sur notre projet serveur, on lance un Kestrel. C’est lui que l’on veut paramétrer pour avoir une config dev, staging, prod.  C’est possible d’éditer en développement ce fichier pour changer de port. Mais c’est uniquement un fichier de conf pour une execution en dev local. C’est pas un fichier de configuration plateforme de prod.  Je place ça là car c’est un fichier qui peut vous semer le doute lors de vos devops et test local. AppSettings.{ENV}.json On va modifier le fichier appSettings.Developement.json et lui rajouter ceci  { « Plateform »: « Dev », « Kestrel »: { « EndPoints »: { « Http »: { « Url »: « http://localhost:1234 » } } }, « Logging »: { « LogLevel »: { « Default »: « Information », « Microsoft.AspNetCore »: « Warning » } } } Perso je rajoute une ligne "Environment" qui me permet de voir dans sur quel type d’environement je suis (et plus tard de l’afficher dans l’app).  Ce qui nous intéresse c’est la section Kestrel. On ajoute un EndPoints et on lui précise un port.  Faites un dotnet run sur votre projet à présent : On a un warning qui nous dit que l’adresse utilisé va écrasé celle du launchsetting.json. C’est ce qu’on veut. On voit bien la nouvelle adresse http://localhost:1234.  C’était pour la démo, en dev ça sert a rien de faire cela, autant utiliser le launchSettings.json qui est la pour ça. Donc enlever la ligne Kestrel, c’était pour la démo. Par contre on va mettre ces configs pour appsettings.staging.json  { « Plateform »: « Staging », « Kestrel »: { « EndPoints »: { « Http »: { « Url »: « http://localhost:7776 » } } }, « Logging »: { « LogLevel »: { « Default »: « Information », « Microsoft.AspNetCore »: « Warning » } } } et appsettings.prod.json { « Plateform »: « Prod », « Kestrel »: { « EndPoints »: { « Http »: { « Url »: « http://localhost:7777 » } } }, « Logging »: { « LogLevel »: { « Default »: « Information », « Microsoft.AspNetCore »: « Warning » } } } ASPNETCORE_ENVIRONMENT Encore une chose à savoir. Pour déterminer sur quel environement .net se trouve, il utilise une variable d’environement ASPNETCORE_ENVIRONMENT Elle est d’ailleurs clairement définie dans le fichier launchSettings.json à "Development". Donc en dev, le fichier de config appsettings utilisé sera appsettings.development.json Pour que ce mécanisme fonctionne en prod, soit on définie la variable ASPNETCORE_ENVIRONMENT à "Production" soit par défaut, si elle n’existe pas, c’est déjà le cas (attention moi j’ai nommé mon appsettings prod et non production). Le problème c’est que une variable d’environement est définie pour tout…

Azure Devops Pipeline (CI/CD) – Versioning

C’est quoi une version ? Je vais faire quelque chose de très basique et qui ne sera probablement pas appliqué tel quel dans un cas plus professionnel. D’ailleurs, chacun ayant ses méthodes de travail, ce post reste un example de "comment on pourrait faire" pour versionner une app.  C’est quoi une version ? La encore, je suis pas sûr que tout le monde utilise la même façon de versionner.  Par exemple les versions du navigateur Firefox utilise simplement un chiffre (https://blog.mozfr.org/pages/Calendrier-versions-Firefox). Mais si on regarde de plus prêt on a quand meme 98.0.1 et donc des points de séparations.  En fait, Pour le grand public on va utiliser "Firefox 98". Mais il faut savoir qu’il existe de nombreuses versions de la 98 ! Du moins en interne. Avant de livrer une version patché 98.0.1 qui va corrigé des bugs mineures.  D’après ce que j’ai pu voir, on utilise principalement ce style de nommage de version X.Y.Z X : Version Majeure (non rétro compatible) (nouvelles fonctionalités, gros correctif, ajout de techno, changement de look…) Y : Version Mineure (rétro compatible) (correctifs, patchs, en gros pas de grosse modif, mais qui répare) Z : Révisions.  (rétro compatible) Sert plutot au dev et product owner pour savoir de quelle version on parle en pre prod pour basculer vers la prod. Quelque part, ce dernier numéro ne devrait à mon sens pas être communiqué officiellement (un peu comme Firefox qq part) Sur la version X, on a je pense tous la même idée. On sort une version X rarement. Par contre, je suis pas certain qu’on utilise tous Y et Z de la même manière. Franchement, c’est freestyle, dans les versions mineures on peut voir de nouvelles (petites) fonctionalités. C’est vraiment selon vos préférences et ceux avec qui vous bosser.  Pour le coté retro compatible, c’est ce que j’ai lu sur le net. Intéressant mais pas du tout respecté par tous.  Autre style, voici un format intéressant ou l’on cumule en plus une date : V 2020.10.23.1629 Je trouve ce format particulièrement intéressant car on sait tout de suite de quand date la version. Mais ca fait bcp de chiffre.  Ce format est utilisé par Larsen Lance avec un scipt powershell qui "incrémente" ce format à chaque build en automatique (http://www.lancelarsen.com/blazor-tips-n-tricks-auto-increment-your-version-tag-with-powershell/) Pourquoi versionner ? La version d’une app sert tout simplement de référenciel. De quoi on parle ? De la version 1.12.665 ou ya un bug qui est corrigé dans la V1.13.005. C’est quand même plus pratique.  Le product owner sera aussi attentif à ces versions concernant l’ajout de nouvelles fonctionalités et communiquer dessus.  Ca permet de prévoir une roadmap large et dire par exemple, cette fonctionalité sera attendu pour la V2.3, pas avant.  En parlant de roadmap, je cite souvent Star Citizen qui est un jeux vidéo en béta depuis des années, et qui affiche ses avancés https://robertsspaceindustries.com/roadmap/release-view Il fixe des objectifs par trimestres sous forme de version avec une majeure et une mineure. C’est clair. Mais bon il sort quand ce jeu !!!! C’est une alpha. Et certain ajoute ce terme dans la version.  Alpha, Beta, Rc (Release Candidate), Nightly Build… On peut préciser le contexte de la version.  Versionner une app .net Core avec une Pipeline automatique Tout l’enjeu de cet article. Comment je peux modifier ma pipeline pour inclure un nouveau numéro de build par exemple à ma version et l’afficher en prod. (comme sur Firefox dans la rubrique aide ou a propos) ? Afficher la version dans l’app Pour que l’on voit directement la version dans mon app Blazor, je vais carrément l’afficher en haut a droite : Une page about avec ce numéro aurait été plus propre sur une vraie App. L’idée ici c’est de l’avoir en permanence sous les yeux.  Pour cela on va modifier MainLayout.razor comme ceci La ligne importante est celle ci : Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion; La version par défaut qui s’affiche est 1.0.0. Si vous faites (sous Visual Studio) un clic droit sur le projet client de Blazor, puis propriété et enfin dans la section Package vous verrez ce numéro. Par contre il n’est pas modifiable.  Pour le modifier, il suffit de rajouter dans le .csproj une ligne version. Easy. Bingo YAML comment gérer une version Je veux une version sous cette forme yyyy.mm.dd.xx, donc date + un numéro auto incrémenté. On commence par ajouter un compteur à une variable. Ce compteur sera automatiquement incrémenté à chaque execution de la pipeline. revision: $[counter(format(‘{0:dd}’, pipeline.startTime), 1)] Pour s’en assurer, on va afficher la valeur de notre variable : #display value – script: echo $(revision) displayName: ‘revision display’ Executez plusieurs fois votre pipeline et aller voir ce que ça donne. Par exemple chez moi : J’en suis à mon 24ième run. Il me faut aussi une date du jour. pour cela on va aussi utiliser une variable dateToday ainsi qu’une derniere variable  »versionString"  qui va concaténer ces deux dernières. (1 h pour trouver comment faire… le ‘>’ magique). Ce qui donne : dateToday: ‘Will be set dynamically’ revision: $[counter(format(‘{0:dd}’, pipeline.startTime), 1)] versionString: > $(dateToday).$(revision) Il suffit à présent de générer dateToday. On va utiliser un script PowerShell (pompé et adapté sur ce site) #Preparing Build Number – task: PowerShell@2 displayName: ‘Preparing Build Number’ inputs: targetType: ‘inline’ script: | $currentDate = $(Get-Date) $year = $currentDate.Year $month = $currentDate.Month $day = $currentDate.Day Write-Host $currentDate Write-Host $day Write-Host $env:revision Write-Host « ##vso[task.setvariable variable=dateToday]$year.$month.$day » On vérifie les données : #display value – script: echo $(revision) displayName: ‘revision display’ – script: echo $(dateToday) displayName: ‘dateToday display’ – script: echo $(versionString) displayName: ‘versionString display’ Normalement on a ce résulat : 2022.3.21.29, version numéro 29 faite le 3 mars 2022, elle est pas belle la vie ?  Publication avec la version J’ai légérement adapté mon script de publication qui était anciennement ceci : # Publish Blazor Demo Server – script: dotnet publish –configuration $(buildConfiguration) src/BlazorDemo/BlazorDemo/Server/BlazorDemo.Server.csproj displayName: ‘dotnet publish $(buildConfiguration)’ Remplacé par : # Publish Blazor Demo Server – task: DotNetCoreCLI@2 displayName: Publish inputs: command: publish publishWebProjects: false projects: ‘$(projectBlazorWasmServer)’ arguments: ‘–configuration $(buildConfiguration) –output $(build.artifactstagingdirectory) /p:Version=$(versionString)’ zipAfterPublish: false modifyOutputPath: false…

Azure Devops Pipeline (CI/CD) – Secrets

Cet article concerne le secret de certaine informations, comme un mot de passe, un clé privée etc.  Dutant mon apprentissage sur azure devops, je suis passé par cette étape. Cependant je ne me suis pas servi précisément de ces varibles/files secrets au final car j’ai utilisé le "service connexions" . Néanmoins je trouve cela instrucif, alors je partage. YAML – sécurité Vous avez remarqué, je ne vous ai pas communiqué ma passphrase ni ma clé privée. Sinon vous auriez accès a mon serveur. Ma clé est sur mon poste de dev. La passphrase lisible dans mes scripts d’upload automatique. C’est pas ouf question sécu. Mais vu que je bosse tout seul dessus… ça va, ça passe.  Qu’en est il lorsque l’on travaille à plusieurs dev, avec des sys admins, du cloisement et un niveau de sécurité qui nécessite de pas trop déconner ? Il faut trouver un moyen de sécuriser ces données. Azure Devops utilise des "secure files" et des "secure variables" pour faire cela.  https://docs.microsoft.com/en-us/azure/devops/pipelines/library/secure-files?view=azure-devops Ces données sont HORS repo git. Ainsi personne n’y aura accès en dehors de vous et des autorisations que vous leurs donnerez sur Azure Devops. Par contre elles seront stockées sur Azure Devops… les américains, la CIA, la NSA tout ça tout ça…   Commençons par enregistrer notre passphrase et lui donner la permission pour la pipeline du projet Azure Devops "Blazor Sandbox". Pour cela on va créer une nouveau groupe de variables que j’ai nommé IKoulaGroup. Il faut aller dans Pipelines/Library et cliquer sur Variable group.  Ajouter une variable, donner lui un nom (ex MyPassPhraseIkoula) et une valeur (dans l’exemple Bangali) Notez qu’à droite de la valeur d’une variable se trouve un petit bouton cadena qui permet de masquer celle ci. On y reviendra. Retour sur le YAML Maintenant que l’on a notre variable stockée sur Azure Devops on va pouvoir la récupérer dans notre pipeline Voici comment : On a une première variable qui était déjà là, celle qui concerne le type de build ‘Release’. Elle a un nom et une valeur. Celle ci ne concerne que la pipeline et ne sera vu que de l’agent qui s’occupe de cette pipeline.  Ensuite on a la déclaration du groupe IkoulaGroup qui contient une variable dont le nom est "MyPassPhrase" avec une valeur. Plus d’info sur la doc officielle ici. Plus loin on affiche ces variables avec un echo. Deux façons de récupérer notre variable MyPassPhrase.  tout simplement en l’appellant par $(MyPassPhrase) en utilisant une « expression de runtime » avec $[variables.MyPassPhraseIkoula]. Je pige pas trop ce truc, mais je pose ça la des fois que ça serve plus tard.  Concrètement, je n’arrive pas à récupérer la variable avec cette deuxième technique. Par contre j’ai bien ma variable avec la première façon :On a récupéré notre passphrase "Bangali" de tout à l’heure.  Maintenant un mot de passe ou une passphrase c’est pas censé être affiché tel quel. Vous vous souvenez du petit bouton cadena de tout à l’heure, qui se trouve a droite de votre variable dans la library de pipeline, cliquez dessus pour le vérouiller ainsi : penser à sauvegarder et relancer la même pipeline : notez que cette fois ci, le contenu de la variable est masqué par des **** On sait maintenant comment cacher des informations. 

Azure Devops Pipeline (CI/CD) – Deploy

Objectif : déploiement en production Dans la précédente partie on a déployé une pipeline sur Azure DevOps. Celle ci nous permet de lancer une build dès que la branche master de notre repo Blazor Sandbox reçoit un nouveau commit. De cette manière on s’assure que le build fonctionne ou pas. On est prévenu du résultat par email.  Ce qui serait intéressant, c’est de pouvoir la déployer sur un serveur web automatiquement en cas de succès.  Ma config J’ai un serveur VPS chez iKoula pour 4€/mois, avec une debian linux.  Ca tourne plutot bien. Mais bon, j’ai pas beaucoup de traffic 🙂 Donc j’ai un site web https://dev.blazordemo.reactor.fr et https://blazordemo.reactor.fr qui tournent tous les deux sous Nginx. Derrière chez deux services kestrel asp.net core sous .net 6.0. (pour faire bien, il aurait fallut dissocier la prod de mon dev (ou recette, staging). Petit budget… Dans /var/www/blazordemo.reactor.fr et /var/www/dev.blazordemo.reactor.fr j’ai mes publications de site web asp.net core Blazor WASM (le serveur et le client WASM "all in one"). L’idée est de dire à Azure Devops Pipeline, lorsque la branche master reçoit un nouveau commit, tu publies une version du serveur et tu balances le tout via SSH (sftp). Pipeline YAML On est fixé sur les objectifs. Maintenant il faut traduire cela en YAML. Voici un bout de doc : https://docs.microsoft.com/fr-fr/azure/devops/pipelines/tasks/deploy/copy-files-over-ssh?view=azure-devops En premier lieu, je vais reprendre la précédente pipeline et modifier build par publish. La différence est notable pour Blazor Server WASM. (voir mon article) Un petit commit + push et on voit que Azure Devops lance un nouveau job. On attend qu’il termine et passe au vert. Je pense qu’on va procédé ainsi, à taton. Petit pas par petit pas.  Une modif + commit + push + retour du Job.  Point SSH/SFTP Pour accéder à mon serveur en toute sécurité, je me connecte via SSH. Je prends donc la main à distance depuis mon poste windows sur mon serveur debian linux, qq part en France. J’ai un mot de passe et un login. Et roule ma poule.  Mais on peut faire mieux. J’ai ajouté une paire de clé privée/publique et une passphrase. Ainsi, il faut avoir la clé privée + la passphrase pour accéder.  C’est plus sécurisant pour moi. Je laisse l’accès SSH classique pour des opérations plus ponctuelles. J’ai de mon côté une serie de fichier .bat qui lance une commande de publication .net core et des commandes winscp (permet de lancer des commandes SFTP depuis Windows). La connection se fait via cette clé et cette passphrase. Ainsi, je peux en un double clic mettre à jour ma prod/staging/recette/dev depuis mon poste, sans lacher mon mdp. Au lieu de le faire à la main depuis un client sftp. Ca marche ! C’est moins rapide qu’un client sftp (car les fichiers uplodé ne sont pas lancés en parrallele). J’ai deux types de scripts. La totale, qui met a jour toute la publication : et un autre "rapide" qui ne mets a jours que le minimum pour aller vite : et son fichier .bat  Donc l’idée, c’est de faire pareil sous Azure Devops. Yaml On va rajouter une nouvelle tâche dans notre pipeline YAML avec CopyFilesOverSSH 4 éléments sont à renseigner : sshEndPoint : c’est le nom de notre connexion SSH que l’on va configurer plus loin sourceFolder : le chemin relatif de votre publish. L’agent de build va s’occuper de récupérer votre publish au bon endroit. Attention au majuscule et minuscule si l’agent est sur linux !!! targetFolder: le chemin de destination de votre publication sur la machine cible.  cleanTargetFolder : facultatif, mais effacer le contenu de la destination fait pas de mal.  Service Connections Le sshEndpoint de tout à l’heure est à configurer dans les "project settings" dans la section Pieplines/Service Connections  Il reste plus qu’à créer la votre en cliquant sur le bouton new connection Sélectionnez SSH : puis remplissez selon votre config, en uploadant votre clé privée. Attention, celle ci doit être au format .pem. Et voila. C’est tout.  Relancer votre pipeline avec un petit git add commit push.  Normalement vous devriez avoir que du vert !