Durante esta semana estou participando de um evento interno na Microsoft em Seattle. Trata-se de um hackaton exploratório de de tecnologias para a nuvem, sempre visando prover feedbacks (de produtos já prontos para uso) para os times de produtos ou ainda, propor novos recursos ou a utilização de novas tecnologias associadas a estes produtos. E fazemos isso codificando lado a lado com os engenheiros da Microsoft que ajudam na construção desses produtos. Um evento muito bacana!
Como é comum em hackatons, fomos divididos em grupos de trabalho e no grupo que estou tendo (ou tive, dependendo de quando você ler este texto) a oportunidade de trabalhar, fomos (estamos sendo) desafiados a entender, testar e propor novas implementações para uma tecnologia open source e relativamente nova que possibilita a execução de código sob demanda (serverless) sobre clusters de containers gerenciados por Kubernetes – o Kubeless.
Escrevi um texto bem completo sobre serverless aqui mesmo no site há algumas semanas e portanto, não quero novamente explorar os conceitos associados. Se este tema é novidade pra você, recomendo fortemente efetuar a leitura do texto ao qual me refiro. Ele está disponível aqui. Também aqui no site falei bastante (e ainda pretendo falar muito mais no futuro) sobre Kubernetes e de igual forma, não vou me ater a estes conceitos aqui. A série de posts sobre Kubernetes pode ser encontrada aqui.
Kubeless
Gosto muito de analogias e por isso mesmo vou recorrer a uma para explicar de maneira direta o conceito do Kubeless. De maneira simplificada, podemos dizer que o Kubeless está para um cluster Kubernetes assim como o Azure Functions ou o AWS Lambda está para um cluster de máquinas virtuais. Ou seja, trata-se de uma tecnologia de execução de código sob demanda que utiliza a característica de gerenciamento de clusters de containers do Kubernetes para prover um recurso escalável e resiliente para “containerless”. Bacana, não?
O projeto é bem bacana e vem ganhando bastante visibilidade por várias razões. A principal delas é que ele possibilita, dentre outras coisas, executar código sob demanda (serverless ou containerless) em qualquer infraestrutura de cluster gerenciada pelo Kubernetes, seja na nuvem pública ou na nuvem privada. Como Kubernetes tem a capacidade de auto escalar e balancear carga entre containers, o potencial de execução é bem parecido com o de virtual machines convencionais (modelo implementado por Azure Functions e AWS Lambda, por exemplo).
Outro aspecto que tem feito com que este projeto evolua é a flexibilidade para incorporar novas tecnologias. Atualmente, são suportados trechos de código nas seguintes linguagens: Python, Node.js e Ruby, entretanto, nada impede que novas linguagens sejam suportadas. É justamente nessa premissa que nosso time está se baseando para implementar o suporte para .NET Core.
Como o Kubeless funciona?
Kubeless é uma extensão do Kubernetes e portanto, é nativo (esta é outra razão que tem feito com que o projeto cresça em popularidade, diga-se de passagem). Não se trata de workaround. Trata-se de um recurso nativo, que executa sobre o fluxo nativo do Kubernetes. Para que esta implementação seja possível o Kubeless utiliza um mecanismo conhecido como Custom Resource Definitions (CRD).
O link traz todos os detalhes desta configuração mas, em linhas gerais, trata-se da adição de um controller adicional dentro de um namespace próprio dentro do Kubernetes. Este controller será o responsável por realizar o deploy de functions como recursos nativos no cluster Kubernetes. Basicamente o que o Kubeless faz é para criar o CRD é:
- Definir um manifesto (em formato yaml) com todas as configurações do controller;
- Utilizando “kubectl create”, ele executa o arquivo de manifesto;
- Ao fazer isso, a API do Kubernetes devolve um endpoint para o consumo do serviço criado;
No caso do Kubeless, controller que é criado internamente tem o nome “function.k8s.io”, como é possível visualizar na Figura 1, a seguir. Esta visão pode ser obtida através da UI de administração do Kubernetes na guia “Third Part Services”.
Figura 1. Custom Resource Definition do Kubeless
A listagem 1 a seguir apresenta o arquivo de manifesto que cria o CRD no cluster Kubernetes. Todo comportamento do CRD no cluster também é definido neste arquivo, incluindo containers de apoio (no caso do Kubeless são dois serviços de apoio: Kafka e ZoomKeeper) para o correto funcionamento do mesmo.
Listagem 1. O arquivo de manifesto do Kubeless que realiza o deploy do CRD no Kubernetes
Recomendo fortemente realizar uma leitura criteriosa do yaml acima. Ao fazê-lo você entenderá vários aspectos funcionais importantes sobre o Kubeless: dependências, containers de apoio, variáveis de ambiente, etc. Além disso, você será capaz de entender também que o Kubeless é dividido basicamente em duas soluções: “kubeless” e “kubeless-controller”. Por isso mesmo dizemos que, uma vez que o CRD esteja de pé no cluster, estará de pé também o “backend” do Kubeless.
“Kubeless” vs “Kubeless-Controller”
Conforme mencionado anteriormente, Kubeless é uma solução que se divide em duas outras. Elas dividem as operações entre o que acontece no cliente (isto é, a origem das requisições para rodar no cluster Kubernetes) e o que acontece dentro do cluster Kubernetes quando a requisição chega. O que acontece no cliente é gerenciado pela aplicação chamada “Kubeless”. O que acontece do lado do cluster é gerenciado por uma aplicação chamada “Kubeless-Controller”.
Kubeless: tem a função de comunicar (através de CLI) o cliente com a API do Kubernetes e também com o “Kubeless-Controller”. Ele está para o Kubeless-Controller como o kubectl está para o Kubernetes. É função dele, por exemplo, atuar como interface de registro, junto ao cluster, de uma function que contém o código para ser executado sob demanda. Poderíamos fazer isso, por exemplo, executando a linha de comandos apresentada pela Listagem 2. Também será responsabilidade do kubeless enviar o código que deverá ser executado.
Listagem 2. Registrando uma function que deverá ser executada sob demanda no futuro
Kubeless-Controller: responsável por articular as ações do lado do cluster. O controller é quem recebe as solicitações do cliente (via kubeless) e faz o despacho das mesmas junto aos recursos já disponibilizados pelo CRD. É função do controller, por exemplo, receber o código que precisa ser executado sob demanda (seja por demanda HTTP simples ou agendamento) e realizar o bind da mesma na function anteriormente registrada no cluster.
Existe ainda uma interface web que facilita a comunicação com o kubeless-controller. Digamos que é uma interface que pode substituir o kubeless CLI (cliente), mencionado há pouco. Para saber mais sobre, por favor, siga este link.
Como utilizar?
Agora que você já possui uma visão geral sobre o projeto, vamos falar um pouco sobre como utilizar. Nem preciso dizer que, para que seja possível utilizar Kubeless, você precisa ter um cluster Kubernetes em pleno funcionamento. Se você não o possui ainda, quero dar duas sugestões para que você possa ter um. Veja:
- Criar o cluster Kubernetes em sua própria máquina. Fiz um post há algum tempinho explicando como fazer isso utilizando um componente chamado “minikube”. Você pode encontrar este conteúdo seguindo este link. Minikube exige que o processador possua a capacidade de nested-virtualization. Atente para isso antes de iniciar o processo.
- Criar uma estrutura de containers já com o Kubernetes no Azure através do serviço Azure Container Services (ACS). Você pode encontrar um bom caminho para fazer isso através deste link.
Uma vez que você já possua seu cluster “up and running”, poderá então iniciar o processo de instalação/configuração do Kubeless. Para este exemplo vou utilizar o Kubeless CLI, mencionado anteriormente neste texto.
Instalando o Kubeless no cluster Kubernetes (ver Listagem 3).
Listagem 3. Instalando o Kubeless-Controller
Entendendo a instalação:
- Linha 1. Adicionamos a uma variável (RELEASE) de ambiente a versão do release do arquivo de manifesto (yaml) que irá criar o CRD no cluster Kubernetes.
- Linha 2. Criamos um namespace específico para que o Kubernetes possa organizar os recursos específicos do Kubeless dentro do cluster. O nome? “kubeless”, claro. Você não pode mudar este nome, uma vez que o “Kuberless-Controller” o utiliza de maneira estática no código diversas vezes. Se o conceito de namespaces no Kubernetes é novo para você, recomendo fortemente realizar a leitua do texto disponível aqui.
- Linha 3. Criamos, de fato, o CRD para rodar no cluster. Note que estamos utilizando a variável de ambiente exportada na linha 1 para completar o nome do arquivo de manifesto. Ao final da execução desta linha, toda a infra de backend para o kubeless estará criada no cluster, incluindo a instalação do Kubeless-Controller.
A próxima etapa consiste na instalação do Kubeless CLI (client). Você pode fazer o processo de instalação manualmente (baixando o fonte em Go e compilando localmente na sua máquina), entretanto, existe uma versão já compilada e pronta para ser instalada no cliente, disponibilizada pela Bitnami (que é quem iniciou e ajuda a manter o projeto Kubeless). A listagem 4 apresenta os três passos para a instalação.
Listagem 4. Instalando o CLI do Kubeless
É fácil notar que o que estamos fazendo é: baixar o fonte, “deszipar” o pacote baixado e após isso, mover o executável para a pasta “/bin” correta. Estou executando este processo no Mac OS. Para ver o processo para outros SOs, por favor, siga este link.
Pronto. Agora que temos tanto o backend quanto o frontend prontos para a execução, podemos testar a execução de uma função simples em Python. Para que seja possível de fato executar algum código sob demanda, você precisará executar duas etapas: 1) Registrar a function como um recurso no Kubernetes; 2) Passar o arquivo com o código Python para ser executado pela função já provisionada.
Veja uma maneira de registrar uma function como um recurso junto ao Kubernetes através da listagem 5.
Listagem 5. Registrando uma function junto ao cluster
O que estamos fazendo é:
- Criar um deploy de uma function chamada “post-python-fabricio”. Na prática, uma function será um Pod com o nome “post-python-fabricio”;
- Informamos o runtime (para que o Kubeless-Controller consiga provisionar o container com a imagem/runtime correta);
- No handler informamos “nome_do_arquivo”.”nome_da_função_python”. Nesse caso, “test-py.foobar”. Neste caso, “foobar” é uma function dentro do arquivo .py.
- Com a diretiva “–from-file” informamos o arquivo onde a function está implementada e o enviamos para a execução. Nesse caso, o arquivo “test-py.py”;
- Com a diretiva “–trigger-http” informamos ao controller que esta function será executada sob demanda com base em requisições http simples. Outra opção seria agendamento.
O código Python do arquivo “test-py.py” é aquele apresentado pela Listagem 6.
Listagem 6. A function que será executada
Para verificar se o processo funcionou corretamente, podemos recorrer ao dashboard do cluster Kubernetes, ou ainda, via kubectl. As Figuras 2 e 3 mostram o sucesso do deployment.
Figura 2. Deployment realizado com sucesso
Figura 3. Sucesso na provisão do Pod para rodar o curso
Pronto. Agora que temos os recursos provisionados, podemos executar nossa function. O snippet de código apresentado pela Listagem 7 mostra como executar de fato uma função, utilizando os recursos já registrados anteriormente.
Listagem 7. Executando a função de exemplo
O que estamos fazendo:
- Chamando a função “post-python-fabricio” já provisionada anteriormente;
- Passando um objeto json simples para ser processado pela function;
Ao fazer isso, se tudo correu bem, você verá o resultado do processamento da função, conforme apresenta a Figura 4.
Figura 4. Exibindo o resultado da execução da function de exemplo
Bacana, não? O que estamos fazendo aqui no hackaton agora é trabalhar para adicionar o suporte a .NET Core 2.0 ao Kubeless. Estamos bem próximos de conseguir. Assim que isso for feito, farei um novo post detalhando este processo.
É isso. Enjoy Kubeless!
Facebook
Twitter
Instagram
LinkedIn
RSS