AWS - Tips d’optimisation de vos Lambda
AWS - Tips d’optimisation de vos Lambda

AWS - Tips d’optimisation de vos Lambda

Cette liste d’optimisation n’est évidement pas exhaustive, il s’agit d’une sélection personnelle sur les nombreux points détaillés lors de la session Best practices for advanced serverless developers de Julian Wood lors de l’AWS re:Invent 2022 à Las Vegas.

Petit rappel

Nous en avons parlé dans un précédent article (“La fin du cold start sur les lambda”) des optimisations apportées par AWS sur les temps de démarrage de certaines fonctions lambdas. Mais comme pour tout service cloud, la responsabilité est partagée, et ce, y compris pour les problématiques d’optimisation.

Ci-dessous, se trouve une représentation des étapes de démarrage d’une fonction lambda avec le porteur des optimisations :

image

Les SnapStart mis en place par AWS remplacent les trois premières étapes par une seule qui consiste à restaurer le snapshot créé lors de la dernière initialisation.

Optimisation du code pre-handler

Côté utilisateur, l’optimisation de l’initialisation de la fonction au travers de toutes les opérations réalisées avant le handler est une première étape.

Voici quelques conseils pour cette étape :

  • Ne chargez que ce dont vous avez besoin : N’importer que les modules nécessaires pour chaque librairie

Exemple :

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

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

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

  • Éviter d’y gérer les connexions : Ne pas gérer les initialisations de client ou les connexions à des bases de données dans les opérations de pre-handler.
  • Utiliser le lazy loading pour les modules partagés : N’initialiser les modules que lorsqu’ils sont nécessaires puis les réutiliser pour les prochains appels

Exemple :

import boto3
s3_client = None
ddb_client = None

def get_objects_handler(event, context):
	if not s3_client:
		s3_client = boto3.client("s3")
		# business logic

deg get_items_handler(event, context):
	if not ddb_client:
		ddb_client = boto3.client("dynamodb")
		# business logic

Dimensionnement de votre lambda

Comme vous le savez, vous pouvez paramétrer la quantité de mémoire à allouer à vos fonctions entre 128 Mo et 10 Go. AWS de son côté va proportionnellement allouer plus de capacité CPU et de bande passante réseau en fonction de ce paramétrage.

image

Avec cela en tête, il est possible d’ajuster l’allocation de ressource en fonction des besoins de votre fonction. Si votre process est consommateur en CPU, vous pouvez allouer plus de mémoire pour obtenir plus de vCPU.

Sur cette page de la documentation, AWS prend pour exemple 1000 exécution d’une lambda qui calcule tous les nombres premiers et obtient les résultats suivants :

Mémoire allouée
Temps moyen d’exécution
Coût
128 MB
11.722 s
$0.024628
256 MB
6.678 s
$0.028035
512 MB
3.194 s
$0.026830
1024 MB
1.465 s
$0.024638

Si on compare la première et la dernière ligne, on peut observer que pour une hausse du coût de 0.00001 $ pour 1000 exécutions, le temps de traitement est passé de presque 12 secondes à environ 1,5 seconde.

Vous trouverez sur ce GitHub un outil permettant d’automatiser le profilage de vos lambda : https://github.com/alexcasalboni/aws-lambda-power-tuning

Favoriser l’asynchrone

À part dans les cas de figure où il est indispensable d’avoir recours à des appels synchrones, il ne faut pas hésiter à tirer parti des services AWS permettant de découpler les composants d’une architecture.

image

AWS SQS, AWS SNS, AWS Event Bridge AWS Kinessis sont autant de services qui permettent d’interfaces les sources d’évènements et leur cible en ajoutant une couche d’asynchronisme. Ces points d’interconnexion simplifient le raccordement de futurs modules complémentaires qui formeront des architectures Event Driven.

“ Events are the language of serverless applications ! “

Conclusion

Enfin évidement, une part importante des optimisations en termes de performance pour vos lambdas va se faire sur le code en lui-même. Mais pour cela, il n’y a pas de formule magique. Cela va dépendre du langage, de vos contraintes ainsi que de vos objectifs.

Merci pour votre lecture. Si cet article vous a plu, merci de le partager sur vos réseaux 😉

Timothée Taron - 30 Nov 2022

image