Déployer un backend TypeScript full-serverless avec SAM (2/3)

Partie 2 - Intégration au template Serverless Application Model (SAM)

Dans le premier article nous avons expliqué en détail la structure du code mise en place pour ce blueprint ainsi que le processus de build à suivre pour obtenir des fonctions lisibles par Lambda. Nous allons maintenant revenir pas à pas sur la création des différentes ressources de l’architecture grâce à SAM ainsi que sur le déploiement de l’application Serverless dans AWS.

SAM, c’est quoi ?

Serverless Application Model, ou SAM, est un framework open-source d’AWS destiné à gérer le développement, le debug et le déploiement d'applications Serverless via Lambda. Il est composé de deux outils : une spécification de template et une interface en ligne de commande.

La spécification, basée sur celle de CloudFormation, offre une syntaxe simple et efficace pour définir les fonctions, API, évènements et autres ressources qui constitueront votre application Serverless. Les Lambda par exemple sont définies par une ressource “AWS::Serverless::Function” comme présenté dans le code ci-dessous.

Au moment du build, SAM va réaliser la traduction de cette syntaxe en syntaxe CloudFormation et va implicitement définir les rôles, permissions et aliases liés à la fonction Lambda. SAM propose également une grand nombre de policies gérées par AWS et prêtes à être utilisées (ici DynamoDBReadPolicy) ce qui simplifie la déclaration de Lambdas. D’autres ressources liées à la partie applicative possèdent une spécification en SAM (table DynamoDB, API Gateway, …) mais la plupart des ressources orientées infra sont définies comme en syntaxe CloudFormation. Certaines propriétés peuvent également être factorisées dans une section Globals située au début du template, ce qui évite de nombreuses répétitions de ces propriétés.

Le principal avantage de SAM est que le template fait office d’entité centralisée (tout dans une seule stack), versionable et déployable de votre application Serverless. Mais le framework propose également d’autres fonctionnalités intéressantes comme l’intégration facile de best practices : tracing avec X-Ray, déploiements avec CodeDeploy, …

La spécification de SAM nécessite que chaque propriété CodeUri des fonctions Lambda pointe sur un dossier contenant un fichier package.json. Seulement, comme explicité dans l’article 1, notre structure projet ne possède qu’un seul package.json, commun à toutes les fonctions et situé à la racine. Pour pouvoir tout de même build le projet, la solution trouvée a été de mettre un package.json vide dans chaque dossier de fonction, en attendant une meilleure gestion de ce cas d’usage par SAM.

La propriété Handler des fonctions Lambda quant à elle est définie dans la section Globals et pointe vers le fichier bundle.js généré par webpack.

Le deuxième composant de SAM est sa puissante interface en ligne de commande. Les commandes proposées par la CLI permettent de vérifier la validité du template, build/deploy l’application Serverless sur AWS mais également d’invoquer les Lambdas en local, ce qui facilite le debug. Ces commandes pourront également être intégrées à des pipelines de CI/CD pour automatiser le build et le déploiement.

Le template “TypeScript backend”

L’architecture que nous avons choisi de mettre en place pour illustrer ce projet nécessite la définition de multiples ressources dans un template SAM. La partie concernant la sécurisation de l’API sera abordée dans l’article suivant, nous nous attarderons ici sur le cœur de l’application : Lambda, API et CloudFront.

Nous avons brièvement évoqué le fonctionnement du Lambda Layer dans l’article précédent : il s’agit d’une ressource Lambda contenant les dépendances partagées par les différentes fonctions d’une application. Sa déclaration dans le template est assez simple et pointe vers le dossier “layers” de notre arborescence.

Le package.json contenu dans le dossier pointé par ContentURI contient un script de build qui, grâce à la commande rsync, vise à exclure les dossiers src et node_modules lors de la constitution du dossier dist du layer. En effet, SAM effectuant une commande “npm install” plus tard dans le processus de build, il n’est pas nécessaire d’inclure node_modules. L’utilisation de la commande rsync sur le répertoire shared est nécessaire car à l’heure actuelle, SAM ne supporte pas les chemins locaux passés en dépendance.

