Serverless : l'Imhotep de vos lambdas

Dans un article précédent, nous vous expliquions comment fonctionnait AWS Lambda. Au delà des avantages de la technologie, on perçoit rapidement qu'il est nécessaire d'avoir une bonne organisation pour l'exploiter sur un gros projet. Serverless, le bien nommé, existe justement pour cadrer tous ça !

En un !

Serverless est un framework qui vous aide à architecturer votre projet et vos multiples fonctions. Il est écrit en Node.js mais peut faire fonctionner d'autres types de runtimes couramment utilisées. Il permet de gérer les principaux providers du marché tel que Microsoft Azure avec Functions, IBM avec Cloud Functions, Google Cloud avec Functions, Oracle avec Fn et évidemment AWS avec lambda.

Serverless ajoute de nombreuses fonctionnalités qui simplifient le test, le déploiement et la gestion des différents services qui gravitent autour de vos fonctions.

Dans la suite de cet article, je décrirais uniquement les cas d'utilisation pour AWS Lambda. Si vous utilisez un autre provider, vous constaterez surement des différences de fonctionnement.

Serverless.yml, une pepita

Le framework Serverless impose une seule chose : avoir un fichier serverless.yml à la racine du projet. Ce fichier est au cœur de la configuration. Pour le reste, libre à vous de ranger vos fonctions dans des dossiers, séparer vos classes métiers de vos utilitaires ou encore utilisez des fichiers de configuration. Serverless reste très flexible. Si vous êtes perdu, vous pouvez vous aider de cet article.

Pour commencer, installez les prérequis (Node.js et Npm) puis le framework :

$ npm install -g serverless

Vous pouvez maintenant lancer la commande d’initialisation (le script va créer un dossier avec le nom de votre projet) :

$ serverless

Serverless propose des services complémentaires en ligne pour monitorer et simplifier le management de vos fonctions. Cette partie est optionnelle et vous devrez répondre "non" à la question du monitoring lors de l'initialisation si vous ne souhaitez pas le mettre en place.

Définissez la base de votre configuration dans le fichier serverless.yml :

