Je m'appelle Vladislav Tankov, en 2018-2020, j'ai étudié au programme de master d'entreprise JetBrains à l'ITMO et depuis 2017, je travaille chez JetBrains .
À l'été 2018, lors du hackathon JetBrains, plusieurs de mes collègues et moi avons essayé de créer un outil pour le langage Kotlin qui simplifie la création d'applications Serverless en analysant le code de l'application.
Après le hackathon, déjà dans le cadre des travaux scientifiques du programme de master d'entreprise chez JetBrains, j'ai décidé de poursuivre le développement de ce projet. En deux ans, l'outil s'est considérablement étendu et a acquis des fonctionnalités, mais a conservé son nom - Kotless ou Kotlin Serverless Framework.
Qu'est-ce que sans serveur
Tout d'abord, rappelons-nous en quoi consiste la plate-forme informatique sans serveur la plus simple. Une telle plateforme comprend trois composants principaux:
- le système d'exécution des fonctions Serverless - petites applications qui traitent certains événements;
- un ensemble d'interfaces différentes du monde extérieur (ou d'une plateforme cloud telle qu'AWS) vers le système d'événements de la plateforme, comme une interface HTTP;
- le système d'événements lui-même, qui assure le transfert d'événements des interfaces aux fonctions et le traitement des résultats des fonctions aux interfaces.
Ces trois composants suffisent à construire une application assez complexe. Par exemple, une application Web n'est qu'une interface HTTP externe (dans le cas d'AWS, ce sera APIGateway ) et pour chaque ressource traitée (comme / route / my ) sa propre fonction de gestionnaire sans serveur. Vous pouvez créer une application plus complexe qui utilise des bases de données et appelle elle-même d'autres fonctions sans serveur, comme dans l'image.
D'accord, vous pouvez créer de telles applications, mais pourquoi?
Les applications sans serveur présentent plusieurs avantages convaincants qui justifient le squat d'architecture.
- Les fonctions sans serveur ne fonctionnent pas lorsqu'elles ne sont pas nécessaires. En effet, la fonction ne traite que les événements - pourquoi devrait-elle consommer des ressources informatiques s'il n'y a pas d'événements?
- Les fonctions sans serveur peuvent gérer des événements du même type en parallèle. Autrement dit, si / route / my est devenu très populaire et qu'un millier d'utilisateurs l'ont demandé à la fois, la plate-forme Serverless peut simplement lancer 1000 gestionnaires, un par événement.
Ensemble, ces points constituent peut-être l'un des mantras sans serveur les plus importants: l'application Serverless évolue de zéro à l'infini. Une telle application ne dépense pas d'argent lorsqu'elle n'est pas demandée et est capable de traiter des milliers de demandes par seconde en cas de besoin.
Problème
Jetons un coup d'œil à un exemple très simple en langage Kotlin:
@Get("/my/route")
fun handler() = "Hello World"
Il est assez évident qu'une telle application peut être implémentée en utilisant l'approche Serverless. À première vue, il suffit de créer une interface HTTP avec une adresse DNS et de mapper / my / route vers le fun handler () .
En fait, créer une telle application prendrait beaucoup plus que l'ajout d'une seule annotation. Par exemple, dans le cas d'AWS:
- Vous devrez implémenter un gestionnaire d'interface pour un événement spécifique, dans ce cas, RequestStreamHandler.
- Vous devrez décrire l'infrastructure de l'application Serverless: décrire l'API HTTP de l'application, décrire toutes les fonctions du gestionnaire et associer leurs fonctions à l'interface, en choisissant soigneusement les autorisations.
- Enfin, vous devrez collecter toutes les fonctions du gestionnaire, les charger dans la plate-forme Serverless et déployer l'infrastructure appropriée.
Il n'y a pas si peu d'étapes pour une application aussi simple, n'est-ce pas?
Pour ceux qui sont initiés au sacrement de l'infrastructure en tant que code, je noterai que, bien sûr, une partie du processus peut être automatisée, mais cette automatisation elle-même nécessite l'étude d'une approche complètement nouvelle (en fait, décrivant l'infrastructure comme un code) et d'un nouveau langage. Cela semble être une tâche inutilement difficile pour un développeur souhaitant déployer une application rudimentaire.
Est-il possible de faire quelque chose de plus facile? Dans certains cas (et en particulier dans ce cas) - oui!
Infrastructure dans le code
Regardons l'autre côté: au lieu de forcer l'utilisateur à décrire l'infrastructure, nous essaierons de la dériver du code utilisateur déjà écrit.
Considérez à nouveau le même exemple:
@Get("/my/route")
fun handler() = "Hello World"
Nous savons que l'utilisateur souhaite que les requêtes vers / my / route soient gérées par cette fonction - synthétisons donc une infrastructure qui créera une API HTTP avec / my / route , créera la fonction Serverless requise et faisons toute la magie nécessaire pour les connecter!
Dans mon article sur Automated Software Engineering 2019, j'ai appelé cette approche Infrastructure in Code. En fait, nous extrayons la description de l'infrastructure du code d'application qui la définit implicitement, c'est-à-dire qu'elle est réellement contenue «à l'intérieur» du code.
Il est à noter que ci-après, seule la synthèse des applications HTTP API est considérée. Une approche similaire peut être utilisée pour traiter les files d'attente et pour traiter les événements sur la plate-forme cloud, mais c'est une question de développement ultérieur de Kotless.
la mise en oeuvre
Espérons qu'à ce stade, l'idée est claire et qu'il reste trois questions principales:
- Comment extraire des informations du code?
- Comment créer une infrastructure à partir de ces informations?
- Comment exécuter une application dans le cloud?
Une analyse
Le compilateur Kotlin Embeddable nous aidera avec cela.
Bien que l'exemple concerne les annotations, en réalité, l'API HTTP de l'application, en fonction de la bibliothèque utilisée, peut être définie de manière complètement différente, par exemple:
//ktor-like style
get("my-route") {
"Hello World"
}
Pour analyser du code arbitraire, le compilateur Kotlin Embeddable s'est avéré à la fois plus familier et plus pratique (en raison du grand nombre d'exemples).
Pour le moment, Kotless peut analyser trois cadres principaux:
- Kotless DSL - Le propre cadre d'annotation de Kotless
- Spring Boot est un framework Web populaire, les annotations sont analysées;
- Ktor est un framework Web Kotlin populaire, les fonctions d'extension sont analysées.
Lors du processus d'analyse du code, le schéma Kotless est collecté - il s'agit d'une représentation indépendante de la plate-forme de l'application Serverless. Il est utilisé pour synthétiser l'infrastructure et rend le processus d'analyse indépendant d'une plate-forme cloud spécifique.
Synthèse
Nous synthétiserons le code Terraform. Terraform a été sélectionné comme l'un des outils d'infrastructure en tant que code les plus populaires avec un large éventail de plates-formes cloud prises en charge, garantissant que Kotless est capable de prendre en charge de nouvelles plates-formes cloud et de stabiliser le déploiement des applications.
La synthèse est faite à partir de Kotless Schema, qui contient une description de l'API HTTP de l'application et de ses fonctions, ainsi que des données supplémentaires (par exemple, le nom DNS souhaité).
Pour la synthèse proprement dite, une bibliothèque Terraform DSL spécialement créée est utilisée. Le code de synthèse ressemble à ceci:
val resource = api_gateway_rest_api("tf_name") {
name = "aws_name"
binary_media_types = arrayOf(MimeType.PNG)
}
DSL garantit le formatage et l'intégrité référentielle entre les différentes ressources Terraform, ce qui facilite considérablement l'extension de l'ensemble des ressources synthétisées.
Le code synthétisé est déployé sur la plateforme cloud avec une simple application Terraform.
Fonctionnement
Il reste à exécuter l'application sur la plateforme Serverless. Comme déjà mentionné, toutes les fonctions Serverless sont essentiellement des gestionnaires pour certains événements, dans notre cas, les requêtes HTTP.
Il est nécessaire de connecter le framework avec lequel l'application est créée (par exemple, Spring Boot) et la plate-forme Serverless. Pour ce faire, au moment de la construction de l'application, Kotless ajoute un «répartiteur» spécial au code de l'application - un gestionnaire d'événements spécifique à la plateforme qui sert d'adaptateur entre le framework utilisé dans l'application et la plateforme cloud.
Outil
L'outil lui-même, qui comprend l'ensemble du pipeline décrit pour la création de l'infrastructure, a été implémenté en tant que plugin pour le système de construction Gradle. De plus, tous les modules principaux sont des bibliothèques séparées, ce qui simplifie considérablement le support des autres systèmes de construction.
L'utilisation du plugin est simple. Après la configuration, l'utilisateur n'a qu'une seule tâche Gradle - deploy , qui prend toutes les étapes nécessaires pour déployer l'application actuelle sur le cloud.
La personnalisation du côté utilisateur est également assez simple. Le plugin lui-même est appliqué en premier:
plugins {
io("io.kotless") version "0.1.5" apply true
}
Après cela, l'utilisateur ajoute le framework dont il a besoin:
dependencies {
//Kotless DSL
implementation("io.kotless", "lang", "0.1.5")
}
Enfin, il configure l'accès AWS afin que Kotless puisse déployer:
kotless {
config {
bucket = "kotless.s3.example.com"
terraform {
profile = "example"
region = "us-east-1"
}
}
}
Lancement local
Il est facile de voir que le dernier point nécessite que l'utilisateur se familiarise avec AWS et possède au moins un compte AWS. Ces exigences ont effrayé les utilisateurs qui voulaient d'abord essayer localement si l'outil leur convenait.
C'est pourquoi Kotless prend en charge le mode de lancement local. En utilisant les fonctionnalités standard du framework choisi (Ktor, Spring Boot et Kotless DSL, bien sûr, peuvent exécuter des applications localement), Kotless déploie l'application sur la machine de l'utilisateur.
De plus, Kotless peut exécuter l'émulation AWS (utilisée par LocalStack ) afin que l'utilisateur puisse vérifier localement que l'application se comporte comme prévu.
La poursuite du développement
En écrivant Kotless (et avec lui ma thèse de master), j'ai réussi à le présenter à ASE 2019, KotlinConf 2019 et dans le podcast Talking Kotlin. En général, l'outil a été accueilli favorablement, bien qu'à la fin de 2019, il ne semblait pas être une telle nouveauté (à ce moment-là, Zappa, Claudia.js et AWS Chalice étaient devenus populaires).
Cependant, pour le moment, Kotless est probablement l'outil le plus célèbre de sa catégorie dans le monde Kotlin, et je prévois certainement de le développer.
Dans un proche avenir, je prévois de stabiliser l'API et les fonctionnalités actuelles, de préparer des tutoriels et des projets de démonstration afin de faciliter l'apprentissage de l'outil pour les nouveaux utilisateurs.
Par exemple, nous prévoyons de préparer un ensemble de didacticiels sur la création de robots de discussion à l'aide de Kotless. Il semble que les technologies Serverless soient idéales pour ce cas d'utilisation (et les utilisateurs de Kotless écrivent déjà des bots Telegram), mais le manque d'outils appropriés entrave considérablement l'utilisation généralisée.
Enfin, l'un des aspects les plus importants de toute l'architecture de l'outil est l'indépendance de sa plateforme. Dans un avenir pas trop lointain, j'espère prendre en charge Google Cloud Platform et Microsoft Azure, permettant aux applications de passer du cloud au cloud avec littéralement un seul bouton.
J'espère que Kotless et des outils similaires aideront vraiment l'introduction des technologies Serverless aux masses et que de plus en plus d'applications ne consommeront des ressources que lorsqu'elles sont en cours d'exécution, réduisant légèrement l'entropie de l'univers :)