我叫弗拉迪斯拉夫·坦科夫(Vladislav Tankov),在2018-2020年,我在ITMO的JetBrains公司硕士课程学习,自2017年以来,我一直在JetBrains工作。
在2018年夏天,在JetBrains黑客马拉松上,我和我的一些同事试图为Kotlin语言制作一个工具,该工具可以通过分析应用程序代码来简化无服务器应用程序的创建。
黑客马拉松之后,我已经在JetBrains公司硕士课程的科学工作框架内,我决定继续开发该项目。在过去的两年中,该工具已大大扩展并获得了功能,但保留了其名称-Kotless或Kotlin Serverless Framework。
什么是无服务器
首先,让我们记住最简单的无服务器计算平台的组成。该平台包括三个主要组件:
- 无服务器功能的执行系统-处理某些事件的小型应用程序;
- 从外部世界(或云平台,例如AWS)到平台的事件系统的一组不同接口,例如HTTP接口;
- 事件系统本身,它提供事件从接口到函数的传递以及处理结果从函数到接口的传递。
这三个组件足以构建一个相当复杂的应用程序。例如,一个Web应用程序只是一个外部HTTP接口(在AWS中,它将是APIGateway),对于处理的每个资源(如/ route / my),它都有自己的Serverless处理函数。您可以构建一个更复杂的应用程序,该应用程序使用数据库并且本身调用其他无服务器功能,如图所示。
好的,您可以构建此类应用程序,但是为什么呢?
无服务器应用程序具有许多不可否认的优势,这些优势证明了架构的合理性。
- 无服务器功能在不需要时不起作用。确实,该功能仅处理事件-如果没有事件,为什么要占用计算资源?
- 无服务器功能可以并行处理相同类型的事件。也就是说,如果/ route / my变得非常流行,并且一千个用户一次请求它,那么无服务器平台可以简单地启动1000个处理程序,每个事件一个。
这些点加起来也许是最重要的无服务器的口头禅之一:无服务器应用程序从零扩展到无穷大。这样的应用程序在不需要时不会花钱,并且能够在需要时每秒处理数千个请求。
问题
让我们看一下Kotlin语言中的一个非常简单的示例:
@Get("/my/route")
fun handler() = "Hello World"
很明显,可以使用无服务器方法来实现这样的应用程序。乍一看,创建一个带有一些DNS地址的HTTP接口并将map / my / route映射到fun处理程序()就足够了。
实际上,创建这样的应用程序将比添加单个注释花费更多。例如,对于AWS:
- 您将需要实现特定的事件处理程序接口,在这种情况下为RequestStreamHandler。
- 您将需要描述无服务器应用程序的基础结构:描述应用程序的HTTP API,描述所有处理程序功能,并将它们的功能与接口相关联,仔细选择权限。
- 最后,您将必须收集所有处理程序功能,加载到无服务器平台中,并部署适当的基础结构。
这样简单的应用程序没有那么几个步骤,对吗?
对于那些开始以代码为基础的圣礼的人来说,我当然会注意到其中的一部分过程可以自动化,但是这种自动化本身需要研究一种全新的方法(实际上是将基础设施描述为代码)和一种新的语言。对于想要部署基本应用程序的开发人员来说,这似乎是不必要的困难任务。
有可能做些简单的事情吗?在某些情况下(特别是在这种情况下)-是的!
代码基础架构
让我们看另一面:我们不是试图强迫用户描述基础结构,而是尝试从已经编写的用户代码中派生它。
再次考虑相同的示例:
@Get("/my/route")
fun handler() = "Hello World"
我们知道用户希望该功能处理/ my /路由的请求-因此,让我们综合一个基础结构,该基础结构将使用/ my / route创建一个HTTP API ,创建所需的Serverless函数,并进行所有必要的魔术连接!
在自动化软件工程2019年的文章中,我将此方法称为代码中的基础架构。实际上,我们从隐式定义基础结构的应用程序代码中提取了基础结构的描述,也就是说,它实际上包含在代码“内部”。
应当注意,在下文中,仅考虑HTTP API应用程序的合成。可以使用类似的方法来处理队列和处理云平台的事件,但这是Kotless进一步开发的问题。
实作
希望在这一点上思路清晰,剩下三个主要问题:
- 如何从代码中提取信息?
- 如何基于此信息创建基础结构?
- 如何在云中运行应用程序?
分析
嵌入式Kotlin编译器将为我们提供帮助。
尽管示例实际上是关于注释的,但实际上,可以使用完全不同的方式来定义应用程序的HTTP API(取决于所使用的库),例如:
//ktor-like style
get("my-route") {
"Hello World"
}
为了分析任意代码,事实证明Kotlin编译器可嵌入性更加熟悉和方便(由于示例众多)。
目前,Kotless可以分析三个主要框架:
- Kotless DSL-Kotless自己的注释框架
- Spring Boot是一个流行的Web框架,注释被解析;
- Ktor是流行的Kotlin Web框架,分析了扩展功能。
在分析代码的过程中,将收集Kotless模式-这是无服务器应用程序的某些独立于平台的表示。它用于综合基础架构并使分析过程独立于特定的云平台。
合成
我们将合成Terraform代码。 Terraform被选为最受欢迎的“基础结构即代码”工具之一,具有广泛的受支持的云平台,这确保Kotless能够支持新的云平台并确保应用程序部署的稳定性。
综合从Kotless模式进行,该模式包含对应用程序的HTTP API及其功能的描述,以及一些其他数据(例如,所需的DNS名称)。
对于综合本身,使用了专门创建的Terraform DSL库。综合代码如下所示:
val resource = api_gateway_rest_api("tf_name") {
name = "aws_name"
binary_media_types = arrayOf(MimeType.PNG)
}
DSL保证了不同Terraform资源之间的格式设置和引用完整性,从而使扩展合成资源集变得更加容易。
使用简单的Terraform应用程序将合成的代码部署到云平台。
跑步
仍然可以在无服务器平台上运行该应用程序。如前所述,所有无服务器功能本质上都是某些事件的处理程序,在我们的例子中是HTTP请求。
必须将用于创建应用程序的框架(例如,Spring Boot)和无服务器平台连接起来。为此,在构建应用程序时,Kotless在应用程序代码中添加了一个特殊的“调度程序”-一个特定于平台的事件处理程序,充当应用程序中使用的框架和云平台之间的适配器。
工具
该工具本身(包括用于创建基础结构的整个描述管道)已作为Gradle构建系统的插件实现。而且,所有主要模块都是单独的库,这大大简化了对其他构建系统的支持。
使用插件非常简单。配置完成后,用户只有一个摇篮任务-部署,这需要所有必要的步骤来部署当前应用程序到云中。
从用户端进行定制也非常简单。插件本身首先被应用:
plugins {
io("io.kotless") version "0.1.5" apply true
}
之后,用户添加他需要的框架:
dependencies {
//Kotless DSL
implementation("io.kotless", "lang", "0.1.5")
}
最后,它设置对AWS的访问权限,以便Kotless可以部署:
kotless {
config {
bucket = "kotless.s3.example.com"
terraform {
profile = "example"
region = "us-east-1"
}
}
}
本地发布
很容易看出,最后一点要求用户熟悉AWS并至少拥有一个AWS账户。这样的要求使想要在本地尝试该工具的用户感到恐惧。
这就是为什么Kotless支持本地启动模式的原因。使用所选框架的标准功能(Ktor,Spring Boot和Kotless DSL当然都可以在本地运行应用程序),Kotless会将应用程序部署到用户的计算机上。
此外,Kotless可以运行AWS仿真(由LocalStack使用),以便用户可以在本地检查应用程序的行为是否符合预期。
进一步的发展
在编写Kotless(以及我的硕士学位论文)时,我设法在ASE 2019,KotlinConf 2019和Talking Kotlin播客中进行介绍。总体而言,该工具受到好评,尽管到2019年底它似乎不再是一种新颖性了(到那时,Zappa,Claudia.js和AWS Chalice变得很流行)。
但是,目前,Kotless可能是Kotlin世界中同类产品中最著名的工具,我当然打算开发它。
我计划在不久的将来稳定当前的API和功能,准备教程和演示项目,以使新用户更轻松地学习该工具。
例如,我们计划准备一组有关如何使用Kotless创建聊天机器人的教程。似乎无服务器技术非常适合此用例(并且Kotless用户已经在编写Telegram机器人),但是缺少合适的工具显着阻碍了广泛使用。
最后,该工具整个体系结构最重要的方面之一是其平台独立性。在不久的将来,我希望支持Google Cloud Platform和Microsoft Azure,这将使应用程序实际上可以通过一个按钮在云之间迁移。
我希望Kotless和类似的工具能够真正帮助将无服务器技术引入大众,并且越来越多的应用程序仅在运行时才会消耗资源,从而略微减少了宇宙的熵:)