Le Blog Amazon Web Services

Améliorer la durabilité de vos fonctions AWS Lambda

AWS Lambda est une technologie de choix lorsque vous souhaitez créer une application serverless robuste, résiliente et rentable. Des millions de clients AWS utilisent AWS Lambda chaque mois. Chez AWS, nous sommes responsables de la durabilité du cloud concernant les services que nous offrons à nos clients et nous nous efforçons de sélectionner les technologies les plus efficaces en termes d’empreinte carbone. D’autre part, nos clients sont responsables de la durabilité dans le cloud, en optimisant leurs applications, l’utilisation des ressources, et en minimisant les ressources totales nécessaires à déployer leurs applications. La durabilité dans le cloud est un effort continu, axé principalement sur la consommation d’énergie et l’efficacité de tous les composants d’une application, en s’efforçant d’utiliser au maximum les ressources provisionnées, tout en minimisant le total nécessaire.

Dans cet article, nous vous proposerons diverses méthodes qui vous permettent d’améliorer la durabilité dans le cloud de vos fonctions AWS Lambda en agissant sur les aspects suivants :

  • Langage d’exécution / programmation
  • Architecture du processeur
  • Consommation de mémoire
  • Optimisation du code
  • Utilisation de la simultanéité provisionnée (provisioned concurrency)

Les choix que vous ferez au niveau des critères mentionnés ci-dessus auront un impact sur votre empreinte carbone.

La durabilité environnementale est une responsabilité partagée entre les clients et AWS :

  • AWS est responsable de l’optimisation de la durabilité du cloud, en fournissant une infrastructure efficace et partagée, l’intendance de l’eau et l’approvisionnement en énergie renouvelable,
  • Les clients sont responsables de la durabilité dans le cloud, en optimisant les charges de travail et l’utilisation des ressources, et en limitant les ressources totales devant être déployées pour vos charges de travail.

Durabilite du cloud

Langage de programmation

La sélection de l’environnement d’exécution de votre fonction Lambda dépend principalement des caractéristiques et des exigences de votre application, mais saviez-vous que tous les langages de programmation ne se valent pas en termes de consommation de mémoire ? Le tableau suivant est un extrait d’une étude intitulée « Energy Efficiency across Programming Languages: How does Energy, Time and Memory Relate? » ( Rui Pereira, Marco Couto, Francisco Ribeiro, Rui Rua, Jácome Cunha, João Paulo Fernandes, João Saraiva ). Il illustre les résultats globaux (en moyenne) pour l’énergie, le temps et le Mb normalisés au langage le plus efficace.

NB : Les runtime indiqués en jaunes sont ceux actuellement supportés par Amazon Lambda.

Architecture processeur

Une fonction Lambda peut être exécutée sur une architecture de processeur x86_64 ou arm64. Les fonctions Lambda qui utilisent l’architecture arm64 (processeur AWS Graviton2) peuvent atteindre un prix et des performances nettement meilleurs que les fonctions équivalentes exécutées sur l’architecture x86_64. Sélectionner AWS Graviton2 (arm64) permet de réduire votre empreinte carbone car les instances Graviton2 sont plus économes en énergie avec une consommation d’énergie 3,5 fois inférieure. Si vous envisagez de migrer vos fonctions Lambda d’une architecture x86_64 vers arm64, nous vous encourageons à consulter cet article (en anglais) : Migrating AWS Lambda functions to Arm-based AWS Graviton2 processors.

Consommation mémoire

Si l’on considère isolément l’impact de la mémoire en termes de consommation énergétique globale par rapport au processeur, on peut le qualifier de mineur. Cependant, une chose importante à garder à l’esprit à propos de Lambda est que la quantité de mémoire provisionnée détermine également la quantité de processeur virtuel disponible pour une fonction. Ainsi, l’ajout de plus de mémoire augmente proportionnellement la quantité de CPU allouée, ce qui augmente la consommation globale d’énergie nécessaire.

Pour rester frugal sur l’approvisionnement et l’utilisation de la mémoire mais aussi nous assurer que nous n’approvisionnons que ce qui est nécessaire afin de permettre à votre fonction d’opérer correctement, vous pouvez utiliser divers outils pour vous aider dans l’analyse. Vous pouvez utiliser par exemple AWS Lambda Power Tuning. Cet outil utilise AWS Step Functions pour exécuter plusieurs versions simultanées d’une fonction Lambda avec différentes allocations de mémoire et mesure leurs performances. Vous pouvez représenter graphiquement les résultats et visualiser la corrélation entre les performances et les coûts, ceci afin de vous aider à faire un compromis entre ces deux critères.

