Avoir une application Django qui tourne en local, c'est le début. La déployer de façon automatisée, reproductible et récupérable après incident, c'est un autre niveau. Voici l'architecture que j'utilise en production.


L'architecture cible (et son coût réel)


  • AWS ECR : registry Docker privé (~2 $/mois)
  • AWS ECS Fargate : exécution sans serveur à gérer (~15-40 $/mois selon le trafic)
  • AWS RDS PostgreSQL : base managée (~15-25 $/mois, db.t3.micro)
  • AWS ALB : load balancer HTTPS (~18 $/mois)
  • GitHub Actions : 2 000 minutes gratuites/mois, largement suffisant


Total estimé au démarrage : 50-85 $/mois. Moins qu'un développeur qui gère des serveurs manuellement.


Dockerfile optimisé : éviter les pièges courants


# Stage 1 : dépendances (cachées si requirements.txt n'a pas changé)
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt

# Stage 2 : image finale légère
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
RUN python manage.py collectstatic --noinput
CMD ["gunicorn", "yannickabohthierry.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "2"]


Erreur fréquente : ne pas utiliser le multi-stage build. Une image avec les dépendances de build peut faire 1,2 Go vs 180 Mo pour une image propre.


Le pipeline GitHub Actions complet


name: Deploy
on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install -r requirements.txt
      - run: python manage.py test --settings=yannickabohthierry.settings_test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Configure AWS
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: eu-west-3
      - name: Build & push image
        run: |
          aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_REGISTRY
          docker build -t $ECR_REGISTRY/yannickabohthierry:$GITHUB_SHA .
          docker push $ECR_REGISTRY/yannickabohthierry:$GITHUB_SHA
      - name: Update ECS
        run: aws ecs update-service --cluster prod --service django-app --force-new-deployment


Gestion des secrets : jamais dans l'image


Utilisez AWS Secrets Manager. Au démarrage du conteneur, les secrets sont injectés comme variables d'environnement via la Task Definition ECS. Aucun secret en clair dans le code ou l'image.


Health check et rollback automatique


from django.http import JsonResponse

def health(request):
    from django.db import connection
    connection.ensure_connection()
    return JsonResponse({"status": "ok"})


Configurez l'ALB sur /health/ et activez le circuit breaker ECS : si 50% des tâches échouent au démarrage, le déploiement est automatiquement annulé et la version précédente reste active.


Migrations en production : le piège à éviter


Ne jamais lancer migrate dans le CMD du Dockerfile. Utilisez un ECS Task séparé déclenché avant le déploiement. Cela garantit que les migrations sont appliquées avant que le nouveau code soit servi.


Résultat


Chaque push sur main déclenche : tests ? build Docker ? push ECR ? migration ? déploiement blue/green. Tout en moins de 8 minutes, sans downtime, avec rollback automatique en cas d'échec.