Une fois le Layer déclaré, on peut le référencer dans nos fonctions Lambda et celle-ci aura ainsi accès à toutes les dépendances qu’il contient. Les autres propriétés essentielles des fonctions Lambda sont le chemin vers le code packagé par webpack, sa Policy associée (gérée par AWS ou bien personnalisée) et bien sûr l’évènement qui va déclencher la Lambda, ici un appel à l’API.

On peut noter sur cette capture d’écran la référence à un paramètre ApiStageName. Tout comme CloudFormation, les templates SAM sont paramétrables, ce qui permet par exemple d’utiliser facilement le même template pour plusieurs environnements (dev, production, …). Ces paramètres sont entrés soit lors d’un prompt via la commande “sam deploy --guided”, soit directement dans la commande via l’option --parameter-overrides.

L’API Gateway est définie succinctement dans le template car les spécifications de chacun de ses endpoints, des fonctions associées, des réponses HTTP et de la sécurité de l’API se situent dans le fichier swagger.yaml référencé par la propriété DefinitionBody. La présence de ce fichier de spécification OpenAPI est une best practice car il permet non seulement d’alléger le template SAM mais également utilisé par API Gateway pour générer une documentation versionnée de l’API.

Nous avons choisi un endpoint de type régional pour notre API, ce qui signifie que le endpoint sera créé et hosté sur la région dans laquelle il a été créé. Dans ce cas là, il est recommandé de positionner un CloudFront devant l’API, ce qui va permettre de distribuer l’API de manière globale via des caches et de pouvoir mettre en place un contrôle d’accès sur lequel nous reviendrons en détail dans l’article suivant. Une distribution CloudFront est donc déclarée dans le template SAM avec comme unique origine l’API Gateway.

Comment déployer cette application Serverless ?

Une fois le code transpilé et packagé grâce à webpack et le template écrit en spécification SAM, il est très simple de déployer notre application Serverless sur AWS. Pour cela on va utiliser deux commandes de la SAM CLI :

  • sam build : Cette commande va préparer l’application au déploiement en allant dans un premier temps lire le fichier swagger.yaml pour construire l’API. Elle va ensuite packager le Layer puis les fonctions Lambda en se servant du code préparé par webpack, pour enfin traduire le template SAM en template CloudFormation. Cette traduction comprend notamment la transformation des ressources Serverless en différentes ressources CloudFormation inhérentes aux Lambda par exemple. Toutes ces ressources sont placées dans un dossier .aws-sam, prêtes pour le déploiement.
  • sam deploy : Une fois le build effectué, la dernière étape est le déploiement. Cette commande peut être utilisée dans un terminal avec l’option --guided, qui lance un prompt permettant à l’utilisateur d’entrer ses paramètres, la région ou le nom de la stack CloudFormation. Ces valeurs peuvent bien sûr être passées grâce à des options afin d’automatiser le processus dans des pipelines CI/CD. La configuration de déploiement peut être sauvegardée dans un fichier samconfig.toml pour faciliter les futurs déploiements.

Il est important de rappeler que nous avons choisi d’implémenter des fonctions Lambda en relation avec une table DynamoDB pour démontrer les avantages d’un tel projet, mais la partie applicative peut être complètement modifiée selon le besoin utilisateur : URL pré-signées S3, log de messages SNS, …

Nous avons choisi SAM pour réaliser ce blueprint au détriment d'autres frameworks comme Serverless Framework ou bien d’outils comme Terraform car SAM est natif à AWS. Nous trouvons que sa syntaxe simplifiée pour les ressources Serverless et son intégration de best practices est idéale pour une application comme celle-ci. Toutefois, Serverless Framework possède de nombreux plugins offrant ces avantages et aurait également pu être un bon choix pour réaliser ce template.

Dans l’article suivant, nous aborderons le besoin de sécuriser cette application, les outils choisis pour mettre en place cette sécurité et comment le template SAM est affecté par ces changements.