Modern Backend Engineering
Chapitre 15
Chapitre 15 — Cloud Déploiement
Chapitre 15 — Cloud Déploiement
Cours — Cloud Déploiement
1. AWS Fondamentaux
VPC (Virtual Private Cloud)
Réseau virtuel isolé dans AWS.
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = { Name = "prod-vpc" }
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = true
}
resource "aws_subnet" "private" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
}
IAM (Identity and Access Management)
Gestion des identités et permissions.
resource "aws_iam_role" "ecs_task" {
name = "ecs-task-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "ecs-tasks.amazonaws.com" }
}]
})
}
resource "aws_iam_policy" "s3_access" {
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = ["s3:GetObject", "s3:PutObject"]
Effect = "Allow"
Resource = "arn:aws:s3:::my-bucket/*"
}]
})
}
2. AWS ECS (Elastic Container Service)
Fargate vs EC2
| Critère | Fargate | EC2 |
|---|---|---|
| Gestion des serveurs | AWS | Vous |
| Pricing | Par tâche | Par instance |
| Isolation | VM | Conteneur |
| GPU | Non | Oui |
| Cas d'usage | Microservices, batch | ML, workloads lourds |
Task Definition (Fargate)
{
"family": "api-task",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::123:role/ecs-execution",
"containerDefinitions": [{
"name": "api",
"image": "registry.example.com/api:latest",
"portMappings": [{ "containerPort": 3000 }],
"environment": [
{ "name": "NODE_ENV", "value": "production" },
{ "name": "DB_HOST", "value": "database.cluster-xxx.eu-west-3.rds.amazonaws.com" }
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/api",
"awslogs-region": "eu-west-3",
"awslogs-stream-prefix": "api"
}
},
"healthCheck": {
"command": ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3
}
}]
}
Service ECS
resource "aws_ecs_service" "api" {
name = "api-service"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.api.arn
desired_count = 3
launch_type = "FARGATE"
network_configuration {
subnets = aws_subnet.private[*].id
security_groups = [aws_security_group.ecs.id]
}
load_balancer {
target_group_arn = aws_lb_target_group.api.arn
container_name = "api"
container_port = 3000
}
deployment_circuit_breaker {
enable = true
rollback = true
}
}
3. AWS RDS (Relational Database Service)
Configuration PostgreSQL
resource "aws_db_instance" "postgres" {
identifier = "app-db"
engine = "postgres"
engine_version = "16.3"
instance_class = "db.t3.medium"
db_name = "app"
username = "admin"
password = random_password.db.result
allocated_storage = 100
storage_type = "gp3"
storage_encrypted = true
backup_retention_period = 30
backup_window = "03:00-04:00"
maintenance_window = "sun:04:00-sun:05:00"
vpc_security_group_ids = [aws_security_group.rds.id]
db_subnet_group_name = aws_db_subnet_group.main.name
deletion_protection = true
skip_final_snapshot = false
enabled_cloudwatch_logs_exports = ["postgresql"]
performance_insights_enabled = true
performance_insights_retention_period = 7
tags = { Name = "app-db" }
}
Aurora Serverless v2
resource "aws_rds_cluster" "aurora" {
engine = "aurora-postgresql"
engine_version = "16.3"
database_name = "app"
master_username = "admin"
master_password = random_password.db.result
serverlessv2_scaling_configuration {
min_capacity = 0.5
max_capacity = 16
}
}
4. AWS Lambda (Serverless)
Lambda function (Node.js)
exports.handler = async (event) => {
const { userId } = JSON.parse(event.body);
const user = await db.users.findById(userId);
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
},
body: JSON.stringify(user),
};
};
Terraform
resource "aws_lambda_function" "get_user" {
filename = "function.zip"
function_name = "get-user"
role = aws_iam_role.lambda.arn
handler = "index.handler"
runtime = "nodejs22.x"
memory_size = 256
timeout = 10
environment {
variables = {
DB_HOST = aws_db_instance.postgres.address
}
}
}
resource "aws_lambda_function_url" "public" {
function_name = aws_lambda_function.get_user.function_name
authorization_type = "AWS_IAM"
cors {
allow_origins = ["*"]
allow_methods = ["GET", "POST"]
}
}
5. AWS SQS, SNS, S3
SQS (Simple Queue Service)
resource "aws_sqs_queue" "order_queue" {
name = "order-queue"
delay_seconds = 0
max_message_size = 262144
message_retention_seconds = 345600 // 4 jours
receive_wait_time_seconds = 10 // long polling
visibility_timeout_seconds = 30
redrive_policy = jsonencode({
deadLetterTargetArn = aws_sqs_queue.order_dlq.arn
maxReceiveCount = 3
})
tags = { Name = "order-queue" }
}
resource "aws_sqs_queue" "order_dlq" {
name = "order-dlq"
}
S3 (Simple Storage Service)
resource "aws_s3_bucket" "assets" {
bucket = "myapp-assets-prod"
}
resource "aws_s3_bucket_versioning" "assets" {
bucket = aws_s3_bucket.assets.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_lifecycle_configuration" "assets" {
bucket = aws_s3_bucket.assets.id
rule {
id = "expire-old-versions"
status = "Enabled"
noncurrent_version_expiration {
noncurrent_days = 30
}
}
}
6. GCP et Azure (Comparatif)
| Service | AWS | GCP | Azure |
|---|---|---|---|
| Containers | ECS/EKS | GKE | AKS |
| Serverless | Lambda | Cloud Functions | Azure Functions |
| Database | RDS/Aurora | Cloud SQL | Azure SQL |
| Queue | SQS | Pub/Sub | Service Bus |
| Storage | S3 | Cloud Storage | Blob Storage |
| CDN | CloudFront | Cloud CDN | Azure CDN |
| DNS | Route53 | Cloud DNS | Azure DNS |
| Monitoring | CloudWatch | Cloud Monitoring | Azure Monitor |
7. Infrastructure as Code
Terraform Best Practices
# Structurer en modules
modules/
├── network/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── database/
├── compute/
└── monitoring/
# Use remote state
terraform {
backend "s3" {
bucket = "myapp-terraform-state"
key = "prod/terraform.tfstate"
region = "eu-west-3"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
# Use workspaces
terraform workspace new prod
terraform workspace select prod
Pulumi (TypeScript)
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";
const cluster = new aws.ecs.Cluster("app-cluster");
const lb = new awsx.lb.ApplicationLoadBalancer("app-lb");
const service = new awsx.ecs.FargateService("app-service", {
cluster,
desiredCount: 3,
taskDefinitionArgs: {
containers: {
api: {
image: "registry.example.com/api:latest",
memory: 512,
portMappings: [{ containerPort: 3000 }],
},
},
},
});
8. Serverless et Edge Computing
Lambda + API Gateway
# serverless.yml
service: my-api
frameworkVersion: '4'
provider:
name: aws
runtime: nodejs22.x
region: eu-west-3
functions:
createUser:
handler: src/handler.createUser
events:
- httpApi:
method: POST
path: /users
getUser:
handler: src/handler.getUser
events:
- httpApi:
method: GET
path: /users/{id}
Edge Computing (CloudFront Functions / Lambda@Edge)
// CloudFront Function (lightweight)
function handler(event) {
var request = event.request;
request.headers['x-edge'] = { value: 'cloudfront' };
return request;
}
9. Auto-scaling et Load Balancing
Application Auto Scaling
resource "aws_appautoscaling_target" "api" {
max_capacity = 20
min_capacity = 2
resource_id = "service/${aws_ecs_cluster.main.name}/${aws_ecs_service.api.name}"
scalable_dimension = "ecs:service:DesiredCount"
service_namespace = "ecs"
}
resource "aws_appautoscaling_policy" "cpu" {
name = "cpu-scaling"
policy_type = "TargetTrackingScaling"
resource_id = aws_appautoscaling_target.api.resource_id
scalable_dimension = aws_appautoscaling_target.api.scalable_dimension
service_namespace = aws_appautoscaling_target.api.service_namespace
target_tracking_scaling_policy_configuration {
predefined_metric_specification {
predefined_metric_type = "ECSServiceAverageCPUUtilization"
}
target_value = 70
}
}
Load Balancer
resource "aws_lb" "main" {
name = "app-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = aws_subnet.public[*].id
}
resource "aws_lb_target_group" "api" {
name = "api-tg"
port = 3000
protocol = "HTTP"
target_type = "ip"
vpc_id = aws_vpc.main.id
health_check {
path = "/health"
healthy_threshold = 2
unhealthy_threshold = 5
timeout = 5
interval = 30
}
}
10. Cost Optimization
Stratégies
- Rightsizing : adapter la taille des instances
- Reserved Instances : engagement 1-3 ans (-40%)
- Spot Instances : jusqu'à -90% (tolérant aux interruptions)
- Auto-scaling : éteindre les ressources inutilisées
- S3 Lifecycle : déplacer vers des classes moins chères
- Serverless : payer seulement à l'usage
- RDS : Aurora Serverless, arrêt la nuit (dev)
AWS Budgets
resource "aws_budgets_budget" "monthly" {
budget_type = "COST"
limit_amount = "5000"
limit_unit = "USD"
time_unit = "MONTHLY"
notification {
comparison_operator = "GREATER_THAN"
threshold = 80
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = ["team@example.com"]
}
}