Garden of KnowledgeApplied Sciences › Computer Science › Software › AWS
February 25, 2026

AWS Lambda

Lambda est le service de calcul serverless d’AWS. On déploie du code (fonctions) sans provisionner ni gérer de serveurs. Lambda alloue les ressources automatiquement et facture uniquement le temps d’exécution.

Principe serverless§

Serverless ne signifie pas “pas de serveurs” — les serveurs existent, mais AWS les gère entièrement. Le développeur se concentre uniquement sur le code métier.

Modèle d’exécution : chaque invocation de Lambda peut s’exécuter sur une instance fraîche (cold start) ou réutiliser une instance déjà initialisée (warm start). Lambda gère automatiquement le scaling de 0 à des milliers d’instances simultanées.

Déclencheurs (Triggers)§

Lambda peut être invoqué par de nombreux services AWS :

SourceModeCas d’usage
API GatewaySynchroneAPI REST, webhooks
Application Load BalancerSynchroneEndpoints HTTP
S3AsynchroneTraitement de fichiers uploadés
DynamoDB StreamsPar lotRéagir aux changements BDD
SQSPar lotTraitement de messages en queue
SNSAsynchroneNotifications, fan-out
EventBridgeAsynchroneÉvénements planifiés (cron), règles
CloudWatch EventsAsynchroneMonitoring, alarmes
CognitoSynchroneHooks d’authentification
KinesisPar lotTraitement de flux temps réel

Exemple complet — Lambda HTTP via API Gateway§

import json
import boto3
from datetime import datetime

# Le client DynamoDB est créé en dehors du handler
# pour être réutilisé entre les warm invocations
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('commandes')

def handler(event, context):
    """
    Endpoint : POST /commandes
    event : contient les informations de la requête HTTP
    context : informations sur l'invocation Lambda
    """
    try:
        # Parser le corps de la requête
        body = json.loads(event.get('body', '{}'))

        client_id = body.get('client_id')
        montant = body.get('montant')

        if not client_id or not montant:
            return reponse(400, {"erreur": "client_id et montant requis"})

        # Créer la commande en DynamoDB
        commande = {
            'id': context.aws_request_id,  # ID unique de l'invocation
            'client_id': client_id,
            'montant': str(montant),  # DynamoDB stocke les Decimal en string
            'statut': 'en_attente',
            'cree_le': datetime.utcnow().isoformat()
        }

        table.put_item(Item=commande)

        return reponse(201, {"commande_id": commande['id']})

    except json.JSONDecodeError:
        return reponse(400, {"erreur": "Corps JSON invalide"})
    except Exception as e:
        print(f"Erreur : {e}")  # CloudWatch Logs
        return reponse(500, {"erreur": "Erreur interne"})

def reponse(code: int, corps: dict) -> dict:
    return {
        'statusCode': code,
        'headers': {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': '*'
        },
        'body': json.dumps(corps)
    }

Configuration§

# template SAM (Serverless Application Model)
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Runtime: python3.12
    Timeout: 30
    MemorySize: 256
    Environment:
      Variables:
        ENV: production

Resources:
  ApiCommandes:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: commandes.handler
      MemorySize: 512        # 128 Mo à 10 Go (par pas de 1 Mo)
      Timeout: 10            # 1 à 900 secondes (15 minutes max)
      ReservedConcurrencyLimit: 100  # Concurrence réservée
      Policies:
        - DynamoDBCrudPolicy:
            TableName: commandes
      Events:
        ApiPost:
          Type: Api
          Properties:
            Path: /commandes
            Method: POST

Paramètres clés§

Mémoire : de 128 Mo à 10 Go. La puissance CPU allouée est proportionnelle à la mémoire. Doubler la mémoire double le CPU mais double aussi le coût par milliseconde. À tester pour trouver le bon compromis.

Timeout : maximum 15 minutes (900 s). Pour les traitements longs, préférer une architecture asynchrone (SQS + Lambda + Step Functions).

