Meu nome é Vladislav Tankov, em 2018-2020 estudei no programa de mestrado corporativo da JetBrains na ITMO, e desde 2017 trabalho na JetBrains .
No verão de 2018, no hackathon JetBrains, vários de meus colegas e eu tentamos fazer uma ferramenta para a linguagem Kotlin que simplifica a criação de aplicativos sem servidor analisando o código do aplicativo.
Após o hackathon, já no âmbito do trabalho científico do programa de mestrado corporativo da JetBrains, decidi continuar o desenvolvimento deste projeto. Ao longo de dois anos, a ferramenta expandiu significativamente e adquiriu funcionalidade, mas manteve seu nome - Kotless ou Kotlin Serverless Framework.
O que é sem servidor
Primeiro, vamos lembrar em que consiste a plataforma de computação sem servidor mais simples. Essa plataforma inclui três componentes principais:
- o sistema de execução de funções sem servidor - pequenos aplicativos que processam determinados eventos;
- um conjunto de interfaces diferentes do mundo externo (ou uma plataforma em nuvem, como AWS) para o sistema de eventos da plataforma, como uma interface HTTP;
- o próprio sistema de eventos, que fornece a transferência de eventos de interfaces para funções e resultados de processamento de funções para interfaces.
Esses três componentes são suficientes para construir um aplicativo bastante complexo. Por exemplo, um aplicativo da Web é apenas uma interface HTTP externa (no caso da AWS, será APIGateway ) e para cada recurso processado (como / route / my ) sua própria função de manipulador sem servidor. Você pode construir um aplicativo mais complexo que usa bancos de dados e chama outras funções sem servidor, como na imagem.
Bem, você pode construir tais aplicativos, mas por quê?
Os aplicativos sem servidor têm várias vantagens atraentes que justificam a ocupação da arquitetura.
- As funções sem servidor não funcionam quando não são necessárias. Na verdade, a função apenas processa eventos - por que deveria consumir recursos de computação se não há eventos?
- As funções sem servidor podem tratar eventos do mesmo tipo em paralelo. Ou seja, se / route / my se tornou muito popular e mil usuários o solicitaram de uma vez, então a plataforma sem servidor pode simplesmente iniciar 1000 manipuladores, um por evento.
Juntos, esses pontos somam talvez um dos mantras mais importantes sem servidor: o aplicativo sem servidor escala de zero a infinito. Esse aplicativo não gasta dinheiro quando não está em demanda e é capaz de processar milhares de solicitações por segundo, quando necessário.
Problema
Vamos dar uma olhada em um exemplo muito simples na linguagem Kotlin:
@Get("/my/route")
fun handler() = "Hello World"
É bastante óbvio que tal aplicativo pode ser implementado usando a abordagem sem servidor. À primeira vista, é suficiente criar uma interface HTTP com algum endereço DNS e mapear / my / route para o manipulador divertido () .
Na verdade, a criação de tal aplicativo exigiria muito mais do que adicionar uma única anotação. Por exemplo, no caso da AWS:
- Você precisará implementar um manipulador de interface para um evento específico, neste caso o RequestStreamHandler.
- Você precisará descrever a infraestrutura da aplicação sem servidor: descrever a API HTTP da aplicação, descrever todas as funções do manipulador e associar suas funções à interface, escolhendo cuidadosamente as permissões.
- Finalmente, você terá que coletar todas as funções do manipulador, carregar na plataforma sem servidor e implantar a infraestrutura apropriada.
Não existem poucos passos para uma aplicação tão simples, certo?
Para aqueles que são iniciados no mistério da infraestrutura como código, observarei que, é claro, parte do processo pode ser automatizado, mas essa automação em si exige o aprendizado de uma abordagem completamente nova (na verdade, descrever a infraestrutura como código) e uma nova linguagem. Esta parece ser uma tarefa desnecessariamente difícil para um desenvolvedor que deseja implantar um aplicativo rudimentar.
É possível fazer algo mais fácil? Em alguns casos (e especificamente neste) - sim!
Infraestrutura em código
Vejamos o outro lado: em vez de forçar o usuário a descrever a infraestrutura, tentaremos derivá-la do código de usuário já escrito.
Considere o mesmo exemplo novamente:
@Get("/my/route")
fun handler() = "Hello World"
Sabemos que o usuário deseja que as solicitações para / my / route sejam tratadas por esta função - então vamos sintetizar uma infraestrutura que criará uma API HTTP com / my / route , criará a função sem servidor necessária e fará toda a mágica necessária para conectá-los!
Em meu artigo na Automated Software Engineering 2019, chamei essa abordagem de Infraestrutura no Código. Na verdade, extraímos a descrição da infraestrutura do código do aplicativo que a define implicitamente, ou seja, ela está realmente contida "dentro" do código.
Deve-se notar que doravante, apenas a síntese de aplicativos HTTP API é considerada. Uma abordagem semelhante pode ser usada para processar filas e para processar eventos na plataforma de nuvem, mas isso é uma questão de desenvolvimento posterior do Kotless.
Implementação
Esperançosamente, neste ponto, a ideia está clara e restam três questões principais:
- Como extrair informações do código?
- Como criar uma infraestrutura com base nessas informações?
- Como executo um aplicativo na nuvem?
Análise
O Kotlin Compiler Embeddable nos ajudará com isso.
Apesar de o exemplo ser sobre anotações, na realidade, a API HTTP da aplicação, dependendo da biblioteca utilizada, pode ser definida de formas completamente diferentes, por exemplo:
//ktor-like style
get("my-route") {
"Hello World"
}
Para analisar código arbitrário, o Kotlin Compiler Embeddable acabou sendo mais familiar e conveniente (devido ao grande número de exemplos).
No momento, Kotless pode analisar três frameworks principais:
- Kotless DSL - framework de anotação próprio do Kotless
- Spring Boot é uma estrutura da Web popular, as anotações são analisadas;
- Ktor é um framework Kotlin da Web popular, as funções de extensão são analisadas.
No processo de análise do código, o Esquema Kotless é coletado - esta é uma representação independente de plataforma do aplicativo sem servidor. É usado para sintetizar a infraestrutura e tornar o processo de análise independente de uma plataforma de nuvem específica.
Síntese
Vamos sintetizar o código do Terraform. Terraform foi selecionada como uma das ferramentas de infraestrutura como código mais populares com uma ampla gama de plataformas de nuvem com suporte, o que garante que a Kotless seja capaz de oferecer suporte a novas plataformas de nuvem e estabilidade na implantação de aplicativos.
A síntese é feita a partir do Esquema Kotless, que contém uma descrição da API HTTP do aplicativo e suas funções, bem como alguns dados adicionais (por exemplo, o nome DNS desejado).
Para a síntese em si, uma biblioteca Terraform DSL criada especialmente é usada. O código de síntese se parece com isto:
val resource = api_gateway_rest_api("tf_name") {
name = "aws_name"
binary_media_types = arrayOf(MimeType.PNG)
}
O DSL garante a formatação e integridade referencial entre os diferentes recursos Terraform, o que simplifica muito a expansão do conjunto de recursos sintetizados.
O código sintetizado é implantado na plataforma de nuvem com um aplicativo Terraform simples.
Corrida
Resta executar o aplicativo na plataforma sem servidor. Como já mencionado, todas as funções sem servidor são essencialmente manipuladores para alguns eventos, em nosso caso, solicitações HTTP.
É necessário conectar a estrutura com a qual o aplicativo é criado (por exemplo, Spring Boot) e a plataforma sem servidor. Para fazer isso, no momento da construção do aplicativo, Kotless adiciona um "dispatcher" especial ao código do aplicativo - um manipulador de eventos específico da plataforma que serve como um adaptador entre a estrutura usada no aplicativo e a plataforma em nuvem.
Ferramenta
A própria ferramenta, que inclui todo o pipeline descrito para a criação da infraestrutura, foi implementada como um plugin para o sistema de compilação do Gradle. Além disso, todos os módulos principais são bibliotecas separadas, o que simplifica muito o suporte de outros sistemas de construção.
Usar o plug-in é simples. Após a configuração, o usuário tem apenas uma tarefa do Gradle - implantar , que executa todas as etapas necessárias para implantar o aplicativo atual na nuvem.
A personalização do lado do usuário também é bastante direta. O próprio plugin é aplicado primeiro:
plugins {
io("io.kotless") version "0.1.5" apply true
}
Depois disso, o usuário adiciona a estrutura de que precisa:
dependencies {
//Kotless DSL
implementation("io.kotless", "lang", "0.1.5")
}
Por fim, ele configura o acesso à AWS para que Kotless possa implantar:
kotless {
config {
bucket = "kotless.s3.example.com"
terraform {
profile = "example"
region = "us-east-1"
}
}
}
Lançamento local
É fácil ver que o último ponto requer que o usuário esteja familiarizado com a AWS e tenha pelo menos uma conta da AWS. Esses requisitos assustaram os usuários que queriam primeiro tentar localmente se a ferramenta fosse certa para eles.
É por isso que Kotless suporta o modo de inicialização local. Usando os recursos padrão da estrutura escolhida (Ktor, Spring Boot e Kotless DSL, é claro, podem executar aplicativos localmente), o Kotless implanta o aplicativo na máquina do usuário.
Além disso, o Kotless pode executar a emulação AWS (usada pelo LocalStack ) para que o usuário possa verificar localmente se o aplicativo está se comportando conforme o esperado.
Desenvolvimento adicional
Enquanto escrevia Kotless (e com ele minha tese de mestrado), consegui apresentá-lo no ASE 2019, KotlinConf 2019 e no podcast Talking Kotlin. Em geral, a ferramenta foi recebida favoravelmente, embora no final de 2019 não parecesse mais uma novidade (naquela época, Zappa, Claudia.js e AWS Chalice já haviam se tornado populares).
No entanto, no momento, Kotless é talvez a ferramenta mais famosa de sua classe no mundo Kotlin, e certamente pretendo desenvolvê-la.
Em um futuro próximo, pretendo estabilizar a API e a funcionalidade atuais, preparar tutoriais e projetos de demonstração para tornar o aprendizado da ferramenta mais fácil para novos usuários.
Por exemplo, planejamos preparar um conjunto de tutoriais sobre como criar bots de bate-papo usando Kotless. Parece que as tecnologias sem servidor são ótimas para esse caso de uso (e os usuários do Kotless já estão escrevendo bots do Telegram), mas a falta de ferramentas adequadas dificulta significativamente o uso generalizado.
Finalmente, um dos aspectos mais importantes de toda a arquitetura da ferramenta é sua independência de plataforma. Em um futuro não muito distante, espero oferecer suporte ao Google Cloud Platform e ao Microsoft Azure, que permitirão que os aplicativos sejam movidos de nuvem para nuvem com literalmente um único botão.
Eu gostaria de esperar que Kotless e ferramentas similares realmente ajudem a introdução de tecnologias sem servidor para as massas e mais e mais aplicativos consumam recursos apenas quando estão em execução, reduzindo ligeiramente a entropia do universo :)