Tech Talk : Construire une architecture sans serveur dans AWS avec l'interaction des fonctions lambda (Partie 2)
Images : https://www.agilytic.be/blog/tech-talk-serverless-architecture-aws-lambda-function-interaction
Dans la première partie de cette série d'articles, nous avons discuté de la façon dont la fonction lambda sert de base aux architectures sans serveur dans le cloud. Maintenant, nous allons explorer comment les interactions entre les fonctions lambda et les files d'attente sont une prochaine étape naturelle pour construire une architecture serverless robuste.
Simple Queuing Service (SQS) dans AWS
Nous avons terminé la première partie de cet article face à un goulot d'étranglement : nos événements ne pouvaient pas être traités par la simple architecture serverless en place. La possibilité la plus simple de résoudre le problème des messages manquants est de tirer parti d'un service de file d'attente de messagerie. AWS appelle ce service Simple Queuing Service (SQS). Il résout le problème du goulot d'étranglement en envoyant, en recevant et en stockant des messages entre les logiciels sans perdre de messages ni exiger que les services soient disponibles. Si la deuxième fonction Lambda de l'exemple précédent est occupée par les 1 000 premiers travaux qu'elle a reçus, SQS retient patiemment les 4 000 travaux suivants jusqu'à ce que " ListenerLambda " puisse les ingérer.
Nous pouvons envoyer le message au SQS de manière programmatique en utilisant le SDK AWS à partir de la fonction Lambda ;
client = boto3.client('sqs')
client.send_message(QueueUrl='string', MessageBody='{"file": "path"})
où 'QueueUrl' est l'URL correspondant à la file d'attente particulière que nous voulons utiliser (encore une fois, consultez la documentation pour une multitude d'autres options que nous laissons de côté dans cet exemple). Pour que la deuxième fonction Lambda écoute cette file d'attente, nous pouvons, une fois de plus, aller à l'écran de la console illustré à la figure 4, cliquer sur "Add trigger" et sélectionner le SQS qui nous intéresse.
Aussi simple que cela, nous avons réussi à résoudre le problème du goulot d'étranglement. Lorsque l'événement est traité dans la fonction d'écoute, il est automatiquement supprimé de la file d'attente pour éviter de l'envoyer à nouveau. Si, pour une raison quelconque, la fonction Lambda d'écoute n'a pas fini de traiter l'événement (elle lève une erreur), la file d'attente essaiera à nouveau de l'envoyer plus tard.
C'est très bien... ou pas ? Les choses peuvent devenir plus compliquées dans de nombreux cas. Imaginons que 'ListenerLambda' ne puisse traiter avec succès que 99% des événements reçus du premier Lambda, et que pour un événement qui a échoué une fois, le code échoue toujours. Dans cette situation, le message est renvoyé à la file d'attente, qui l'envoie à 'ListenerLambda', qui échoue, donc le message est renvoyé à la file d'attente... vous le voyez, n'est-ce pas ? Pour éviter cette boucle de désespoir, le SQS est équipé d'une période de rétention qui efface automatiquement les messages qui n'ont pas été traités au cours de cette période (figure 5).
Imaginez que vous ayez estimé que tous vos travaux se terminent en deux jours s'ils se déroulent sans erreur. Vous supposez alors que tout message encore présent dans la file d'attente après deux jours est dû à l'échec du travail et qu'il peut être effacé de la file d'attente en toute sécurité. Vous fixez donc votre délai de conservation à deux jours. Là encore, le travail est fait, mais ce n'est pas une bonne pratique. Comment pouvez-vous déboguer votre code et l'améliorer si vous avez effacé toutes les informations sur le type d'événements à l'origine de l'erreur ?
Encore une fois, pas d'inquiétude, nous ne sommes pas les premiers à y penser. En faisant défiler l'écran de la console AWS vers le bas, vous trouverez la possibilité de définir une Dead-Letter Queue pour votre SQS. Il s'agit d'un autre SQS vers lequel vous redirigez les événements problématiques. En définissant une longue période de rétention dans la Dead-Letter Queue, vous pouvez surveiller votre travail à tout moment sans avoir à vous inquiéter que les événements qui n'ont pas été traités correctement par votre code soient continuellement envoyés à votre fonction Lambda.
Félicitations ! Grâce à cela, nous avons réussi à faire dialoguer deux fonctions Lambda de manière robuste, en gérant les goulots d'étranglement et les événements problématiques avec SQS et Dead-Letter Queues.
Si vous vous penchez sur votre cas d'utilisation et que vous constatez que vous avez des dizaines de fonctions Lambda prêtes à être déployées, avec une logique non séquentielle qui les relie, et que vous devez gérer la capture d'erreurs et la relance, vous pouvez penser que les outils à votre disposition sont limités. C'est vrai. Ce qui se passe, c'est que nous n'avons pas encore introduit la fonction AWS Step.
Fonction d'échelon AWS
AWS Step Function est un orchestrateur pour les fonctions sans serveur. Vous pouvez coordonner visuellement des Lambdas et d'autres services AWS serverless rapidement. Le terme clé ici est " visuellement ", car son interface graphique clarifie ce qui se passe à chaque instant, qu'il s'agisse d'une fonction Lambda envoyant un événement à la suivante, attrapant une erreur et réessayant ou mettant en œuvre une logique if-then-else, pour donner quelques exemples. La figure 6 présente une capture d'écran de la page de configuration, où vous pouvez trouver plusieurs modèles d'exemple pour commencer et découvrir les possibilités offertes par ce service.
Il fait tout cela, en gérant automatiquement les événements entre les fonctions sans serveur. Pas besoin de tout ce yadda-yadda que vous venez de lire ! En tant que tel, c'est un outil très puissant pour orchestrer vos Lambdas. Alors, pourquoi ne l'utiliserions-nous pas systématiquement pour simplifier notre code et gagner du temps en lisant cet article ? La raison est que cette solution a un prix, vous devez donc trouver un équilibre entre le prix que vous voulez payer et le niveau de gestion que vous êtes prêt à supporter. Cette option vaut vraiment la peine pour les flux de travail modérément compliqués.
Autorisations minimales avec AWS
Nous terminons cet article par le sujet le plus important de tous : la sécurité. Suivre des tutoriels et des modèles en ligne est un excellent moyen d'apprendre à déployer une infrastructure, mais en général, ces tutoriels favorisent le déploiement rapide d'un pipeline au détriment de considérations telles que la sécurité. Après tout, il s'agit d'un tutoriel et vous allez détruire toutes les ressources créées (le cloud est un endroit dangereux : détruisez toujours toutes les ressources que vous n'utiliserez pas). Dans le contexte actuel, cela signifie que pour faire interagir une fonction Lambda avec une file d'attente SQS, il faut aller dans la console IAM d'AWS et y affecter une politique de sécurité intégrée au rôle de la fonction Lambda, souvent l'accès complet, comme dans la figure 7.
Après avoir assigné cette politique au rôle Lambda, on peut trouver son contenu au format JSON dans la console IAM, où il se lira quelque chose de similaire à :
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sqs:*"
],
"Resource": "*"
}
]
}
Le problème de sécurité avec ce type de politique réside dans les jokers '*'. En particulier, "sqs:*" permet à la Lambda d'interagir avec la file d'attente SQS sans restriction. Il est même possible de l'effacer ou de créer plus de messages à consommer par la Lambda, entrant ainsi dans une boucle infinie ! Le pire, c'est le deuxième joker de "Resource" : "*" permet à la fonction Lambda d'interagir avec n'importe quelle file d'attente, il existe donc une réelle possibilité que nous invoquions la fonction serverless avec les mauvais événements qui sont destinés à une fonction différente ou, pire encore, à un projet complètement indépendant.
Vous pouvez atténuer le premier de ces problèmes en choisissant plutôt le rôle SQS Queue Execution, comme le montre la figure 8. Dans ce cas, l'ensemble des actions que la Lambda peut exécuter concernant le SQS est limité. Le résumé JSON des politiques autorisées est maintenant le suivant :
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueAttributes"
],
"Resource": "*"
}
]
}
Cela permet de recevoir des messages, de supprimer des messages ou d'obtenir les attributs de la file d'attente (plus de suppression de la file d'attente ou de création de nouveaux messages par accident). Le seul point faible restant ici est le terme "Resource" : "*" qui permet à la Lambda d'interagir avec n'importe quelle file d'attente. Nous pouvons y remédier en limitant la politique à une ressource particulière : le SQS à partir duquel la Lambda est censée lire les messages. Pour ce faire, vous devez obtenir l'ARN du SQS, qui peut être obtenu dans la console AWS, comme le montre la Figure 9.
En écrivant ce code ARN au lieu du joker "*" dans le champ Ressource de la politique, vous vous assurez que la fonction Lambda ne peut recevoir des messages, supprimer des messages et obtenir les attributs de la file d'attente qu'à partir de cette même file d'attente.
Une dernière remarque vient de la question évidente : que se passe-t-il si je veux lire des messages d'une file d'attente et en envoyer à une autre, par exemple pour invoquer une troisième fonction Lambda ? Limiter la ressource à la file d'attente de réception ne serait-il pas alors contre-productif ? La réponse à cette question est simple, vous pouvez attacher plus de politiques à votre rôle Lambda. Par exemple, vous pouvez ajouter la permission d'envoyer des messages à une file d'attente différente, comme suit :
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sqs:ChangeMessageVisibility",
"sqs:DeleteMessage",
"sqs:GetQueueAttributes",
"sqs:ReceiveMessage"
],
"Resource": "ARN_OF_QUEUE_TO_READ_MESSAGES_FROM"
},
{
"Effect": "Allow",
"Action": [
"sqs:SendMessage"
],
"Resource": "ARN_OF_QUEUE_TO_SEND_MESSAGES_TO"
}
}
Limitez toujours les actions et les ressources autorisées par votre politique au minimum nécessaire. Vous éviterez ainsi de rencontrer des problèmes lors de la mise en production de votre application.
Conclusion
Nous avons vu comment construire des architectures serverless robustes et efficaces dans le cloud en utilisant des fonctions Lambda, un service SQS, des Step Functions et un paramétrage restrictif des permissions.
Pour les charges de travail qui connaissent des pics d'activité ou de longs temps d'inactivité, il s'agit d'une alternative rentable et facile à gérer au déploiement de morceaux de code monolithiques dans des machines virtuelles.
Dans cet article, nous nous sommes concentrés sur l'environnement AWS à titre d'illustration, mais ces commentaires s'appliquent directement à d'autres fournisseurs de services en nuage tels que MS Azure (voir l'article : description d'un cas d'utilisation) ou Google Cloud.