DevOps : bonnes pratiques, CI/CD et automatisation
1) Introduction : qu’est-ce que le DevOps (et ce que ce n’est pas)
Le DevOps est une approche organisationnelle et technique visant à réduire le temps entre une idée (ou un correctif) et sa mise en production, tout en augmentant la fiabilité. Il s’appuie sur la collaboration entre développement, opérations, sécurité et qualité, et sur l’automatisation (tests, build, déploiement, observabilité).
Ce n’est pas :
- Un poste unique (“la personne DevOps” qui fait tout).
- Un outil (GitLab CI, Jenkins, Kubernetes…).
- Une excuse pour “déployer vite sans tests”.
C’est plutôt :
- Une culture (responsabilité partagée, feedback rapide).
- Des pratiques (CI/CD, IaC, revue de code, tests, observabilité).
- Des outils choisis pour servir ces pratiques.
2) Principes clés : flux, feedback, amélioration continue
2.1 Flux (Flow)
Objectif : faire circuler le travail sans blocage.
- Petites unités de changement (petits PR/MR).
- Branches courtes (feature branches qui vivent peu).
- Standardisation (scripts, conventions, templates).
2.2 Feedback rapide
Objectif : détecter tôt.
- Tests unitaires à chaque commit.
- Analyse statique (lint, SAST) en CI.
- Environnements éphémères (review apps).
- Observabilité et alerting en production.
2.3 Amélioration continue
Objectif : apprendre et corriger le système.
- Post-mortems sans blâme.
- Mesure (DORA metrics : lead time, fréquence de déploiement, MTTR, taux d’échec).
- Réduction de la dette technique et de la dette d’automatisation.
3) Git : stratégie de branches et hygiène de dépôt
3.1 Stratégies de branches (pragmatiques)
Deux approches courantes :
Trunk-based development
- Une branche principale (
main) toujours déployable. - Petites branches très courtes, fusion rapide.
- Feature flags pour masquer les fonctionnalités incomplètes.
GitFlow (plus lourd)
main+develop+ branches de release/hotfix.- Utile dans certains contextes (versions longues, contraintes fortes), mais augmente la complexité.
Dans la majorité des équipes modernes, trunk-based + feature flags + CI robuste est souvent plus efficace.
3.2 Conventions de commit
Adopter des messages structurés aide l’automatisation (changelog, versioning sémantique). Exemple (Conventional Commits) :
git commit -m "feat(api): ajout de l'endpoint /health"
git commit -m "fix(auth): correction du rafraîchissement de token"
git commit -m "chore(ci): accélération du cache npm"
3.3 Revue de code : règles simples mais strictes
- PR/MR petits (< 300 lignes modifiées si possible).
- Checklist : tests ajoutés, logs pertinents, doc mise à jour.
- Interdire le merge si la CI échoue.
- Exiger au moins 1 ou 2 approbations selon criticité.
4) CI (Intégration Continue) : pipeline de base et bonnes pratiques
4.1 Objectifs de la CI
- Construire (build) de manière reproductible.
- Tester automatiquement.
- Produire des artefacts versionnés (images Docker, packages).
- Bloquer l’intégration si la qualité n’est pas au rendez-vous.
4.2 Exemple concret : projet Node.js
Structure typique :
npm testpour testsnpm run lintpour lintnpm run buildpour build
Commandes locales (à exécuter avant de pousser) :
npm ci
npm run lint
npm test
npm run build
4.3 Caching et reproductibilité
- Utiliser des lockfiles (
package-lock.json,poetry.lock,go.sum). - Préférer
npm ciànpm installen CI. - Éviter les dépendances non pinées.
4.4 Qualité : lint, tests, couverture, analyse statique
Exemples d’outils :
- Lint : ESLint, Pylint, golangci-lint
- Tests : Jest, Pytest, JUnit
- Couverture : Istanbul/nyc, coverage.py, JaCoCo
- SAST : Semgrep, SonarQube
- Dépendances :
npm audit,pip-audit,trivy
Exemples de commandes :
# JavaScript/TypeScript
npx eslint . --max-warnings=0
npm test -- --ci
# Python
python -m pip install -r requirements.txt
pytest -q --disable-warnings --maxfail=1
pip-audit
# Conteneurs (scan d'image)
trivy image myapp:1.2.3
5) Docker : construire, exécuter, optimiser
5.1 Dockerfile : bonnes pratiques
- Images de base minimales (ex:
alpinesi compatible, sinonslim). - Multi-stage build.
- Ne pas exécuter en root.
.dockerignorepour réduire le contexte.
Exemple multi-stage (Node.js) :
# build
FROM node:20-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# runtime
FROM node:20-slim
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=build /app/dist ./dist
# utilisateur non-root
RUN useradd -m appuser
USER appuser
EXPOSE 3000
CMD ["node", "dist/server.js"]
Construire et lancer :
docker build -t myapp:local .
docker run --rm -p 3000:3000 myapp:local
5.2 Gestion des secrets : ne jamais les mettre dans l’image
- Pas de
.envdans l’image. - Injecter via variables d’environnement au runtime, ou via un gestionnaire de secrets.
Exemple :
docker run --rm -p 3000:3000 \
-e DATABASE_URL="postgres://user:pass@db:5432/app" \
myapp:local
6) CD (Déploiement Continu) : stratégies de déploiement et sécurité
6.1 Livraison continue vs déploiement continu
- Livraison continue : chaque changement est prêt à être déployé, mais déclenchement manuel possible.
- Déploiement continu : chaque changement validé est automatiquement déployé.
6.2 Stratégies de déploiement
Rolling update
- Remplacement progressif des instances.
- Simple, mais attention aux migrations DB.
Blue/Green
- Deux environnements identiques (blue et green).
- Bascule du trafic en une action.
- Rollback rapide.
Canary
- Déploiement sur un petit pourcentage d’utilisateurs.
- Augmentation progressive si tout va bien.
- Nécessite métriques/alertes solides.
6.3 Migrations de base de données
Règles pratiques :
- Migrations compatibles (backward-compatible) quand possible.
- Séparer “déployer code” et “activer feature” (feature flags).
- Tester les migrations sur un clone de prod si possible.
Exemple (PostgreSQL) : exécuter une migration
psql "$DATABASE_URL" -f migrations/2026_04_28_add_index.sql
7) Infrastructure as Code (IaC) : rendre l’infra versionnée et reproductible
7.1 Pourquoi l’IaC
- Historique et revue de changements d’infra.
- Reproductibilité (environnements cohérents).
- Moins d’actions manuelles risquées.
7.2 Terraform : commandes essentielles
Initialiser :
terraform init
Voir les changements :
terraform plan -out plan.tfplan
Appliquer :
terraform apply plan.tfplan
Détruire (attention) :
terraform destroy
Bonnes pratiques Terraform :
- Utiliser un backend distant (state partagé) : S3 + DynamoDB lock, ou Terraform Cloud.
- Séparer par environnements (workspaces ou dossiers).
- Modules réutilisables.
terraform fmtetterraform validateen CI.
Commandes de qualité :
terraform fmt -check -recursive
terraform validate
8) Kubernetes : déployer proprement (sans magie)
8.1 Notions minimales
- Pod : unité d’exécution (un ou plusieurs conteneurs).
- Deployment : gestion des replicas et mises à jour.
- Service : exposition interne (ClusterIP) ou externe (LoadBalancer).
- Ingress : routage HTTP(S) vers les services.
- ConfigMap/Secret : configuration et secrets.
8.2 Commandes kubectl utiles
Voir les ressources :
kubectl get pods -n myns
kubectl get deploy -n myns
kubectl get svc -n myns
Décrire et diagnostiquer :
kubectl describe pod mypod -n myns
kubectl logs -f deploy/myapp -n myns
kubectl exec -it deploy/myapp -n myns -- sh
Rollback d’un deployment :
kubectl rollout status deploy/myapp -n myns
kubectl rollout history deploy/myapp -n myns
kubectl rollout undo deploy/myapp -n myns
8.3 Health checks : readiness vs liveness
- Readiness : “je suis prêt à recevoir du trafic”.
- Liveness : “je suis vivant, sinon redémarre-moi”.
Sans readiness, Kubernetes peut envoyer du trafic trop tôt, causant des erreurs au démarrage.
9) Automatisation : scripts, Makefile, tâches reproductibles
9.1 Makefile comme interface standard
Un Makefile simple unifie les commandes :
.PHONY: install lint test build docker run
install:
npm ci
lint:
npm run lint
test:
npm test
build:
npm run build
docker:
docker build -t myapp:local .
run:
docker run --rm -p 3000:3000 myapp:local
Utilisation :
make install
make lint
make test
make docker
make run
9.2 Pré-commit hooks (qualité avant push)
Installer pre-commit (Python) ou des hooks Git maison.
Exemple hook simple .git/hooks/pre-commit :
#!/usr/bin/env bash
set -euo pipefail
npm run lint
npm test
Activer :
chmod +x .git/hooks/pre-commit
10) CI/CD avec GitHub Actions : exemple complet (sans “magie”)
Ci-dessous, un exemple de pipeline conceptuel (à adapter) : lint + tests + build d’image + scan + push.
Les commandes sont réelles, mais l’intégration exacte dépend de votre registre (GHCR, Docker Hub, ECR…).
10.1 Étapes typiques côté CI
En local, simuler les étapes :
npm ci
npm run lint
npm test
docker build -t ghcr.io/ORG/myapp:sha-$(git rev-parse --short HEAD) .
trivy image ghcr.io/ORG/myapp:sha-$(git rev-parse --short HEAD)
Se connecter à un registre (exemple Docker Hub) :
echo "$DOCKERHUB_TOKEN" | docker login -u "$DOCKERHUB_USER" --password-stdin
docker push myorg/myapp:1.2.3
10.2 Versioning et tags d’images
Bonnes pratiques :
- Tag immuable :
:sha-<commit> - Tag lisible :
:1.2.3(release) - Éviter
:latestcomme seule référence en prod.
11) Sécurité DevSecOps : intégrer la sécurité au pipeline
11.1 Surfaces à couvrir
- Dépendances (SCA) : vulnérabilités dans les libs.
- Code (SAST) : patterns dangereux.
- Secrets : fuite de clés API, tokens.
- Images : CVE dans OS/packages.
- Infra : mauvaises configurations (ports ouverts, IAM trop large).
11.2 Commandes utiles
Scan de secrets (ex: gitleaks) :
gitleaks detect --source . --no-git
Audit npm :
npm audit --audit-level=high
Scan d’image :
trivy image --severity HIGH,CRITICAL myapp:local
11.3 Politique de “gates”
Définir des seuils :
- Refuser le merge si tests KO.
- Refuser si vulnérabilités CRITICAL non justifiées.
- Exceptions documentées (ticket + expiration).
12) Observabilité : logs, métriques, traces (et surtout actionnables)
12.1 Logs structurés
Préférer JSON (facile à indexer). Exemple (Node) : log minimal structuré :
node -e 'console.log(JSON.stringify({level:"info",msg:"server started",port:3000}))'
Bonnes pratiques :
- Corrélation : inclure
request_id,user_id(si pertinent),trace_id. - Éviter les logs sensibles (tokens, mots de passe).
- Niveaux : debug/info/warn/error.
12.2 Métriques (SLI/SLO)
Définir des SLI (indicateurs) :
- Latence p95/p99
- Taux d’erreur (5xx)
- Disponibilité
- Saturation (CPU, mémoire)
Puis des SLO (objectifs) :
- “99,9% des requêtes < 300 ms sur 30 jours”
- “Taux d’erreur < 0,1%”
12.3 Traces distribuées
Indispensables en microservices. Les traces permettent de voir où part le temps (DB, cache, appels externes). Outils : OpenTelemetry, Jaeger, Tempo.
13) Stratégies de rollback et gestion d’incident
13.1 Rollback applicatif
- Garder l’artefact précédent disponible (image taggée).
- Déployer l’ancienne version rapidement.
Exemple (Kubernetes) :
kubectl rollout undo deploy/myapp -n myns
13.2 Rollback base de données
Plus délicat :
- Préférer migrations “expand/contract” (ajouter colonne, écrire dans les deux, migrer, puis supprimer).
- Sauvegardes testées régulièrement.
Sauvegarde PostgreSQL :
pg_dump "$DATABASE_URL" > backup_$(date +%F).sql
Restauration (attention, destructif) :
psql "$DATABASE_URL" < backup_2026-04-28.sql
13.3 Runbooks
Documenter les actions :
- Comment diagnostiquer (logs, dashboards).
- Comment mitiger (désactiver feature flag, rollback).
- Qui contacter, escalade, communication.
14) Environnements : dev, staging, prod (et la parité)
14.1 Parité des environnements
Objectif : réduire les “ça marche sur ma machine”.
- Même type de base de données.
- Même versions majeures (runtime, OS, DB).
- Config via variables, pas via code.
14.2 Staging réaliste
- Données anonymisées si clone de prod.
- Charge représentative (tests de performance).
- Déploiement identique à prod (mêmes manifests, mêmes charts).
15) Exemple de chaîne complète (de l’idée à la prod)
- Développeur crée une branche courte :
git checkout -b feat/healthcheck
- Développement + tests locaux :
make install
make lint
make test
- Build container local :
make docker
- Push + PR/MR, CI exécute :
- lint
- tests
- build image
- scan sécurité
- publication artefact si OK
- Déploiement en staging :
- automatique après merge (selon politique)
- tests end-to-end
- Promotion en production :
- déploiement canary ou blue/green
- monitoring renforcé
- rollback si SLO en danger
16) Anti-patterns fréquents (et comment les éviter)
-
Pipeline lent : trop d’étapes séquentielles, pas de cache
→ paralléliser, cacher dépendances, réduire tests redondants. -
Déploiements manuels non tracés
→ tout déploiement passe par pipeline, traçabilité (qui, quoi, quand). -
Secrets dans Git
→ scanners de secrets + rotation + gestionnaire (Vault, AWS Secrets Manager…). -
“On verra en prod”
→ staging réaliste, tests contractuels, feature flags. -
Monitoring décoratif (dashboards sans alertes utiles)
→ alertes basées sur SLO, runbooks, tests de chaos ciblés.
17) Checklist opérationnelle (prête à appliquer)
CI
- Build reproductible (lockfiles, versions pinées)
- Lint + tests à chaque PR/MR
- Artefacts versionnés (tag SHA)
- Scans SAST/SCA + scan d’images
- Temps de pipeline acceptable (objectif : < 10–15 min si possible)
CD
- Déploiement automatisé vers staging
- Stratégie (rolling/blue-green/canary) documentée
- Rollback testé
- Migrations DB maîtrisées (expand/contract)
Sécurité
- Secrets gérés hors Git
- Moindre privilège (IAM/RBAC)
- Audit régulier des dépendances
Observabilité
- Logs structurés + corrélation
- Métriques SLI + SLO définis
- Alertes actionnables + runbooks
18) Conclusion : commencer petit, standardiser, puis accélérer
Mettre en place le DevOps efficacement consiste rarement à “tout faire d’un coup”. Le meilleur chemin est itératif :
- Standardiser les commandes (Makefile/scripts).
- Mettre une CI fiable (lint/tests/build).
- Produire des artefacts immuables (images taggées).
- Automatiser le déploiement (staging puis prod).
- Renforcer sécurité et observabilité.
- Mesurer et améliorer (DORA, SLO, post-mortems).
Si vous souhaitez, je peux proposer une architecture de pipeline adaptée à votre stack (Node/Python/Java/Go), votre cible (VM, Kubernetes, serverless) et votre registre (GHCR/ECR/GCR), avec des scripts concrets et une organisation de dépôt recommandée.