Une question que vous pouvez vous poser face à ces éléments, est de savoir quelles sont vos exigences métiers. Devez-vous proposer à vos clients la meilleure performance possible ? Ou bien une performance moyenne est-elle acceptable ?

Vous pouvez également utiliser AWS Compute Optimizer (en anglais) pour obtenir une analyse du dimensionnement optimal de la mémoire pour toutes vos fonctions Lambda. Ce service évalue les fonctions qui ont été exécutées au moins 50 fois au cours des 14 derniers jours et fournit des recommandations automatiques pour l’allocation de mémoire. Voici un exemple de fonction Lambda pour laquelle AWS Compute Optimizer vous conseille d’allouer moins de mémoire :

Optimisation du code

Au début de cet article, nous avons vu que l’environnement d’exécution du langage de programmation a un impact sur les ressources de mémoire et de processeur dont votre fonction Lambda aura besoin. La nature et la qualité du code que vous exécutez dans votre Lambda ont également un impact. Lorsque votre code exécute la même tâche avec moins de CPU, vos applications s’exécutent plus rapidement, l’expérience client s’améliore et vos coûts diminuent parallèlement à vos émissions carbones dans le cloud. Chez AWS, nous comprenons que le réglage fin de l’efficacité du code n’est pas nécessairement un temps que tous nos clients peuvent se permettre de consacrer dans leurs opérations quotidiennes, pour gérer une entreprise prospère, c’est pourquoi nous avons publié CodeGuru Profiler (anglais) pour vous aider dans cette tâche. CodeGuru Profiler peut vous aider à identifier vos lignes de code les plus gourmandes en CPU, qui contribuent le plus à vos émissions carbone. CodeGuru Profiler suggère ensuite des moyens d’améliorer le code, pour le rendre moins exigeant en termes de CPU. CodeGuru Profiler fournit différentes visualisations des données de profilage pour vous aider à identifier le code en cours d’exécution sur le processeur, voir combien de temps est consommé, et suggérer des moyens de réduire l’utilisation du processeur. L’optimisation de votre code avec le profileur CodeGuru conduit à :

  • L’amélioration des performances des applications
  • La réduction du coût du cloud
  • La réduction des émissions de carbone attribuables à votre charge de travail cloud.

Pour vous familiariser avec l’utilisation de CodeGuru, nous vous recommandons ce Workshop intitulé Code Quality Workshop (en anglais).

Voici un exemple de visualisation de l’analyse effectuée par CodeGuru. Le résultat est affiché sous forme de graphique de type « flamme ». Ce graphique montre chaque fonction du code sous forme de rectangle empilé les uns sur les autres pour représenter la pile d’appels. Les rectangles les plus larges et les plus hauts indiquent les fonctions qui ont pris le plus de temps à s’exécuter, ce qui permet d’identifier les goulots d’étranglement et d’optimiser le code.

Utilisation de la simultanéité provisionnée

La simultanéité provisionnée est un paramètre que vous pouvez régler dans Amazon Lambda afin d’améliorer ses performances lorsque vous faites face à des délais trop longs de démarrage à froid (« cold start »). Le démarrage à froid est le temps passé par votre Lambda pendant la phase INIT (en anglais). L’importation, la configuration des modules et la création de clients sdk se produisent pendant cette phase INIT, comme indiqué sur l’image ci-dessous :

Augmenter la mémoire de votre fonction Lambda n’aura pas d’impact positif sur la partie INIT, car l’exécution de Lambda a réellement lieu lorsque vous atteignez le « handler ». La solution proposée par AWS pour réduire la durée de la phase INIT est la simultanéité provisionnée qui consiste à pré-provisionner des environnements Lambda prêts à répondre aux demandes de vos clients. Il est important de comprendre que la simultanéité provisionnée a un impact sur l’utilisation de vos ressources, car elle initialise à l’avance un nombre demandé d’environnements d’exécution en attendant de répondre immédiatement aux appels de votre fonction. Il est important d’être conscient de cette conséquence et d’en limiter son utilisation si vous souhaitez réduire votre empreinte carbone. Bien que dans certains cas, vous ne puissiez pas éviter de l’utiliser, vous pouvez faire diverses choses pour limiter son utilisation et ne l’utiliser qu’en cas de besoin afin d’avoir une démarche éco-responsable.

