Mi nombre es Vladislav Tankov, en 2018-2020 estudié en el programa de maestría corporativa de JetBrains en ITMO, y desde 2017 trabajo en JetBrains .
En el verano de 2018, en el hackathon de JetBrains, varios de mis colegas y yo intentamos hacer una herramienta para el lenguaje Kotlin que simplifica la creación de aplicaciones sin servidor analizando el código de la aplicación.
Después del hackathon, ya en el marco del trabajo científico en el programa de maestría corporativa de JetBrains, decidí continuar con el desarrollo de este proyecto. En dos años, la herramienta se ha expandido significativamente y ha adquirido funcionalidad, pero ha conservado su nombre: Kotless o Kotlin Serverless Framework.
¿Qué es Serverless?
Primero, recordemos en qué consiste la plataforma informática sin servidor más simple. Esta plataforma incluye tres componentes principales:
- el sistema de ejecución de funciones sin servidor: pequeñas aplicaciones que procesan ciertos eventos;
- un conjunto de diferentes interfaces del mundo exterior (o una plataforma en la nube como AWS) al sistema de eventos de la plataforma, como una interfaz HTTP;
- el sistema de eventos en sí, que proporciona la transferencia de eventos de interfaces a funciones y los resultados de procesamiento de funciones a interfaces.
Estos tres componentes son suficientes para construir una aplicación bastante compleja. Por ejemplo, una aplicación web es solo una interfaz HTTP externa (en el caso de AWS, será APIGateway ) y para cada recurso procesado (como / route / my ) su propia función de controlador sin servidor. Puede crear una aplicación más compleja que use bases de datos y que ella misma llame a otras funciones sin servidor, como en la imagen.
Está bien, puede crear esas aplicaciones, pero ¿por qué?
Las aplicaciones sin servidor tienen varias ventajas convincentes que justifican la arquitectura okupa.
- Las funciones sin servidor no funcionan cuando no son necesarias. De hecho, la función solo procesa eventos: ¿por qué debería ocupar recursos informáticos si no hay eventos?
- Las funciones sin servidor pueden manejar eventos del mismo tipo en paralelo. Es decir, si / route / my se ha vuelto muy popular y mil usuarios lo han solicitado a la vez, entonces la plataforma Serverless puede simplemente lanzar 1000 controladores, uno por evento.
Juntos, estos puntos se suman a quizás uno de los mantras sin servidor más importantes: la aplicación sin servidor escala de cero a infinito. Una aplicación de este tipo no gasta dinero cuando no está en demanda y es capaz de procesar miles de solicitudes por segundo cuando es necesario.
Problema
Echemos un vistazo a un ejemplo muy simple en lenguaje Kotlin:
@Get("/my/route")
fun handler() = "Hello World"
Es bastante obvio que una aplicación de este tipo se puede implementar utilizando el enfoque sin servidor. A primera vista, es suficiente crear una interfaz HTTP con alguna dirección DNS y mapear / my / route al manejador divertido () .
De hecho, crear una aplicación de este tipo requeriría mucho más que agregar una sola anotación. Por ejemplo, en el caso de AWS:
- Deberá implementar una interfaz de controlador de eventos específica, en este caso RequestStreamHandler.
- Deberá describir la infraestructura de la aplicación sin servidor: describir la API HTTP de la aplicación, describir todas las funciones del controlador y asociar sus funciones con la interfaz, eligiendo cuidadosamente los permisos.
- Finalmente, deberá recopilar todas las funciones del controlador, cargarlas en la plataforma Serverless e implementar la infraestructura adecuada.
No hay tan pocos pasos para una aplicación tan sencilla, ¿verdad?
Para aquellos iniciados en el sacramento de la Infraestructura como Código, señalaré que, por supuesto, parte del proceso puede automatizarse, pero esta automatización en sí misma requiere el estudio de un enfoque completamente nuevo (de hecho, describiendo la infraestructura como un código) y un nuevo lenguaje. Esto parece una tarea innecesariamente difícil para un desarrollador que desea implementar una aplicación rudimentaria.
¿Es posible hacer algo más fácil? En algunos casos (y específicamente en este), ¡sí!
Infraestructura en código
Veamos el otro lado: en lugar de obligar al usuario a describir la infraestructura, intentaremos derivarla del código de usuario ya escrito.
Considere el mismo ejemplo nuevamente:
@Get("/my/route")
fun handler() = "Hello World"
Sabemos que el usuario quiere que las solicitudes a / my / route sean manejadas por esta función, así que sinteticemos una infraestructura que creará una API HTTP con / my / route , creemos la función sin servidor requerida y hagamos toda la magia necesaria para conectarlos.
En mi artículo en Automated Software Engineering 2019, llamé a este enfoque Infraestructura en código. De hecho, extraemos la descripción de la infraestructura del código de la aplicación que la define implícitamente, es decir, realmente está contenida "dentro" del código.
Cabe señalar que en lo sucesivo, solo se considera la síntesis de aplicaciones HTTP API. Se puede utilizar un enfoque similar para procesar colas y para procesar eventos en la plataforma en la nube, pero esto es una cuestión de desarrollo adicional de Kotless.
Implementación
Ojalá en este punto la idea sea clara y queden tres preguntas principales:
- ¿Cómo extraer información del código?
- ¿Cómo crear una infraestructura basada en esta información?
- ¿Cómo ejecuto una aplicación en la nube?
Análisis
El compilador Kotlin Embeddable nos ayudará con esto.
A pesar de que el ejemplo trata sobre anotaciones, en realidad, la API HTTP de la aplicación, dependiendo de la biblioteca utilizada, se puede definir de formas completamente diferentes, por ejemplo:
//ktor-like style
get("my-route") {
"Hello World"
}
Para analizar código arbitrario, Kotlin Compiler Embeddable resultó ser más familiar y más conveniente (debido a la gran cantidad de ejemplos).
Por el momento, Kotless puede analizar tres marcos principales:
- Kotless DSL: el propio marco de anotaciones de Kotless
- Spring Boot es un marco web popular, las anotaciones se analizan;
- Ktor es un marco web Kotlin popular, se analizan las funciones de extensión.
En el proceso de análisis del código, se recopila el esquema Kotless: esta es una representación independiente de la plataforma de la aplicación Serverless. Se utiliza para sintetizar la infraestructura y hace que el proceso de análisis sea independiente de una plataforma en la nube específica.
Síntesis
Sintetizaremos el código de Terraform. Terraform fue seleccionada como una de las herramientas de Infraestructura como Código más populares con una amplia gama de plataformas en la nube compatibles, lo que garantiza que Kotless pueda admitir nuevas plataformas en la nube y estabilidad en la implementación de aplicaciones.
La síntesis se realiza a partir de Kotless Schema, que contiene una descripción de la API HTTP de la aplicación y sus funciones, así como algunos datos adicionales (por ejemplo, el nombre DNS deseado).
Para la síntesis en sí, se utiliza una biblioteca Terraform DSL especialmente creada. El código de síntesis se parece a esto:
val resource = api_gateway_rest_api("tf_name") {
name = "aws_name"
binary_media_types = arrayOf(MimeType.PNG)
}
DSL garantiza el formato y la integridad referencial entre diferentes recursos de Terraform, lo que simplifica enormemente la expansión del conjunto de recursos sintetizados.
El código sintetizado se implementa en la plataforma en la nube con una aplicación Terraform simple.
Corriendo
Queda por ejecutar la aplicación en la plataforma Serverless. Como ya se mencionó, todas las funciones sin servidor son esencialmente controladores para algunos eventos, en nuestro caso, solicitudes HTTP.
Es necesario conectar el framework con el que se crea la aplicación (por ejemplo, Spring Boot) y la plataforma Serverless. Para hacer esto, al momento de construir la aplicación, Kotless agrega un "despachador" especial al código de la aplicación - un manejador de eventos específico de la plataforma que sirve como un adaptador entre el marco usado en la aplicación y la plataforma en la nube.
Herramienta
La herramienta en sí, que incluye todo el proceso descrito para crear la infraestructura, se implementó como un complemento para el sistema de compilación de Gradle. Además, todos los módulos principales son bibliotecas independientes, lo que simplifica enormemente el soporte de otros sistemas de compilación.
Usar el complemento es sencillo. Después de la configuración, el usuario solo tiene una tarea de Gradle: implementar , que toma todos los pasos necesarios para implementar la aplicación actual en la nube.
La personalización desde el lado del usuario también es bastante sencilla. El complemento en sí se aplica primero:
plugins {
io("io.kotless") version "0.1.5" apply true
}
Después de eso, el usuario agrega el marco que necesita:
dependencies {
//Kotless DSL
implementation("io.kotless", "lang", "0.1.5")
}
Finalmente, configura el acceso a AWS para que Kotless pueda implementar:
kotless {
config {
bucket = "kotless.s3.example.com"
terraform {
profile = "example"
region = "us-east-1"
}
}
}
Lanzamiento local
Es fácil ver que el último punto requiere que el usuario esté familiarizado con AWS y tenga al menos una cuenta de AWS. Estos requisitos asustaban a los usuarios que querían probar primero localmente si la herramienta era adecuada para ellos.
Es por eso que Kotless admite el modo de lanzamiento local. Usando las características estándar del marco elegido (tanto Ktor, Spring Boot y Kotless DSL, por supuesto, pueden ejecutar aplicaciones localmente), Kotless implementa la aplicación en la máquina del usuario.
Además, Kotless puede ejecutar la emulación de AWS (utilizada por LocalStack ) para que el usuario pueda verificar localmente que la aplicación se está comportando como se esperaba.
Mayor desarrollo
Mientras escribía Kotless (y con ella mi tesis de maestría), logré presentarlo en ASE 2019, KotlinConf 2019 y en el podcast Talking Kotlin. En general, la herramienta fue recibida favorablemente, aunque a finales de 2019 ya no parecía una novedad tan grande (para ese momento, Zappa, Claudia.js y AWS Chalice se habían vuelto populares).
Sin embargo, en este momento, Kotless es quizás la herramienta más conocida de su clase en el mundo de Kotlin, y ciertamente planeo desarrollarla.
En un futuro próximo, planeo estabilizar la API y la funcionalidad actuales, preparar tutoriales y proyectos de demostración para facilitar el aprendizaje de la herramienta para los nuevos usuarios.
Por ejemplo, planeamos preparar un conjunto de tutoriales sobre cómo crear bots de chat usando Kotless. Parece que las tecnologías sin servidor son excelentes para este caso de uso (y los usuarios de Kotless ya están escribiendo bots de Telegram), pero la falta de herramientas adecuadas dificulta significativamente su uso generalizado.
Finalmente, uno de los aspectos más importantes de toda la arquitectura de la herramienta es su independencia de plataforma. En un futuro no muy lejano, espero ser compatible con Google Cloud Platform y Microsoft Azure, lo que permitirá que las aplicaciones se muevan de una nube a otra con literalmente un solo botón.
Me gustaría esperar que Kotless y herramientas similares realmente ayuden a la introducción de tecnologías Serverless a las masas y cada vez más aplicaciones consuman recursos solo cuando se están ejecutando, reduciendo ligeramente la entropía del universo :)