Python e AWS Lambda: Guia Serverless

Crie funcoes serverless com Python e AWS Lambda. Aprenda deploy, API Gateway, layers, testes locais e boas praticas de producao.

5 min de leitura Equipe Python Brasil

AWS Lambda revolucionou a forma como construimos aplicacoes na nuvem. Em vez de gerenciar servidores, voce escreve funcoes que executam sob demanda, pagando apenas pelo tempo de execucao. Python e uma das linguagens mais populares no Lambda, e neste guia vamos explorar como criar, testar e fazer deploy de funcoes serverless de forma profissional.

O Que e AWS Lambda

AWS Lambda e um servico de computacao serverless. Voce envia seu codigo, define um gatilho (HTTP, fila, agendamento) e a AWS cuida de toda a infraestrutura. Nao ha servidores para provisionar, escalar ou manter.

As principais vantagens sao: escalonamento automatico, cobranca por milissegundo de execucao, integracao nativa com outros servicos AWS e suporte a Python 3.12.

Sua Primeira Funcao Lambda

Uma funcao Lambda em Python e simples. Ela recebe um evento e um contexto:

import json
from datetime import datetime

def handler(event, context):
    """Funcao Lambda basica que retorna uma saudacao."""
    nome = event.get("nome", "Mundo")
    hora = datetime.now().strftime("%H:%M:%S")

    resposta = {
        "mensagem": f"Ola, {nome}! Sao {hora}.",
        "funcao": context.function_name,
        "memoria_mb": context.memory_limit_in_mb,
        "request_id": context.aws_request_id,
    }

    return {
        "statusCode": 200,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps(resposta, ensure_ascii=False),
    }

O parametro event contem os dados do gatilho (payload HTTP, mensagem da fila, etc). O context traz metadados da execucao, como nome da funcao, memoria disponivel e tempo restante.

Configurando com SAM (Serverless Application Model)

O AWS SAM simplifica o desenvolvimento local e o deploy de funcoes Lambda:

pip install aws-sam-cli
sam init --runtime python3.12 --name meu-projeto

O arquivo template.yaml define a infraestrutura:

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Timeout: 30
    MemorySize: 256
    Runtime: python3.12

Resources:
  ApiFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: app.handler
      CodeUri: src/
      Events:
        ApiEvent:
          Type: Api
          Properties:
            Path: /saudacao
            Method: get

  ProcessarPedido:
    Type: AWS::Serverless::Function
    Properties:
      Handler: pedidos.processar
      CodeUri: src/
      Events:
        FilaSQS:
          Type: SQS
          Properties:
            Queue: !GetAtt FilaPedidos.Arn

  FilaPedidos:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: fila-pedidos

API REST com API Gateway

O caso de uso mais comum e expor funcoes Lambda como endpoints HTTP:

import json
import uuid
from datetime import datetime

# Simulando banco de dados em memoria
PRODUTOS = {}

def listar_produtos(event, context):
    """GET /produtos - Lista todos os produtos."""
    produtos = list(PRODUTOS.values())
    return {
        "statusCode": 200,
        "body": json.dumps({"produtos": produtos, "total": len(produtos)}),
    }

def criar_produto(event, context):
    """POST /produtos - Cria um novo produto."""
    try:
        body = json.loads(event.get("body", "{}"))
        nome = body.get("nome")
        preco = body.get("preco")

        if not nome or preco is None:
            return {
                "statusCode": 400,
                "body": json.dumps({"erro": "Nome e preco sao obrigatorios"}),
            }

        produto_id = str(uuid.uuid4())[:8]
        produto = {
            "id": produto_id,
            "nome": nome,
            "preco": float(preco),
            "criado_em": datetime.now().isoformat(),
        }
        PRODUTOS[produto_id] = produto

        return {
            "statusCode": 201,
            "body": json.dumps(produto),
        }
    except (json.JSONDecodeError, ValueError) as e:
        return {
            "statusCode": 400,
            "body": json.dumps({"erro": str(e)}),
        }

def buscar_produto(event, context):
    """GET /produtos/{id} - Busca um produto pelo ID."""
    produto_id = event["pathParameters"]["id"]
    produto = PRODUTOS.get(produto_id)

    if produto is None:
        return {
            "statusCode": 404,
            "body": json.dumps({"erro": "Produto nao encontrado"}),
        }

    return {
        "statusCode": 200,
        "body": json.dumps(produto),
    }

Lambda Layers para Dependencias

