O blog da AWS

Modernizando cargas de trabalho Lambda + S3 com Amazon S3 Files

Por Sahithi Ginjupalli, Cloud Support Engineer II e D Surya Sai, Technical Account Manager na Amazon Web Services.

Aprenda como o Amazon S3 Files simplifica as funções Lambda eliminando código de transferência e restrições de /tmp. Veja três padrões de modernização com exemplos de código para processamento de imagens, pipelines ETL e cargas de trabalho de IA multi-agente.

As funções do AWS Lambda que interagem com o Amazon Simple Storage Service (Amazon S3) normalmente seguem um padrão familiar: baixar um objeto para /tmp, processá-lo localmente e fazer upload do resultado de volta para o S3. Este padrão é bem compreendido e confiável, mas requer que você escreva código para gerenciar transferências, monitorar a capacidade de /tmp e limpar o armazenamento efêmero juntamente com sua lógica de processamento real.

O Amazon S3 Files muda isso permitindo que sua função Lambda monte um bucket S3 como um sistema de arquivos. Sua função lê e escreve arquivos em um caminho de montagem local (como /mnt/data), e o sistema de arquivos lida com a sincronização com o S3 automaticamente. O código de transferência e gerenciamento de armazenamento desaparece, e o que resta é sua lógica de processamento trabalhando diretamente com arquivos.

Nesta publicação, percorremos três cargas de trabalho comuns de Lambda + S3 e mostramos como modernizar cada uma usando o S3 Files. Você verá como o código fica mais curto, a restrição de tamanho de /tmp desaparece e a experiência do desenvolvedor melhora.

Passo a passo

Pré-requisitos

Antes de começar, certifique-se de ter:

  • Uma conta AWS com permissões para criar funções Lambda, sistemas de arquivos S3 e recursos VPC.
  • Uma VPC existente com sub-redes privadas e grupos de segurança apropriados.

Primeiros passos

Para integrar uma função Lambda com o S3 Files, você pode seguir estas três etapas:

  1. Crie um sistema de arquivos S3 para seu bucket. Você pode fazer isso através do console S3, AWS Command Line Interface (AWS CLI), ou AWS CloudFormation. Esta única operação cria o sistema de arquivos, destinos de montagem em sua Amazon Virtual Private Cloud (Amazon VPC) e um ponto de acesso.
  2. Adicione a configuração do sistema de arquivos à sua função Lambda. Especifique o ARN do ponto de acesso e o caminho de montagem local (por exemplo, /mnt/data). Sua função deve estar em uma VPC com acesso ao destino de montagem. Para throughput ideal em arquivos grandes, configure sua função com 512 MB ou mais de memória para ativar leituras diretas do S3.
  3. Se você estiver modernizando o código de sua função Lambda existente, substitua o código de transferência boto3 por caminhos de arquivo. Altere s3.download_file(bucket, key, '/tmp/file') para open('/mnt/data/' + key) e remova a lógica de upload e limpeza.

A função de execução da sua função precisa das permissões s3files:ClientMount e s3files:ClientWrite (incluídas na política gerenciada AmazonS3FilesClientReadWriteAccess). Para leituras diretas do S3 em arquivos grandes, adicione também s3:GetObject e s3:GetObjectVersion.

Padrão 1: Espaço de trabalho compartilhado multi-agente

Cargas de trabalho de IA agêntica, onde múltiplos agentes autônomos colaboram em uma tarefa, requerem estado mutável compartilhado. Os agentes precisam ler as saídas uns dos outros, escrever artefatos intermediários e coordenar sem acoplamento rígido. Com o Lambda hoje, isso normalmente significa serializar o estado para objetos S3 ou Amazon DynamoDB entre cada etapa, adicionando latência e código para cada transferência.

O S3 Files fornece a múltiplas funções Lambda um sistema de arquivos compartilhado. Os agentes se comunicam através do próprio sistema de arquivos, sem chamadas de API S3 e sem sobrecarga de serialização.

Exemplo: Agentes de pesquisa colaborativa

Três funções Lambda montam o mesmo bucket S3 em /mnt/workspace. Um orquestrador prepara a tarefa, agentes de pesquisa trabalham em paralelo e um agente de síntese combina suas descobertas:

import os
import json

WORKSPACE = "/mnt/workspace"

# --- Orchestrator Agent ---
def orchestrator_handler(event, context):
    session_id = event["session_id"]
    session_dir = f"{WORKSPACE}/sessions/{session_id}"

    os.makedirs(f"{session_dir}/research", exist_ok=True)
    os.makedirs(f"{session_dir}/output", exist_ok=True)

    # Write task assignments directly to shared workspace
    with open(f"{session_dir}/manifest.json", "w") as f:
        json.dump({
            "query": event["research_query"],
            "agents": ["market_analysis", "technical_review", "competitor_scan"],
            "status": "in_progress"
        }, f)

    return {"session_dir": session_dir}