Stockage temporaire /tmp : jusqu’à 10 Go. Persistant pendant la durée de vie de l’instance Lambda (warm invocations). Réinitialisé sur les cold starts.

Lifecycle — Cold start vs Warm start§

Première invocation (cold start) :
  1. Télécharger le package de code
  2. Démarrer l'environnement d'exécution
  3. Initialiser le runtime (JVM, Node.js, Python interpreter)
  4. Exécuter le code d'initialisation (code en dehors du handler)
  5. Appeler le handler
  Total : 100ms à quelques secondes selon le langage et la taille

Invocation suivante dans la même instance (warm start) :
  1. Appeler le handler directement
  Total : latence du code uniquement

Optimiser les cold starts :

Layers (couches)§

Les Lambda Layers permettent de partager du code et des dépendances entre fonctions.

# Créer un layer avec des dépendances Python
mkdir python
pip install requests boto3 -t python/
zip -r layer.zip python/
aws lambda publish-layer-version \
    --layer-name dependances-communes \
    --zip-file fileb://layer.zip \
    --compatible-runtimes python3.11 python3.12

Concurrence§

Concurrence : nombre d’instances Lambda s’exécutant simultanément.

Concurrence par défaut : 1000 par région (soft limit, augmentable).

Concurrence réservée (Reserved Concurrency) : garantit un nombre max d’instances pour une fonction, ET plafonne cette fonction à ce nombre.

Concurrence provisionnée (Provisioned Concurrency) : maintient X instances initialisées et prêtes. Élimine les cold starts pour ces instances.

# Configurer la concurrence réservée
aws lambda put-function-concurrency \
    --function-name ma-fonction \
    --reserved-concurrent-executions 50

# Configurer la concurrence provisionnée
aws lambda put-provisioned-concurrency-config \
    --function-name ma-fonction \
    --qualifier mon-alias \
    --provisioned-concurrent-executions 10

Gestion des erreurs et Destinations§

# Retry automatique pour les invocations asynchrones
# Lambda réessaie 2 fois en cas d'erreur par défaut

# Dead Letter Queue — messages non traités après les retries
# À configurer dans la console ou via IaC

# Destinations — pour les invocations asynchrones
# On Success → autre Lambda, SQS, SNS, EventBridge
# On Failure → même options (DLQ recommandée)
# SAM : configurer les destinations
EventInvokeConfig:
  DestinationConfig:
    OnSuccess:
      Type: SQS
      Destination: !GetAtt FileSucces.Arn
    OnFailure:
      Type: SQS
      Destination: !GetAtt DLQ.Arn
  MaximumRetryAttempts: 2
  MaximumEventAgeInSeconds: 3600

Bonnes pratiques§

Idempotence : concevoir les fonctions pour qu’elles produisent le même résultat si invoquées plusieurs fois avec les mêmes données. Lambda peut invoquer une fonction plus d’une fois dans certains cas (retry asynchrone).

Stateless : ne pas stocker d’état dans la mémoire de la fonction. Utiliser DynamoDB, S3, ElastiCache pour la persistance.

Garder le handler court : déléguer la logique à des fonctions auxiliaires. Le code en dehors du handler est exécuté une seule fois par instance (warm cache).

Variables d’environnement : pour la configuration. Ne jamais hardcoder les secrets — utiliser SSM Parameter Store ou Secrets Manager.

import os
import boto3
import json

# Récupération de secrets depuis Secrets Manager (à l'init, pas dans le handler)
def obtenir_secret(nom: str) -> dict:
    client = boto3.client('secretsmanager')
    response = client.get_secret_value(SecretId=nom)
    return json.loads(response['SecretString'])

# Mise en cache au niveau de l'instance
SECRET = obtenir_secret(os.environ['SECRET_NAME'])

def handler(event, context):
    mot_de_passe_bdd = SECRET['password']
    # ...

Pricing§

Exemple : 1 million d’invocations de 512 Mo pendant 200 ms = 0.20$ + 1M × 0.5Go × 0.2s × 0.0000166667$/Go-s ≈ 1.87$

—The Gardener