Comment optimiser les fonctions AWS Lambda ?

Tarek Zaibet
DevOps at PISQUARE
Published in
6 min readMay 3, 2021

--

Photo by Maxwell Nelson on Unsplash

La vitesse à laquelle votre application répond et la disponibilité de celle-ci, sont deux aspects critiques de votre expérience utilisateur.

Lorsque vous utilisez le serverless, la performance de votre application impacte non seulement votre expérience utilisateur mais aussi vos coûts.

Avec AWS Lambda la facturation dépend de la durée d’exécution de votre fonction, pondérée par la mémoire que vous lui attribuez.

Le serverless et en particulier Lambda, élimine de nombreuses possibilité pour l’optimisation des performances, telles que la scalabilité ou la modification des configurations de serveur.

Ceci, rend difficile pour les nouveaux utilisateurs, de comprendre comment procéder afin d’avoir des fonctions serverless performantes et peu couteuses.

Dans cet article nous allons vous présentez les principaux outils et approches que nous utilisons au sein de PISQUARE pour améliorer les performances des fonctions AWS Lambda et ainsi réduire les couts liés à leurs utilisations.

Quels sont les étapes qui sont exécutés lorsqu’une fonction AWS Lambda est appelé ?

Avant d’expliquer comment optimiser les fonctions Lambdas, il faut tout d’abord avoir en tête les différentes étapes qui sont exécutés une fois la fonction appelé.

La figure suivante montre les différentes étapes :

1- Téléchargement de votre lambda dans la couche de calcul (compute)

2 — Création d’un nouvel environnement d’exécution

3 — Instanciation des dépendances et du runtime

4 — Assigner l’environnement à la requête

5 -Exécution du code

6 — Dé-assigner l’environnement d’exécution

7 — Environnement d’exécution mis en cache (temps variable)

Une fois la fonction lambda appelé, elle va tout d’abord vérifier si un environnement d’exécution est présent dans le cache, si c’est le cas, l’environnement sera affecté à la requête et le code de la fonction sera exécuté.

En revanche si la lambda ne trouve pas l’environnement qui correspond à l’appel dans le cache, le code de la fonction sera de nouveau téléchargé et un nouvel environnement d’exécution sera instancié. AWS n’a pas publié une durée exacte pour la création d’un nouvel environnement, mais d’après notre expérience cela peut varier entre 5 et 20 minutes.

La latence des applications serverless et celle des lambdas en particulier à un impact direct sur le cout des fonctions et sur l’expérience utilisateur.

Cold Start vs Warm Start

Le process décrit ci-dessus, traduit les différents causes du Cold et du Warm start.

Une pénalité Cold Start, qui se traduit par une durée d’exécution de la fonction plus longue, se produit dans trois situations :

  • La fonction lambda est appelé pour la première fois
  • La fonction lambda est appelé après une longue période depuis sa dernière exécution
  • Si le nombre de requêtes est trop élevés, obligeant AWS Lambda à instancier de nouveaux environnements d’exécution.

Le Warm start est à l’opposé du cold start, il se traduit par une durée d’exécution assez courte du a un environnement et un code présent dans le cache.

Afin d’optimiser la latence et le cout de nos fonctions lambdas nous travaillons sur trois aspects afin de réduire au maximum le cold start et obtenir une durée d’exécution la plus courte possible :

  • La taille de l’artéfact (du code compressé que nous déployons)
  • Le calibre de l’environnement d’exécution
  • Le code de la fonction

1 — Minimiser la taille de l’artéfact :

La taille de votre package de déploiement à un impact direct sur le cold start (temps pris par la lambda pour télécharger le code et instancier un environnement d’exécution).

Plus le code est volumineux plus le cold start sera long et plus la latence sera grande.

Afin de réduite la taille de l’artéfact il faut auditer toutes les dépendances de la fonction :

Existe-t-il des dépendances de bibliothèque lourdes à supprimer ou des versions allégées pouvant être utilisées ?

Recherchez en particulier les bibliothèques qui agissent comme des serveurs ou des agents HTTP car ils n’ont aucune utilité dans les fonctions Lambda, puisque Lambda agit comme le serveur pour vous.

