Public cible
Débutants, étudiants et professionnels qui souhaitent acquérir une pratique structurée dans ce domaine.
Mettre une application en production de façon reproductible et réversible : chaîne d'intégration, image de conteneur reproductible, contrôle de qualité bloquant, stratégies de déploiement, sécurisation de la chaîne et retour arrière testé.
Débutants, étudiants et professionnels qui souhaitent acquérir une pratique structurée dans ce domaine.
Comprendre les notions essentielles et les appliquer dans un projet vérifiable.
Aucune connaissance préalable n’est strictement obligatoire ; les prérequis détaillés sont précisés dans les ateliers.
Tous niveaux
La validation repose sur les évaluations actives et les livrables pratiques du cours.
Un ordinateur, un navigateur et les outils indiqués dans le premier atelier sont suffisants pour commencer.
Développeurs et exploitants. Aucune expérience préalable de l'intégration continue n'est requise ; des bases de Git et de Docker sont suffisantes.
35 heures. Chaque module finit par un atelier où le pipeline est réellement exécuté.
L'intégration continue (CI) consiste à intégrer fréquemment du code dans une branche commune, en le vérifiant automatiquement à chaque apport. La livraison continue (CD) consiste à rendre chaque intégration vérifiable déployable en production, sans étape manuelle.
Chaque étape est un état : succès ou échec. Un état en échec arrête la chaîne — c'est tout l'intérêt.
La CI/CD ne corrige pas un mauvais code, ne remplace pas les tests, et ne garantit pas qu'un déploiement est sans risque. Elle rend le feedback immédiat : c'est la rapidité du retour qui fait la qualité.
Plus une integration est petite et frequente, moins le conflit est cher a resoudre. L'integrer une fois par semaine, en fin de sprint, avec des branches longues, produit des conflits et desyel merges reportes : c'est l'inverse du principe meme de l'integration continue.
| Outil | Ce que c'est | Point d'attention |
|---|---|---|
| GitHub Actions | CI/CD intégré à l'hébergement Git, configuration en YAML dans le dépôt | Le coût dépend du nombre d'exécutionsminutes, pas du nombre de dépôt |
| GitLab CI/CD | intégré à GitLab, pipeline déclaratif en YAML | fonctionnalités avancées liées à l'offre |
| Jenkins | serveur d'automatisation autonome, très flexible | à maintenir soi-même : JVM, plugins, sauvegardes |
| Argo CD | déploiement continu déclaratif à partir de l'état voulu du cluster | spécifique à Kubernetes |
Le choix se fait sur trois critères : l'hébergement déjà en place, la compétence de l'équipe, et le coût réel (le temps de maintenance est un coût).
Sur GitHub Actions, un workflow est un fichier YAML dans
.github/workflows/. Trois parties : un
déclencheur, un ou plusieurs jobs, des étapes.
name: Intégration continue
on:
push:
branches: [main]
pull_request:
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: pip
- name: Installer les dépendances
run: pip install -r requirements.txt
- name: Vérifier la qualité
run: ruff check .
- name: Exécuter les tests
run: python -m pytest -q --cov=app --cov-report=term-missing
on: indique quand la chaîne part. On peut
cibler une branche (branches:), un chemin de
fichiers (paths:), tags, ou être déclenché par
une autre chaîne (workflow_run).
on:
push:
branches: [main]
paths:
- 'src/**'
- 'tests/**'
paths-ignore:
- '**.md' # la doc seule ne déclenche rien
node_modules, vendor,
.venv) : ils se réinstallent à chaque
exécution.Tester sur une seule version masque les problèmes qui n'apparaissent qu'ailleurs. Une matrice se déclare en une ligne.
jobs:
tests:
runs-on: ubuntu-latest
strategy:
fail-fast: false # on veut le résultat de toutes les versions
matrix:
python-version: ['3.11', '3.12', '3.13']
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip
- run: pip install -r requirements.txt
- run: python -m pytest -q
Chaque combinaison est un job indépendant. Pour
suivre la progression d'un job précis, on utilise la
syntaxe ${{ matrix.python-version }}.
Le cache se déclare sur l'action qui le gère, pas globalement : la clé est calculée à partir d'un fichier qui décrit les dépendances, pour être invalidée dès qu'il change.
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: pip-${{ runner.os }}-${{ hashFiles('requirements.txt') }}
restore-keys: |
pip-${{ runner.os }}-
hashFiles porte sur le fichier qui décrit les
dépendances : si une dépendance change, la clé change et le
cache précédent n'est plus réutilisé.
push sur
main, installe les dépendances, lance les
tests et échoue si la couverture passe sous 80 %.fail-fast: false.Un artefact de livraison doit être reproductible : deux constructions de la même révision donnent la même image. Sans cela, « ça marche en préproduction » ne prouve rien.
docker build -t mon-service:1.4.2 .
docker tag mon-service:1.4.2 registry.exemple.fr/mon-service:1.4.2
docker push registry.exemple.fr/mon-service:1.4.2
Le tag est le numéro de version, jamais
latest seul : latest est une
étiquette mouvante, et un déploiement doit pouvoir dire
exactement quelle version tourne.
# syntaxe moderne : 'AS etape' nomme l'etape
FROM python:3.12-slim AS deps
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
FROM python:3.12-slim AS runtime
WORKDIR /app
COPY --from=deps /usr/local /usr/local
COPY . .
USER 1000:1000
CMD ["python", "-m", "app"]
La construction multi-étapes garde la couche de compilation hors de l'image finale : l'artefact livré ne contient que ce qui sert à exécuter. Exécuter avec un utilisateur non privilégié fait partie de la même discipline.
.dockerignore pour exclure
.git, les tests et les fichiers locaux ;La chaîne doit bloquer ce qui ne doit pas être livré, pas seulement signaler. Trois contrôles à placer avant la construction :
- name: Vérifier les vulnérabilités
run: pip-audit --strict
- name: Construire
run: docker build -t mon-service:1.4.2 .
- name: Scanner l'image
run: |
test "$(trivy image --severity HIGH,CRITICAL --format json mon-service:1.4.2 | jq '.Results | length')" = "0"
Le point clé : l'étape échoue si la vérification échoue. Un tableau de bord consultable après coup ne remplace pas une porte fermée dans la chaîne.
Un registre d'images est un dépôt d'artefacts. Il doit être accessible par la chaîne, protégé par des identifiants dédiés, et able de signaler ce qu'il accepte.
# version semantique : MAJEUR.MINEUR.PATCH
1.4.2 # version stable fixee
1.4 # serie de correctifs
1 # toute la serie 1.x
sha-9f2c1ab # version exacte et immuable
Déployer en production avec une étiquette mouvante est la première cause de « ça marchait hier ». Le déploiement doit nommer la révision, par exemple par empreinte de commit.
Un environnement est un déploiement avec ses propres données et sa propre configuration. Trois environnements minimum : développement, préproduction, production.
La règle : le même artefact est déployé partout. Reconstruire par environnement casse la reproductibilité — ce qui est validé en préproduction n'est alors plus l'artefact qui part en production.
Le code ne contient ni clé ni adresse de production : il lit des variables, injectées par la plateforme selon l'environnement.
# developpement, dans un fichier local non versionne
DATABASE_URL=postgresql://localhost:5432/app
# production, dans les variables de l'hebergeur
DATABASE_URL=postgresql://...:...@db.interne:5432/app
Ce qui change d'un environnement à l'autre : adresses, identifiants, seuils, activation de fonctionnalités. Ce qui ne change pas : le code et la version de l'artefact.
Choisir une stratégie, c'est choisir un compromis entre la rapidité du retour arrière et la disponibilité pendant la bascule.
| Stratégie | Principe | Retour arrière |
|---|---|---|
| Arrêt / démarrage | on arrête, on met à jour, on démarre | immédiat mais coupure de service |
| Défilant (rolling) | instances remplacées par lots successifs | possible tant qu'aucun lot n'est terminé |
| Bleu / vert | deux environnements identiques, bascule du trafic | immédiat, on rebascule le trafic |
| Canari | petite fraction du trafic vers la nouvelle version | on retire les canaris, effet local |
Le déploiement du code est réversible ; une migration de base ne l'est pas toujours. Trois règles :
Une migration destructive dans le même déploiement que la mise à jour du code, c'est le meilleur moyen de ne pas pouvoir revenir en arrière.
Le retour arrière doit avoir été préparé avant le déploiement : c'est une décision et une procédure, pas une improvisation.
- name: Déployer la version preparee
run: kubectl apply -f manifeste.yaml
- name: Vérifier la sante
run: ./verification-sante.sh
- name: Revenir en arriere si la verification echoue
if: failure()
run: kubectl rollout undo deployment/mon-service
Le déploiement progressif consiste à étaler la bascule : une fraction du trafic d'abord, on observe, puis on élargit. L'observation porte sur des signaux métier — taux d'erreur, latence, et si possible une métrique qualitative du produit — pas seulement sur « le processus répond ».
Un runbook écrit avant l'incident vaut dix minutes gagnées pendant : dernière version saine, commande de retour arrière, personnes à prévenir, critères de décision. Il doit être dans le dépôt, versionné comme le reste.
telephone à supprimer et une
table factures à créer. Écrivez les étapes
de déploiement dans l'ordre, en indiquant à chaque fois
ce qui reste réversible.La chaîne d'intégration est elle-même du code : elle dépend de la plateforme, d'actions tierces et de dépendances. Une action compromise, ou un jeton volé, donne un accès direct à la production.
Un jeton de déploiement est aussi un mot de passe. Il se protège comme tel : périmètre restreint, durée de vie courte, révocation immédiate possible, journalisation des usages.
Une action est un logiciel que l'on exécute avec nos identifiants. Deux questions à poser avant de l'ajouter : qui la maintient, et que se passe-t-il si elle est compromise ? On privilégie les actions de l'éditeur du dépôt lui-même, et on épingle les versions.
La règle est simple et rarement appliquée : la chaîne reçoit le strict nécessaire, et pas plus.
Une pull request qui touche la configuration de la chaîne elle-même mérite un regard humain : c'est elle qui définit ce qui a le droit de se produire.
Un secret ne doit apparaître ni dans le dépôt, ni dans les journaux, ni dans les artefacts.
echo ; - name: Publier
env:
JETON: ${{ secrets.JETON_PUBLICATION }}
run: |
# la variable d'environnement est injectee, elle n'apparait pas dans la commande
curl --header "Authorization: Bearer $JETON" https://registre.exemple.fr/v2/
Le point souvent ignoré : un secret retiré d'un fichier ne l'est pas de l'historique git, ni des caches de la chaîne, ni des journaux déjà collectés. Le changer est la seule action qui vaut.
À la fin, deux questions doivent pouvoir obtenir une réponse rapide : quelle version tourne en production, et quelle version a introduit ce comportement.
Sans cela, un incident se passe à deviner. Avec cela, on remonte de l'incident à la révision en une requête.
Le projet est realized sur une petite application de votre choix, et vise un résultat vérifiable : la chaîne part d'un commit et mène jusqu'à une version déployée et traçable.
.dockerignore, empreinte reproductible
vérifiée par deux constructions.git clone suivi de la procédure
documentée reconstruit tout, sans connaissance
préalable ;pytest qui passe en moins de
cinq minutes ;Commencer par le déploiement automatisé sur une base non testée, c'est automatiser un accident : on ne saura jamais si un échec vient du code ou de l'infrastructure.
Une chaîne imposée sans explication est contournée. Le meilleur moyen de la faire adopter : rendre le délai entre « j'ai poussé » et « je sais si ça marche » plus court que le délai d'attente du prochain test manuel. L'adoption suit le bénéfice ressenti, pas la note de couverture.
ruff, black,
mypy : qualité Pythonpytest : teststrivy, pip-audit : sécurité des dépendanceshadolint : qualité des fichiers de constructioncosign : signature d'imageMettre en service un projet qui n'en a pas, et ledemontrer de bout en bout.
Publié le 28/09/2026