· Nolwen Brosson · Blog · 5 min read
Construire un backend scalable et économique avec AWS lambda et docker
Repenser l’architecture backend avec le Serverless
Tu développes une application avec un backend qui a des pics d’activités pendant la journée, et quasiment aucune activité la nuit. Il y a plusieurs solutions face à ça:
- Avoir un nombre de serveurs adaptés aux plus gros pics d’activité. C’est simple à mettre en place, mais ça va te coûter cher, puisque ces serveurs sont sous-utilisées une bonne partie du temps
- Mettre en place des mécanismes d’auto-scaling pour ajuster le nombre de serveurs à l’activité. C’est trés pertinent, mais demande un gros travail pour être mis en place
- La solution dont on va parler aujourd’hui: déployer ton backend sur AWS Lambda
Cette approche a pas mal d’avantages. Déjà, si on prend l’exemple d’AWS, c’est trés facile à mettre en place. Il suffit (dans les grandes lignes) de créer un load balancer, une fonction AWS Lambda avec le code du backend (trés facile avec le runtime Docker), et de les connecter ensemble. En terme de coûts, c’est généralement beaucoup moins couteux qu’une infrastructure classique comme Kubernetes, ECS, où des serveurs tournent en permanence pour des pics d’activité variable. Enfin, c’est super scalable. AWS Lambda peut lancer des milliers d’instances par minute, pouvant donc s’ajuster à quasiment n’importe quel nombre de requêtes faites sur votre backend.

Le dockerfile
Créer un conteneur Docker sur Lambda peut sembler complexe, mais c’est finalement trés simple. Pour une application Python avec Django comme backend, voici à quoi il ressemble:
# base optimisée pour Lambda
FROM public.ecr.aws/lambda/python:3.12
COPY app.py ${LAMBDA_TASK_ROOT}
COPY requirements.txt ${LAMBDA_TASK_ROOT}
RUN pip install -r requirements.txt
# point d'entrée de la fonction
CMD [ "app.lambda_handler" ]La plupart des étapes sont classiques et on les retrouve dans n’importe quel Dockerfile. Quelques détails sont quand même importants:
- l’image de base: bien utiliser une image fournie par AWS pour créer une fonction lambda avec un runtime Docker
LAMBDA_TASK_ROOTest une variable d’environnement intégrée à l’image officielle AWS Lambda et la valeur est:/var/task. C’est là que Lambda execute tout le code, et c’est donc là que le code doit se trouver.- On voit que le point d’entrée est est
app.lambda_handler. Voilà à quoi ressemble le fichierapp.py:
import os
# Mangum sert d’adaptateur entre le load balancer et l'app Django.
# Il transforme les requêtes Lambda en requêtes HTTP WSGI.
from mangum import Mangum
from django.core.wsgi import get_wsgi_application
# Étape 1 : définir les variables d'environnement nécessaires à Django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
# Étape 2 : créer l'application WSGI de Django
application = get_wsgi_application()
# Étape 3 : encapsuler Django avec Mangum pour le rendre compatible avec Lambda + API Gateway
lambda_handler = Mangum(application)Pour plus de détails, vous pouvez aller voir la documentation officielle AWS.
Le processus de déploiement sur AWS
Une fois notre conteneur prêt, voici les étapes pour le sur AWS Lambda.
Étape 1 : Construire l’image et la pousser sur ECR
Amazon Elastic Container Registry (ECR) est un entrepôt privé et sécurisé sur AWS pour vos images Docker. Avec les commandes docker build et docker push, cette étape est généralement trés simple. Voici un exemple:
export AWS_ACCESS_KEY_ID=<your_access_key>
export AWS_SECRET_ACCESS_KEY=<your_secret_key>
export AWS_DEFAULT_REGION=<your_region>
aws ecr get-login-password --region <your_region> | docker login --username AWS --password-stdin <account_id>.dkr.ecr.eu-west-3.amazonaws.com
docker build --platform linux/amd64 -t <account_id>.dkr.ecr.<your_region>.amazonaws.com/<ecr_repo_name>:<tag> -f deploy/Dockerfile .
docker push <account_id>.dkr.ecr.<your_region>.amazonaws.com/<ecr_repo_name>:<tag>Étape 2 : Créer la fonction Lambda
Dans la console AWS, vous créez une nouvelle fonction Lambda en choisissant « Image de conteneur » comme source. Il suffit ensuite de fournir l’adresse (l’URI) de l’image que vous venez de stocker dans ECR.
Étape 3 : Configurer la fonction
Les derniers ajustements concernent des paramètres clés comme la mémoire allouée, les variables d’environnement et le délai d’expiration (timeout). Ces réglages permettent de s’assurer que la fonction dispose de suffisamment de ressources pour s’exécuter correctement .
Diriger le trafic avec un Application Load Balancer (ALB)
Notre fonction Lambda est prête, mais comment les utilisateurs peuvent-ils y accéder? Il y a plusieurs options, mais l’Application Load Balancer (ALB) est une des plus simples à mettre en place. Il se place en première ligne, reçoit toutes les requêtes et les dirige efficacement vers la bonne fonction Lambda.
Le rôle de l’ALB est double : il assure la fiabilité et la performance. Lors d’un pic de trafic, semblable à une heure de pointe, il distribue les requêtes entrantes sur plusieurs exécutions simultanées de votre fonction. Surtout, l’intégration entre ALB & Lambda est fluide, et facile à faire avec AWS.
Pour l’utilisateur final, tout est transparent. Il envoie une requête HTTP standard, l’ALB déclenche la fonction Lambda, attend sa réponse, puis la traduit en une réponse HTTP classique.
Conclusion: Les limites de cette infrastructure
Aucune solution n’est parfaite, sinon, tout le monde choisirait cette infra 😊
La contrainte la plus importante concerne la taille des données. L’intégration entre l’ALB et Lambda impose une limite de 1 Mo pour les requêtes et les réponses (cf doc). Cela signifie que cette architecture est idéale pour les API, les microservices et les fonctions qui échangent des données peu volumineuses (ce qui, entre nous, devrait être le cas autant que possible). En revanche, elle n’est pas adaptée au téléversement ou au téléchargement de fichiers volumineux. Une infrastructure intéressante pour combler cela est d’avoir deux groupe cibles: un sur AWS Lambda, et un autre sur, par exemple, Kubernetes, ECS, ou autre. Selon les requêtes, on redirige soit sur Lambda, soit sur la cible classique.
Deux autres lacunes sont à considérer : les « démarrages à froid » (une latence initiale lors du premier appel après une période d’inactivité) et le temps d’exécution maximal de 15 minutes. Les démarrages à froid peuvent être problématiques, et une bonne solution est d’avoir une requête envoyé trés fréquemment (toutes les 5 secondes) pour s’assurer de garder les Lambda “chaudes”.
