Lors du déploiement d'une application Web en production, il est souvent nécessaire de gérer simultanément différentes versions, pour permettre une restauration rapide et contrôlée par exemple. Dans cet article, nous verrons comment utiliser AWS Lambda@Edge pour gérer plusieurs versions d'une application Web et comment il permet des déploiements blue/green.
Qu'est-ce que le déploiement Blue/Green ?
Le déploiement bleu/vert est une stratégie dans laquelle deux environnements presque identiques mais distincts sont maintenus en production. Un environnement (bleu, dans cet exemple) exécute la version actuelle de l'application et est exposé aux utilisateurs, tandis que l'autre (vert, dans cet exemple) héberge la nouvelle version. Une fois que l'environnement vert est stable et testé, il commence à recevoir du trafic entrant et l'environnement bleu est obsolète. Cette approche nécessite une couche de gestion du trafic devant les deux environnements.

Les avantages du déploiement Blue/Green
Temps d’arrêt minimisé : les utilisateurs continuent d’accéder à l’application sans interruption pendant le basculement, car il n’est pas nécessaire de redémarrer le serveur ou de prévoir des fenêtres de maintenance.
Retours en arrière simplifiés : si un problème survient après le déploiement de la nouvelle version, le trafic peut facilement être redirigé vers l’environnement blue. Le processus de retour en arrière est rapide et évite les redéploiements stressants.
Risque réduit : la nouvelle version peut être testée dans son environnement de production prévu avant d’être exposée au trafic réel.
Bien entendu, le déploiement blue/green comporte aussi ses défis :
Coûts d’infrastructure accrus : étant donné que certaines ressources sont dupliquées, cela peut entraîner des coûts plus élevés. Cependant, les services cloud peuvent atténuer ce problème en proposant des solutions temporaires à la carte.
Gestion du cache : le cache sous diverses formes (CDN, côté client, côté serveur) peut compliquer ou retarder le basculement vers l’environnement vert. Une invalidation efficace du cache, un contournement du cache et des valeurs TTL (Time to Live) correctement configurées sont des solutions pour relever ce défi.
Qu'est-ce qu'AWS Lambda@Edge ?
Comme expliqué dans la documentation AWS,
Lambda@Edge est une extension d'AWS Lambda. Lambda@Edge est un service de calcul qui vous permet d'exécuter des fonctions qui personnalisent le contenu fourni par Amazon CloudFront.
CloudFront intercepte les requêtes et les réponses, les transmettant aux fonctions Lambda@Edge qui peuvent être déclenchées à quatre étapes distinctes :
Lorsque CloudFront reçoit une requête d'un utilisateur (requête de l'utilisateur)
Avant que CloudFront ne transmette une requête à l'origine (requête d'origine)
Lorsque CloudFront reçoit une réponse de l'origine (réponse d'origine)
Avant que CloudFront ne renvoie la réponse à l'utilisateur (réponse de l'utilisateur)

Parmi ses nombreux cas d'utilisation, Lambda@Edge est une excellente solution pour mettre en œuvre des déploiements Blue/Green dans un environnement qui utilise déjà CloudFront, en tirant parti de sa capacité à modifier les requêtes et les réponses.
Les atouts de Lambda@Edge :
Logique au niveau du code : prend en charge une logique conditionnelle complexe via le code, offrant une flexibilité presque illimitée et des tests précis du comportement de routage.
Distribution transparente : répartit les utilisateurs sans redirection ni modification de l'URL
Faible latence : s'adapte automatiquement et traite les requêtes dans les emplacements AWS proches du spectateur, ce qui réduit considérablement la latence
Mais Lambda@Edge comporte aussi un ensemble de limitations :
Limites des fonctionnalités : par rapport à AWS Lambda, Lambda@Edge présente plusieurs limitations, notamment :
Pas de journalisation unifiée : les journaux Cloudwatch sont placés dans la région où la fonction est exécutée.
Pas de variables d'environnement : l'utilisation d'un service AWS tel que SSM pour stocker la configuration d'exécution peut aider à atténuer le problème.
Aucune couche de dépendances : nécessite de regrouper les dépendances directement avec le code de fonction
Complexité du cycle de vie : il n'est pas facile de supprimer complètement une fois déployé, car les modifications se propagent sur les emplacements périphériques et peuvent prendre du temps à être complètement invalidées.
Comment effectuer la configuration de Lambda@Edge ?
Lambda@Edge définit une fonction de gestionnaire qui est automatiquement invoquée par AWS en réponse à des événements CloudFront spécifiques. AWS transmet un objet d'événement au gestionnaire, qui contient des détails sur la demande entrante et la réponse sortante en fonction du type de déclencheur. Le gestionnaire traite cet événement et peut modifier la demande ou la réponse avant de la transmettre à l'origine ou de la renvoyer au visualiseur
Dans cet exemple, nous utilisons une application Web simple où le frontend est servi via CloudFront à l'aide d'un bucket S3. Nous supposons que la nouvelle version est la v1.0.2. Nous utilisons une fonction Lambda@Edge déclenchée au stade de la demande d'origine.