# --- Research Agent (one of many, running in parallel) ---
def research_agent_handler(event, context):
    session_dir = event["session_dir"]
    agent_name = event["agent_name"]

    # Read task from shared workspace (no S3 GET call)
    manifest = json.load(open(f"{session_dir}/manifest.json"))

    # Perform research (invoke Amazon Bedrock, search, etc.)
    # TODO: Implement perform_research() for your use case
    findings = perform_research(manifest["query"], agent_name)

    # Write results to shared workspace (no S3 PUT call)
    with open(f"{session_dir}/research/{agent_name}.json", "w") as f:
        json.dump(findings, f, indent=2)

    return {"status": "complete", "agent": agent_name}


# --- Synthesis Agent ---
def synthesis_handler(event, context):
    session_dir = event["session_dir"]

    # Read all research outputs from shared directory
    all_findings = {}
    for f in os.listdir(f"{session_dir}/research"):
        with open(f"{session_dir}/research/{f}") as fh:
            all_findings[f.replace(".json", "")] = json.load(fh)

    # Synthesize and write final report
    # TODO: Implement synthesize_findings() for your use case
    report = synthesize_findings(all_findings)
    with open(f"{session_dir}/output/report.md", "w") as f:
        f.write(report)

    return {"report_path": f"{session_dir}/output/report.md"}

Na abordagem tradicional, cada agente precisaria chamar s3.get_object() para ler o manifesto, s3.put_object() para escrever descobertas, e o agente de síntese precisaria chamar s3.list_objects() e então s3.get_object() para cada resultado. São oito ou mais chamadas de API S3 por execução de fluxo de trabalho substituídas por I/O de arquivo.

O que o padrão de espaço de trabalho compartilhado oferece:

  • Os agentes descobrem as saídas uns dos outros listando um diretório (nenhuma lógica de coordenação necessária).
  • Sessões, agentes e saídas mapeiam para diretórios, não convenções de chaves de objeto planas.
  • A consistência de fechar-para-abrir significa que quando um agente fecha um arquivo após escrever, o próximo agente a abri-lo vê o conteúdo completo.
  • Não há necessidade de organizar o estado em chamadas PutObject do S3 entre etapas.

Padrão 2: Geração de miniaturas de imagem

O gerador de miniaturas S3 é um padrão comum de Lambda + S3. Uma imagem é carregada no S3, uma função Lambda é acionada, ela baixa a imagem, redimensiona com Pillow e faz upload da miniatura para um bucket de destino.

A abordagem tradicional

import boto3
import os
import uuid
from urllib.parse import unquote_plus
from PIL import Image

s3_client = boto3.client('s3')

def resize_image(image_path, resized_path):
    with Image.open(image_path) as image:
        image.thumbnail(tuple(x / 2 for x in image.size))
        image.save(resized_path)

def handler(event, context):
    for record in event['Records']:
        bucket = record['s3']['bucket']['name']
        key = unquote_plus(record['s3']['object']['key'])
        tmpkey = key.replace('/', '')
        download_path = '/tmp/{}{}'.format(uuid.uuid4(), tmpkey)
        upload_path = '/tmp/resized-{}'.format(tmpkey)

        s3_client.download_file(bucket, key, download_path)
        resize_image(download_path, upload_path)
        s3_client.upload_file(
            upload_path, '{}-resized'.format(bucket), 'resized-{}'.format(key)
        )

O que esta abordagem requer que você gerencie além da lógica central de redimensionamento:

  • Orquestração de transferência: Baixar a origem, fazer upload do resultado e lidar com falhas de transferência parcial.
  • Capacidade de armazenamento: Tanto a imagem de origem quanto a redimensionada devem caber em /tmp simultaneamente.
  • Limpeza de armazenamento efêmero: Se a função falhar no meio da execução ou for reutilizada entre invocações, arquivos órfãos podem se acumular em /tmp.
  • Downloads redundantes: Se a mesma imagem acionar uma nova tentativa, ela deve ser baixada novamente.

Com a abordagem de sistema de arquivos

import os
from urllib.parse import unquote_plus
from PIL import Image

MOUNT = "/mnt/images"

def resize_image(image_path, resized_path):
    with Image.open(image_path) as image:
        image.thumbnail(tuple(x / 2 for x in image.size))
        image.save(resized_path)

