Skip to content

📖 Recommandations — Best Practices Consolidées

Distillé des 30 incidents. À lire avant de coder. 🚀


🔧 Pipeline CI/CD

Variables & Secrets

  • Masked = cache la valeur dans logs (pour toutes les branches)
  • Protected = uniquement branches protégées (main, develop)
  • 📌 Règle : Masked par défaut, Protected pour secrets production SEULEMENT

Variables Priorité (haute → basse)

1. Trigger/Schedule variables
2. Settings → CI/CD → Variables (TRÈS haute)
3. Variables fichier YAML
4. Variables groupe

→ Si les variables projet écrasent tes values YAML : utiliser before_script export

Kaniko vs Docker-in-Docker

  • Kaniko : pas besoin Docker daemon, plus léger, plus rapide
  • Docker-in-DinD : lourd, Alpine incompatible
  • 📌 Pattern : candidate → scan → promote (jamais push sans scan)

Image scanning

stages:
  - build          # Kaniko → ECR (tag: SHA-candidate)
  - scan-image     # Trivy depuis ECR
  - promote        # Si OK → tag SHA + latest

Outils spéciaux (entrypoint)

jobs:
  terraform:
    image: alpine/terraform:latest
    entrypoint: [""]  # ← OBLIGATOIRE pour images non-standard

  kaniko:
    image: gcr.io/kaniko-project/executor:latest
    entrypoint: [""]  # ← OBLIGATOIRE

🏗️ Terraform & Infrastructure

State & Locks

  • ⏸️ Ne JAMAIS interrompre terraform apply/destroy (Ctrl+C = state lock)
  • 📌 Utiliser tmux pour les longues opérations
  • 🔓 Si bloqué : terraform force-unlock LOCK_ID ou aws s3 rm s3://bucket/.tflock

Workspaces (persistent vs ephemeral)

persistent/  → ECR, IAM (jamais détruits)
ephemeral/   → VPC, EKS, RDS (destroy quotidien)

Ressources hors state

  • ❌ Ne jamais créer de ressources AWS manuellement (console)
  • 🔄 En prod : toujours terraform import avant restructurer
  • 🗑️ En dev : terraform destroy → restructurer → terraform apply

EKS requirements

  • NAT Gateway OBLIGATOIRE pour nodes dans subnets privés
  • Cleanup K8s AVANT terraform destroy (sinon ENIs orphelins)
  • 📌 Ordre critique : kubectl delete gateway → attendre 30s → terraform destroy

Coûts AWS (ephemeral)

NAT Gateway  : 0.045$/h  → 0.18$/jour (4h)
EKS Control  : 0.10$/h   → 0.40$/jour
t3.medium    : 0.047$/h  → 0.19$/jour
RDS micro    : 0.02$/h   → 0.08$/jour
─────────────────────────────────
Total        :           → ~0.85$/jour → ~17$/mois

RDS snapshots

  • Dev/ephemeral : skip_final_snapshot = true
  • ⚠️ Production : skip_final_snapshot = false + gestion snapshots
  • 🔍 Attention aux coûts cachés (snapshots, EBS, etc)

☸️ Kubernetes & EKS

Deployments & Probes

livenessProbe:
  httpGet:
    path: /health
    port: 8000
  initialDelaySeconds: 30  # ← FastAPI slow startup
  timeoutSeconds: 10

readinessProbe:
  httpGet:
    path: /health
    port: 8000
  initialDelaySeconds: 20

Database Migrations (Alembic)

# ✅ Init container pattern
initContainers:
  - name: migrate
    image: fastapi:latest
    command: ["alembic", "upgrade", "head"]
    env:
      - name: DATABASE_URL
        valueFrom:
          secretKeyRef:
            name: db-secret
            key: url

containers:
  - name: fastapi
    image: fastapi:latest
    # App démarre APRÈS migrations

ServiceAccount & RBAC

