From b23344a36f8f04600bd331a5a5351637707f3f20 Mon Sep 17 00:00:00 2001 From: Olivier DOSSMANN Date: Mon, 27 Jan 2025 17:05:57 +0100 Subject: [PATCH] =?UTF-8?q?chore(content):=20[WIP]=20-=20D=C3=A9marche?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/40_demarche.md | 177 ++++++++++++++++++++------ media/feature-branch-rebase-final.png | Bin 0 -> 17254 bytes 2 files changed, 141 insertions(+), 36 deletions(-) create mode 100644 media/feature-branch-rebase-final.png diff --git a/content/40_demarche.md b/content/40_demarche.md index a9ff92e..403f7fd 100644 --- a/content/40_demarche.md +++ b/content/40_demarche.md @@ -1,8 +1,6 @@ \newpage -# Démarche suivie et opportunité(s) de collaboration {#demarche} - -TODO: introduction +# Démarche suivie {#demarche} La réalisation de ce projet va plus loin que la simple mise en place d'une application dans le Cloud. C'est une nouvelle équipe, dans un nouvel @@ -11,28 +9,151 @@ environnement avec une application inconnue et des savoirs à acquérir. En pareille situation, c'est tout un **écosystème DevOps** qu'il faut mettre en place. -## Démarche et outils +TODO: détailler le plan -TODO : compléter +## Méthodologie -Gitlab, Slack, SemVer, Git OneFlow, Conventional commit, différents environnements +### Agile -* D'abord une plateforme avec un groupe (gitlab.com/devu42) et un projet principal (`projet`) + base de connaissances (wiki) -* Un domaine devu42.fr pour avoir une adresse courriel (team@devu42.fr) partagée -* Création d'étapes (jalons) progressives : - * Étape 1 : préparation - outils (config. gitlab, projets, wiki, etc.), cahier des charges - * Étape 2 (avec 3) : CI/CD sur le projet principal - * Étape 3 (avec 2) : Infrastructure (Terraform) - * Étape 4 (avec 5) : Données (postgreSQL) - * Étape 5 (avec 4) : Observabilité - * Étape 6 : extras (si on a le temps) -* Compte-rendus réguliers +Le choix d'une méthodologie agile « basique » s'est offerte à nous, notamment par : -### Normes +* des **comptes-rendus journaliers rapides de 10mn** en respectant les points suivants : + * ce que j'ai fais hier, + * ai-je rencontré un/des problème(s) qui vaille(nt) d'être cité(s), + * ce que je compte faire aujourd'hui, +* la **création de jalons** avec un ensemble de tâches sur une période d'une semaine, +* des échanges réguliers en **pair-programming** sur des sujets difficiles **via [la plateforme Slack](slack.com)**, +* et l'utilisation d'un **tableau façon Kanban** (méthode de gestion de production en flux tendus). -* SemVer -* Git OneFlow -* Conventional commit +C'est ainsi que les jalons ont parsemés la durée du projet en objectifs à atteindre. + +### Jalons + +Les jalons (milestones en anglais) permettent de savoir si le projet dérive de l'objectif attendu. Voici les jalons qui ont été utilisés : + +* Étape 1 : préparation - outils (config. gitlab, projets, wiki, etc.), **cahier des charges** +* Étape 2 (avec 3) : **CI/CD** sur le projet principal +* Étape 3 (avec 2) : **Infrastructure** (Terraform) +* Étape 4 (avec 5) : Données (**postgreSQL**) +* Étape 5 (avec 4) : **Observabilité** +* Étape 6 : **extras** (s'il y a du temps restant) + +Les jalons n'étaient pas suffisants, d'autres éléments ont dû être pris en compte. + +### Choix de standards et normes + +Plusieurs standards/normes ont été choisies pour rassembler plutôt que diviser le groupe : SemVer, Git OneFlow et Conventional commit. + +#### SemVer + +[SemVer](https://semver.org/) ou **Semantic Versioning** est un standard visant à codifier la manière d'écrire le numéro de version d'une application et leur hiérarchisation (c'est à dire la façon dont on incrémente un numéro de version). + +Respecter SemVer, c'est permettre d'utiliser des outils conçus pour cela mais aussi et surtout **faciliter la gestion des dépendances**. + +Nous ne détaillerons pas l'ensemble du standard, mais voici quelques exemples de versions : + +``` +1.0.0 +2.1.4 +0.6.3-alpha +0.6.3-alpha.1 +1.5.2-rc.2 +``` + +Cela fonctionne donc sous la forme `Majeur`.`Mineur`.`Correctif`. + +#### Git OneFlow + +[Git OneFlow](https://www.endoflineblog.com/oneflow-a-git-branching-model-and-workflow) est un workflow Git conçu par Adam Ruka en alternative à [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/). + +Il se base principalement sur un objectif simple : avoir un arbre Git dont la **branche principale** est **la plus linéaire possible**. C'est à dire avec le moins d'embranchements possibles. + +En somme on va favoriser le **rebase** sur les branches dédiées à une nouvelle fonctionnalité AVANT de fusionner sur la branche principale. Cela aura l'avantage : + +* de réduire le travail de la personne qui fusionne la branche de fonctionnalités, +* et de favoriser la résolution des conflits de fusion aux développeurs responsables de la branche de fonctionnalités. + +![Avant/après la fusion d'une branche en utilisant Git OneFlow](./media/feature-branch-rebase-final.png){width=50%}\ + +#### Conventional commit + +Dans le cadre de la rédaction de message de commit pour chaque changement effectué sur un dépôt, nous avons choisi de suivre la spécification [Commits Conventionnels](https://www.conventionalcommits.org/fr/v1.0.0/#r%C3%A9sum%C3%A9). + +Cela se résume à peu près à cela : + +``` +[étendue optionnelle]: + +[corps optionnel] + +[pied optionnel] +``` + +Qui donne par exemple : + +``` +feat(backend): include postgreSQL BDD + backend + +* change EC2 instances to t3.large +* change min_size nodes to 2 +* use backend version 0.0.1 +* use annotations to have api.r53.devu42.fr as domain for backend + Let's Encrypt with cert-manager +* update EBS CSI driver to v1.37.0-eksbuild.1 +* declare a StorageClass EBS in gp3 for postgreSQL +* use this EBS StorageClass in our backend chart for postgreSQL +* set database size to 1Gi + +ref projet#18 +``` + +## Outils + +Au delà d'une à plusieurs méthodologies/normes/standards, nous avons utilisés des outils afférents. + +### Nom de domaine + +Afin d'avoir une **boîte courriel commune** (team@devu42.fr) et des **noms de domaine** qui pointent sur le résultat de nos travaux, nous avons opté pour la location d'un nom de domaine : **devu42.fr**. + +Ce domaine fait référence à notre cursus de DevOps dans un centre de formation nommé **DevU**niversity. Le nombre 42 est un chiffre connu lié aux ouvrages de Douglas Adams dans sa trilogie en 5 volumes de « H2G2, Guide du Voyageur Galactique ». + +Nous avons ainsi pu avoir des noms de domaines spécifiques pour : + +* le backend (API) dans l'environnement de pré-production, +* le backend (API) dans l'environnement de production, +* le frontend dans l'environnement de pré-production, +* et le frontend dans l'environnement de production. + +C'était donc un bon départ. + +### Plateforme DevOps + +Notre outils principal a été la **plateforme Gitlab**. C'est une plateforme complète de DevOps qui couvre bon nombre de besoins DevOps parmi : + +* des dépôts de code source (dépôts Git), +* un système d'intégration continu (GitlabCI), +* une gestion des rôles utilisateurs pour une granularité de gestion de permissions tout en finesse, +* des dépôts pour : + * les Chart Helm, + * les images Docker, + * les modules Terraform, + * les états Terraform, + * les librairies Python, PHP, JS, + * etc., +* un système de gestion de tickets pour le retour régulier (feedback) des utilisateurs, +* l'usage de jalons, kanban et métriques des équipes pour une gestion efficace d'une à plusieurs équipe dans un projet, +* la gestion des versions (release), +* l'intégration avec Kubernetes pour un suivi depuis Gitlab de nos clusters Kubernetes, +* etc. + +De nombreuses fonctionnalités supplémentaires existent dans Gitlab. Cela en fait donc un outil de choix pour notre projet. + +#### Workflow des tickets + +schéma workflow tickets + +#### Base de connaissance + +Wiki ### Environnements @@ -43,22 +164,6 @@ Gitlab, Slack, SemVer, Git OneFlow, Conventional commit, différents environneme TODO: donner schéma effectué -### Plateforme DevOps - -* couvre les principes DevOps -* Github vs. Gitlab ? -* services utilisés : - * Tickets - * Jalons - * Kanbans -* Base de connaissance collaborative : Wiki sous Gitlab - -### Méthodologie - -* Méthodologie Agile basique -* Compte-rendus journaliers -* Jalon pour chaque semaine, avec quelques tickets par étapes - ## Cas d'une collaboration inter-équipe TODO : compléter diff --git a/media/feature-branch-rebase-final.png b/media/feature-branch-rebase-final.png new file mode 100644 index 0000000000000000000000000000000000000000..a1ec2e0d0510c3a774fa3afff12b4328be20cfe8 GIT binary patch literal 17254 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 literal 0 HcmV?d00001