Une approche simple consiste à utiliser des cookies pour diriger les utilisateurs vers des versions spécifiques de l'application. Cette méthode permet de contrôler l'audience de la nouvelle version et garantit une expérience cohérente pour les utilisateurs au cours d'une même session.

"use strict";
exports.handler = async (event) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
let targetRelease = "1.0.2";
const cookiesHeader = headers["cookie"] && headers["cookie"][0]?.value;
if (cookiesHeader) {
const cookies = cookiesHeader.split(";");
const releaseCookie = cookies.find((cookie) =>
cookie.startsWith("release="),
);
if (releaseCookie) {
targetRelease = releaseCookie.split("=")[1];
}
}
const basePath = `/${targetRelease}`;
request.origin.s3.path = `${basePath}${request.uri}`;
return request;
};
Cette configuration nécessiterait de redéployer la fonction Lambda@Edge pour chaque nouvelle version. Cela est fastidieux car le délai de propagation n'est pas instantané dans CloudFront. Nous pouvons éliminer la nécessité de redéployer la fonction en exploitant Parameter Store d'AWS Systems Manager pour savoir quelle version cibler. Nous pouvons également ajouter un cache pour stocker la valeur de la version pour deux raisons :
- pour minimiser la latence causée par les appels à SSM
- pour rester dans les limites de transactions par seconde imposées par SSM.

"use strict";
const { SSMClient, GetParameterCommand } = require("@aws-sdk/client-ssm");
const client = new SSMClient({
region: "eu-central-1",
});
let cachedReleaseVersion = null;
let lastCacheUpdateTime = 0;
const CACHE_TTL = 300000; // 5 minutes
exports.handler = async (event) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
const cookiesHeader =
headers && headers["cookie"] && headers["cookie"][0]?.value;
if (cookiesHeader) {
const cookies = cookiesHeader.split(";");
const releaseCookie = cookies.find((cookie) =>
cookie.startsWith("release="),
);
if (releaseCookie) {
const targetRelease = releaseCookie.split("=")[1];
request.origin.s3.path = `/${targetRelease}${request.uri}`;
return request;
}
}
const now = Date.now();
if (cachedReleaseVersion && now - lastCacheUpdateTime < CACHE_TTL) {
request.origin.s3.path = `/${cachedReleaseVersion}${request.uri}`;
return request;
}
const input = {
Name: "targetRelease", // SSM Parameter Name
};
const result = await client.send(GetParameterCommand(input));
cachedReleaseVersion = result.Parameter?.Value;
lastCacheUpdateTime = now;
request.origin.s3.path = `/${cachedReleaseVersion}${request.uri}`;
return request;
};
Avec cette configuration, le passage de l'environnement bleu à l'environnement vert implique simplement la mise à jour du paramètre SSM. Cela permet également des retours en arrière plus rapides et plus faciles si nécessaire.
Bien entendu, cet exemple devrait être enrichi d'une structure de code appropriée ainsi que d'une journalisation, d'une gestion des erreurs et de tests pour être prêt pour la production.
Conclusion
Bien que l'approche décrite ici fonctionne bien pour les déploiements bleu/vert de base avec Lambda@Edge, des flux de travail plus complexes peuvent nécessiter des considérations supplémentaires.
Lambda@Edge | API Gateway | Route 53 | ELB | |
---|---|---|---|---|
Headers Support | Yes | Yes | No | Partial |
Cookies Support | Yes | Indirect (via Lambda) | No | No |
URL-Based Routing | Yes | Yes | No | Yes |
Query String Support | Yes | Yes | No | No |
Traffic Weighting | Indirect (via code) | Indirect (via stages) | Yes | Yes |
Chez Lenstra, nous avons mis en œuvre pour nos clients des stratégies avancées telles que la distribution multidimensionnelle du trafic et la gestion des pages de maintenance. Ces approches permettent d'équilibrer la vitesse, la fiabilité et l'expérience utilisateur.