service: {{ nom de votre projet }}
provider:
  name: aws
  region: {{ zone de déploiement (ex: eu-west-3) }}
  runtime: {{ runtime (ex: nodejs12.x) }}
  memorySize: {{ taille de l'instance (ex: 256q) }}

Nous allons aussi partir d'une structure simple de projet :

![Titre_de_l_image](/uploads/documents/lambda_folders.PNG)

On retrouve évidemment votre package.json qui contiendra les dépendances de vos fonctions mais aussi une première arborescence pour ordonner vos fichiers.

Votre première fonction

Nous allons maintenant déclarer une nouvelle lambda très simplement dans nos fichiers de configuration :

hello:
    handler: src/function1.main

helloest le nom de votre fonction et le handler, le chemin vers celle-ci (comprenant le chemin fichier src/function1.js et la fonction appelée main).

Cette première fonction va juste renvoyer un 'hello' :

module.exports.main = (event, context, callback) => {
  callback(null, {
        statusCode: 500,
        body: 'hello'
      })
}

Il y a une notion très importante à connaitre avant de développer une fonction Lambda : le mode d'appel. Vous pouvez en effet appeler vos fonctions soit d'une manière synchrone (retour immédiat du résultat) soit en asynchrone (réponse immédiate mais traitement différé). Le code que vous écrirez sera donc différent en fonction de votre choix.

Vous pouvez maintenant déployer très simplement votre fonction avec la commande serverless ou l'alias sls :

$ serverless deploy

ou

$ serverless deploy function -f hello

Développer en local

Pouvoir développer et tester son code en local est essentiel mais pas évidemment quand son projet repose intégralement sur des solutions en ligne. Serverless propose un plugin d'exécution de votre fonction hors-ligne. Il va monter un serveur local et simuler l'environnement AWS au mieux. Au mieux car évidemment il y a de nombreuses subtilités que vous ne pourrez vérifier que sur un vrai déploiement dans le cloud.

Pour utiliser ce mode offline, il vous suffit d'installer le plugin par la commande :

$serverless plugin offline

puis pour le démarrer : serverless offline. À la fin de la commande, le serveur vous communique les urls d'accès aux fonctions.

Surtout ne pensez pas que si votre lambda fonctionne en local, elle marchera une fois chez AWS. Soyez vigilant !

Gestion des environnements

Pour séparer les environnements vous allez devoir utiliser le système de stage. Je recommande de le mettre en place dès le départ car comme nous l'avons vu précédemment, vous allez devoir parfois tester vos lambdas aussi lors de la phase de développement en local. On peut identifier de base trois stages : local, dev et prod.

La notion de stage (d'environnement) va vous permettre de séparer les ressources de votre projet :

stage: ${opt:stage, 'local'}

Vous pouvez maintenant l'utiliser pour isoler certaines de vos ressources ou pour différencier vos fichiers de configuration :

custom:
  config: ${file(./config/config.${opt:stage, self:provider.stage}.json)}
  DynamoTable: ${self:provider.stage}-medical-form
  SQSQueue: https://sqs.eu-west-3.amazonaws.com/xxxxxxxxxx/${self:provider.stage}-submit-form

Au déploiement, vous pouvez indiquer le stage utilisé pour le déploiement (sinon c'est celui par défaut qui est utilisé) :

$ serveless deploy --stage=prod

Accéder à vos fonctions en HTTP

Comme nous l'avons vu dans l'article précédent, une lambda est exécutée à partir d'un événement. Dans notre cas (le plus courant), nous voulons que cela se fasse par un appel HTTP. Pour cela, on va s'appuyer sur le service API Gateway qui permet de monter des urls d'API résilients, capable d'accepter des grosses charges, permettant de gérer des versions mais aussi de mettre en place un système de droit d'accès. On va donc paramétrer ce service à travers notre configuration de fonction :

hello:
    handler: src/function1.main
    events:
      - http:
          path: hello/{id}
          method: GET
          cors: true

La route sera donc ici hello/ avec un paramètre {id}. On précise la méthode d'accès (GET/POST/PUT...) et si l'on autorise les CORS (Cross-origin resource sharing). L'URL complète est affichée en fin de déploiement dans votre shell et prend automatique comme paramètre le stage :

https://{id}.execute-api.{region aws}.amazonaws.com/{stage}/hello/{id}

Si vous avez besoin d'appeler votre fonction depuis un autre domaine, activer les CORS dans l'API Gateway ne suffit pas ! Vous devez aussi le faire dans votre code.

Autorisations, erreurs

Comme l'écosystème d'AWS est fermé, vous serez surement amené à utiliser d'autres ressources AWS. Par défaut, elles sont inaccessibles sans une configuration des droits d'accès. Pas besoin d'aller trifouiller dans la console AWS, Serverless vous permet de le faire directement. Pour cela vous devez identifier les ARN de vos ressources mais aussi les droits nécessaires (on évite le droit propriétaire !).

iamRoleStatements:
    - Effect: "Allow"
      Action:
        - dynamodb:DescribeTable
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
      Resource:
        - "arn:aws:dynamodb:eu-west-3:xxxxxxx:table/dev-xxxxx"
        - "arn:aws:dynamodb:eu-west-3:xxxxxxx:table/prod-xxxxx"
    - Effect: "Allow"
      Action:
        - ses:SendEmail 
        - ses:SendRawEmail
      Resource:
        - "*"

Vous pouvez consulter vos logs d'une de vos fonctions grâce à la commande :

$ sls logs -f hello

Authorizer

Comme il est parfois nécessaire de rajouter une couche d’authentifications à vos lambdas, vous devrez alors passer par Authorizer. Pour vous éviter de configurer vous-même cette partie, serverless est encore là pour ça !

authentification:
    handler: src/AuthentificationHandler.post
    events:
      - http:
          path: authentification
          method: post
hello:
    handler: src/function1.get
    events:
      - http:
          path: hello
          method: get
          authorizer: authorizerJWT

authorizerJWT:
    handler: src/AuthorizerHandler.handler
    environment:
      AUTH_PUBLIC_KEY: ${self:custom.public_key}
      AUTH_EXPIRES_IN: ${self:custom.config.AUTH_EXPIRES_IN}

Le principe est simple : Une fonction d'authentification (authentification) va vérifier les informations de connexion et générer un token. Ensuite la fonction authorizer (authorizerJWT) va contrôler devant chacune de vos fonctions que le token est toujours valide.

Prendre de la hauteur

Serverless est l'outil incontournable pour mettre en place des FaaS. Simple et pratique à utiliser, il vous donne toute la flexibilité pour travailler comme bon vous semble. Toutefois, il est encore plus facile à utiliser si vous restez sur du langage node.js.

Il y a plein d'autres choses à découvrir sur Serverless et le projet bénéficie d'une communauté active.De nombreux exemples ont été mis à disposition pour vous aider dans vos implémentations. Je vous conseille très fortement de les consulter.

Tags de
l'article

Cet article n'est pas taggué.

Catégories de l'article

Développement

Commentaires

Il n'y a actuellement aucun commentaire. Soyez le premier !

Articles liés