- Démo - Stratégies de CI/CD d'une application web conteneurisée
- Tester
- Workflow
- Gestion des différents environnements
- Mise en production côté serveur (rapatrier la nouvelle image)
- Stratégies de déploiement
- Références
Dans une CI :
- Avant le build (tests sources, dev, avant de commit/merge sur le dépôt principal). Mettre en place de l'analyse statique de code, suite de tests, force bonnes pratiques (linter), detect smells, etc. à placer sur un hook pre-commit. Test du code (via une image de test) :
- environnement identique pour tous
- isolation complète
- reproductibilité
- Après le build. Test app + dépendances internes + dep externes (variables d'env, base de données, API), tests d'intégration/end2end, etc. Test de l'artefact (via l'image de prod)
Ce qui est critique c'est de tester l'image qui sera déployée (il faut que ce soit exactement la même !)
- Développe ;
- Test en local via un conteneur. Voir le résultat sous forme de status code (
echo $?). A placer par ex sur hook gitpre-commit. Ici :
#utilise stage development (voir fichier compose)
docker compose run --build --rm server ./vendor/bin/phpunit tests/HelloWorldTest.php- Si tests passent, commit puis push sur le dépôt remote. Un évènement (par ex commit sur main, PR + merge) déclenche un job CI (avec Github Actions ici):
- Build image de test/test unitaires (ex: lancement de la suite de tests pendant le build d'une image de test);
- Build image de prod;
- Tests externes sur image prod (contre d'autres conteneurs comme bdd, redis, services ext, etc.)
- Steps supplémentaires : analyse image (Docker Scout), SonarQube, etc.
- Si tests passent, push nouvelle image sur un registre ave un tag unique (version, hash commit). Fin de la CI.
- Début de la CD : déploiement de l'image validée (pull + rollout) ! pull la nouvelle image et instancier (run) de nouveaux conteneurs à partir de celle-ci.
- Déploiement de la nouvelle version.
Faire un fichier
Makefilepour simplifier en local, ou un alias ou un script. Vous pouvez aussi utiliser les hooks de git. L'idée c'est que vous ne devez pas pouvoir échapper à votre procédure. Si vous oubliez de faire quelque chose, la procédure ne doit pas être déclenchée (pas de procédure incomplète) et vous devez être prévenu par un message d'erreur. Faire en sorte d'avoir le moins de choses auxquelles penser. Par ex, le push sur le dépôt distant devrait être automatiquement empêché si la suite de tests en local ne passe pas.
Chaque environnement peut définir des valeurs (variables d'env), des valeurs secretes (clé)
- Une configuration compose par environnement. Plusieurs stratégies (include, merge, extends, profiles) :
- Externalisation des variables d'environnement dans fichiers correspondants;
- Chaque fichier compose utilise son fichier d'env;
- Externalisation des secrets (à ne pas placer en variables d'environnement!)
Une mise en production doit:
- Être réversible (rollback) ;
- Être définie par une séquence d'instructions déterministe ;
- Idempotente : déclencher 2 fois la procédure avec les mêmes artefacts revient à le faire une fois ;
- Gérer automatiquement les configurations ;
- Être déclenchée idéalement par une seule commande/instruction.
Un exemple :
- Disposer des fichiers compose sur le serveur de prod, plusieurs solutions :
- Cloner le dépôt sur le serveur de prod pour récup les fichiers compose
- Créer un dépôt dédié uniquement aux fichiers compose
- Copier directement dans le CI/CD les fichiers compose présents dans le dépôt (plusieurs solutions)
- Etc.
- Pull la dernière image puis compose up
Pour pull dernière image et être réversible on peut :
- Placer l'id de la nouvelle image dans un fichier d'env (
.env.x.y.z) faire un lien symboliqueln -s .env.x.y.z .env. Le fichier.envpointe sur le dernier fichier.env.x.y.zcontenant la version de la nouvelle image (nouveau tag) ; - Le fichier
.envest utilisé par le fichiercompose.yaml: il utilise et interpole la variable d'environnement pour le tag de l'image à utiliser dans la sectionservices:${REGISTRE}/app:${VERSION} - Si besoin d'avancer à la version
x.y.z:- Créer un nouveau fichier
.env.x.y.z - Créer/Refaire le lien symbolique
ln -sfn .env.x.y.z .env - Relancer les services basées sur les images mise à jour (par ex:
docker compose -f compose.yaml --env-file .env.prod --env-file .env up)
- Créer un nouveau fichier
- Si besoin de rollback, il suffit de repointer sur le fichier d'env précédent et relancer les conteneurs à partir de l'image précédente (
compose up)
Cette méthode de liens symboliques est très utilisée et commode. C'est ce que fait par exemple l'excellent outil Capistrano.
compose.yaml
#Lien symbolique vers la version actuelle
.env -> .env.1.2
#Ancienne version pour le rollback
.env.1
.env.1.1
.env.1.2avec compose.yaml:
services:
app:
image: ${REGISTRE}/app:${VERSION}et les fichiers .env, par ex .env.1.1 :
REGISTRE=vendor
VERSION=1.1Docker n'est pas un Framework de déploiement. Docker offre l'artefact et la plateforme standardisés, ne vous dit pas comment vous devez distribuer vos artefacts.
Il existe de nombreuses façons de mettre de déployer des images Docker, à vous d'utiliser la plus adaptée à votre contexte. Ce qui compte c'est que votre mise en production possède les caractéristiques énoncées plus haut (reproductible, déterministe, simple et réversible)
Le Blue/Green deployment est une stratégie de déploiement visant à réduire au maximum le risque de mise en production d’une nouvelle version avec aucun downtime (sans interruption)
Deux environnements identiques sont maintenus en production :
- Blue : version actuellement en production (stable)
- Green : nouvelle version prête à être mise en production
- Base de données unique partagée
BLUE APP ─┐
├── DB (unique)
GREEN APP ─┘
Le trafic est envoyé vers une seule version à la fois.
Prérequis : la CI est passée avec succès.
- (Opt) Migrer la base de données (backward compatible). La migration supporte Blue et Green*
- Déployer Green
- Basculer le traffic de Blue vers Green
- En cas de problème, rollback immédiat vers Blue
- (Opt) Une fois déploiement stabilisé, migrer de la base pour retirer support version Green* (clean).
À venir...
Alternative simple : Workflow direct minimal (sans passer par une plateforme CI/CD ni registre), ni rollback
- Développe;
- Test;
- Build et test :
docker build --platform <votre plateforme cible> -t app:1 . - Compresse et déploie image via SSH avec scp :
docker save app:1 | gzip | ssh user@ip docker load
L'image est directement envoyée via la sortie standard (stout) sur le serveur et chargée depuis l'entrée standard (stdin)
- Instancie nouveau conteneur:
ssh user@ip docker compose up -d app
- Containerize a PHP application, bon guide sur la mise en place d'un projet Docker avec une pipeline CI/CD