APIs e Microsserviços

22 ago, 2016

Melhores práticas serverless com Lambda e API Gateway

Publicidade

Bem-vindos à última parte do nosso guia serveless. Se você perdeu os dois primeiros artigos, neste aqui eu falei um pouco sobre o que é Lambda e como criar uma função, e no segundo eu falei sobre API Gateway. Agora, vamos falar de dicas e melhores práticas que descobrimos enquanto aprendíamos sobre Lambda e API Gateway. Serei um pouco raso e modular, pois é muita informação para pouco espaço. Caso tenha interesse em saber detalhes, deixe seu comentário e, se houver demanda por mais textos, eu garanto a oferta.

Um pacote para todos dominarem

Quando iniciamos com Lambda, procuramos ferramentas para automatizar o deployment. Encontramos o JAWS, que para cada método de cada recurso criava um pacote lambda. Conforme fomos criando novos endpoints com novos métodos, vimos a quantidade de pacotes e, consequentemente, o tempo de deploy explodir. Começamos a ter dezenas de pacotes e criamos uma bagunça. Nosso amigo Alexis Henaut nos mostrou que utilizar apenas um pacote não só é mais simples, como possui vários benefícios.

Apesar de a AWS ser responsável por manter seu código funcional 24 horas, caso um pacote lambda não seja chamado durante um tempo, ele é posto para dormir. Caso você tenha centenas de pacotes, a chance de algum deles estar dormindo é grande, e isso pode adicionar alguns segundos no tempo de resposta da API como um todo. Caso existam partes do sistema de pouco uso, isso pode tornar a utilização do sistema frustrante.

Conseguimos resolver esse problema criando apenas um pacote para todos os pedidos da API. Isso pode parecer estranho, já que aprendemos que cada função lambda é apenas uma função e não um conjunto delas. Isso ainda está correto, porém contornamos esse problema criando uma função lambda roteadora, que por meio de um pedido da API Gateway consegue decidir qual sub função ela deve executar.

A ideia é que a API Gateway injete, além dos dados do pedido, o nome do recurso e o método utilizado no objeto events da função lambda. Com isso, conseguimos carregar dinamicamente a função que vai atender a esse pedido.

Erros! Muitos erros!

Uma coisa que nos incomodou muito foi como teríamos que mapear as respostas (200, 300, 404 etc.) com texto. Resolvemos esse incômodo criando um padrão que seguimos em todo o projeto e unificando os Integration Responses da nossa API.

É bem simples: a integração (função lambda) retorna um JSON que para o passointegration response não passa de um texto. Sabendo disso, qualquer resposta do Lambda que possua o texto “BAD REQUEST” será taxada como status 400 na nossa aplicação. Isso é válido para todas as funções lambdas do nosso sistema.

Automatize seus deploys

Automatizar os deploys foi uma dificuldade muito grande no início do projeto CS Billing – não só a forma de se trabalhar era diferente da habitual, como não encontrávamos informações nem ferramentas decentes sobre como utilizar o Lambda e a API Gateway. Então, tivemos que criar nosso próprio deployer, que começou como um script gambiarrístico e terminou como um projeto open source.

Nosso deployer faz algumas abstrações que simplificaram um bocado a configuração de um endpoint.

Por instância, para criar um endpoint só precisamos adicionar o endpoint e o método no arquivo de configuração do Tutu. Com isso, criamos não apenas o recurso com os cabeçalhos de CORS criados, mas também um método OPTIONS para pedidos de preflight. Esses detalhes tornam a criação de uma API com lambda e a API Gateway praticamente transparente para o desenvolvedor.

Apesar de totalmente funcional, novas formas de subir as configurações da API Gateway apareceram, utilizando Swagger, e considero nosso deployer obsoleto no aspecto de velocidade de deployment.

Teste local antes

Empacotar, subir e configurar um evento apenas para testar sua função lambda é algo inconcebível. Para isso, recomendamos apenas executar suas funções localmente. Lembre-se de que as funções lambdas são apenas funções e nada mais.

Para executar uma função, basta mockar os dois objetos passados para função lambda: events e context. O events precisa possuir todos os dados necessários para o processamento correto. O context precisa apenas de uma função de callback para ser executada no final do processo, que pode ser apenas um console.log() para visualizar o resultado final.

Caso você utilize algum serviço da AWS, talvez precise de um arquivo com as credenciais na sua máquina. O SDK da AWS achará essa permissão automaticamente. Lembre-se de que essas credenciais devem idealmente refletir as permissões da role que o lambda possui na AWS.

Variáveis de ambiente

Provavelmente será necessário adicionar algum parâmetro que varia de acordo com o Stage em que será feito o deployment. Inicialmente colocamos esses dados em um dynamo, no qual todos os lambdas acessavam a cada pedido. Depois, percebemos que isso adicionava um overhead de quase meio segundo aos pedidos.

A melhor forma que achamos é injetar esses valores diretamente no pacote Lambda por meio de um arquivo que será lido em algum passo da função lambda ou, como no caso do TS2, injetando diretamente os valores no código quando buildamos nosso pacote.

Ressalto que, apesar de essa ser a melhor forma que encontramos, o seu pacote lambda pode ser baixado pelo console. Por isso, cuide bem das suas permissões de acesso, principalmente em produção.

Subida de arquivos

A subida de arquivo se mostrou um desafio grande para o Lambda até esse ponto. Não existe uma maneira direta de receber um arquivo multipart e manipulá-lo como é possível em um servidor.

Atualmente, a melhor solução que encontramos foi utilizar o serviço da AWS Cognito com o AWS S3 para subir um arquivo para uma pasta do S3 utilizando o SDK. Não entrarei em detalhes, mas o funcionamento básico é o seguinte:

Primeiro, o usuário executa o login no sistema com sua conta. Depois disso, o cliente recebe uma chave de usuário que será utilizada em conjunto com algum SDK da AWS para realizar uma ação de PUT de objeto em algum bucket do S3 ao qual esse usuário tenha acesso.

Para associar um processamento do Lambda a esse arquivo, é necessário criar um Trigger de evento no S3 para disparar uma função específica do Lambda.

Esse é um dos casos em que o Lambda deixa de ser atrativo como plataforma.

Conclusão

Serveless é um conceito muito interessante e novo. Pessoalmente, acho que, apesar das dificuldades e da grande mudança de paradigma, esse conceito está aí para ficar e quanto mais pessoas contribuem, mais maduro esse conceito ficará e menos hipster ele será.

Espero que essa sequência de artigos tenha sido de alguma ajuda ou, no mínimo, uma boa introdução para Lambda e API Gateway. Ficou alguma dúvida ou tem alguma contribuição? Aproveite os campos abaixo. Abraços e até a próxima!