# ✅ Pour les jobs qui ont besoin de K8s API
serviceAccountName: fastapi-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: fastapi-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: fastapi-sa
    namespace: fastapi

Metrics & HPA

# IMPORTANT : metrics-server OBLIGATOIRE
helm install metrics-server metrics-server/metrics-server -n kube-system

# HPA ne fonctionne que si metrics-server est actif
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: fastapi-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: fastapi
  minReplicas: 1
  maxReplicas: 5
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80

ConfigMaps & Secrets

  • ✅ Vérifier que ConfigMap/Secret existe : kubectl get configmap/secret
  • ✅ Vérifier le mountPath : doit correspondre au path attendu en app
  • ✅ Mettre à jour configMapKeyRef / secretKeyRef si changements

Image Pull Secrets (ECR)

imagePullSecrets:
  - name: ecr-secret
---
# Créer le secret :
kubectl create secret docker-registry ecr-secret \
  --docker-server=199167114788.dkr.ecr.eu-west-3.amazonaws.com \
  --docker-username=AWS \
  --docker-password=$(aws ecr get-login-password --region eu-west-3)

🔐 Sécurité & Dépendances

Audit régulier

# Python
pip-audit
trivy fs .
semgrep --config p/security-audit .

# Docker image
trivy image fastapi:latest

Dépendances abandonnées = DANGER 🚨

  • passlib (2023) → remplacé par bcrypt
  • python-jose (peu maintenu) → remplacé par PyJWT
  • 📌 Toujours vérifier : dernière release, issues, mainteneurs actifs
  • 🔗 Outils : deps.dev, snyk.io, PyPI stats

Multi-stage Docker (CVEs réduites)

# ✅ Élimine packages de build, réduit CVEs
FROM python:3.12-slim AS builder
COPY requirements.txt .
RUN pip install --user -r requirements.txt

FROM python:3.12-slim AS runtime
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
COPY app /app
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0"]

CVEs & .trivyignore

# ✅ TOUJOURS justifier + dater
CVE-2024-XXXXX
  expiry: 2026-06-01
  justification: "Build dependency, not runtime"

📦 Dependencies (requirements.in → requirements.txt)

Utiliser pip-tools

# requirements.in
fastapi==0.100.0
sqlalchemy==2.0.0
pydantic==2.0.0
alembic==1.12.0
PyJWT>=2.12.0
bcrypt>=4.0.0

# Générer requirements.txt
pip-compile requirements.in

Principle of Least Dependency

  • ❌ N'installer QUE ce qui est utilisé
  • 📌 Auditer régulièrement : pip list --outdated, pip-audit
  • 🗑️ Supprimer http-tools, mitmproxy, etc. si non utilisés

🚀 Déploiement & Monitoring

Logs

# FastAPI logs
kubectl logs -n fastapi -l app=fastapi -f

# Tous les containers d'un pod
kubectl logs POD_NAME -c CONTAINER_NAME

# Previous crash
kubectl logs POD_NAME --previous

Port forwarding

# Local dev
kubectl port-forward -n fastapi svc/fastapi 8080:80
# Puis http://localhost:8080

Health checks

# app/main.py
@app.get("/health")
async def health():
    return {"status": "ok"}

🎯 Checklist avant production

  • [ ] Terraform : pas de Ctrl+C, tmux obligatoire
  • [ ] Dépendances : pip-audit, deps.dev, pas de projects abandonnés
  • [ ] Image : Trivy scan, CVEs justifiées, multi-stage build
  • [ ] K8s : RBAC, ServiceAccount, probes configurées, metrics-server
  • [ ] Migrations : Alembic en init container, pas dans main pod
  • [ ] Secrets : jamais en plaintext, utiliser AWS Secrets Manager
  • [ ] Logs : structurés, pas de passwords
  • [ ] GitLab CI : avant_script export pour credentials sensibles
  • [ ] Cleanup : kubectl delete ressources AVANT terraform destroy

À jour : 2026-05-18