diagram_ph/DEPLOYMENT.md

14 KiB

Guide de déploiement - API Diagramme PH

Déploiement sur AWS Elastic Beanstalk

Prérequis

  • Compte AWS avec droits IAM appropriés
  • AWS CLI installé et configuré
  • EB CLI (Elastic Beanstalk CLI) installé
  • Docker installé localement
  • Fichiers DLL/SO pour Linux préparés

Installation des outils

# AWS CLI
pip install awscli
aws configure

# EB CLI
pip install awsebcli

# Vérification
aws --version
eb --version

Configuration Docker

Dockerfile (Production)

# docker/Dockerfile

FROM python:3.12-slim

# Variables d'environnement
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1

# Installer dépendances système pour les .so
RUN apt-get update && apt-get install -y \
    gcc \
    g++ \
    libgomp1 \
    && rm -rf /var/lib/apt/lists/*

# Créer utilisateur non-root
RUN useradd -m -u 1000 appuser

# Répertoire de travail
WORKDIR /app

# Copier requirements et installer dépendances Python
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copier le code de l'application
COPY app/ ./app/
COPY libs/ ./libs/

# Permissions pour fichiers .so
RUN chmod -R 755 libs/so/ && \
    chown -R appuser:appuser /app

# Basculer vers utilisateur non-root
USER appuser

# Exposer le port
EXPOSE 8000

# Healthcheck
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
    CMD python -c "import requests; requests.get('http://localhost:8000/api/v1/health')"

# Commande de démarrage
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]

Dockerfile.dev (Développement)

# docker/Dockerfile.dev

FROM python:3.12-slim

ENV PYTHONUNBUFFERED=1

RUN apt-get update && apt-get install -y \
    gcc g++ libgomp1 vim curl \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY requirements.txt requirements-dev.txt ./
RUN pip install -r requirements.txt -r requirements-dev.txt

COPY . .

RUN chmod -R 755 libs/

EXPOSE 8000

# Mode rechargement automatique pour dev
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

docker-compose.yml (Développement local)

# docker/docker-compose.yml

version: '3.8'

services:
  api:
    build:
      context: ..
      dockerfile: docker/Dockerfile.dev
    ports:
      - "8000:8000"
    volumes:
      - ../app:/app/app
      - ../libs:/app/libs
      - ../tests:/app/tests
    environment:
      - ENV=development
      - LOG_LEVEL=DEBUG
      - PYTHONPATH=/app
    command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload

  # Redis pour cache (optionnel)
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

volumes:
  redis_data:

Configuration AWS Elastic Beanstalk

Dockerrun.aws.json (Single Container)

{
  "AWSEBDockerrunVersion": "1",
  "Image": {
    "Name": "your-ecr-repo/diagram-ph-api:latest",
    "Update": "true"
  },
  "Ports": [
    {
      "ContainerPort": 8000,
      "HostPort": 80
    }
  ],
  "Logging": "/var/log/nginx",
  "Volumes": [],
  "Environment": [
    {
      "Name": "ENV",
      "Value": "production"
    },
    {
      "Name": "LOG_LEVEL",
      "Value": "INFO"
    }
  ]
}

.ebextensions/01_packages.config

# deployment/aws/.ebextensions/01_packages.config

packages:
  yum:
    gcc: []
    gcc-c++: []

files:
  "/etc/nginx/conf.d/01_timeout.conf":
    mode: "000644"
    owner: root
    group: root
    content: |
      client_max_body_size 20M;
      proxy_connect_timeout 300s;
      proxy_send_timeout 300s;
      proxy_read_timeout 300s;

container_commands:
  01_reload_nginx:
    command: "sudo service nginx reload"

.ebextensions/02_python.config

# deployment/aws/.ebextensions/02_python.config

option_settings:
  aws:elasticbeanstalk:container:python:
    WSGIPath: app.main:app
  aws:elasticbeanstalk:application:environment:
    PYTHONPATH: "/var/app/current"
  aws:elasticbeanstalk:environment:proxy:
    ProxyServer: nginx
  aws:autoscaling:launchconfiguration:
    InstanceType: t3.medium
    EC2KeyName: your-key-pair
  aws:autoscaling:asg:
    MinSize: 2
    MaxSize: 10
  aws:elasticbeanstalk:cloudwatch:logs:
    StreamLogs: true
    DeleteOnTerminate: false
    RetentionInDays: 7

.ebextensions/03_https_redirect.config

# deployment/aws/.ebextensions/03_https_redirect.config

files:
  "/etc/nginx/conf.d/https_redirect.conf":
    mode: "000644"
    owner: root
    group: root
    content: |
      server {
        listen 80;
        return 301 https://$host$request_uri;
      }

cloudwatch-config.json

{
  "logs": {
    "logs_collected": {
      "files": {
        "collect_list": [
          {
            "file_path": "/var/log/nginx/access.log",
            "log_group_name": "/aws/elasticbeanstalk/diagram-ph-api/nginx/access",
            "log_stream_name": "{instance_id}"
          },
          {
            "file_path": "/var/log/nginx/error.log",
            "log_group_name": "/aws/elasticbeanstalk/diagram-ph-api/nginx/error",
            "log_stream_name": "{instance_id}"
          },
          {
            "file_path": "/var/log/eb-docker/containers/eb-current-app/*.log",
            "log_group_name": "/aws/elasticbeanstalk/diagram-ph-api/app",
            "log_stream_name": "{instance_id}"
          }
        ]
      }
    }
  },
  "metrics": {
    "namespace": "DiagramPH/API",
    "metrics_collected": {
      "cpu": {
        "measurement": [
          {
            "name": "cpu_usage_idle",
            "rename": "CPU_IDLE",
            "unit": "Percent"
          }
        ],
        "metrics_collection_interval": 60
      },
      "mem": {
        "measurement": [
          {
            "name": "mem_used_percent",
            "rename": "MEMORY_USED",
            "unit": "Percent"
          }
        ],
        "metrics_collection_interval": 60
      }
    }
  }
}

Scripts de déploiement

deploy.sh

#!/bin/bash
# deployment/scripts/deploy.sh

set -e  # Exit on error

# Configuration
APP_NAME="diagram-ph-api"
ENV_NAME="diagram-ph-api-prod"
REGION="eu-west-1"
ECR_REPO="123456789012.dkr.ecr.eu-west-1.amazonaws.com/diagram-ph-api"
VERSION_LABEL="v$(date +%Y%m%d-%H%M%S)"

echo "🚀 Starting deployment of $APP_NAME..."

# 1. Build Docker image
echo "📦 Building Docker image..."
docker build -f docker/Dockerfile -t $APP_NAME:latest .

# 2. Tag image for ECR
echo "🏷️  Tagging image for ECR..."
docker tag $APP_NAME:latest $ECR_REPO:latest
docker tag $APP_NAME:latest $ECR_REPO:$VERSION_LABEL

# 3. Login to ECR
echo "🔐 Logging in to ECR..."
aws ecr get-login-password --region $REGION | \
    docker login --username AWS --password-stdin $ECR_REPO

# 4. Push to ECR
echo "⬆️  Pushing images to ECR..."
docker push $ECR_REPO:latest
docker push $ECR_REPO:$VERSION_LABEL

# 5. Update Dockerrun.aws.json
echo "📝 Updating Dockerrun.aws.json..."
cat > Dockerrun.aws.json << EOF
{
  "AWSEBDockerrunVersion": "1",
  "Image": {
    "Name": "$ECR_REPO:$VERSION_LABEL",
    "Update": "true"
  },
  "Ports": [{"ContainerPort": 8000, "HostPort": 80}],
  "Environment": [
    {"Name": "ENV", "Value": "production"},
    {"Name": "LOG_LEVEL", "Value": "INFO"}
  ]
}
EOF

# 6. Create application version
echo "📋 Creating application version..."
eb appversion create $VERSION_LABEL \
    --source Dockerrun.aws.json \
    --label $VERSION_LABEL

# 7. Deploy to Elastic Beanstalk
echo "🎯 Deploying to Elastic Beanstalk..."
eb deploy $ENV_NAME --version $VERSION_LABEL

# 8. Verify deployment
echo "✅ Verifying deployment..."
sleep 30
./deployment/scripts/health_check.sh

echo "✨ Deployment completed successfully!"
echo "Version: $VERSION_LABEL"
echo "Environment: $ENV_NAME"

health_check.sh

#!/bin/bash
# deployment/scripts/health_check.sh

set -e

ENV_NAME="diagram-ph-api-prod"

# Get environment URL
URL=$(eb status $ENV_NAME | grep CNAME | awk '{print $2}')
HEALTH_ENDPOINT="https://$URL/api/v1/health"

echo "🏥 Checking health at $HEALTH_ENDPOINT..."

# Retry logic
MAX_RETRIES=5
RETRY_COUNT=0

while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
    HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" $HEALTH_ENDPOINT)
    
    if [ $HTTP_CODE -eq 200 ]; then
        echo "✅ Health check passed (HTTP $HTTP_CODE)"
        
        # Get full response
        RESPONSE=$(curl -s $HEALTH_ENDPOINT)
        echo "Response: $RESPONSE"
        exit 0
    else
        echo "⚠️  Health check failed (HTTP $HTTP_CODE). Retrying..."
        RETRY_COUNT=$((RETRY_COUNT + 1))
        sleep 10
    fi
done

echo "❌ Health check failed after $MAX_RETRIES retries"
exit 1

rollback.sh

#!/bin/bash
# deployment/scripts/rollback.sh

set -e

ENV_NAME="diagram-ph-api-prod"

echo "⏮️  Rolling back to previous version..."

# Get previous version
PREVIOUS_VERSION=$(eb appversion --query "ApplicationVersions[1].VersionLabel" --output text)

echo "Rolling back to version: $PREVIOUS_VERSION"

# Deploy previous version
eb deploy $ENV_NAME --version $PREVIOUS_VERSION

echo "✅ Rollback completed"

Procédure de déploiement complète

1. Préparation initiale (une seule fois)

# Initialiser EB dans le projet
cd diagram-ph-api
eb init -p docker -r eu-west-1 diagram-ph-api

# Créer l'environnement
eb create diagram-ph-api-prod \
    --instance-type t3.medium \
    --instance-profile aws-elasticbeanstalk-ec2-role \
    --service-role aws-elasticbeanstalk-service-role \
    --scale 2 \
    --envvars ENV=production,LOG_LEVEL=INFO

# Créer le repository ECR
aws ecr create-repository \
    --repository-name diagram-ph-api \
    --region eu-west-1

2. Déploiement d'une nouvelle version

# Tester localement
docker-compose -f docker/docker-compose.yml up --build

# Exécuter les tests
pytest tests/

# Déployer
chmod +x deployment/scripts/deploy.sh
./deployment/scripts/deploy.sh

3. Surveillance post-déploiement

# Voir les logs en temps réel
eb logs --stream

# Vérifier le statut
eb status

# Voir les métriques CloudWatch
aws cloudwatch get-metric-statistics \
    --namespace DiagramPH/API \
    --metric-name APICallDuration \
    --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S) \
    --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
    --period 300 \
    --statistics Average,Maximum \
    --region eu-west-1

4. Rollback en cas de problème

chmod +x deployment/scripts/rollback.sh
./deployment/scripts/rollback.sh

Configuration HTTPS avec Certificate Manager

# Demander un certificat SSL
aws acm request-certificate \
    --domain-name api.diagramph.com \
    --validation-method DNS \
    --region eu-west-1

# Configurer le Load Balancer pour utiliser HTTPS
eb config

# Dans la configuration, ajouter:
# aws:elbv2:listener:443:
#   Protocol: HTTPS
#   SSLCertificateArns: arn:aws:acm:eu-west-1:xxx:certificate/xxx

Auto-scaling configuration

# .ebextensions/04_autoscaling.config

option_settings:
  aws:autoscaling:asg:
    MinSize: 2
    MaxSize: 10
  aws:autoscaling:trigger:
    MeasureName: CPUUtilization
    Statistic: Average
    Unit: Percent
    UpperThreshold: 70
    UpperBreachScaleIncrement: 2
    LowerThreshold: 30
    LowerBreachScaleIncrement: -1

Coûts estimés (AWS)

Configuration minimale (production)

Service Configuration Coût mensuel (approx.)
EC2 (2x t3.medium) 2 vCPU, 4 GB RAM ~$60
Load Balancer Application LB ~$20
Data Transfer 100 GB/mois ~$9
CloudWatch Logs + métriques ~$5
TOTAL ~$94/mois

Configuration haute disponibilité

Service Configuration Coût mensuel (approx.)
EC2 (4x t3.medium) 2 vCPU, 4 GB RAM ~$120
Load Balancer Application LB ~$20
RDS Redis cache.t3.small ~$30
Data Transfer 500 GB/mois ~$45
CloudWatch Logs + métriques détaillées ~$15
TOTAL ~$230/mois

Checklist pré-déploiement

  • Tests unitaires passent (pytest)
  • Tests d'intégration passent
  • Image Docker se build sans erreur
  • Variables d'environnement configurées
  • Fichiers .so Linux présents et testés
  • Certificat SSL configuré
  • IAM roles configurés
  • CloudWatch alarms configurées
  • Documentation API à jour
  • Plan de rollback préparé

Troubleshooting

Problème: Image Docker ne démarre pas

# Vérifier les logs
eb logs --all

# Tester l'image localement
docker run -p 8000:8000 your-image

# Vérifier les dépendances .so
docker run -it your-image bash
ldd libs/so/libR134a.so

Problème: High CPU usage

# Vérifier les métriques
eb health --refresh

# Augmenter les instances temporairement
eb scale 5

# Profiler l'application
# Ajouter py-spy dans requirements-dev.txt

Problème: DLL/SO not found

# Vérifier présence des fichiers
docker run -it your-image ls -la libs/so/

# Vérifier permissions
docker run -it your-image ls -la libs/so/*.so

# Tester chargement
docker run -it your-image python -c "import ctypes; ctypes.CDLL('libs/so/librefifc.so')"

Maintenance

Mise à jour des dépendances

# Mettre à jour requirements.txt
pip install --upgrade -r requirements.txt
pip freeze > requirements.txt

# Tester localement
docker-compose up --build

# Déployer
./deployment/scripts/deploy.sh

Nettoyage des anciennes versions

# Lister les versions
eb appversion

# Supprimer les anciennes versions (garder les 10 dernières)
aws elasticbeanstalk describe-application-versions \
    --application-name diagram-ph-api \
    --query 'ApplicationVersions[10:].VersionLabel' \
    --output text | xargs -n1 eb appversion delete