def handler(event, context):
    for record in event['Records']:
        key = unquote_plus(record['s3']['object']['key'])
        input_path = f"{MOUNT}/source/{key}"
        output_path = f"{MOUNT}/resized/resized-{os.path.basename(key)}"

        os.makedirs(os.path.dirname(output_path), exist_ok=True)
        resize_image(input_path, output_path)

O que mudou

A função passa de um pipeline de baixar-processar-fazer upload para I/O de arquivo direto. Sem cliente boto3, sem gerenciamento de /tmp, sem etapa de upload. A função resize_image permanece inalterada porque sempre funcionou com caminhos de arquivo. A diferença é que esses caminhos agora apontam para um sistema de arquivos S3 montado em vez de armazenamento local efêmero.

Você ainda lida com erros em sua lógica de processamento (por exemplo, formatos de imagem inválidos). O que você não precisa mais lidar são modos de falha específicos de transferência, como downloads parciais, uploads com falha ou verificações de capacidade de /tmp.

Métrica Tradicional S3 Files
Linhas de código (não vazias) 22 15
Chamadas de API S3 por invocação 2 (GET + PUT) 0
Tamanho máximo de imagem Arquivos de origem + saída compartilham /tmp Sem restrição de /tmp
Dependência boto3 Obrigatória Não necessária

Padrão 3: Pipeline ETL CSV para Parquet

Outro padrão ETL Serverless comumente usado é um evento S3 que aciona uma função Lambda quando arquivos CSV chegam em um bucket. A função baixa o CSV, transforma-o em Parquet usando pandas e pyarrow, e faz upload do resultado.

A abordagem tradicional

import boto3
import pandas as pd
import os

s3 = boto3.client("s3")
BUCKET = "data-pipeline-bucket"

def handler(event, context):
    key = event["Records"][0]["s3"]["object"]["key"]
    filename = os.path.basename(key)
    local_input = f"/tmp/{filename}"
    local_output = f"/tmp/{filename.replace('.csv', '.parquet')}"

    try:
        # Download from S3
        s3.download_file(BUCKET, key, local_input)

        # Check /tmp space (10 GB limit)
        tmp_usage = sum(
            os.path.getsize(f"/tmp/{f}")
            for f in os.listdir("/tmp") if os.path.isfile(f"/tmp/{f}")
        )
        if tmp_usage > 9 * 1024**3:  # 9 GB safety margin
            raise RuntimeError("Approaching /tmp storage limit")

        # Transform
        df = pd.read_csv(local_input)
        df["processed_at"] = pd.Timestamp.now()
        df.to_parquet(local_output, engine="pyarrow", compression="snappy")

        # Upload result back to S3
        output_key = key.replace("raw/", "processed/").replace(".csv", ".parquet")
        s3.upload_file(local_output, BUCKET, output_key)

        return {"status": "success", "output_key": output_key}

    finally:
        # Clean up /tmp
        for f in [local_input, local_output]:
            if os.path.exists(f):
                os.remove(f)

O que esta abordagem requer que você gerencie além da lógica central de transformação:

  • Capacidade de armazenamento: Arquivos de origem e saída compartilham /tmp, limitando o tamanho prático do arquivo.
  • Custo de inicialização a frio: Inicializar o cliente boto3 adiciona latência de inicialização.
  • Modos de falha de transferência: Downloads parciais, uploads com falha e arquivos /tmp órfãos precisam de seu próprio tratamento.
  • Downloads redundantes: Novas tentativas ou reprocessamento requerem baixar o mesmo arquivo novamente.

Com a abordagem de sistema de arquivos

import pandas as pd
import os

MOUNT = "/mnt/data"

def handler(event, context):
    key = event["Records"][0]["s3"]["object"]["key"]
    input_path = f"{MOUNT}/{key}"
    output_path = f"{MOUNT}/{key.replace('raw/', 'processed/').replace('.csv', '.parquet')}"

    # Ensure output directory exists
    os.makedirs(os.path.dirname(output_path), exist_ok=True)

    # Transform: direct file access, no download/upload
    df = pd.read_csv(input_path)
    df["processed_at"] = pd.Timestamp.now()
    df.to_parquet(output_path, engine="pyarrow", compression="snappy")

    return {"status": "success", "output_path": output_path}

O que mudou

Com esta mudança, um desenvolvedor lendo este código vê apenas a lógica de transformação (ler CSV, adicionar coluna, escrever Parquet). A mecânica de armazenamento é tratada pelo sistema de arquivos.