Chez PISQUARE nos auditions nos dépendances pour Node à l’aide d’outils tels que https://npm.anvaka.com/, ou pour Python en utilisant https://pypi.org/project/modulegraph/. Ce qui nous permet de réduire considérablement la taille du code.

2 — Calibrer l’environnement d’exécution

Le code de la lambda nécessite des ressources de calcul (CPU, mémoire) pour s’exécuter.

AWS Lambda fournit une seule option pour définir les ressources requises par votre fonction : le paramètre de mémoire.

AWS Lambda alloue la puissance du processeur proportionnellement à la mémoire en utilisant le même ratio qu’un type d’instance Amazon EC2 à usage général tel qu’un type M3.

Par exemple, si vous allouez 256 Mo de mémoire, votre fonction Lambda recevra deux fois la part du processeur que si vous allouiez seulement 128 Mo.

La tarification AWS Lambda pondère la durée facturée pour votre fonction en fonction de son paramètre de mémoire. Ainsi, 1 seconde de temps d’exécution de fonction à 1024 Mo coûte le même prix que 8 secondes d’exécution à 128 Mo.

Trouver la bonne allocation de ressources pour votre fonction nécessitera une certaine expérimentation.

Le chemin le plus simple consiste à commencer avec un paramètre élevé et à le réduire jusqu’à ce que vous constatiez un changement dans les caractéristiques de performance.

Une bonne allocation de l’environnement d’exécution permettra ainsi d’éviter d’instancier de nouveaux environnements inutilement qui pourraient augmenter la latence (cold start) et le cout.

3 — Optimiser le code de la fonction

AWS Lambda facture votre utilisation en fonction du moment où votre fonction commence à s’exécuter jusqu’au moment où elle s’arrête, et non en fonction des cycles de processeur dépensés ou de toute autre métrique basée sur le temps.

Cela implique que ce que fait votre fonction pendant cela est important.

Considérez la fonction de service de redimensionnement d’image : lorsque vous téléchargez l’objet S3, votre code attend simplement que le service S3 réponde, et vous payez ce temps d’attente.

Dans le cas de cette fonction, le temps passé est négligeable, mais ce temps d’attente peut devenir excessif pour les services qui ont des temps de réponse longs (par exemple, l’attente d’une instance EC2 en cours de provisionnement) ou des temps d’attente (comme le téléchargement d’un fichier très volumineux). Il existe deux options pour minimiser ce temps d’inactivité :

Minimiser l’orchestration dans le code :

Au lieu d’attendre une opération dans votre fonction, utilisez les fonctions AWS Step Functions pour séparer la logique « avant » et « après » en deux fonctions distinctes.

Par exemple, si vous avez une logique qui doit s’exécuter avant et après un appel d’API, séquencez-les comme deux fonctions distinctes et utilisez une fonction AWS Step pour orchestrer entre elles.

Utiliser des threads pour des opérations intensives d’I/O :

Vous pouvez utiliser plusieurs threads dans une fonction Lambda (si le langage de programmation la prend en charge), tout comme le code s’exécutant dans n’importe quel environnement de calcul. Cependant, contrairement aux programmes conventionnels, la meilleure utilisation du multi-threading n’est pas la parallélisation des calculs.

En effet, Lambda n’alloue pas plusieurs cœurs aux fonctions Lambda exécutées avec une mémoire inférieure à 1,8 Go, vous devez donc allouer plus de ressources pour bénéficier de la parallélisation.

Au lieu de cela, vous pouvez utiliser des threads pour paralléliser les opérations d’I/O. Par exemple, une version python de la fonction image_resizer pourrait agir sur plusieurs fonctions en exécutant le téléchargement S3 sur un thread séparé pour la vignette.

En suivant ces bonnes pratiques, vous pouvez réduire considérablement la latence (et le coût !) De votre application sans serveur.

Le serverless offre beaucoup de possibilités, mais peut engendrer une facture couteuse si les bonnes pratiques ne sont pas mises en places.

Chez PISQUARE nous mettons en places ces bonnes pratiques de FinOps et de développement afin d’optimiser au maximum le cout des infrastructures et des applications.

--

--

Tarek Zaibet
DevOps at PISQUARE
0 Followers

CEO @ pisquare | Cloud Architect AWS / GCP