Section A : Présentation du Projet
Approche Globale
Lorsque je commence à réfléchir à tout ce qui touche à l’infrastructure, plusieurs facteurs clés me viennent à l’esprit : le budget, le temps, l’effort disponible, et bien sûr, les tradeoffs associés à chaque décision. À partir de ces éléments, nous pouvons commencer à architecturer l’infrastructure. Pour ce legacy project, je pense que la priorité de cette migration est de découpler l’architecture monolithique existante et de faire évoluer l’application vers un modèle stateless. Cela signifie séparer strictement la compute layer de la data layer et du file storage. Si la décomposition en microservices est une option à long terme, la première étape immédiate doit être la standardisation de l’infrastructure. Passer directement aux microservices depuis un legacy monolith est souvent un overkill, sauf si de nouveaux besoins métier le justifient. Une fois le monolith containerisé et l’infrastructure modernisée sur AWS, le découplage architectural futur devient beaucoup plus simple. De plus, nous auditerons les versions actuelles de PHP et de MariaDB, en les mettant à jour vers des releases stables si nécessaire pour atténuer tout risque de sécurité potentiel.
Risques et Défis Principaux
- Compatibilité de l’Application : La mise à jour de la version PHP peut casser la logique applicative legacy ou des librairies obsolètes, nécessitant un refactoring significatif du code.
- Gestion de l’État : Mettre à jour la logique applicative pour gérer de manière sécurisée les file uploads et les accès via un service externe (Amazon S3) plutôt que via le filesystem local.
- Perte de Données : S’assurer qu’aucun fichier ni enregistrement de base de données n’est perdu lors du transfert du legacy server vers les nouveaux managed services.
- Migration sans Interruption de Service : Planifier soigneusement le cutover final. La base de données et le file storage doivent être entièrement synchronisés afin que les utilisateurs n’écrivent pas de données sur l’ancien serveur pendant la mise en production du nouvel environnement.
- Lacunes d’Observabilité : L’application actuelle manque d’un logging adapté. Nous devons mettre à jour l’application pour qu’elle génère des logs standard, afin qu’ils puissent être capturés par notre système de logging centralisé.
- Transition du Modèle d’Accès : Le périmètre VPN legacy ne s’adapte pas à un environnement cloud. L’accès doit être re-architecturé autour de l’identité plutôt que de la localisation réseau, sans créer de failles de sécurité.
- Gestion des Coûts : Passer d’un seul legacy server à des managed AWS services (ALB, ECS Fargate, RDS) augmentera inévitablement nos coûts d’infrastructure mensuels. Nous devons gérer activement ce nouveau budget en right-sizing les ressources et en configurant des billing alerts strictes.
Vue d’Ensemble de l’Architecture
Scroll to zoom · Drag to pan · Double-click to reset
Section B : Plan d’Action Détaillé
Phase : Analyse et Préparation
| Tâche | Responsable | Temps Estimé | Prérequis |
|---|---|---|---|
| Auditer le code, les dependencies et la compatibilité des versions PHP/MariaDB | Dev / DevOps | 2 Jours | Accès au source code |
| Définir le budget infrastructure et les décisions d’architecture | DevOps | 2 Jours | Aucun |
| Provisionner le compte AWS et configurer les billing alerts | DevOps | 0.5 Jour | Budget approuvé |
| Sélectionner et finaliser les outils infrastructure (Terraform, plateforme CI/CD, etc.) | DevOps | 0.5 Jours | Budget approuvé |
| Établir les canaux de communication Dev/DevOps | DevOps / Dev | 0.5 Jours | Aucun |
Phase : Application Refactoring
| Tâche | Responsable | Temps Estimé | Prérequis |
|---|---|---|---|
| Créer une Git branch isolée pour la migration et mettre à jour l’application pour utiliser des environment variables | Developer | 0.5 Jours | Accès au version control |
| Mettre à jour la version PHP et résoudre les dependency conflicts | Developer | 2 Jours | Audit du code terminé |
| Intégrer l’AWS SDK et mettre à jour la logique de file upload/lecture pour utiliser les S3 presigned URLs | Developer | 1 Jour | PHP upgrade terminé, Lambda endpoint disponible |
Phase : Infrastructure Provisioning
| Tâche | Responsable | Temps Estimé | Prérequis |
|---|---|---|---|
| Provisionner les fondations réseau : VPC, subnets, security groups et NAT Gateway | DevOps | 1 Jour | Compte AWS prêt |
| Provisionner les S3 buckets avec des IAM roles et bucket policies sécurisés | DevOps | 0.5 Jour | VPC configuré |
| Déployer une Lambda function pour générer des S3 presigned URLs pour l’application | DevOps | 0.5 Jour | S3 buckets créés |
| Provisionner l’instance Amazon RDS (MariaDB) | DevOps | 0.25 Jour | VPC et security groups configurés |
| Configurer un AWS Backup plan pour RDS (snapshots quotidiens automatisés avec politique de rétention) | DevOps | 0.25 Jour | Instance RDS provisionnée |
| Créer un Amazon ECR repository pour les Docker images | DevOps | 0.25 Jour | Compte AWS prêt |
| Provisionner l’Identity Provider (AWS Cognito user pool ou configurer un IdP externe : Okta, Entra ID) | DevOps | 1 Jour | Compte AWS prêt |
| Configurer la règle d’authentification OIDC sur l’ALB pour imposer le SSO avant de router le trafic vers ECS | DevOps | 0.25 Jour | Cognito ou IdP provisionné, ALB déployé |
Phase : Containerization et Déploiement Initial
| Tâche | Responsable | Temps Estimé | Prérequis |
|---|---|---|---|
| Écrire le Dockerfile et containeriser l’application | DevOps | 2 Jours | Code refactorisé prêt |
| Builder et push manuellement le Docker image vers ECR | DevOps | 0.1 Jours | ECR repository créé, Dockerfile prêt |
| Écrire le Terraform code (IaC) pour définir l’ECS Fargate cluster, l’ALB et les task definitions | DevOps | 2 Jours | Docker image dans ECR, toutes les dépendances provisionnées |
| Valider la santé de l’application après le déploiement Terraform | Dev / DevOps | 0.5 Jour | Terraform code écrit |
Phase : CI/CD et Automation
| Tâche | Responsable | Temps Estimé | Prérequis |
|---|---|---|---|
| Configurer les environment variables et secrets du CI/CD | DevOps | 0.5 Jours | Infrastructure déployée |
| Implémenter l’analyse statique du code (SonarQube) | DevOps | 1 Jour | Accès à l’outil CI/CD |
| Automatiser le build du Docker image | DevOps | 0.5 Jours | Dockerfile prêt |
| Implémenter le scan de vulnérabilités du container (Trivy) | DevOps | 1 Jour | Étape de build du image terminée |
| Automatiser le tagging et le push du image vers Amazon ECR | DevOps | 0.5 Jours | ECR repository créé |
| Déclencher la mise à jour du service ECS après le push du image | DevOps | 1 Jour | ECS cluster en cours d’exécution |
Phase : Migration et Production Cutover
| Tâche | Responsable | Temps Estimé | Prérequis |
|---|---|---|---|
| Synchronisation initiale des fichiers utilisateurs existants du legacy server vers S3 (AWS DataSync) | DevOps | 0.5 Jour | S3 bucket prêt |
| Mettre en place une réplication continue de la base de données legacy vers RDS (AWS DMS) | DevOps | 0.5 Jours | Instance RDS en cours d’exécution |
| Tests end-to-end complets du nouvel environnement | Dev / DevOps | 1 Jour | Tous les services en cours d’exécution |
| Planifier et annoncer le code freeze sur l’application legacy | Dev / Product | 0.5 Jours | Toutes les fonctionnalités validées |
| Effectuer le transfert de données final (copier uniquement les nouveaux fichiers et enregistrements de base de données créés depuis la synchronisation initiale) et valider l’intégrité | DevOps | 0.5 Jours | Code freeze actif |
| DNS cutover (Route 53) vers le nouvel Application Load Balancer | DevOps | 0.5 Jours | Données entièrement synchronisées |
| Décommissionner le legacy server après une période de monitoring de stabilité | DevOps | 0.5 Jours | Nouvel environnement stable |
Section C : Focus Technique
Stratégie de Monitoring
Étant donné que nous utilisons des managed services comme Amazon ECS Fargate et Amazon RDS, nous n’avons pas besoin de gérer ni de monitorer les serveurs sous-jacents. Notre monitoring strategy se concentrera entièrement sur la santé des containers, les performances de l’application et les métriques de la base de données. L’utilisation des services AWS natifs est très rentable et réduit la charge opérationnelle. Nous utiliserons Amazon CloudWatch Logs pour le logging centralisé de l’application et CloudWatch Metrics pour configurer des alertes sur les seuils de CPU, de mémoire et de connexions à la base de données. Optionnellement, nous pouvons intégrer AWS X-Ray pour le request tracing afin d’identifier les bottlenecks de performance dans le PHP monolith. Nous construirons des CloudWatch Dashboards personnalisés pour visualiser l’état général de la nouvelle infrastructure.
- Approche Serverless : Élimine la surcharge de gestion des serveurs sous-jacents.
- Métriques Natives : CloudWatch Metrics pour les alertes CPU, mémoire et base de données.
- Logging Centralisé : CloudWatch Logs et dashboards personnalisés pour la visibilité.
- Traçage des Requêtes : Intégration optionnelle d’AWS X-Ray pour l’identification des bottlenecks.
Containerization et Orchestration
Amazon ECS sur Fargate est le choix d’orchestration idéal pour ce cas d’usage. Kubernetes (EKS) introduit une complexité et une charge opérationnelle inutiles pour une application monolithique simple. ECS offre un moyen plus simple et plus rentable d’exécuter des containers, sans avoir à gérer les serveurs sous-jacents. De plus, ECS Fargate supporte le scaling horizontal automatique : via Application Auto Scaling, le nombre de containers en cours d’exécution augmente ou diminue automatiquement en fonction de la charge (CPU, mémoire ou métriques personnalisées), sans aucune intervention manuelle. Nous utiliserons Amazon Elastic Container Registry (ECR) pour stocker nos Docker images. ECR s’intègre nativement à l’écosystème AWS et offre de meilleures contrôles de sécurité que les registries publics comme Docker Hub. Notre CI/CD pipeline buildera, scannera et pushera automatiquement les images vers ECR, déclenchant ECS pour déployer la dernière version. Si l’entreprise décide de passer aux microservices à l’avenir, cette mise en place fondamentale d’ECR et de containerization facilitera grandement la migration vers Kubernetes.
- Simplicité avant tout : Évite la complexité inutile de Kubernetes (EKS) pour un monolith.
- Scaling Horizontal Automatique : Application Auto Scaling ajuste le nombre de containers en fonction de la charge, sans intervention manuelle.
- Registry Sécurisé : Amazon ECR pour le stockage sécurisé des images, le scanning et l’intégration AWS.
- Déploiements Automatisés : Le CI/CD pipeline déclenche automatiquement les mises à jour ECS.
Sécurité et Gestion des Secrets
La sécurité est intégrée à chaque couche. Pendant le CI/CD pipeline, le source code et les Docker images sont scannés pour détecter des vulnérabilités. Au niveau réseau, la compute layer (ECS) et la base de données (RDS) sont déployées dans des private subnets. La base de données n’est pas accessible publiquement ; les Security Groups limitent strictement le trafic entrant aux ECS containers.
Nous pouvons également éviter la gestion de règles d’IP statiques en utilisant AWS Systems Manager (SSM) Session Manager avec du port forwarding, ou en déployant un Bastion Host temporaire. Cela offre un accès sécurisé et auditable sans dépendre d’un whitelisting d’IP manuel.
Pour le file storage, l’accès à Amazon S3 est étroitement contrôlé via des IAM roles et des bucket policies strictes. L’application utilise des presigned URLs temporaires pour gérer les file uploads de manière sécurisée. De plus, en mettant à jour PHP et MariaDB vers des versions supportées, nous atténuons les CVEs connus. Enfin, les credentials codés en dur sont entièrement supprimés ; toutes les données sensibles sont stockées de manière sécurisée dans AWS Secrets Manager et injectées dynamiquement dans les ECS containers au runtime.
- Défense en Profondeur : Le source code et les Docker images sont scannés pendant le CI/CD.
- Isolation Réseau : ECS et RDS dans des private subnets ; SSM Session Manager pour l’accès sécurisé à la base de données.
- Protection des Données : File uploads S3 sécurisés via presigned URLs et IAM policies.
- Gestion des Secrets : Les credentials codés en dur sont remplacés par AWS Secrets Manager.
- Identity-Aware Proxy (IAP) : Au lieu d’un VPN legacy, l’Application Load Balancer utilise l’OIDC intégré pour authentifier les utilisateurs auprès d’un Identity Provider (IdP) avant de router le trafic. Cela remplace le périmètre réseau par un périmètre d’identité, offrant une expérience SSO sans friction, une révocation d’accès immédiate et une base pour le RBAC.
Annexe : Extraits de Configuration
A1 — Dockerfile (PHP Application)
FROM php:8.3-fpm-alpine
WORKDIR /var/www/html
# Installer les dépendances système
RUN apk add --no-cache nginx supervisor
# Copier le code source de l'application
COPY . .
# Installer les extensions PHP
RUN docker-php-ext-install pdo pdo_mysql
# Utiliser des variables d'environnement pour la configuration (aucun identifiant codé en dur)
ENV APP_ENV=production
EXPOSE 80
CMD ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
A2 — ECS Task Definition (Terraform)
resource "aws_ecs_task_definition" "app" {
family = "php-app"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 512
memory = 1024
execution_role_arn = aws_iam_role.ecs_task_execution.arn
container_definitions = jsonencode([{
name = "php-app"
image = "${aws_ecr_repository.app.repository_url}:latest"
portMappings = [{ containerPort = 80 }]
# Secrets injectés depuis AWS Secrets Manager au runtime
secrets = [
{ name = "DB_PASSWORD", valueFrom = aws_secretsmanager_secret.db_password.arn },
{ name = "APP_KEY", valueFrom = aws_secretsmanager_secret.app_key.arn }
]
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = "/ecs/php-app"
"awslogs-region" = var.aws_region
"awslogs-stream-prefix" = "ecs"
}
}
}])
}
A3 — ALB + AWS Cognito Authentication (Terraform)
# Cognito User Pool
resource "aws_cognito_user_pool" "app" {
name = "php-app-users"
password_policy {
minimum_length = 12
require_uppercase = true
require_numbers = true
require_symbols = true
}
auto_verified_attributes = ["email"]
}
# Cognito App Client (used by the ALB)
resource "aws_cognito_user_pool_client" "alb" {
name = "alb-client"
user_pool_id = aws_cognito_user_pool.app.id
generate_secret = true
allowed_oauth_flows = ["code"]
allowed_oauth_scopes = ["email", "openid", "profile"]
allowed_oauth_flows_user_pool_client = true
callback_urls = ["https://app.example.com/oauth2/idpresponse"]
supported_identity_providers = ["COGNITO"]
}
# Cognito Domain (héberge la page de login SSO)
resource "aws_cognito_user_pool_domain" "app" {
domain = "php-app-auth"
user_pool_id = aws_cognito_user_pool.app.id
}
# ALB Listener Rule — authentification Cognito avant de router vers ECS
resource "aws_lb_listener_rule" "cognito_auth" {
listener_arn = aws_lb_listener.https.arn
priority = 100
action {
type = "authenticate-cognito"
authenticate_cognito {
user_pool_arn = aws_cognito_user_pool.app.arn
user_pool_client_id = aws_cognito_user_pool_client.alb.id
user_pool_domain = aws_cognito_user_pool_domain.app.domain
on_unauthenticated_request = "authenticate"
}
}
action {
type = "forward"
target_group_arn = aws_lb_target_group.app.arn
}
condition {
path_pattern { values = ["/*"] }
}
}
A4 — CI/CD Pipeline (GitHub Actions)
name: Build & Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
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-1
- name: Static code analysis (SonarQube)
uses: sonarsource/sonarqube-scan-action@v2
- name: Build Docker image
run: docker build -t ${{ secrets.ECR_REGISTRY }}/php-app:${{ github.sha }} .
- name: Scan image for vulnerabilities (Trivy)
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ secrets.ECR_REGISTRY }}/php-app:${{ github.sha }}
exit-code: 1
severity: HIGH,CRITICAL
- name: Push image to ECR
run: |
aws ecr get-login-password | docker login --username AWS --password-stdin ${{ secrets.ECR_REGISTRY }}
docker push ${{ secrets.ECR_REGISTRY }}/php-app:${{ github.sha }}
- name: Deploy to ECS
run: |
aws ecs update-service \
--cluster php-app-cluster \
--service php-app-service \
--force-new-deployment