Quando sua funcao precisa de bibliotecas externas, use Lambda Layers:

mkdir -p layer/python
pip install requests boto3 -t layer/python/

No template SAM:

Resources:
  DependenciasLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      LayerName: dependencias-python
      ContentUri: layer/
      CompatibleRuntimes:
        - python3.12

  MinhaFuncao:
    Type: AWS::Serverless::Function
    Properties:
      Handler: app.handler
      Layers:
        - !Ref DependenciasLayer

Processando Eventos do S3

Lambda pode reagir a uploads no S3, ideal para processamento de arquivos:

import boto3
import json
from urllib.parse import unquote_plus

s3_client = boto3.client("s3")

def processar_upload(event, context):
    """Processa arquivos enviados ao S3."""
    for registro in event["Records"]:
        bucket = registro["s3"]["bucket"]["name"]
        chave = unquote_plus(registro["s3"]["object"]["key"])
        tamanho = registro["s3"]["object"]["size"]

        print(f"Arquivo recebido: s3://{bucket}/{chave} ({tamanho} bytes)")

        # Baixar e processar o arquivo
        resposta = s3_client.get_object(Bucket=bucket, Key=chave)
        conteudo = resposta["Body"].read().decode("utf-8")

        # Exemplo: contar linhas de um CSV
        linhas = conteudo.strip().split("\n")
        print(f"O arquivo tem {len(linhas)} linhas")

        # Salvar resultado processado
        resultado = {
            "arquivo_original": chave,
            "total_linhas": len(linhas),
            "processado": True,
        }
        s3_client.put_object(
            Bucket=bucket,
            Key=f"processados/{chave}.json",
            Body=json.dumps(resultado),
            ContentType="application/json",
        )

    return {"processados": len(event["Records"])}

Testes Locais

Testar funcoes Lambda localmente e fundamental antes do deploy:

import pytest
import json

from app import handler, criar_produto

def test_handler_basico():
    evento = {"nome": "Python Brasil"}
    contexto = type("Context", (), {
        "function_name": "teste",
        "memory_limit_in_mb": 256,
        "aws_request_id": "abc-123",
    })()

    resultado = handler(evento, contexto)
    assert resultado["statusCode"] == 200

    body = json.loads(resultado["body"])
    assert "Python Brasil" in body["mensagem"]

def test_criar_produto_sucesso():
    evento = {
        "body": json.dumps({"nome": "Teclado", "preco": 129.90})
    }
    resultado = criar_produto(evento, None)
    assert resultado["statusCode"] == 201

    body = json.loads(resultado["body"])
    assert body["nome"] == "Teclado"
    assert body["preco"] == 129.90

def test_criar_produto_sem_nome():
    evento = {"body": json.dumps({"preco": 99.90})}
    resultado = criar_produto(evento, None)
    assert resultado["statusCode"] == 400

Com o SAM CLI, voce tambem pode invocar localmente:

sam local invoke ApiFunction -e events/saudacao.json
sam local start-api

Boas Praticas para Lambda em Producao

Mantenha suas funcoes pequenas e focadas em uma unica responsabilidade. Reutilize conexoes de banco e clientes AWS fora do handler para aproveitar o warm start. Defina variaveis de ambiente para configuracoes que mudam entre ambientes. Use o CloudWatch para monitorar logs, metricas e alarmes.

Configure o timeout adequado para cada funcao e ajuste a memoria conforme necessario. Mais memoria tambem significa mais CPU disponivel. Implemente tratamento de erros robusto e retorne codigos HTTP apropriados.

import os

# Variaveis de ambiente (fora do handler)
TABELA_NOME = os.environ.get("TABELA_DYNAMODB", "produtos-dev")
REGIAO = os.environ.get("AWS_REGION", "us-east-1")

# Cliente reutilizado entre invocacoes (warm start)
dynamodb = boto3.resource("dynamodb", region_name=REGIAO)
tabela = dynamodb.Table(TABELA_NOME)

Conclusao

AWS Lambda com Python e uma combinacao poderosa para construir aplicacoes escaláveis sem a complexidade de gerenciar infraestrutura. Comece com funcoes simples, teste localmente com SAM e evolua para arquiteturas serverless completas com filas, eventos do S3 e APIs REST. O ecossistema de ferramentas facilita todo o ciclo de desenvolvimento e deploy.

E

Equipe Python Brasil

Contribuidor do Python Brasil — Aprenda Python em Português