Comment rendre n'importe quelle application NodeJS sans serveur

J'espère que vous adorerez Serverless autant que moi car c'est encore un autre post sur ce sujet.

Maintenant, si nous parlons d'une simple API REST sans serveur, votre configuration est assez évidente sur AWS: Lambda + API Gateway.

Mais qu'en est-il des autres (micro) services que votre backend peut avoir? Vous savez, ce n'est pas la meilleure idée de mettre tout votre code d'application dans une seule fonction AWS Lambda monolithique.

Le défi

Nous voulons déployer facilement des modules d'application en tant que microservices sans serveur, qui doivent également communiquer entre eux. De préférence, la communication entre les services doit être réglementée par une sorte de liste de contrôle d'accès.

Tentative 1. Passerelle API

C'est la première pensée que j'ai eue lorsque j'essayais de résoudre le problème: il suffit d'exposer tous les microservices via API Gateway. Le problème est que… Les API créées sont publiques.

Pourquoi c'est un problème? Par exemple, nous ne voulons pas qu'un service de facturation soit exposé au monde entier, même si l'accès est restreint à l'aide d'une sorte d'autorisation.

Eh bien, vous pouvez rendre l'API privée, mais les politiques de sécurité sont assez limitées:

Vous pouvez utiliser des stratégies de ressources API Gateway pour permettre à votre API d'être invoquée en toute sécurité par:
* utilisateurs d'un compte AWS spécifié
* plages d'adresses IP source ou blocs CIDR spécifiés
* Clouds virtuels privés (VPC) ou points de terminaison VPC spécifiés (dans n'importe quel compte)

Cela rend assez gênant le contrôle des communications entre ces services. La seule façon de le faire ici est de mettre des services dans des VPC séparés, trop de travail.

Tentative 2. Lambda

Pourquoi ne plaçons-nous pas simplement chaque microservice dans un AWS Lambda distinct? Est-ce que cela résoudra le problème?

Oui, en fait, ce sera un microservice sans serveur, et vous pourrez utiliser des politiques IAM pour régler les accès entre les services, mais… Ce n'est pas «facile».

Je sais que c'est tout à fait normal de nos jours d'avoir une petite fonction comme unité de déploiement. Et dans le cas où votre service a plus d'un point de terminaison / méthode / fonction, il est considéré comme correct de le déployer en plusieurs Lambdas.

J'en comprends les avantages, mais vous sacrifiez la facilité de maintenance et de développement. De plus, je n'aime vraiment pas l'idée d'avoir un service déployé comme un ensemble de fonctions Lambda. Imaginez, plusieurs fonctions distinctes traitant de la facturation? Ce n'est plus un contexte borné. Bien qu'il existe des cas où une telle granularité peut être utile, mais c'est un cas rare.

Tentative 3. Fat Lambda

Pouvons-nous réellement déployer un ensemble de points de terminaison en tant que Lambda unique (sans utiliser API Gateway, bien sûr)?

Si nous pouvions le faire, nous bénéficierions de tous les avantages de l'option précédente, mais nous pourrions également choisir la granularité de nos unités de déploiement.

La façon dont je le veux est la suivante: chaque service déployable doit être un simple ancien objet JS simple avec des méthodes. C'est assez simple à réaliser en ajoutant quelques lignes de code de collage entre votre objet et AWS Lambda.

Voici mon implémentation: aws-rpc. Ce module nodejs expose la fonction lambdaHandler, où vous passez juste un objet, et il est automatiquement exposé à toute personne qui est capable d'accéder à Lambda:

importer {lambdaHandler} depuis 'aws-rpc';
import {TestServiceImpl} à partir de './TestServiceImpl';
// c'est votre unité de déploiement
// c'est ce que vous spécifiez comme fonction de gestionnaire de Lambda
export const handler = lambdaHandler (new TestServiceImpl ());

Maintenant, vous pouvez simplement déployer le «gestionnaire» en tant qu'AWS Lambda. Voici comment vous invoquez ses méthodes:

importer {TestService} depuis './TestService';
const client = attendre createClient  ("LambdaName", "test");
console.log (attendre client.test ());

Veuillez noter que pour pouvoir générer des méthodes pour l'objet stub client, vous devez passer tous les noms de méthode à createClient, comme nous l'avons fait dans l'exemple.

Cela est nécessaire car JS ne dispose d'aucune information d'exécution sur les interfaces TypeScript. Je pourrais l'implémenter en utilisant des classes abstraites, mais je n'aime pas ça ¯ \ _ (ツ) _ / ¯.

Prime! Vous pouvez tout exécuter localement!

Je pense qu'il est très important que votre environnement de développement local soit aussi confortable que possible. C'est pourquoi j'ai également ajouté la possibilité d'exécuter le service et le client localement sans déployer quoi que ce soit sur AWS (voir les fonctions runService et createClient). Pour des exemples, consultez le référentiel sur GitHub.

Sommaire

Il est très facile de se perdre dans les services offerts par les fournisseurs de cloud et de surcharger votre infrastructure.

Je choisis toujours la solution la plus simple et explicite à laquelle je puisse penser. Rappelez-vous également que de nombreuses techniques et pratiques peuvent être réutilisées à partir d'autres plates-formes (l'idée du gros NodeJS Lambda est inspirée des soi-disant pots de graisse du monde Java).

Si vous avez aimé ce sujet, consultez-les également:

  • Vous devez apprendre à créer la meilleure architecture sans serveur
  • Comment créer un pipeline CI / CD sans serveur gratuit: 3 exemples simples
  • Comment répliquer facilement DynamoDB entre les régions
  • Comment faire une demande multirégionale (et payer zéro)
  • Rendre toute application Web Java sans serveur

Les commentaires, les likes et les partages sont très appréciés. À votre santé!