O Objetivo desse repositório é realizar um teste em relação a melhor forma de fazer um deploy usando o novo conceito de Lambda Container. A prova de conceito foi feita tendo como base e ponto de partida o artigo de lançamento da funcionalidade da AWS.
O principal requisito para esse estudo é manter a facilidade de deploy e a portabilidade de código do desenvolvedor em relação a desenvolvimento no modelo Serverless, em espacial, para utilização de Lambdas como APIs HTTP.
Além disso, também são pontos a serem considerados:
- Facilidade para desenvolvimento e teste local
- Não dependência de Frameworks externos
- Portabilidade de código e baixo acoplamento em relação ao provedor de nuvem
- Possibilidade de expandir ou migrar as funções para outras soluções de Container (Fargate, ECS, Kubernetes, Cloudrun,etc) de maneira simples
- Padronização e Segurança
Visando atender os requisitos citados anteriormente as principais linhas analisadas foram as seguintes:
-
Usar a API de emulação de Lambda para simulações em DEV.
- Informações sobre essa API. Lambda - Runtime Interface Emulator AWS. Teria que colocar esse emulador dentro do container apenas para testes em ambiente de desenvolvimento. Na ida para produção esse ponto poderia ser desconsiderado.
-
Em relação a chamada da função Lambda para execução tratamento de uma requisição HTTP. Teriamos algumas opções:
-
Ir na linha do próprio artigo de lançamento e usar uma imagem pronta da AWS que já vem com tudo pronto desde API de Runtime, API de Emulação e tudo mais que é necessário para rodar. Nesse caso é basicamente seguir o passos do artigo da AWS. Evitei ir por essa linha pela falta de controle e pelo fato de ter que usar uma imagem da pronta da AWS o qual não teria tando controle em relação a "tudo que tem dentro". A ideia é ter padronização e segurança, para isso saber tudo que tem no Conteiner ou ter a possibilidade de fazer um build usando o conceito de distrolless foi o mais razoável para questões de segurança e controle desejados.
-
A outra opção seria implementar a Runtime Extension Api do Lambda, no modelo de proxy. Nesse caso como a ideia era resolver o problema para serviços HTTP, bastaria implementar um proxy que recebesse um request do API Gateway da AWS ou HTTP API e transfira a chamada para o meu server HTTP local dentro do container. Eu já tinha visto algo nessa linha anteriormente para um framework chamado UP. Foi evitado ir nessa linha pois possivelmente dentro do container teriamos que colocar essa tradução como um binário/comando de tradução que seria executado a cada chamada / request e isso poderia ter um overhead maior. Na prática o ideal seria testar para ver as questões de performace. Ainda assim dado a documentação e o propósito que consegui entender do que é um Lambda Extension / Layer esse pelo menos me
-
Por fim o conceito que me pareceu mais interessante foi o do Lambda Layers / Extensions. Lendo a documentação e comparando com o propósito do que estava sendo requisitado essa opção parecia atender ou facilitar alcançar rapidamente os requisitos enumerado anteriormente inclusive a criação de uma imagem própria com tudo que era necessario para rodar a solução. Documentação sobre Lambda e criação de imagens
-
-
Para parte de emulação e testes locais
- A documentação que fala de testes locais de Lambda em conteiners e foi fundamental para conseguir realizar os testes encontra-se aqui.
A figura abaixo mostra de maneira macro o funcionamento da Execução de um
ambiente Lambda para os casos de Extensions API e Runtime API.
Uma figura que mostra em detalhes como o ambiente de Extensions funciona está logo abaixo:
Para um melhor entendimento de como a extensions do Lambda funciona recomenda-se a leitura dos seguintes artigos:
Como dito a ideia inicial seria partir para o desenvolvimento de uma Extensão Lambda que pudesse ser colocada dentro do container e magicamente fazer a tradução de um request vindo do API Gateway (Lambda proxy integrations) da AWS para o server HTTP local.
Para minha sorte acabei achando um repositório no Github com o pessoal que fez exatamente o que eu queria fazer. Com isso, busquei apenas criar um repositório que já use essa extensão e documentar o processo, uma vez que a documentação lá não é tão legal.
Abaixo é possível ver um docker file que foi criado para o ambiente de DEV
FROM golang:1.15 AS build
WORKDIR /teste
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o main
FROM public.ecr.aws/c2t6n2x5/serverlessish:2 AS s
#FROM gcr.io/distroless/static
FROM alpine
RUN apk add --no-cache bash
ADD https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie /usr/bin/aws-lambda-rie
RUN chmod 755 /usr/bin/aws-lambda-rie
COPY ./entry.sh /
RUN chmod 755 /entry.sh
COPY --from=s /opt/extensions/serverlessish /opt/extensions/serverlessish
COPY --from=build /teste/main /main
ENV PORT=8081
ENTRYPOINT ["/entry.sh"]
No caso ambiente de DEV foi criado um container do alpine para se instalar um Bash e desta forma conseguir fazer algum teste / depuração.
Build do Container Docker
docker build -f ./Dockerfiles/dev/Dockerfile -t container/serverless:dev .
Rodando o container
docker run --name serverteste -p 8080:8080 -p9090:8081 -it container/serverless:dev
Com isso vamos ter a aplicação de teste rodando na porta 9090 e a emulação do lambda na porta 8080.
Commando CURL que emula uma chama o endpoint /ping
curl -XPOST "http://localhost:8080/2015-03-31/functions/function/invocations" -d @teste.json
Command CURL que emula uma chamada no /
curl -XPOST "http://localhost:8080/2015-03-31/functions/function/invocations" -d @teste2.json
Com isso foi possível ver que o nosso ambiente de DEV está funcional e podemos ver que a Extensão lambda foi devidamenta carregada repassando a chamada de fato para o servidor http também instalado no container.
Caso seja necessário logar no container e verificar algo ou fazer alguma troubleshooting você poderá usar o seguinte comando:
docker exec -it serverteste /bin/bash
Para o ambiente de produção basta fazer o buid da imagem seguindo o arquivo Dockerfile
que está em ./Dockerfiles/prod
.
docker build -f ./Dockerfiles/prod/Dockerfile -t container/serverless:prod .
Rodando o container de PROD localmente para realizar testes.
docker run --name serverteste -p9090:8081 -it container/serverless:prod
Neste caso por se tratar de um serviço HTTP e neste examplo os seguintes endpoints deverão responder.
http://localhost:9090/ping
=> OK
http://localhost:9090/
=> this is what i received on port 8081:
No caso deste Dockerfile
do PROD está sendo usado o conceito de Distroless e foram
removidos a parte de emulação do ambiente de DEV.
Daqui por diante basta seguir com o Deploy do container de forma similar ao que foi feito neste artigo AWS.