Métrica Tradicional S3 Files
Linhas de código (não vazias) 33 14
Chamadas de API S3 por invocação 2 (GET + PUT) 0
Tamanho máximo de arquivo Origem + saída compartilham /tmp Sem restrição de /tmp
Lógica de limpeza necessária Sim Não
Monitoramento de espaço /tmp Sim Não

Escolhendo a abordagem certa: montagens de sistema de arquivos vs. acesso tradicional

Caso de uso Recomendação
Lambda lê/escreve arquivos do S3 S3 Files (elimina código padrão de transferência)
Múltiplas funções compartilham dados S3 Files (montagem compartilhada substitui coordenação de API)
Arquivos > 10 GB S3 Files (sem restrição de tamanho de /tmp)
Processamento orientado a eventos (acionado no upload) S3 Files (gatilhos de eventos S3 ainda funcionam, função lê da montagem)
Recursos diretos da API S3 (URLs pré-assinadas, S3 Select, upload multipart) Tradicional (estes requerem a API S3)
Funções fora de uma VPC Tradicional (S3 Files requer conectividade VPC)

Limpeza

Se você criou recursos enquanto acompanhava esta publicação, exclua-os para evitar incorrer em custos futuros. Comece removendo a configuração do sistema de arquivos das configurações da sua função Lambda. Em seguida, remova o sistema de arquivos S3, que também exclui seus destinos de montagem e pontos de acesso associados. Depois, exclua os buckets S3 usados para dados de origem e saída, juntamente com as funções Lambda criadas para os exemplos. Por fim, remova as funções e políticas IAM criadas para execução do Lambda ou, se você adicionou as permissões do S3 Files (s3files:ClientMount, s3files:ClientWrite, s3:GetObject, s3:GetObjectVersion) a uma função existente, remova essas permissões. Além disso, se você criou uma nova VPC para este tutorial, exclua a VPC, o que também removerá as sub-redes privadas, grupos de segurança e tabelas de rotas associadas. Se você usou uma VPC existente, remova os grupos de segurança e sub-redes criados para este teste.

Aviso: A exclusão de um bucket S3 e seu conteúdo exclui permanentemente todos os objetos nos buckets e não pode ser desfeita. Certifique-se de ter feito backup de todos os dados que você precisa reter antes de prosseguir.

Conclusão

Nesta publicação, demonstramos como modernizar três cargas de trabalho comuns de Lambda + S3 usando o Amazon S3 Files. Em geração de miniaturas de imagem, pipelines ETL e cargas de trabalho de IA multi-agente, a migração segue o mesmo princípio: substituir a lógica de transferência da API S3 por I/O de arquivo nativo e deixar o sistema de arquivos lidar com a sincronização.

As melhorias são consistentes:

  • Menos código: A lógica de transferência e limpeza desaparece, deixando apenas sua lógica de processamento.
  • Sem restrição de tamanho de /tmp: Processe arquivos grandes sem limites de armazenamento local.
  • Zero chamadas de API S3 para acesso a dados: Leituras e escritas passam pela montagem do sistema de arquivos.
  • Menos modos de falha para lidar: Problemas específicos de transferência (downloads parciais, uploads com falha, arquivos temporários órfãos) não se aplicam mais.

Para equipes executando cargas de trabalho Lambda + S3 hoje, o S3 Files não é uma nova arquitetura para aprender. É código de transferência que você pode remover. Para saber mais, consulte a seção S3 Files na documentação do Lambda. Para acompanhar os próximos recursos no roadmap do AWS Lambda, você pode consultar o roadmap do AWS Lambda.


Este conteúdo foi traduzido do post original do blog, que pode ser encontrado aqui.

Autores

Sahithi Ginjupalli é Cloud Support Engineer II na Amazon Web Services.
D Surya Sai é Technical Account Manager na Amazon Web Services.

Tradutores

Daniel Abib é Arquiteto de Soluções Sênior e Especialista em Amazon Bedrock na AWS, com mais de 25 anos trabalhando com gerenciamento de projetos, arquiteturas de soluções escaláveis, desenvolvimento de sistemas e CI/CD, microsserviços, arquitetura Serverless & Containers e especialização em Machine Learning. Ele trabalha apoiando Startups, ajudando-os em sua jornada para a nuvem.
https://www.linkedin.com/in/danielabib/
Nicolas Tarzia é Senior Technical Account Manager na AWS, com mais de 13 anos de experiência, com ampla experiência em arquitetura cloud, engenharia e design de software. Atualmente está habilitando empresas do ramo de ISV (Independent Software Vendors) simplificando a operação na nuvem e otimizando os custos em cloud. Sua área de interesse são tecnologias serverless.
https://www.linkedin.com/in/nicolastarzia