Un SaaS multi-locataire tourne sur ECS Fargate derrière un ALB — six services, de 2 à 24 tâches chacun selon la charge. L’équipe utilise les déploiements progressifs natifs ECS depuis le lancement. Après une régression de déploiement où un bug de configuration a empoisonné la moitié des tâches pendant le cycle progressif et où l’équipe a dû drainer manuellement les mauvaises tâches, la revue d’architecture propose de migrer l’API exposée au client vers le bleu/vert CodeDeploy. La question est : bleu/vert pour tout, ou uniquement pour l’API client. Les deux modèles fonctionnent; la différence se situe dans le modèle de défaillance, le temps de déploiement et le coût. Voici la matrice de décision.
Ce qui se passe
Le déploiement progressif natif ECS est le comportement UpdateService par défaut. Vous mettez à jour la définition de tâche, ECS calcule combien de tâches remplacer en fonction de minimumHealthyPercent et maximumPercent, et il fait passer les tâches par l’inscription au groupe cible. Une configuration typique de minimumHealthyPercent: 100, maximumPercent: 200 signifie qu’ECS lance un ensemble complet de nouvelles tâches, attend qu’elles soient saines, et ne désinscrit qu’ensuite les anciennes. Avec minimumHealthyPercent: 50, maximumPercent: 100, le progressif est plus serré — la moitié est remplacée à la fois sans lancer d’extras.
Le bleu/vert CodeDeploy pour ECS utilise plutôt des jeux de tâches. Le service a deux jeux de tâches — un étiqueté bleu, un vert. CodeDeploy crée le jeu de tâches vert avec la nouvelle définition de tâche, attend que les tâches s’inscrivent comme saines dans un groupe cible « vert » séparé, puis bascule les règles du listener du bleu vers le vert. Le jeu de tâches bleu reste pendant une fenêtre d’attente configurable avant d’être terminé.
Les différences qui comptent en pratique :
| Dimension | Progressif | Bleu/vert |
|---|---|---|
| Mécanisme de bascule de trafic | Désinscription/inscription par tâche | Échange de groupe cible au niveau du listener |
| Temps d’annulation | 5–10 minutes (redéploiement de l’ancienne déf de tâche) | Sous la minute (rebascule du listener) |
| Coût de calcul pendant le déploiement | 100–200 % (un ensemble supplémentaire pendant le chevauchement) | 200 % (jeu de tâches complet en double) |
| Fenêtre d’attente pour régressions tardives | Aucune (l’annulation est un nouveau déploiement) | Attente avant terminaison (10–60 min) |
| Complexité de configuration du listener | Un groupe cible | Deux groupes cibles + règles de listener |
| Rôles IAM requis | Rôle d’exécution de tâche ECS | Rôle d’exécution de tâche ECS + rôle de service CodeDeploy |
| Comportement de drainage | Délai de désinscription par tâche (300s par défaut) | Par tâche sur le bleu à la terminaison |
Le progressif est plus simple — pas de groupe cible supplémentaire, pas de rôle de service, pas de groupe de déploiement séparé. C’est le bon modèle quand le service a un chemin de récupération rapide (une régression est attrapée par les alarmes CloudWatch en quelques minutes et l’équipe est à l’aise pour redéployer la définition de tâche précédente). C’est le mauvais modèle quand une régression est latente — elle apparaît 20 minutes après le déploiement sur un endpoint lent qui n’est pas dans la sonde de fumée — parce qu’à ce moment-là le progressif est terminé et il n’y a pas de chemin rapide de retour.
Le bleu/vert est le bon modèle quand :
- Le service est exposé au client et une régression se mesure en dollars par minute.
- L’équipe a des alarmes CloudWatch qui se déclenchent sur une fenêtre de cinq minutes — assez longue pour nécessiter une période d’attente pour attraper les alarmes avant que le bleu ne disparaisse.
- Le coût de faire tourner un deuxième jeu de tâches pendant 15–30 minutes par déploiement est acceptable. Pour un service de 24 tâches sur Fargate, c’est deux à quatre heures de temps de tâche supplémentaire par déploiement.
Le progressif est le bon modèle quand :
- Le service est interne ou orienté lot, et une régression est un incident mineur plutôt qu’un événement visible par le client.
- La fréquence de déploiement est élevée — 10+ par jour — et le coût d’un deuxième jeu de tâches par déploiement s’accumule.
- La tâche démarre en moins de 30 secondes et la sonde de fumée attrape les défaillances évidentes.
Comment vérifier
Deux vérifications décident du modèle : fréquence de déploiement et temps de récupération. Tirez les deux des données existantes.
Pour la fréquence de déploiement, interrogez l’historique de déploiement de CodeDeploy :
aws deploy list-deployments \
--application-name saas-api \
--include-only-statuses Succeeded Failed Stopped \
--create-time-range start=$(date -u -v-30d +%Y-%m-%dT%H:%M:%S),end=$(date -u +%Y-%m-%dT%H:%M:%S) \
--query 'length(deployments)'
Pour un service qui se déploie 5+ fois par jour, le coût récurrent des jeux de tâches doublés s’accumule — c’est un candidat progressif à moins que l’impact client d’une régression soit sévère. Pour un service qui se déploie 2–3 fois par semaine, le bleu/vert est essentiellement gratuit.
Pour le temps de récupération, vérifiez le temps avant annulation sur les trois derniers incidents. Si le motif récurrent est « déploiement à 14:00, alarme à 14:18, annulation terminée à 14:25 », le progressif a coûté 25 minutes de service dégradé par incident. Le bleu/vert avec une attente de 20 minutes avant terminaison aurait annulé le même incident en moins de 90 secondes.
Une heuristique qui tient en pratique : coût des minutes de mauvais déploiement × déploiements par mois × probabilité de régression > coût de calcul supplémentaire du bleu/vert → utilisez le bleu/vert.
La correction
La configuration de service ECS complète pour le bleu/vert via CodeDeploy. Le service est créé avec deploymentController.type: CODE_DEPLOY, deux groupes cibles dans le même ALB, et deux règles de listener (une pour le trafic de production, une pour le trafic de test pendant le canari) :
{
"serviceName": "saas-api",
"cluster": "saas-prod",
"taskDefinition": "saas-api:127",
"desiredCount": 12,
"launchType": "FARGATE",
"deploymentController": {"type": "CODE_DEPLOY"},
"loadBalancers": [{
"targetGroupArn": "arn:aws:elasticloadbalancing:ca-central-1:111122223333:targetgroup/saas-api-blue/abc",
"containerName": "api",
"containerPort": 8080
}],
"networkConfiguration": {
"awsvpcConfiguration": {
"subnets": ["subnet-aaa", "subnet-bbb"],
"securityGroups": ["sg-xxx"],
"assignPublicIp": "DISABLED"
}
}
}
Le groupe de déploiement :
aws deploy create-deployment-group \
--application-name saas-api \
--deployment-group-name saas-api-prod \
--service-role-arn arn:aws:iam::111122223333:role/CodeDeployServiceRoleECS \
--deployment-config-name CodeDeployDefault.ECSAllAtOnce \
--deployment-style deploymentType=BLUE_GREEN,deploymentOption=WITH_TRAFFIC_CONTROL \
--blue-green-deployment-configuration '{
"terminateBlueInstancesOnDeploymentSuccess": {
"action": "TERMINATE",
"terminationWaitTimeInMinutes": 20
},
"deploymentReadyOption": {
"actionOnTimeout": "STOP_DEPLOYMENT",
"waitTimeInMinutes": 30
}
}' \
--ecs-services '[{"serviceName": "saas-api", "clusterName": "saas-prod"}]' \
--load-balancer-info '{
"targetGroupPairInfoList": [{
"targetGroups": [
{"name": "saas-api-blue"},
{"name": "saas-api-green"}
],
"prodTrafficRoute": {"listenerArns": ["arn:aws:elasticloadbalancing:ca-central-1:111122223333:listener/app/saas-alb/abc/443"]}
}]
}' \
--auto-rollback-configuration '{
"enabled": true,
"events": ["DEPLOYMENT_FAILURE", "DEPLOYMENT_STOP_ON_ALARM"]
}' \
--alarm-configuration '{
"enabled": true,
"alarms": [
{"name": "saas-api-5xx-rate"},
{"name": "saas-api-p95-latency"}
]
}'
Le appspec.yaml pour ECS est plus court que pour EC2 — pas de hooks (l’équivalent s’exécute à l’intérieur de la tâche elle-même), juste une référence à la nouvelle définition de tâche :
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: arn:aws:ecs:ca-central-1:111122223333:task-definition/saas-api:128
LoadBalancerInfo:
ContainerName: api
ContainerPort: 8080
PlatformVersion: LATEST
Hooks:
- BeforeAllowTraffic: arn:aws:lambda:ca-central-1:111122223333:function:saas-api-before-traffic
- AfterAllowTraffic: arn:aws:lambda:ca-central-1:111122223333:function:saas-api-after-traffic
Trois règles que nous tenons en choisissant entre les deux :
Faites correspondre le modèle de déploiement à l’impact client. API exposée au client en bleu/vert; panneau d’administration interne en progressif; jobs par lot en progressif. Tous les services n’ont pas besoin de la même réponse.
La différence de coût est le deuxième jeu de tâches pendant la fenêtre de déploiement. Sur Fargate à 12 tâches de 1 vCPU / 2 GiB pendant 20 minutes par déploiement, c’est petit mais réel — environ 4 vCPU-heures par déploiement. À 30 déploiements/mois, 120 vCPU-heures. Comparez au coût d’un seul incident de mauvais déploiement; le bleu/vert se paie après le premier qu’il attrape.
Ne pas mélanger les modèles dans un seul CodePipeline. Un pipeline qui déploie vers deux services où l’un est progressif et l’autre est bleu/vert finira par déployer la mauvaise paire de versions quand le bleu/vert se bloquera dans l’attente avant terminaison et le progressif sera terminé. Soit les deux services utilisent le même modèle, soit le pipeline les met en scène séparément.
La décision progressif-contre-bleu/vert ne porte pas sur lequel est meilleur — elle porte sur quel modèle de défaillance le service tolère. Référez-vous au guide de référence appspec pour les détails d’ordonnancement des hooks à travers les modèles.
Stack Harbor opère CodeDeploy + CodePipeline sur les flottes EC2 / ECS / Lambda de production dans le cadre de Gestion AWS et des Environnements infogérés en continu. Si vos déploiements demandent encore une fenêtre de maintenance, commencez par une consultation d’infrastructure.