ECS : service Fargate simple
À la fin de ce tutoriel, vous créerez un cluster ECS, une task definition Fargate (image publique
nginx:alpine), un servicedesiredCount=1sans ALB, exposerez le port 80 via IP publique + Security Group, testerez aveccurl, puis nettoierez (desired 0, delete-service, delete-cluster) — enca-central-1, profillab.Niveau : Intermédiaire · Temps estimé : 60–75 min · Versions testées : ECS / Fargate 2026, AWS CLI v2 · Dernière vérification : 2026-09-10 · Region :
ca-central-1Slug :
aws-ecs-fargate· Série : AWS Solutions Architect · Mot-clé SEO : ECS Fargate← Précédent : Elastic Beanstalk · → Suivant : EKS concepts · Hub : Démarrer avec AWS
Prérequis
- Profil AWS CLI
labavec droits ECS, EC2 (SG/VPC), IAM (rôles de service), CloudWatch Logs - AWS CLI v2,
jqou--queryCLI,curl - Notions VPC / Security Groups et un premier contact PaaS (Beanstalk)
- Budget d’alerte : Fargate facture à l’heure (vCPU + mémoire) — lab court, nettoyage immédiat
export AWS_PROFILE=lab
export AWS_DEFAULT_REGION=ca-central-1
aws sts get-caller-identity
Vérifiez l’identité lab — jamais un compte de production. Aucun secret dans les fichiers de ce lab.
Ce que nous allons construire
Client ──HTTP:80──► IP publique ENI (assignPublicIp)
│
▼
Task Fargate (awsvpc)
└── container nginx:alpine :80
│
Cluster ECS « lab-ecs »
Service desiredCount=1
│
(option) awslogs → CloudWatch
(Schéma — alt : « Client HTTP vers IP publique ENI Fargate, task nginx:alpine dans un service ECS desiredCount=1 sans ALB ».)
Pas d’Application Load Balancer dans ce lab : l’ALB est une option utile en prod (HTTPS, health, multi-AZ) mais coûteuse pour un essai de 30–60 min. On expose directement la task via assignPublicIp=ENABLED dans un subnet public.
Étape 1 — ECS vs EKS vs Beanstalk : quand Fargate
| Approche | Quand ça a du sens | Limites |
|---|---|---|
| Elastic Beanstalk | App ZIP / plateforme gérée, peu de concepts | Moins de contrôle runtime / multi-services |
| ECS + Fargate | Conteneurs Docker, microservices AWS-native, sans gérer des EC2 | Moins « Kubernetes pur » ; écosystème AWS |
| ECS + EC2 | Contrôle des instances, GPU, spot agressif | Vous patch / capacity provider |
| EKS | Standard Kubernetes, portabilité, Helm/operators | Coût control plane + courbe d’apprentissage |
Fargate = launch type serverless pour ECS (et EKS aussi) : vous définissez CPU/mémoire de la task, AWS place les conteneurs. Choisissez Fargate quand vous voulez des conteneurs sans cluster EC2 à entretenir. Passez à EKS si vous avez besoin de l’API Kubernetes (Ingress, CRD, multi-cloud tooling).
Pour l’examen SA : ECS orchestre des tasks ; EKS orchestre des Pods ; Beanstalk abstrait encore plus (PaaS). Fargate ≠ Lambda : facturation continue tant que la task tourne.
Étape 2 — Concepts ECS à maîtriser
| Concept | Rôle |
|---|---|
| Cluster | Espace logique qui regroupe services / tasks (ex. lab-ecs) |
| Task definition | Blueprint JSON/YAML : image, CPU, mémoire, ports, rôles, logs |
| Task | Instance en cours d’exécution d’une task definition |
| Service | Maintient desiredCount tasks (restart, rolling) |
| Launch type Fargate | AWS gère le compute ; mode réseau awsvpc (ENI dédiée) |
| Launch type EC2 | Vos instances dans le cluster ; plus de knobs capacity |
En Fargate, chaque task reçoit une ENI dans votre VPC. Pour joindre Internet en lab : subnet public (route vers IGW) + assignPublicIp ENABLED + SG autorisant le port applicatif.
Étape 3 — VPC, subnet public et Security Group
Réutilisez le VPC default (lab rapide) ou un VPC existant avec subnet public. Créez un SG dédié :
VPC_ID=$(aws ec2 describe-vpcs --filters Name=isDefault,Values=true
--query 'Vpcs[0].VpcId' --output text)
SUBNET_ID=$(aws ec2 describe-subnets
--filters Name=vpc-id,Values=$VPC_ID Name=default-for-az,Values=true
--query 'Subnets[0].SubnetId' --output text)
SG_ID=$(aws ec2 create-security-group
--group-name lab-ecs-fargate-sg
--description "Lab ECS Fargate HTTP 80"
--vpc-id "$VPC_ID"
--query GroupId --output text)
# Lab court : 0.0.0.0/0 — ou restreignez à VOTRE_IP/32
aws ec2 authorize-security-group-ingress
--group-id "$SG_ID"
--protocol tcp --port 80 --cidr 0.0.0.0/0
echo "VPC=$VPC_ID SUBNET=$SUBNET_ID SG=$SG_ID"
En pratique, préférez VOTRE_IP/32 dès que le lab dure plus de quelques minutes. Le SG sortant default (all) suffit pour tirer l’image depuis Docker Hub / ECR Public.
Étape 4 — Rôle d’exécution IAM ecsTaskExecutionRole
Le execution role permet au agent Fargate de tirer l’image et d’écrire les logs. Le task role (app → AWS APIs) n’est pas nécessaire pour nginx statique.
cat > ecs-trust.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "ecs-tasks.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}
EOF
aws iam create-role --role-name ecsTaskExecutionRole
--assume-role-policy-document file://ecs-trust.json 2>/dev/null || true
aws iam attach-role-policy --role-name ecsTaskExecutionRole
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
EXEC_ARN=$(aws iam get-role --role-name ecsTaskExecutionRole
--query Role.Arn --output text)
sleep 8
echo "$EXEC_ARN"
Si le rôle existe déjà (console « first run » ECS), réutilisez-le. Aucune clé d’accès dans la task definition.
Étape 5 — Cluster, task definition et service
aws ecs create-cluster --cluster-name lab-ecs
cat > task-nginx.json << EOF
{
"family": "lab-nginx",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"executionRoleArn": "$EXEC_ARN",
"containerDefinitions": [{
"name": "nginx",
"image": "public.ecr.aws/nginx/nginx:alpine",
"portMappings": [{"containerPort": 80, "protocol": "tcp"}],
"essential": true,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/lab-nginx",
"awslogs-region": "ca-central-1",
"awslogs-stream-prefix": "nginx",
"awslogs-create-group": "true"
}
}
}]
}
EOF
aws ecs register-task-definition --cli-input-json file://task-nginx.json
aws ecs create-service
--cluster lab-ecs
--service-name lab-nginx-svc
--task-definition lab-nginx
--desired-count 1
--launch-type FARGATE
--network-configuration "awsvpcConfiguration={subnets=[$SUBNET_ID],securityGroups=[$SG_ID],assignPublicIp=ENABLED}"
cpu=256 / memory=512 = taille Fargate minimale courante (très bon marché pour un lab). Image publique ECR Public nginx — pas besoin de push ECR privé. L’option awslogs-create-group crée le log group à la volée (sinon créez /ecs/lab-nginx avant).
Attendez que la task soit RUNNING :
aws ecs wait services-stable --cluster lab-ecs --services lab-nginx-svc
TASK_ARN=$(aws ecs list-tasks --cluster lab-ecs --service-name lab-nginx-svc
--query 'taskArns[0]' --output text)
aws ecs describe-tasks --cluster lab-ecs --tasks "$TASK_ARN"
--query 'tasks[0].{lastStatus:lastStatus,cpu:cpu,memory:memory,attachments:attachments}'
Étape 6 — IP publique et curl
Récupérez l’ENI puis l’IP publique associée :
ENI_ID=$(aws ecs describe-tasks --cluster lab-ecs --tasks "$TASK_ARN"
--query 'tasks[0].attachments[0].details[?name==`networkInterfaceId`].value'
--output text)
PUBLIC_IP=$(aws ec2 describe-network-interfaces --network-interface-ids "$ENI_ID"
--query 'NetworkInterfaces[0].Association.PublicIp' --output text)
echo "Public IP: $PUBLIC_IP"
curl -sS -o /dev/null -w "%{http_code}n" "http://$PUBLIC_IP/"
curl -sS "http://$PUBLIC_IP/" | head -n 5
HTTP 200 et la page welcome nginx = succès. Sans ALB, l’IP change à chaque remplacement de task : acceptable en lab, pas en prod.
Option ALB (coûteuse) : cible group + listener HTTP/HTTPS + loadBalancers sur le service — utile pour DNS stable et multi-tasks ; pour un lab Free Tier / budget serré, évitez l’ALB (frais horaires + LCU).
Étape 7 — Logs CloudWatch (court)
Avec awslogs dans la task definition :
aws logs tail /ecs/lab-nginx --since 10m --format short
Les access logs nginx apparaissent après quelques requêtes curl. Si le stream est vide : vérifiez executionRoleArn, la policy AmazonECSTaskExecutionRolePolicy, et que le log group existe (awslogs-create-group ou création manuelle).
Vérification
aws ecs describe-services --cluster lab-ecs --services lab-nginx-svc
--query 'services[0].{status:status,running:runningCount,desired:desiredCount,launchType:launchType}'
aws ecs list-tasks --cluster lab-ecs --service-name lab-nginx-svc
curl -sS -o /dev/null -w "%{http_code}n" "http://$PUBLIC_IP/"
Checklist : service ACTIVE · runningCount=1 · launch type FARGATE · HTTP 200 · plan de nettoyage prêt.
Nettoyage (obligatoire) — coûts Fargate à l’heure
Fargate facture vCPU-heure + Go-heure tant que la task tourne. Coupez dès la vérif OK :
aws ecs update-service --cluster lab-ecs --service lab-nginx-svc --desired-count 0
aws ecs wait services-stable --cluster lab-ecs --services lab-nginx-svc
aws ecs delete-service --cluster lab-ecs --service lab-nginx-svc --force
aws ecs delete-cluster --cluster lab-ecs
aws ec2 revoke-security-group-ingress --group-id "$SG_ID"
--protocol tcp --port 80 --cidr 0.0.0.0/0 2>/dev/null || true
aws ec2 delete-security-group --group-id "$SG_ID"
aws logs delete-log-group --log-group-name /ecs/lab-nginx 2>/dev/null || true
# Optionnel : détacher policy / delete-role ecsTaskExecutionRole si créé uniquement pour ce lab
Ordre important : desired 0 (drain) → delete-service --force → delete-cluster. Un cluster vide ne facture presque rien ; une task oubliée facture à l’heure.
Erreurs fréquentes
| Symptôme | Cause | Correction |
|---|---|---|
Task STOPPED / ResourceInitializationError |
Subnet sans route Internet / pas d’IP publique | Subnet public + assignPublicIp=ENABLED |
CannotPullContainerError |
Pas d’egress / DNS / image privée | Image publique ECR/Docker Hub ; SG egress all |
curl timeout |
SG trop fermé / mauvaise IP | Ingress tcp/80 ; revérifier PUBLIC_IP ENI |
AccessDenied logs |
Execution role manquant / policy | AmazonECSTaskExecutionRolePolicy attachée |
| Coût surprise | Service desired>0 oublié ou ALB | desired 0 + delete-service ; pas d’ALB en lab |
Debug utile : aws ecs describe-tasks ... --query 'tasks[0].stoppedReason'.
Quiz (3 questions)
1. Pour un lab ECS peu cher sans load balancer, on expose la task Fargate ainsi :
– A. Toujours via un ALB multi-AZ
– B. assignPublicIp=ENABLED + SG port 80 sur subnet public
– C. Uniquement via Transit Gateway
2. Le rôle ecsTaskExecutionRole sert surtout à :
– A. Donner à nginx un accès S3 admin
– B. Tirer l’image et écrire les logs (agent Fargate)
– C. Remplacer le Security Group
3. Fin de lab correcte côté coûts Fargate :
– A. Laisser desiredCount=1 une semaine
– B. update-service --desired-count 0 puis delete-service / delete-cluster
– C. Supprimer seulement le log group
Réponses : 1‑B · 2‑B · 3‑B
Pour aller plus loin
- Amazon ECS Developer Guide — Fargate
- Task definition parameters
- Fargate pricing (vCPU / mémoire à l’heure)
- Suite série : EKS : concepts et premier cluster — même idée « conteneurs managés », API Kubernetes
Maillage série AWS (P1)
| ← Précédent | Elastic Beanstalk : déployer une app |
| → Suivant | EKS : concepts et premier cluster |
| Aussi | Démarrer avec AWS · ALB |
Retour parcours AWS — hub de la série et leçons sœurs.


