Amazon Cloudfront, c’est quoi ?

Si vous ne connaissez pas le service, je vous invite à le découvrir ici. En bref, c’est un CDN (Content Delivery Network) mais pas que : il propose plein de fonctionnalités au top.

Aujourd’hui on se concentre sur les fonctionnalités de base des CDN, le cache.

La première fois qu’un utilisateur consulte notre site, CloudFront va récupérer auprès de S3 les pages visitées et va les mettre en cache pour une durée déterminée. La seconde fois qu’un utilisateur consulte des pages déjà visitées, CloudFront les renvoie directement sans passer par S3 (c’est vrai sur tout type de contenu : des pages, du contenu multimedia, etc.).

Exemple :

Je vous montre au travers d’un exemple ; on se rend bien compte du comportement en ouvrant la console du navigateur. Je viens de déployer mon site, premier accès : le header de réponse “X-Cache” contient la valeur “Miss from cloudfront”: IMG-20250329082415809.png

Je recharge plusieurs fois ma page pour que CloudFront comprenne qu’il doit mettre en cache la page et j’accède de nouveau à la même page, cette fois le header de réponse “X-Cache” contient la valeur “Hit from cloudfront”, indiquant que la page est fournie depuis le cache : IMG-20250329082451899.png

Invalider son cache

C’est très bien le cache, mais si je redéploie mon site comment ça se passe s’il est déjà en cache ? Et bien globalement… mal ! Et oui, CloudFront n’est pas au courant que votre magnifique pipeline CI/CD vient de déposer une nouvelle version du site dans S3.

On le démontre facilement : mon site ne contient qu’un fichier index.html. On l’édite, on remplace V1 par V2, on le redéploie dans le bucket S3 et quand on accède au site, celui-ci affiche toujours la V1 et le header indique toujours qu’on utilise le cache : IMG-20250329082550761.png Heureusement, rien n’est perdu, et CloudFront nous fournit un outil pour invalider le cache - ça se passe ici : IMG-20250329082617501.png

On crée notre invalidation, on relance le site et on accède désormais à la V2 et le header est cohérent : IMG-20250329082652049.png

Quoi ? A chaque déploiement, je vais devoir me connecter à la console et invalider mon cache ?

Non et encore heureux ! Je vous propose une méthode pour automatiser l’invalidation du cache lors du redéploiement du site, basée sur les événements émis par S3, l’intégration avec Amazon EventBrige et Lambda.

Premier point : quand on supprime, on modifie, on ajoute un fichier dans S3, celui-ci émet un événement qu’il diffuse dans le bus par defaut EventBridge : un peu de documentation ici. Deuxième point : on peut interagir avec CloudFront au travers du SDK AWS, notamment pour créer programmatiquement une invalidation.

On va donc mixer ces deux éléments pour réagir aux événements émis par S3 lors du déploiement d’une nouvelle version du site - il va donc falloir :

  1. Activer l’intégration S3 - EventBridge qui est désactivée par défaut
  2. Créer une règle dans EventBridge pour attraper l’événement qui nous intéresse
  3. Déclencher un traitement, une lambda, qui va utiliser le SDK AWS pour créer une invalidation

Un petit schéma qui résume ce qui va se passer, et on passe au détail : IMG-20250329082757141.png

Le détail

L’activation de l’intégration S3 - EventBrige se passe dans l’onglet Properties du bucket S3, c’est un simple booléen à activer ici : IMG-20250329082843278.png

L’événement qui nous intéresse a cette tête :

{    
	"version": "0",
    "id": "2d43f235-d322-0482-63b4-5ef35684ed9c",
    "detail-type": "Object Created",
    "source": "aws.s3",
    "account": "xxxxxxxxx",
    "time": "2024-11-29T14:33:50Z",
    "region": "eu-west-3",
    "resources": [
        "arn:aws:s3:::blog-invalidate-cache-example"
    ],
    "detail": {
        "version": "0",
        "bucket": {
            "name": "blog-invalidate-cache-example"
        },
        "object": {
            "key": "index.html",
            "size": 28,
            "etag": "785f300f574d30e1f5c6fdacecc2b4da",
            "sequencer": "006749D0CEE1C975C3"
        },
        "request-id": "08XB1HCQ3HVMVEHR",
        "requester": "851725616487",
        "source-ip-address": "xxxxxxxxxxxx",
        "reason": "PutObject"
    }
}

On crée donc une règle dans Amazon EventBridge pour le catcher : IMG-20250329083030816.png

Ensuite on crée une lambda toute simple qui est la cible de cette règle, et réalise l’invalidation :

from aws_lambda_powertools import Logger
from os import getenv
import boto3
import time

logger = Logger()
function_name = getenv("AWS_LAMBDA_FUNCTION_NAME")
cf = boto3.client('cloudfront')


def lambda_handler(event, context):
    logger.info("Récupération de la distribution cloudfront...!")
    distributionId = getenv("CLOUFRONT_DISTRIBUTION_ID")
    logger.info("Distribution cloudfront : " + distributionId)


    logger.info("Invalidation du cache...")
    raw_paths = getenv("CLOUFRONT_PATHS_TO_INVALIDATE")
    logger.info("Path à invalider : " + raw_paths)
    paths = raw_paths.split(',')
    paths = [item.strip() for item in paths]
    res = cf.create_invalidation(
          DistributionId=distributionId,
          InvalidationBatch={
              'Paths': {
                  'Quantity': 1,
                  'Items': paths
              },
              'CallerReference': str(time.time()).replace(".", "")
          }
      )

    invalidation_id = res['Invalidation']['Id']
    logger.info("Invalidation du cache terminée : " + invalidation_id)

Points saillants sur la lambda :

  • En python parce que j’ai l’habitude
  • Avec les powertools, pour logguer plus simplement dans CloudWatch
  • Pour créer l’invalidation :
    • On instancie un objet pour manipuler le SDK avec python : cf = boto3.client(‘cloudfront’)
    • On appelle une méthode avec deux paramètres : l’identifiant de la distribution et les paths à invalider (/index.html, /assets, …)
    • C’est fini !

Conclusion

Désormais à chaque redéploiement, automatiquement, CloudFront est à jour pour récupérer la dernière version de notre site. C’est une façon de faire parmi d’autres, on peut imaginer déclencher cette invalidation depuis notre pipeline de CI/CD directement ou encore utiliser l’intégration native S3/Lambda, sans passer par Event Bridge : toutes ces solutions fonctionnent et sont valables, tout dépend de vos habitudes, de votre cas d’usage, etc. pas de dogmatisme ;-)