Dans cet article, je vous explique pourquoi redimensionner les images de vos sites web et comment tirer parti de Google Cloud Functions, du GCP Load Balancer et de Cloud CDN pour servir des images redimensionnées à la volée, gagner en performance, économiser un espace de stockage coûteux et réduire le temps de chargement de vos pages.

Image générée avec ChatGPT 5
La quasi-totalité des grands sites web redimensionnent les photos qu'ils affichent, et ce pour trois raisons principales :
- Télécharger des images volumineuses entraîne une consommation élevée de bande passante et des coûts de transfert de données importants.
- Réduire la taille des images via HTML oblige le navigateur client à télécharger l'image entière, puis à mobiliser du temps CPU pour la redimensionner, ce qui ralentit le chargement de votre site.
- Sans redimensionnement à la volée, vous devez stocker plusieurs tailles d'une même image pour les versions Desktop, Mobile, Tablet et email (newsletter) de votre site. Or, le stockage cloud démarre à 0,02 $/Go par mois, une facture qui peut vite grimper.
Pour approfondir l'optimisation des images, consultez le livre de Steve Souders, Even Faster Web Sites , chapitre 10.
Recourir à Google Cloud Content Delivery Network (CDN) accélère le chargement de notre contenu statique, puisque les objets sont mis en cache sur le réseau de périphérie, déployé à l'échelle mondiale.

La solution présentée dans cet article s'appuie sur les services suivants :
- Google Cloud Storage — pour stocker les images en taille originale.
- Cloud Function — pour redimensionner les objets à la volée.
- Google Cloud Load Balancer avec Cloud CDN activé — les utilisateurs passent par le load balancer pour récupérer les images.
Nous créons un GCP Cloud Load Balancer (LB) et le configurons pour invoquer une Cloud Function. La fonction récupère une image depuis Google Cloud Storage (GCS), la redimensionne, puis le LB renvoie l'image redimensionnée et la conserve en périphérie pour les requêtes suivantes.

Comment déployer la solution
Nous créons un bucket Google Cloud Storage (GCS) et y téléversons tous les objets (images) dans leur taille originale.

Nous nommons le bucket, configurons un emplacement régional, puis cliquons sur le bouton Create :

Nous y avons téléversé une image, par exemple nasdaq.jpg (le bâtiment du Nasdaq à Times Square, photo prise en 2019), à sa taille originale de 6,3 Mo.

Étape suivante : créer une fonction. Rendez-vous dans la console Cloud Functions et cliquez sur Write a function.

S'il s'agit de votre première utilisation de Cloud Functions, un message Cloud Functions API is enabled peut s'afficher. Cela signifie que les fonctionnalités Cloud Functions sont activées dans votre projet, sans coût supplémentaire.
À cette étape, nous configurons la fonction :
- Nous utilisons l'éditeur en ligne.
- Nous nommons la fonction.
- Nous choisissons la région — qui doit être identique à celle du GCS.
- Nous définissons le runtime. Pour cette démo, nous travaillons en Python.
- Nous décochons l'authentification IAM afin que le LB puisse invoquer la fonction.

Cliquez maintenant sur le bouton Create. S'il s'agit de votre première utilisation des services GCP, vous devrez peut-être activer quelques API supplémentaires :

Dans cet exemple, nous devons activer Cloud Build pour générer le conteneur qui exécute la fonction.
Place à la partie la plus intéressante : écrire le code qui récupère le fichier depuis GCS, le redimensionne et renvoie le fichier redimensionné.
Nous voulions vérifier si tout cela pouvait être réalisé entièrement par ChatGPT. J'ai donc enchaîné quelques itérations avec le LLM, à partir des prompts suivants :
"Write Python code to resize jpg png images to x,y based on the string provided."
"Let's assume it's google cloud function that pass the params as query string and the file needs to be loaded from gcs and the result should be sent to a load balancer with the appropriate mime type."
"What should I put in requirements.txt for this code?"
"Now I'm getting 'Error processing image: module 'PIL.Image' has no attribute 'ANTIALIAS''"
Le résultat est le code ci-dessous. À la ligne 9, indiquez le bucket utilisé :
Voici le fichier requirements.txt qui indique à Cloud Functions quelles bibliothèques Python utiliser pour générer le conteneur :
Lorsque nous mettons à jour le code dans main.py et les bibliothèques dans requirements.txt, un avertissement s'affiche : The specified function (entry point) might not be present in your source code. Please ensure the entry point in your code matches the input field.
Cela vient du fait que la fonction appelle resize_image, alors que le nom de fonction par défaut de Cloud Functions est hello_http :

