Pour aller plus loin
Introduction
Nous nous approchons maintenant de la fin de notre voyage dans le monde merveilleux du serverless et des fonctions Lambda d’AWS. Beaucoup de choses ont été dites et expliquées et nous espérons que le lecteur aura beaucoup appris en parcourant ce livre. Mais lorsque l’on regarde la richesse du service AWS Lambda, ainsi que des services conjoints comme API Gateway, SQS, DynamoDB, etc., et qu’on réalise à quel point cette technologie est en plein mouvement et à quelle vitesse de nouvelles fonctionnalités apparaissent, on se rend compte que, finalement, tous les sujets n’ont pas pu être abordés.
Ainsi, ce chapitre final se propose d’adresser tout ce qui n’a pas pu être adressé jusqu’à présent, soit parce qu’il s’agit de détails qui n’auraient pas pu faire l’objet d’un chapitre entier, soit parce qu’il s’agissait tout simplement de fonctionnalités qui n’existaient pas encore pendant toute la durée de la rédaction de l’ouvrage et qui ne sont apparues que vers la fin de cette période. Et aussi surprenant que cela puisse paraître, cette dernière alternative s’est présentée beaucoup plus fréquemment que l’on peut penser.
L’extensibilité
De l’anglais scalability, l’extensibilité désigne la capacité d’un système à s’adapter et à maintenir ses performances dans des conditions de montées en charge, c’est-à-dire une forte augmentation de la demande.
L’extensibilité est un des éléments clés du service AWS Lambda. Une fonction Lambda est créée par l’infrastructure AWS en un certain nombre d’instances et si toutes ces instances sont déjà en exécution au moment où un nouvel événement arrive, alors l’infrastructure va automatiquement créer une nouvelle instance, dans le but de servir la demande courante. C’est ce qu’on appelle le processus de scale out. Ensuite, après une éventuelle période d’inactivité, des instances de cette même fonction Lambda peuvent être récupérées automatiquement par l’infrastructure et ce processus est connu sous le nom de scale in.
Jusqu’à présent, nous n’avons pas beaucoup abordé la perspective du coût des fonctions Lambda et nous n’allons pas le faire ici non plus, tellement les tarifs et les modèles de facturation changent d’une période à une autre. Mais il convient de noter quand même que ce coût...
Limitation, simultanéité et quotas
AWS Lambda n’est pas un espace infini et, par conséquent, il y a des limites à l’extension présentée au paragraphe précédent. Amazon limite le nombre d’exécutions concurrentes des fonctions Lambda en fonction de la région. Par exemple, au moment de la rédaction de cet ouvrage cette limite est de 1000 exécutions concurrentes par compte utilisateur, mais cette valeur est en changement continu. Bien sûr, elle peut être augmentée sur demande, mais son existence est justifiée, d’un côté par les restrictions posées par notre univers matériel et, d’un autre côté, par une nécessité de garde-fou sans quoi les factures de services pourraient exploser.
Lorsqu’on atteint cette limite maximum, la métrique CloudWatch nommé Throttles présente une valeur supérieure à zéro et, soudainement, nos fonctions Lambda se mettent à ne plus fonctionner et à lever des exceptions de type HTTP 500 ou tout simplement à se bloquer indéfiniment. Et comme cette métrique est globale au niveau d’un compte, ses effets vont affecter toutes les fonctions Lambda s’exécutant pour le même compte, quelle que soit la région. D’où la nécessité d’utiliser...
Le multi-threading
Une conséquence du mécanisme d’extension dont disposent les fonctions Lambda, et qu’on vient de voir précédemment est la garantie qu’au maximum un événement par instance de fonction sera traité à tout moment. En d’autres termes, on n’est jamais concerné par le cas où des événements multiples seraient traités par la même instance Lambda. Ainsi, le problème du multi-threading ne se pose pas, à moins que vous créiez et manipuliez vos propres threads.
Les applications et les services lancent des threads pour une des raisons suivantes :
-
Obtenir de l’extension (scaling) en autorisant le traitement de plusieurs requêtes à la fois, dans le cadre du même processus.
-
Effectuer des traitements parallèles à travers des CPU multiples.
-
Effectuer des opérations d’entrée-sortie non bloquantes.
Parmi les trois cas ci-dessus, le premier ne se pose jamais avec AWS Lambda. Car avec AWS Lambda, chaque requête est traitée dans un environnement d’exécution séparé, qu’on a appelé précédemment container.
Quant au deuxième, il est plutôt rare que dans un environnement cloud on rencontre des problèmes liés au nombre de CPU et leur utilisation en parallèle. En effet...
Versions, alias et déplacement de trafic
Lors des déploiements effectués tout au long de cet ouvrage, s’il existait déjà un déploiement de la librairie courante, il a été remplacé systématiquement par celui en cours. Les fonctions Lambda faisant l’objet du déploiement précédent ont été détruites et d’autres ont été créées à leur place. Cependant, ce n’est typiquement pas ce que nous souhaitons faire dans notre pratique quotidienne.
Lorsqu’on crée ou met à jour des fonctions Lambda, on a la possibilité de leur attribuer des versions. Il s’agit d’une valeur numérique qui démarre à 1 et qui est gérée automatiquement sans que vous ayez la possibilité de la modifier manuellement. Ainsi, on crée une nouvelle version sans avoir à spécifier son identifiant, mais à partir de la version courante, identifiée par le mot-clé $LATEST. Si un identifiant de version existe, alors il sera incrémenté et affecté à la nouvelle version, autrement il sera initialisé à 1. Pour référencer explicitement une certaine version on doit ajouter son identifiant à la fin de son ARN, séparé par « : ». Aussi dans les commandes AWS CLI, cet identifiant...
Le démarrage à froid
Nous avons vu que, lorsqu’une fonction Lambda est appelée pour la première fois, une micro machine virtuelle de type spécial est créée, un environnement Linux est démarré faisant tourner une JVM avec l’environnement d’exécution Lambda dans lequel on charge pour exécution le code Java associé. Ensuite, selon la nature exacte de la fonction Lambda en question, différentes choses peuvent se passer. Toutes ces opérations sont groupées collectivement sous le nom de démarrage à froid.
Ce qu’il est important de préciser au sujet du démarrage à froid, c’est qu’il est effectué lorsque la fonction est appelée, pas avant. Il n’est pas systématiquement effectué à chaque appel car, une fois que la fonction a fini son exécution, la plateforme gèle l’instance en question et la garde sous la main pendant un certain temps, au cas où une autre requête ou événement à son attention arrive. Si un tel événement arrive en temps utile alors la plateforme va dégeler cette instance de manière à ce qu’elle puisse le traiter. Pour la plupart des fonctions Lambda le démarrage à froid est effectué en proportion de 1 % de leur cycle de vie, mais, malgré tout, il est important de savoir exactement quand.
Pour résumer, le démarrage...
La gestion de l’état
Comme évoqué plusieurs fois déjà, les fonctions Lambda sont nativement sans état. Cela signifie que, vu le processus d’extension expliqué plus haut dans ce chapitre, il serait impossible de garantir qu’un état conversationnel, appelé aussi « affinité », soit établi avec des clients, de manière à ce que des requêtes émanant du même client soient systématiquement traitées par les mêmes instances Lambda. En d’autres termes, il serait irraisonnable de considérer que l’état local d’une fonction Lambda lors d’une certaine requête, c’est-à-dire le contexte présent en mémoire ou sauvegardé au niveau du file-system, soit le même que lors d’une autre requête. C’est donc dans ce sens que l’on dit que les fonctions Lambda sont sans état.
Néanmoins, il existe des méthodes non natives pour créer et partager un état externalisé. Autrement dit, si on veut maintenir un état conversationnel à travers deux ou plusieurs requêtes, on doit le faire en le sauvegardant explicitement dans une base de données, dans des fichiers, dans un container S3, dans un cache, etc. Et s’il s’agit de fonctions Lambda synchrones, alors...
VPC (Virtual Private Cloud)
Lors des exemples présentés tout au long de cet ouvrage, les différentes ressources et services, par exemple DynamoDB, API Gateway, SQS, etc., étaient accessibles sur Internet sans restriction. Mais ceci n’est pas typique des applications d’entreprise où les ressources sont généralement protégées par des pare-feu. Dans ce dernier cas, lorsqu’on ne peut pas accéder directement aux ressources à cause des pare-feux, la solution la plus commune et standard est l’utilisation des VPC.
Un VPC est une notion de bas niveau de l’infrastructure Amazon dont la compréhension nécessite des connaissances poussées dans le domaine des réseaux, de l’adressage IP, des ENI (Elastic Network Interface), les blocs CIDR (Classless Inter Domain Routing), des groupes de sécurité, etc. Par conséquent, les VPC ne font pas partie de la portée de notre ouvrage.
Pour plus de détails sur VPC et les notions associées comme ENI, CIDR, EC (Elastic Cache), EC2 (Elatic Cloud Computing), etc. vous pouvez consulter le livre « AWS : Gérez votre infrastructure sur la plateforme cloud d’Amazon » du même auteur paru aux Éditions ENI en décembre 2019.
Néanmoins, on ne peut pas finir notre longue incursion dans le monde...
Couches et environnements d’exécution
Le déploiement d’une fonction Lambda nécessite le packaging du code et de toutes les dépendances, dans une archive qui, dans les exemples présentés tout au long de cet ouvrage, était soit un ZIP, soit un JAR soit un uberjar. Et on a vu que l’impact du démarrage à froid sur les fonctions Lambda était d’autant plus important que l’était la taille de cette archive. C’est là que la notion de « couche » intervient.
Une couche est une partie de l’artefact courant, mais qui fait l’objet d’un déploiement séparé de celui des fonctions Lambda proprement dites. En quelque sorte, c’est un concept similaire à celui de « couche » Docker. Si on considère, par exemple, que les dépendances d’un artefact sont constantes, on peut envisager leur déploiement séparé, en tant que couche à part, si bien que toutes les fonctions Lambda ayant besoin de ces dépendances pourraient faire l’objet d’une autre couche supérieure venant se superposer sur la première.
Lors du chapitre Les fonctions Lambda et Eclipse Microprofile nous avons présenté Eclipse Microprofile et son implémentation Quarkus. Cette librairie consiste en plusieurs centaines de classes...