# 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 ```bash # AWS CLI pip install awscli aws configure # EB CLI pip install awsebcli # Vérification aws --version eb --version ``` --- ## Configuration Docker ### Dockerfile (Production) ```dockerfile # 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) ```dockerfile # 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) ```yaml # 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) ```json { "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 ```yaml # 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 ```yaml # 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 ```yaml # 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 ```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 ```bash #!/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 ```bash #!/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 ```bash #!/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) ```bash # 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 ```bash # 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 ```bash # 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 ```bash chmod +x deployment/scripts/rollback.sh ./deployment/scripts/rollback.sh ``` --- ## Configuration HTTPS avec Certificate Manager ```bash # 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 ```yaml # .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 ```bash # 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 ```bash # 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 ```bash # 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 ```bash # 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 ```bash # 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