Nous remplaçons le Function entry point par resize_image et cliquons sur Save and redeploy.
Cloud Functions génère le conteneur de notre code. L'opération peut prendre quelques minutes ; le statut du build s'affiche en haut du dashboard :

Note : Cloud Function invoque une fonction. Celle-ci utilise le Compute Engine Default Service Account, qui lui ouvre l'accès à l'ensemble des buckets GCS du projet. Nous ne le détaillerons pas ici, mais en bonne pratique vous devriez utiliser un service account dédié appliquant le principe du moindre privilège.
L'étape suivante consiste à créer un Load Balancer. Recherchez Load Balancer dans la console, accédez au dashboard Load Balancer et cliquez sur Create load balancer :

Nous voulons créer un Application Load Balancer global et public. Sous le type de load balancer, sélectionnez Application Load Balancer (HTTP/HTTPS) :

Nous voulons que nos utilisateurs du monde entier puissent accéder au load balancer ; nous choisissons donc Public Facing (external) :

Nous avons retenu le load balancer Global pour profiter de la fonctionnalité Google Cloud CDN, qui permet de mettre en cache l'image redimensionnée sur le serveur de périphérie (sans coût supplémentaire).

Nous sélectionnons ensuite la dernière génération de Load Balancer, puis cliquons sur Next et Create.

Pour simplifier cette étape, nous créons un endpoint frontend HTTP pour le Load Balancer.
Indiquez un nom pour le Load Balancer ainsi que pour l'IP frontend.

Cliquez sur Backend Configuration, puis dans la zone Backend services & backend buckets, cliquez sur Create a backend service.

Le backend service indique au Load Balancer à quelle ressource transmettre la requête ; dans notre configuration, il s'agit d'une Cloud Function.
Renseignez un nom et une description pour le backend service, puis remplacez le Backend type par Serverless network endpoint group.

Faites défiler jusqu'à la section Backends. Sous New backend, cliquez sur Serverless network endpoint groups, puis sur Create Serverless network endpoint group.

Nommez l'endpoint et choisissez la région dans laquelle vous avez créé la Cloud Function.

Si vous n'avez jamais invoqué la fonction auparavant, cette erreur apparaît :

Ouvrez le lien indiqué dans l'erreur et activez l'API Cloud Function :

Revenez sur la page Serverless network endpoint, donnez un nom à l'endpoint, indiquez la région où se trouve la fonction et choisissez Cloud Run (cela peut prêter à confusion, mais Cloud Functions s'appelle désormais Cloud Run Functions et repose sur la même technologie sous-jacente).
Sélectionnez la fonction, puis cliquez sur Create.

Sur la page backend, nous pouvons configurer les paramètres de cache Cloud CDN :
Le CDN peut conserver la réponse en cache pendant la durée souhaitée (1 heure à la ligne 54 du script), voire plus longtemps.
Précision importante : le CDN fonctionne avec un cache à chaud, autrement dit seul le contenu fréquemment demandé y est conservé. Si vous définissez un cache d'un an pour un contenu peu sollicité, attendez-vous à ce que le CDN finisse par aller chercher ce contenu à l'origine (la fonction).

Notez la fonctionnalité Logging (signalée par la flèche ci-dessus). Très pratique pour le debugging et le suivi de l'utilisation, mais coûteuse (512 $ par To de logs).
Si vous générez un volume important de requêtes, réglez le Sample rate sur 0,01 pour ne logger qu'une requête sur 100.
Faisons défiler jusqu'à Security :
Par défaut, Google active Cloud Armor, le Web Application Firewall (WAF), pour notre backend. Comme cela engendre un coût supplémentaire et qu'il s'agit d'une démo, nous le désactivons en cliquant sur Cloud Armor backend security policy et en le passant sur None.

Il ne reste plus qu'à cliquer sur Create, puis dans le dashboard Load Balancer, à cliquer à nouveau sur Create.
Note : la création et la mise à jour d'un Load Balancer dans GCP peuvent demander jusqu'à 15 minutes de propagation.
Une fois le load balancer prêt, cliquez sur son nom : l'IP générée s'affiche.

Nous interrogeons cette IP en précisant le nom de l'image ainsi que la hauteur et la largeur :
http://34.49.21.153/?image=nasdaq.jpg&size=500x600
Résultat : au lieu d'un fichier de 6,5 Mo, nous obtenons une image redimensionnée bien plus légère, de 63 Ko.

Mon métier consiste à accompagner les clients dans leur usage du cloud. Découvrez ce que nous pouvons faire pour vous sur doit.com/services