Optimiser la phase INIT

Nous avons vu ci-dessus ce qui se passe pendant la phase INIT, vous pouvez donc faire une chose pour minimiser la taille de l’artefact de déploiement. Si vous utilisez JavaScript comme environnement d’exécution, nous vous encourageons à migrer vers JavaScript SDK v3 pour bénéficier de packages modulaires. Les packages modulaires vous permettent d’importer uniquement les librairies et les dépendances dont vous avez besoin, réduisant ainsi la charge sur la phase INIT. Par exemple, si vous utilisez uniquement Amazon DynamoDB dans le SDK AWS, vous pouvez spécifier un service unitairement au lieu d’importer le SDK dans son intégralité. Comparez les trois exemples suivants :

// Instead of const AWS = require('aws-sdk'), use:
const DynamoDB = require('aws-sdk/clients/dynamodb')

// Instead of const AWSXRay = require('aws-xray-sdk'), use:
const AWSXRay = require('aws-xray-sdk-core')

// Instead of const AWS = AWSXRay.captureAWS(require('aws-sdk')), use:
const dynamodb = new DynamoDB.DocumentClient()
AWSXRay.captureAWSClient(dynamodb.service)

Lors des tests, l‘importation de la bibliothèque DynamoDB au lieu de l’ensemble du SDK AWS était 125 ms plus rapide. L’importation de la bibliothèque principale X-Ray était 5 ms plus rapide que le SDK X-Ray. De même, lors de l’encapsulation d’une initialisation de service, la préparation d’un DocumentClient avant l’encapsulation a montré un gain de 140 ms. La version 3 du kit AWS SDK pour JavaScript (en anglais) prend désormais en charge les importations modulaires, ce qui peut encore aider à réduire les dépendances inutilisées.

Tirez parti de l’autoscaling

Si AWS Lambda est auto-scalable par défaut, ce n’est pas le cas de la fonctionnalité de simultanéité provisionnée (provisioned concurrency) que nous avons vu plus haut dans cet article. Il est possible de configurer un auto-scaling pour cette fonctionnalité. Aussi, au lieu de provisionner une quantité statique de simultanéité provisionnée cet autoscaling s’adaptera à votre charge de travail et limitera la quantité de ressources que vous provisionnerez en attendant de répondre à votre charge de travail.

Utilisez SnapStart

Si vous utilisez Java Corretto 11 pour vos fonctions Lambda, vous pouvez bénéficier de la technologie SnapStart (en anglais). SnapStart vous permet d’obtenir des performances au démarrage («Cold Start») de vos fonctions jusqu’à 10 fois plus rapides pour les fonctions Java, sans frais supplémentaires et généralement avec peu ou pas de modifications de code. Ce qui est intéressant dans l’utilisation de SnapStart d’un point de vue durabilité, est que contrairement à la fonctionnalité de simultanéité provisionnée, vous ne provisionnez pas d’environnement à l’avance, dans l’attente que votre fonction Lambda soit sollicitée. L’approche de SnapStart est de prendre un snapshot chiffré de l’environnement d’exécution initialisé au préalable, et de le conserver en cache à plusieurs niveaux, pour permettre un accès à faible latence. Lorsque la fonction est appelée pour la première fois, puis mise à l’échelle, Lambda reprend l’environnement d’exécution depuis le snapshot au lieu de l’initialiser à partir de zéro. Cela se traduit par une latence de démarrage plus faible bien sûr, mais aussi une consommation d’énergie moindre car les ressources en mémoire et processeur ne sont sollicitées que lorsque cela est nécessaire (restauration du snapshot) et non en permanence dans l’attente d’une éventuelle exécution comme c’est le cas pour la simultanéité provisionnée.

Conclusion

Nous avons vu dans cet article quelques pistes pour améliorer la durabilité de vos fonctions Lambda. Il y a donc plusieurs éléments que vous devez avoir en tête si vous souhaitez impacter positivement votre empreinte carbone, à savoir :

  • Sélectionner un langage de programmation économe en énergie
  • Exécutez votre lambda sur Graviton
  • Optimiser et minimiser la mémoire que vous avez provisionnée
  • Optimiser l’efficacité de votre code et de sa phase d’initialisation
  • Utiliser la simultanéité provisionnée uniquement lorsque cela est nécessaire.

Cet article a été contribué par Benjamin Lecoq, Senior Technical Account Manager dans les équipes AWS France.