Bonnes pratiques DevOps : CI/CD, IaC et observabilité
Ce tutoriel propose une vue complète et pratique des bonnes pratiques DevOps autour de trois piliers : CI/CD, Infrastructure as Code (IaC) et observabilité. L’objectif est de fournir des explications approfondies, des choix d’outillage réalistes et des commandes concrètes pour construire une chaîne de livraison fiable, reproductible et exploitable en production.
1) Principes DevOps : ce qu’on cherche à optimiser
Avant les outils, il faut clarifier les objectifs :
- Réduire le lead time : passer d’un commit à une mise en production rapidement.
- Augmenter la fréquence de déploiement : livrer souvent, par petits incréments.
- Diminuer le taux d’échec des changements : éviter les régressions et incidents.
- Réduire le MTTR (Mean Time To Recovery) : restaurer vite en cas de panne.
Ces objectifs se traduisent en pratiques :
- Automatisation systématique (tests, build, déploiement, infra).
- Standardisation (conventions, templates, pipelines).
- Traçabilité (qui a changé quoi, quand, pourquoi).
- Feedback rapide (tests, alertes, dashboards).
- Sécurité intégrée (DevSecOps) : contrôle dès le début.
2) CI/CD : construire une chaîne fiable
2.1 CI vs CD : définitions utiles
- CI (Continuous Integration) : intégrer fréquemment, exécuter des tests automatiques, produire des artefacts (binaire, image Docker), et garantir un état “main” stable.
- CD (Continuous Delivery/Deployment) :
- Delivery : artefact prêt à être déployé à tout moment, déploiement déclenché manuellement.
- Deployment : déploiement automatique jusqu’en production (avec garde-fous).
Bonnes pratiques clés :
- Pipeline rapide (idéalement < 10–15 min pour la boucle principale).
- Tests en pyramide : unitaires nombreux, intégration ciblée, end-to-end parcimonieux.
- Artefacts immuables (versionnés) : on déploie un artefact déjà testé.
- Environnements reproductibles (containers, IaC).
- Stratégies de déploiement progressives (blue/green, canary).
2.2 Exemple d’application : API Node.js containerisée
Structure minimale
mkdir -p demo-devops && cd demo-devops
npm init -y
npm install express
cat > index.js <<'EOF'
const express = require("express");
const app = express();
app.get("/health", (req, res) => res.json({ status: "ok" }));
app.get("/", (req, res) => res.send("Hello DevOps"));
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on ${port}`));
EOF
node index.js
curl -s http://localhost:3000/health
Dockerfile (artefact immuable : image)
cat > Dockerfile <<'EOF'
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY index.js ./
ENV PORT=3000
EXPOSE 3000
USER node
CMD ["node", "index.js"]
EOF
docker build -t demo-devops:local .
docker run --rm -p 3000:3000 demo-devops:local
curl -s http://localhost:3000/health
Pourquoi c’est important :
- Le build produit une image reproductible.
- On exécute la même image en test, staging, prod (seules les variables changent).
2.3 Versioning et traçabilité
Adoptez des conventions :
- SemVer :
MAJOR.MINOR.PATCH(ex :1.4.2). - Ou version basée sur le commit :
git describe --tags --always.
Commandes utiles :
git init
git add .
git commit -m "init: API demo"
git tag v0.1.0
git describe --tags --always
Pour une image Docker :
GIT_SHA=$(git rev-parse --short HEAD)
docker build -t registry.example.com/demo-devops:${GIT_SHA} .
2.4 Qualité : tests, lint, SAST, dépendances
Même si l’exemple est minimal, une chaîne CI sérieuse inclut :
- Tests unitaires (Jest, pytest, go test…)
- Lint (ESLint, golangci-lint…)
- SAST (Semgrep, CodeQL…)
- Scan dépendances (npm audit, pip-audit…)
- Scan image (Trivy, Grype…)
Exemples de commandes (Node) :
npm install --save-dev eslint
npx eslint --init
npx eslint .
npm audit --audit-level=high
Scan d’image Docker avec Trivy :
brew install trivy # macOS
# ou sur Linux : voir doc officielle, ou via package manager
trivy image demo-devops:local
Bonnes pratiques :
- Faites échouer le pipeline si vulnérabilités critiques.
- Conservez une “liste d’exceptions” limitée, versionnée, avec expiration.
2.5 Construire un pipeline CI/CD (exemple GitHub Actions)
Même si GitHub Actions utilise un fichier YAML, l’intérêt ici est de comprendre les étapes et de pouvoir les exécuter localement. Voici les commandes typiques que votre CI doit exécuter :
- Installer dépendances :
npm ci
- Lint + tests :
npx eslint .
npm test
- Build image :
docker build -t registry.example.com/demo-devops:${GIT_SHA} .
- Scan image :
trivy image --exit-code 1 --severity CRITICAL,HIGH registry.example.com/demo-devops:${GIT_SHA}
- Push registry :
docker login registry.example.com
docker push registry.example.com/demo-devops:${GIT_SHA}
- Déploiement (ex : Kubernetes) :
kubectl set image deployment/demo-devops api=registry.example.com/demo-devops:${GIT_SHA}
kubectl rollout status deployment/demo-devops
Points de contrôle à ajouter :
- Déploiement en staging automatique, prod avec approbation.
- Garde-fous : tests smoke après déploiement, rollback auto si échec.
2.6 Stratégies de déploiement et rollback
Rolling update (par défaut Kubernetes)
- Avantage : simple.
- Risque : si bug, impact progressif mais réel.
Commandes :
kubectl rollout status deployment/demo-devops
kubectl rollout history deployment/demo-devops
kubectl rollout undo deployment/demo-devops
Blue/Green
- Deux environnements (blue = prod, green = nouveau).
- Bascule via load balancer / service.
Canary
- Déployer sur un faible pourcentage, observer métriques/erreurs, augmenter progressivement.
- Souvent couplé à un service mesh (Istio/Linkerd) ou à un ingress avancé.
Bonnes pratiques :
- Toujours prévoir un rollback rapide.
- Ne jamais “patcher à la main” en prod : corriger dans le code/IaC puis redéployer.
3) Infrastructure as Code (IaC) : reproductibilité et maîtrise du changement
3.1 Pourquoi l’IaC est non négociable
Sans IaC, l’infrastructure devient :
- non reproductible (environnements divergents),
- difficile à auditer,
- risquée (changements manuels),
- lente à faire évoluer.
Avec IaC :
- vous versionnez l’infrastructure comme du code,
- vous faites des revues (pull requests),
- vous automatisez les déploiements infra,
- vous gardez un historique complet.
Deux grandes familles :
- Déclaratif (Terraform, Pulumi en mode déclaratif, CloudFormation) : “voici l’état voulu”.
- Configuration management (Ansible, Chef, Puppet) : “voici les actions à exécuter”.
En pratique : Terraform pour provisionner, Ansible pour configurer (selon contexte), et Kubernetes manifests/Helm pour déployer les workloads.
3.2 Terraform : workflow sain
Installation et vérification
terraform version
Workflow standard
- Initialiser :
terraform init
- Formater :
terraform fmt -recursive
- Valider :
terraform validate
- Planifier :
terraform plan -out tfplan
- Appliquer :
terraform apply tfplan
- Détruire (en sandbox uniquement) :
terraform destroy
Bonnes pratiques :
- Ne jamais appliquer sans plan.
- Exécuter
fmt/validate/planen CI. - Utiliser un remote state (S3 + DynamoDB lock, GCS, Terraform Cloud…).
- Séparer par environnements (workspaces ou dossiers) avec variables explicites.
3.3 Exemple IaC concret : AWS S3 + verrouillage d’état (concepts)
Même si vous adaptez au cloud, voici les commandes et concepts essentiels.
Créer un bucket S3 et une table DynamoDB (exemple AWS CLI) :
aws s3api create-bucket --bucket mon-tf-state-123456 --region eu-west-1 \
--create-bucket-configuration LocationConstraint=eu-west-1
aws dynamodb create-table \
--table-name terraform-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
Pourquoi :
- Le bucket stocke le state.
- DynamoDB empêche deux
applysimultanés (verrouillage).
3.4 Gestion des secrets : règles strictes
- Jamais de secrets dans Git.
- Préférez :
- Vault (HashiCorp Vault),
- AWS Secrets Manager / GCP Secret Manager / Azure Key Vault,
- SOPS (chiffrement GitOps),
- variables chiffrées CI.
Exemple : lire un secret depuis Vault (illustratif) :
vault login
vault kv get -field=password secret/db
Exemple Kubernetes : créer un secret à partir d’un fichier (à éviter si le fichier traîne)
kubectl create secret generic db-credentials \
--from-literal=username='app' \
--from-literal=password='supersecret'
Bonnes pratiques :
- Rotation régulière.
- Principe du moindre privilège (IAM).
- Audit des accès.
- Séparer secrets applicatifs et secrets d’infra.
3.5 GitOps : déployer via Git comme source de vérité
GitOps = l’état désiré est dans Git, et un contrôleur (Argo CD / Flux) applique et réconcilie.
Avantages :
- Audit naturel (PR, reviews).
- Rollback facile (revert commit).
- Réconciliation automatique (drift correction).
Commandes utiles côté Kubernetes :
kubectl get ns
kubectl get deployments -A
kubectl diff -f k8s/
kubectl apply -f k8s/
Même si vous utilisez Argo CD, vous gardez ces commandes pour diagnostiquer.
4) Observabilité : logs, métriques, traces (et alertes utiles)
4.1 Observabilité vs monitoring
- Monitoring : “Est-ce que ça marche ?” (symptômes, seuils, disponibilité)
- Observabilité : “Pourquoi ça ne marche pas ?” (capacité à expliquer l’état interne via signaux)
Les trois signaux :
- Métriques : séries temporelles (latence, CPU, taux d’erreur)
- Logs : événements textuels/structurés
- Traces : parcours d’une requête à travers services (latence par composant)
Objectif : réduire le temps de diagnostic et éviter les “war rooms” interminables.
4.2 Métriques : RED, USE, SLI/SLO
Méthodes
-
RED (services) :
- Rate (débit)
- Errors (erreurs)
- Duration (latence)
-
USE (ressources) :
- Utilization
- Saturation
- Errors
SLI/SLO (fiabilité pilotée)
- SLI : indicateur (ex : % de requêtes < 300 ms)
- SLO : objectif (ex : 99.9% sur 30 jours)
- Error budget : marge d’erreur acceptable (100% - SLO)
Exemple : SLO 99.9% => budget d’erreur 0.1% sur 30 jours. Si vous le consommez trop vite, vous ralentissez les releases et investissez dans la fiabilité.
4.3 Logs : structurés, corrélables, exploitables
Bonnes pratiques :
- Logs structurés (JSON) plutôt que texte libre.
- Inclure :
timestamp,level,service,env,request_id,trace_id. - Éviter données sensibles (PII, tokens).
- Définir une politique de rétention.
Exemple (Docker) : consulter logs d’un conteneur
docker logs -f <container_id>
Sur Kubernetes :
kubectl logs -f deploy/demo-devops
kubectl logs -f deploy/demo-devops --previous
4.4 Traces : OpenTelemetry comme standard
OpenTelemetry (OTel) est devenu le standard de facto pour instrumenter et exporter traces/métriques/logs.
Concepts :
- Span : unité de travail (ex : requête HTTP, requête DB)
- Trace : ensemble de spans corrélés
- Context propagation : propagation d’un identifiant de trace entre services
Même sans instrumenter tout de suite, commencez par :
- un reverse proxy/ingress qui injecte des headers,
- un SDK OTel dans l’app,
- un collector OTel pour router vers votre backend (Jaeger, Tempo, Datadog, etc.).
4.5 Prometheus : métriques et alerting (commandes)
Prometheus scrappe des endpoints /metrics. Vous pouvez tester localement avec curl (si votre app expose des métriques) :
curl -s http://localhost:3000/metrics | head
Interroger Prometheus via son API HTTP (exemple) :
curl -G 'http://prometheus:9090/api/v1/query' --data-urlencode 'query=up'
Lister des cibles :
curl -s 'http://prometheus:9090/api/v1/targets' | head
Bonnes pratiques alerting :
- Alerter sur l’impact utilisateur (taux d’erreur, latence) avant CPU.
- Éviter les alertes “bruit” (flapping).
- Ajouter une action attendue dans la description (runbook).
4.6 Grafana : dashboards utiles (ce qu’il faut afficher)
Dashboards recommandés :
- Service Overview (RED) : RPS, erreurs 4xx/5xx, p95/p99 latence.
- Infra : CPU/mémoire, saturation, throttling, réseau.
- Kubernetes : restarts, pending pods, HPA, erreurs d’image pull.
- Business (si applicable) : conversions, commandes, etc.
Le piège : dashboards trop complexes. Préférez 5–10 graphes essentiels + drill-down.
4.7 Alertes : philosophie et exemples concrets
Une alerte utile doit répondre :
- Quoi ? (symptôme)
- Où ? (service, cluster, région)
- Gravité ?
- Depuis quand ?
- Que faire ? (runbook)
Commandes de diagnostic typiques (Kubernetes) :
kubectl get pods -n prod
kubectl describe pod <pod> -n prod
kubectl top pods -n prod
kubectl get events -n prod --sort-by=.metadata.creationTimestamp | tail -n 30
kubectl exec -it deploy/demo-devops -n prod -- sh
Réseau / DNS :
kubectl exec -it deploy/demo-devops -n prod -- nslookup kubernetes.default
kubectl exec -it deploy/demo-devops -n prod -- wget -qO- http://service-backend:8080/health
5) Sécurité intégrée (DevSecOps) : contrôles continus
5.1 Chaîne de confiance : signature et provenance
Objectifs :
- Savoir d’où vient un artefact.
- Empêcher le déploiement d’images non approuvées.
Outils :
- cosign (Sigstore) pour signer des images.
- SBOM (Software Bill of Materials) : Syft, CycloneDX.
Exemple : générer une SBOM avec Syft
brew install syft
syft demo-devops:local -o cyclonedx-json > sbom.json
Signer une image avec cosign (conceptuel, nécessite config) :
cosign sign registry.example.com/demo-devops:${GIT_SHA}
cosign verify registry.example.com/demo-devops:${GIT_SHA}
5.2 Politiques : “policy as code”
But : empêcher des déploiements non conformes (ex : conteneur root, pas de limites CPU, image latest).
Sur Kubernetes, vous pouvez utiliser :
- OPA Gatekeeper
- Kyverno
Exemples de contrôles à imposer :
runAsNonRoot: trueresources.requests/limitsobligatoires- pas d’images
:latest - provenance signée
6) Standardiser : templates, conventions, “golden paths”
Une organisation DevOps performante propose des chemins standards :
- Template de repo (structure, Makefile, scripts).
- Pipeline CI standard (jobs réutilisables).
- Base Dockerfile durcie.
- Helm chart ou Kustomize standard.
- Observabilité “par défaut” (logs JSON, OTel, métriques).
6.1 Exemple de Makefile pour unifier les commandes
cat > Makefile <<'EOF'
APP=demo-devops
IMAGE=$(APP):local
.PHONY: install lint test build run scan
install:
npm ci
lint:
npx eslint .
test:
npm test
build:
docker build -t $(IMAGE) .
run:
docker run --rm -p 3000:3000 $(IMAGE)
scan:
trivy image $(IMAGE)
EOF
make install
make build
make scan
Pourquoi :
- Les développeurs et la CI exécutent les mêmes cibles.
- Réduction des “ça marche sur ma machine”.
7) Exploitation : runbooks, postmortems, gestion d’incident
7.1 Runbooks
Un runbook doit contenir :
- Symptômes
- Vérifications rapides
- Commandes de diagnostic
- Actions de mitigation
- Escalade
- Liens vers dashboards/logs/traces
Exemple de commandes “first response” :
kubectl get deploy -n prod
kubectl get pods -n prod
kubectl logs -n prod deploy/demo-devops --tail=200
kubectl rollout status -n prod deploy/demo-devops
7.2 Postmortems sans blâme
Après incident :
- Timeline factuelle
- Impact utilisateur
- Cause racine (souvent multiple : technique + process)
- Actions correctives (priorisées)
- Leçons apprises
Bonnes pratiques :
- “Blameless”: on améliore le système, pas la culpabilisation.
- Mesurer l’efficacité des actions (réduction d’occurrence, meilleur MTTR).
8) Architecture de référence (pragmatique)
Voici une architecture réaliste pour une équipe :
- CI : GitHub Actions / GitLab CI
- Registry : GHCR / ECR / GCR
- IaC : Terraform + remote state
- Kubernetes : EKS/GKE/AKS (ou VM + systemd si plus simple)
- CD : Argo CD (GitOps) ou déploiement via pipeline
- Observabilité :
- Prometheus + Alertmanager
- Grafana
- Loki (logs) ou ELK/OpenSearch
- Tempo/Jaeger (traces) via OpenTelemetry Collector
- Sécurité :
- Trivy (images)
- Semgrep (SAST)
- cosign + SBOM
Le plus important : cohérence et adoption. Mieux vaut une stack simple, bien utilisée, qu’une stack “parfaite” ignorée.
9) Checklist finale : à appliquer dès maintenant
CI/CD
-
maintoujours déployable - Tests automatiques + lint
- Artefacts immuables (images taggées par commit)
- Scan dépendances + scan image
- Déploiement progressif + rollback documenté
- Approvals pour prod (au minimum)
IaC
- Infra versionnée, revue en PR
- Remote state + verrouillage
- Variables par environnement
- Pas de secrets dans Git
- Détection de drift (GitOps ou audits réguliers)
Observabilité
- Dashboards RED par service
- Logs structurés + corrélation (request_id/trace_id)
- Traces distribuées (OTel)
- Alertes orientées utilisateur + runbooks
- Postmortems et actions correctives suivies
10) Aller plus loin (progression recommandée)
- Stabiliser la CI (tests, lint, build image, scan).
- Standardiser le packaging (Dockerfile, tags, registry).
- Automatiser le déploiement en staging, puis prod avec garde-fous.
- Passer l’infra en IaC (Terraform) + remote state.
- Mettre en place observabilité “minimum viable” (métriques + logs + alertes).
- Ajouter traces distribuées et SLO/error budgets.
- Renforcer la supply chain (SBOM, signature, policy as code).
En appliquant ces pratiques, vous construisez une chaîne DevOps qui ne se contente pas de “déployer”, mais qui garantit la qualité, réduit le risque, et améliore la capacité à diagnostiquer et à restaurer rapidement le service. L’efficacité vient de l’alignement entre automatisation (CI/CD), reproductibilité (IaC) et feedback (observabilité), le tout renforcé par la sécurité et des processus d’exploitation matures.