AWS Lambda Layers na prática com Python: compartilhando dependências entre funções
Quando começamos a criar várias funções AWS Lambda, é comum perceber que parte do código se repete. Uma função pode precisar de uma biblioteca externa, outra pode usar a mesma biblioteca e, às vezes, ambas compartilham validadores, formatadores de resposta ou utilitários de logging. Uma opção é incluir tudo no pacote de deployment de cada função. Isso funciona, mas pode gerar duplicação desnecessária.
Neste artigo, vamos seguir por outro caminho: colocar os componentes compartilhados em uma AWS Lambda Layer e deixar cada função responsável apenas pelo seu próprio código.
Bora lá?
O que é o Lambda Layer
Uma AWS Lambda Layer é um pacote que reúne bibliotecas, dependências ou outros componentes reutilizáveis por uma ou mais funções Lambda. Em vez de incluir esses arquivos no pacote de cada função, podemos disponibilizá-los em uma camada separada. Quando uma função utiliza uma Layer, o Lambda disponibiliza seu conteúdo durante a execução, permitindo que o código importe os módulos e as bibliotecas incluídos nela.
Na prática, isso ajuda a:
- Evitar duplicação: compartilhar dependências e código comum entre várias funções;
- Simplificar a manutenção: atualizar componentes compartilhados sem precisar incorporá-los manualmente ao código de cada função;
- Organizar o projeto: manter o código de negócio separado das dependências e dos utilitários reutilizáveis;
- Reduzir o tamanho dos pacotes individuais: retirar deles os componentes que já estão disponíveis na Layer.
Uma Layer possui versões próprias, cada uma identificada por um ARN específico. Assim, diferentes funções podem usar versões distintas da mesma camada. Vale lembrar que Layers não substituem o gerenciamento de dependências nem dispensam testes de compatibilidade. Elas são uma forma de organizar e compartilhar componentes, especialmente útil quando várias funções precisam das mesmas bibliotecas.
O que vamos construir
Neste laboratório, vamos criar duas funções Lambda em Python que utilizarão a mesma Layer, composta por uma dependência externa, e um módulo com funções auxiliares.
As funções serão:
process_order: simula o processamento de um pedido, validando os campos obrigatórios e verificando a disponibilidade de estoque;validate_customer: verifica os dados obrigatórios de um cliente e retorna informações sobre seu cadastro.
A Layer fornecerá os seguintes componentes:
requests: biblioteca Python utilizada como dependência externashared_utils.py: módulo com funções reutilizáveis para padronizar respostas, validar dados e registrar logs.
O problema
Imagine duas funções com a seguinte estrutura:
process-order.zip
├── process_order.py
├── requests/
└── shared_utils.py
validate-customer.zip
├── validate_customer.py
├── requests/
└── shared_utils.py
A aplicação até funciona, mas aqui estamos distribuindo a mesma dependência e o mesmo código compartilhado duas vezes. E a ideia é seguirmos a lógica DRY (don't repeat yourself). Com uma Layer, a nossa arquitetura passa a ser:
Lambda Layer
┌─────────────────────┐
│ requests │
│ shared_utils.py │
└──────────┬──────────┘
│
┌────────┴────────┐
│ │
▼ ▼
process_order validate_client
Com isso, os pacotes das funções ficam menores e cada função mantém apenas o que é específico da sua responsabilidade. As Layers também ajudam a separar a lógica da função de suas dependências e a compartilhar componentes entre várias funções.
Resultado esperado
O nosso resultado será:
AWS Lambda
│
Layer compartilhada
│
┌───────────┴───────────┐
│ │
▼ ▼
process-order validate-client
│ │
└───────────┬───────────┘
│
shared_utils.py
+
requests
A Layer será publicada como uma versão própria e as duas funções usarão essa versão.
Pré-requisitos
- AWS CLI configurada, com um profile com permissões adequadas para subir os recursos;
- Python 3;
pip;zipeunzip;- uma região AWS disponível para o laboratório (por padrão, vamos usar a famigerada e amada us-east-1).
Passo a passo
1. Clonar o repositório e configurar o profile
Acesse o repositório deste laboratório no GitHub: lab-lambda-layers para clonar e entrar no diretório do projeto a fim de obter os arquivos Python e o requirements.txt:
git clone https://github.com/ludsilva/lab-lambda-layers.git
cd lab-lambda-layers
Para este hands-on, estou usando o Linux como sistema operacional padrão. Então, podemos definir o profile e a região AWS para a sessão atual do terminal:
export AWS_PROFILE=teste_lab
export AWS_DEFAULT_REGION=us-east-1
export AWS_REGION=us-east-1
OBS: Substitua teste_lab pelo nome do profile que você utiliza na AWS.
Confirme a identidade:
aws sts get-caller-identity
É uma boa prática fazer essa verificação antes de criar recursos, principalmente quando existem múltiplos profiles AWS configurados. Isso é importante para garantir que você está utilizando a conta e as credenciais corretas antes de começar a criar recursos. Afinal, ninguém quer descobrir que executou o laboratório na conta errada, não é mesmo?
2. Preparando a Layer
Para uma Layer Python, o ZIP precisa ter um diretório python na raiz. Um dos caminhos aceitos para bibliotecas é python/lib/python3.x/site-packages. Então, vamos criar essa estrutura:
mkdir -p lambda-layer/python/lib/python3.12/site-packages
Agora instalamos requests diretamente dentro dela:
pip install requests \
-t lambda-layer/python/lib/python3.12/site-packages/
O arquivo requirements.txt fixa a versão utilizada neste exemplo:
requests==2.32.5
Em um projeto real, eu recomendaria fixar a versão da dependência em vez de deixar a resolução totalmente aberta.
3. Adicionando código próprio à Layer
A Layer não precisa conter apenas bibliotecas de terceiros, ela também pode fornecer módulos Python próprios. Neste exemplo, o módulo se chama:
shared_utils.py
Copie-o para:
cp shared_utils.py \
lambda-layer/python/lib/python3.12/site-packages/
A estrutura deverá ficar parecida com:
lambda-layer/
└── python/
└── lib/
└── python3.12/
└── site-packages/
├── requests/
├── urllib3/
├── certifi/
├── ...
└── shared_utils.py
O shared_utils.py fornece quatro funções reutilizáveis:
standard_response(): padroniza a resposta;validate_request(): verifica campos obrigatórios;log_event(): registra logs estruturados em JSON;requests_version(): retorna a versão da bibliotecarequests, para confirmar que a dependência está acessível pela Layer.
4. Empacotando a Layer
Entre no diretório:
cd lambda-layer
Compacte o diretório python de forma que ele permaneça na raiz do ZIP:
zip -r ../shared-python-components.zip python
Volte e confira o conteúdo:
cd ..
unzip -l shared-python-components.zip
O arquivo deve conter python/ no nível raiz, e não um diretório externo como lambda-layer/python/.
5. Publicando a Layer
Obtenha o ID da conta:
ACCOUNT_ID=$(aws sts get-caller-identity \
--query Account \
--output text)
Publique a Layer:
aws lambda publish-layer-version \
--layer-name shared-python-components \
--region "$AWS_REGION" \
--description "Shared Python dependencies and utilities" \
--zip-file fileb://shared-python-components.zip \
--compatible-runtimes python3.12 \
--compatible-architectures x86_64
Guarde o ARN retornado pelo comando. Ele identifica uma versão específica da Layer. Você pode armazená-lo em uma variável, substituindo o número da versão pelo valor retornado:
Ele terá este formato:
LAYER_ARN="arn:aws:lambda:${AWS_REGION}:${ACCOUNT_ID}:layer:shared-python-components:1"
Podemos armazená-lo em uma variável:
LAYER_ARN="arn:aws:lambda:${AWS_REGION}:${ACCOUNT_ID}:layer:shared-python-components:1"
OBS: o número 1 é um exemplo; confirme o ARN real retornado na publicação.
6. Criando a IAM Role
As funções Lambda precisam de uma IAM Role que permita ao serviço assumir a role e gravar logs no CloudWatch. Se você já tiver uma role adequada, poderá reutilizá-la.
Caso precise criar uma role para o laboratório, crie o arquivo de confiança:
cat > trust-policy.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
Crie a Role:
aws iam create-role \
--role-name lambda-layers-lab-role \
--assume-role-policy-document file://trust-policy.json
Adicione a policy básica de execução:
aws iam attach-role-policy \
--role-name lambda-layers-lab-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Obtenha o ARN:
ROLE_ARN=$(aws iam get-role \
--role-name lambda-layers-lab-role \
--query 'Role.Arn' \
--output text)
7. Empacotar e criar a função process-order
O pacote da função deve conter apenas seu handler. Não inclua requests nem shared_utils.py, pois esses componentes estarão na Layer.
zip process-order.zip process_order.py
Crie a função:
aws lambda create-function \
--function-name process-order \
--runtime python3.12 \
--handler process_order.lambda_handler \
--zip-file fileb://process-order.zip \
--role "$ROLE_ARN" \
--layers "$LAYER_ARN"
O ARN da Layer precisa apontar para uma versão específica.
8. Empacotar e criar a função validate-customer
Faça o mesmo com a segunda função:
zip validate-customer.zip validate_customer.py
Crie a função:
aws lambda create-function \
--function-name validate-customer \
--runtime python3.12 \
--handler validate_customer.lambda_handler \
--zip-file fileb://validate-customer.zip \
--role "$ROLE_ARN" \
--layers "$LAYER_ARN"
Agora as duas funções compartilham exatamente a mesma Layer.
9. Testando a função de pedidos
Crie um evento de pedido:
cat > order-event.json <<'EOF'
{
"product_id": "SKU-8472",
"quantity": 4,
"customer_id": "CUS-208"
}
EOF
Invoque a função:
aws lambda invoke \
--function-name process-order \
--payload fileb://order-event.json \
--cli-binary-format raw-in-base64-out \
order-response.json
Visualize a resposta:
cat order-response.json
A resposta deve conter os dados do pedido, o status do processamento e a versão de requests fornecida pela Layer.
10. Testar a validação de campos obrigatórios
Agora envie um evento incompleto:
cat > invalid-order-event.json <<'EOF'
{
"product_id": "SKU-8472"
}
EOF
Invoque a função:
aws lambda invoke \
--function-name process-order \
--payload fileb://invalid-order-event.json \
--cli-binary-format raw-in-base64-out \
invalid-order-response.json
Confira o resultado:
cat invalid-order-response.json
A função deve retornar uma resposta HTTP com statusCode igual a 400, informando os campos ausentes.
11. Testando a função de clientes
Crie um evento válido:
cat > customer-event.json <<'EOF'
{
"email": "ana@example.com",
"name": "Ana"
}
EOF
Invoque a função:
aws lambda invoke \
--function-name validate-customer \
--payload fileb://customer-event.json \
--cli-binary-format raw-in-base64-out \
customer-response.json
Visualize a resposta:
cat customer-response.json
O retorno deve incluir os dados do cliente e a versão da biblioteca requests disponibilizada pela Layer.
12. Verificando a Layer na função
Para confirmar, consulte as Layers configuradas na função de pedidos:
aws lambda get-function-configuration \
--function-name process-order \
--query 'Layers[*].Arn' \
--output text
Repita para a função de clientes:
aws lambda get-function-configuration \
--function-name validate-customer \
--query 'Layers[*].Arn' \
--output text
Os comandos devem retornar o ARN da versão da Layer associada a cada função.
13. Entender como a Layer fica disponível
Durante a execução, o Lambda disponibiliza o conteúdo das Layers no diretório /opt. Para o runtime Python, os caminhos de importação são configurados para que os módulos da camada possam ser encontrados. Por isso, o handler pode importar diretamente:
from shared_utils import standard_response, validate_request
import requests
Esses componentes não precisam estar duplicados no ZIP da função, pois a dependência é fornecida pela Layer.
14. Consultar versões da Layer
Liste as versões publicadas:
aws lambda list-layer-versions \
--layer-name shared-python-components \
--region "$AWS_REGION"
Cada publicação cria uma nova versão, e as versões publicadas são imutáveis: se uma função utiliza a versão 1, ela não passa automaticamente para a versão 2 quando uma nova versão é publicada. A atualização da função para outra versão precisa ser feita explicitamente.
15. Limpando o laboratório
Para finalizar o lab, exclua as funções:
aws lambda delete-function \
--function-name process-order
aws lambda delete-function \
--function-name validate-customer
Exclua a versão publicada da Layer, e ajuste o número se a versão criada for diferente de 1:
aws lambda delete-layer-version \
--layer-name shared-python-components \
--version-number 1 \
--region "$AWS_REGION"
Se você criou a IAM Role exclusivamente para este laboratório, remova a policy e a role:
aws iam detach-role-policy \
--role-name lambda-layers-lab-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam delete-role \
--role-name lambda-layers-lab-role
Remova os arquivos gerados localmente, se desejar:
rm -rf lambda-layer
rm -f shared-python-components.zip
rm -f process-order.zip validate-customer.zip
rm -f trust-policy.json
rm -f order-event.json invalid-order-event.json customer-event.json
rm -f order-response.json invalid-order-response.json customer-response.json
OBS: cuidado para não excluir as roles que sejam utilizadas por outros laboratórios ou aplicações.
Limites e cotas
Uma função Lambda pode ter até cinco Layers associadas. O tamanho descompactado combinado da função, das Layers e dos runtimes não pode ultrapassar 250 MB. Isso não significa que precisamos criar cinco Layers para cada função, pois a separação deve fazer sentido para a arquitetura.
Por exemplo, uma Layer de utilitários compartilhados pode atender a várias funções. Criar uma Layer diferente para cada pequeno arquivo, por outro lado, pode aumentar a complexidade operacional.
Quando o uso de Layers faz sentido?
Alguns cenários comuns:
- Dependências compartilhadas: várias funções usam a mesma biblioteca ou conjunto de bibliotecas;
- Código interno reutilizado: módulos de logging, validação ou tratamento de respostas são usados por várias funções;
- Separação de responsabilidades: o pacote da função contém a lógica da aplicação, enquanto a Layer concentra componentes compartilhados;
- Atualização controlada: uma nova versão pode ser publicada e adotada pelas funções de maneira explícita.
Quando uma Layer pode ser desnecessária?
Se apenas uma função usa determinada biblioteca, adicionar uma Layer pode não trazer vantagens suficientes para justificar a complexidade. Incluir a dependência diretamente no pacote da função pode ser mais simples.
Também é importante considerar a compatibilidade das dependências. Bibliotecas Python com componentes nativos precisam ser compatíveis com o ambiente Linux e com a arquitetura utilizados pelo Lambda. Quando necessário, construa o pacote em um ambiente compatível, por exemplo, usando Docker.
Cuidados com reprodutibilidade
Instalar dependências sem fixar versões pode produzir resultados diferentes em builds realizados em momentos distintos. Neste laboratório, o requirements.txt utiliza uma versão explícita:
requests==2.32.5
Em projetos reais, mantenha as dependências atualizadas de forma controlada e teste a compatibilidade antes de publicar uma nova versão da Layer. Fixar versões ajuda a tornar os builds mais previsíveis, mas não substitui testes e revisão de segurança.
Conclusão
Ao longo deste passo a passo, criamos uma AWS Lambda Layer com a biblioteca requests e um módulo de utilitários próprios, publicamos uma versão da camada e a compartilhamos entre duas funções Lambda em Python. Também testamos as funções para verificar se os componentes disponibilizados pela Layer estavam acessíveis durante a execução.
Com isso, vimos na prática como centralizar dependências compartilhadas sem precisar incluí-las no pacote de cada função. Além de reduzir a duplicação, essa abordagem facilita a organização e a manutenção do código quando diferentes funções precisam dos mesmos componentes.
Agora é sua vez: experimente adaptar o laboratório às dependências e aos utilitários que você já utiliza nos seus projetos. Essa é uma boa forma de consolidar o aprendizado e entender como as Lambda Layers podem simplificar o desenvolvimento das suas funções.
Até a próxima!
Referências
- AWS Lambda - Managing Lambda dependencies with layers
- AWS Lambda - Working with layers for Python Lambda functions
- AWS Lambda - Packaging your layer content
- AWS Lambda - Working with .zip file archives for Python Lambda functions
- AWS Lambda - Adding layers to functions
- AWS Lambda — Quotas
- AWS CLI -
publish-layer-version - Artigo - Introduction to AWS Lambda: A Starter Guide
