Esta postagem do blog aborda os princípios básicos das imagens de contêiner personalizadas do AWS Lambda, quando usá-las e como criá-las com Docker e AWS CDK em Python.
Como construir funções AWS Lambda com dependências externas
Ao criar uma função do AWS Lambda, você fornece o código do manipulador e seus módulos internos (camada lógica, manipuladores de banco de dados, etc.). No entanto, mais frequentemente do que nunca, seu código depende de dependências externas. Quando sua função do Lambda é executada, ela é executada em uma imagem base de contêiner da AWS. A imagem de contêiner fornecida pela AWS pode não conter todas as suas dependências externas. Nesse caso, você precisa fornecê-las você mesmo.
Você tem quatro opções:
Crie e carregue uma imagem de contêiner Lambda que você mesmo cria, que inclui código de manipulador e todas as dependências externas e carregue-a no repositório Amazon ECR.
Crie e carregue um arquivo ZIP contendo todo o código Lambda e as dependências externas por função AWS Lambda. Veja exemplos de código aqui .
Crie e carregue um arquivo ZIP contendo dependências externas como uma camada AWS Lambda. Veja exemplos de código e explicações técnicas aqui .
Use um ARN de camada Lambda existente que contenha as dependências externas.
Neste blog, vamos nos concentrar na opção número um: criar uma imagem de contêiner Lambda.
Minha postagem anterior se concentrou nas opções três e quatro.
O que é uma imagem de contêiner Lambda
As imagens de contêiner do AWS Lambda fornecem uma maneira conveniente de empacotar bibliotecas e outras dependências que você pode usar com suas funções do Lambda.
O AWS Lambda oferece suporte a imagens de até 10 GB, diferentemente dos métodos de compilação da função regular do Lambda, que permitem dependências descompactadas de apenas até 250 MB.
O AWS CDK requer um Dockerfile para construir a imagem do contêiner. Uma vez construído, ele é carregado no Amazon ECR (Elastic Container Registry), onde será armazenado e usado pela função Lambda durante a invocação.
Quando você deve usá-lo
Imagens de contêiner do AWS Lambda são um método válido para criar funções do Lambda.
No entanto, é mais complexo do que construir camadas Lambda ou usar o criador ZIP de dependências integrado do CDK conforme descrito nas opções 2-4 acima.
Além disso, as imagens de contêiner apresentam muitas desvantagens:
Em comparação com as alternativas (opções 2 a 4 nas opções de construção do Lambda mencionadas acima), você precisa escrever um arquivo Docker, o que não é uma ótima experiência para o usuário.
Construir uma imagem leva consideravelmente mais tempo do que as alternativas. A imagem contém o tempo de execução do Lambda, seu código manipulador e suas dependências. O resultado será muito maior do que os arquivos ZIP que você cria nas alternativas.
Você carrega uma imagem no Amazon ECR e paga pelo armazenamento , enquanto nas outras opções você não paga.
Imagens maiores demoram mais para serem criadas e carregadas, o que resulta em um tempo maior para a conta de produção do que para criar uma camada ou um ZIP contendo apenas código Lambda e dependências.
Considero as imagens de contêiner Lambda um nicho e acredito que você deve usá-las apenas em casos em que:
Você deseja controle total da imagem do contêiner por motivos de segurança ou otimizações personalizadas.
Seu código tem dependências que excedem 250 MB quando descompactado. Esse é o principal fator pró para usá-lo. Simples assim.
Crie um contêiner Lambda com AWS CDK e Docker em Python com Poetry
Nosso Objetivo
Vamos supor que nosso serviço Serverless use poesia para gerenciamento de dependências e que o arquivo pyproject.toml se pareça com isto:
Queremos agrupar nosso código de função Lambda e suas dependências (AWS Lambda Powertools, mypy-boto3-dynamodb, cachetools e outros) em uma imagem de contêiner Lambda e implantá-la com o AWS CDK.
O AWS CDK criará uma imagem de contêiner, fará o upload dela para o Amazon ECR, o repositório de contêineres, e definirá uma função do AWS Lambda para usar como imagem de contêiner.
Suposições
Você instalou o Docker.
Você instalou o AWS CDK.
Você usa poesia como seu gerenciador de dependências Python.
Você usa apenas um arquivo geral pyproject.toml para todas as funções do AWS Lambda. Ele inclui todos os requisitos de todas as funções.
Começando
Antes de escrever qualquer código CDK, precisamos preparar o dockerfile e o arquivo Lambda requirements.txt, que descreve as dependências necessárias do Lambda e suas versões.
Armazenaremos todos os artefatos de compilação necessários em uma nova pasta: a pasta '.build', que não fará parte da base de código.
Primeiro, precisamos criar um arquivo requirements.txt a partir do nosso pyproject.toml.
Neste exemplo, usamos poetry, mas pipenv também é uma opção.
Geramos um requirements.txt a partir da seção [tool.poetry.dependencies] do arquivo toml.
Diferentemente da seção [tool.poetry.dev-dependencies] no toml, as bibliotecas na seção 'tool.poetry.dependencies' são as bibliotecas que as funções do Lambda no projeto exigem em tempo de execução e devem ser carregadas na AWS.
Execute o seguinte comando para criar a pasta '.build/ecr' e gerar o arquivo requirements.txt.
O arquivo gerado pode ter a seguinte aparência:
Agora, adicionamos o código do manipulador Lambda. Vamos supor que, em nosso projeto, os manipuladores de função Lambda residam na pasta raiz do projeto, sob a pasta 'service'.
Antes de construir e implementar as funções do Lambda na AWS, copiamos a pasta 'service' para a pasta '.build/ecr'. O código CDK que cria a imagem do contêiner do Lambda pegará o código do manipulador do Lambda, requirements.txt e Dockerfile da pasta '.build/ecr'.
Agora, tudo o que resta é criar um Dockerfile e copiá-lo para a pasta '.build/ecr'.
Tempo do Dockerfile
Conforme mencionado na documentação oficial da AWS, sua imagem de contêiner precisa implementar a API de tempo de execução do Lambda:
A AWS fornece um conjunto de imagens base de código aberto que você pode usar para criar sua imagem de contêiner. Essas imagens base incluem um cliente de interface de tempo de execução para gerenciar a interação entre o Lambda e seu código de função.
Usaremos uma imagem de contêiner base e começaremos a construir a partir dela.
A linha 1 configura a imagem base. Usamos uma imagem oficial com suporte do AWS Python 3.9 e usamos a versão mais recente.
A linha 4 copia o requirements.txt para a pasta raiz, enquanto a linha 7 instala as dependências do Lambda na pasta raiz da tarefa do Lambda. Conforme especificado aqui , a variável de ambiente define a pasta onde seu código reside.
As linhas 9 e 10 copiam o código do manipulador e seus módulos internos, a pasta 'service' e suas subpastas para a pasta de código lambda.
A linha 13 define a função de entrada do manipulador de função Lambda. No meu projeto, o serviço tinha uma pasta interna chamada 'handlers' onde um 'create_order.py' reside com uma função chamada 'create_order' dentro como seu ponto de entrada.
É isso!
Agora você deve ter a seguinte estrutura de pasta '.build':
Dica geral:
Recomendo que você use um Makefile ou qualquer script simples para automatizar essas ações antes do comando 'cdk deploy'.
Implantação do CDK
Agora que tudo está no lugar, vamos escrever uma construção CDK que cria uma imagem de contêiner Lambda e uma função Lambda que é construída de acordo com a configuração na pasta '.build/ecr'.
A construção do AWS CDK 'MyConstruct' cria:
Função básica do Lambda (linhas 14-20).
Função Lambda baseada na imagem do contêiner (linhas 22-30). O CDK procura um requirements.txt e Dockerfile no diretório de ativos ('.build/ecr') e usa o Docker para construir a imagem do contêiner. Uma vez construída, ela é carregada no Amazon ECR.
Mac M1 te peguei
Na linha 29, especificamos a arquitetura Lambda como ARM64. Como eu estava usando um Mac com um chip M1, tive que definir esse parâmetro como ARM64. Quando não o especifiquei, a função Lambda falhou ao iniciar com um erro de endpoint que não fazia sentido (felizmente o stackoverflow foi chamado para o resgate!).
Após a implantação, vá até o console do AWS Lambda e procure a aba "imagem":
Como você pode ver, a imagem foi carregada no Amazon ECR e tem a arquitetura ARM64.