Criar uma aplicação de processamento de arquivos com tecnologia sem servidor
Um dos casos de uso mais comuns do Lambda é a realização de tarefas de processamento de arquivos. Por exemplo, você pode usar uma função do Lambda para criar automaticamente arquivos PDF a partir de arquivos HTML ou de imagens, ou para criar miniaturas quando um usuário carrega uma imagem.
Neste exemplo, você cria uma aplicação que criptografa automaticamente os arquivos PDF quando eles são carregados em um bucket do Amazon Simple Storage Service (Amazon S3). Para implementar essa aplicação, você cria os seguintes recursos:
-
Um bucket do S3 no qual os usuários carregam arquivos PDF
-
Uma função do Lambda em Python que lê o arquivo carregado e cria uma versão dele criptografada e protegida por senha
-
Um segundo bucket do S3 no qual o Lambda salva o arquivo criptografado
Você também cria uma política do AWS Identity and Access Management (IAM) para dar à função do Lambda permissão para realizar operações de leitura e gravação nos buckets do S3.
dica
Se você nunca tiver trabalhado com o Lambda antes, recomendamos que faça o tutorial Criar sua primeira função do Lambda antes de criar a aplicação deste exemplo.
Você pode implantar a aplicação manualmente criando e configurando os recursos com o AWS Management Console ou a AWS Command Line Interface (AWS CLI). Você também pode implantar a aplicação usando o AWS Serverless Application Model (AWS SAM). O AWS SAM é uma ferramenta de infraestrutura como código (IaC). Usando IaC, você não cria os recursos manualmente, mas os define no código e depois os implanta automaticamente.
Se você quiser saber mais sobre como usar o Lambda com IaC antes de implantar esta aplicação de exemplo, consulte Usar o Lambda com a infraestrutura como código (IaC).
Pré-requisitos
Antes de criar a aplicação de exemplo, verifique se as ferramentas de linha de comando necessárias estão instaladas.
-
AWS CLI
Você pode implantar manualmente os recursos para a aplicação usando o AWS Management Console ou a AWS CLI. Para usar a CLI, instale-a seguindo as installation instructions do AWS Command Line Interface User Guide.
-
AWS SAM CLI
Se você quiser implantar a aplicação de exemplo usando o AWS SAM, precisará instalar a AWS CLI e a CLI do AWS SAM. Para usar a CLI do AWS SAM, siga as installation instructions do AWS SAM User Guide.
-
módulo pytest
Depois que implantar a aplicação, você pode testá-la usando um script de teste automatizado em Python que fornecemos. Para usar esse script, instale o pacote
pytest
no ambiente de desenvolvimento local executando o seguinte comando:pip install pytest
Para implantar a aplicação usando o AWS SAM, o Docker
Baixar os arquivos da aplicação de exemplo
Para criar e testar a aplicação de exemplo, você cria os seguintes arquivos no diretório do projeto:
-
lambda_function.py
: o código de função em Python para a função do Lambda que realiza a criptografia de arquivos -
requirements.txt
: um arquivo de manifesto que define as dependências que o código de função em Python exige -
template.yaml
: um modelo do AWS SAM que você pode usar para implantar a aplicação -
test_pdf_encrypt.py
: um script de teste que você pode usar para testar automaticamente a aplicação -
pytest.ini
: um arquivo de configuração para o script de teste
Expanda as seções a seguir para visualizar o código e saber mais sobre o papel de cada arquivo na criação e no teste da aplicação. Para criar os arquivos na máquina local, copie e cole o código abaixo ou baixe os arquivos do repositório aws-lambda-developer-guide
do GitHub
Copie e cole o código a seguir em um novo arquivo denominado lambda_function.py
.
from pypdf import PdfReader, PdfWriter import uuid import os from urllib.parse import unquote_plus import boto3 # Create the S3 client to download and upload objects from S3 s3_client = boto3.client('s3') def lambda_handler(event, context): # Iterate over the S3 event object and get the key for all uploaded files for record in event['Records']: bucket = record['s3']['bucket']['name'] key = unquote_plus(record['s3']['object']['key']) # Decode the S3 object key to remove any URL-encoded characters download_path = f'/tmp/{uuid.uuid4()}.pdf' # Create a path in the Lambda tmp directory to save the file to upload_path = f'/tmp/converted-{uuid.uuid4()}.pdf' # Create another path to save the encrypted file to # If the file is a PDF, encrypt it and upload it to the destination S3 bucket if key.lower().endswith('.pdf'): s3_client.download_file(bucket, key, download_path) encrypt_pdf(download_path, upload_path) encrypted_key = add_encrypted_suffix(key) s3_client.upload_file(upload_path, f'{bucket}-encrypted', encrypted_key) # Define the function to encrypt the PDF file with a password def encrypt_pdf(file_path, encrypted_file_path): reader = PdfReader(file_path) writer = PdfWriter() for page in reader.pages: writer.add_page(page) # Add a password to the new PDF writer.encrypt("my-secret-password") # Save the new PDF to a file with open(encrypted_file_path, "wb") as file: writer.write(file) # Define a function to add a suffix to the original filename after encryption def add_encrypted_suffix(original_key): filename, extension = original_key.rsplit('.', 1) return f'{filename}_encrypted.{extension}'
nota
No código deste exemplo, uma senha para o arquivo criptografado (my-secret-password
) é gravada permanentemente no código da função. Não inclua informações confidenciais, como senhas, no código de função, em uma aplicação em produção. Use o AWS Secrets Manager para armazenar parâmetros confidenciais em segurança.
O código de função em python contém três funções: a função de handler, que o Lambda executa quando a função é invocada, e duas funções separadas denominadas add_encrypted_suffix
e encrypt_pdf
, que o handler chama para realizar criptografia de PDF.
Quando a função é invocada pelo Amazon S3, o Lambda passa um argumento evento no formato JSON para a função que contém os detalhes do evento que causou a invocação. Nesse caso, as informações incluem o nome do bucket do S3 e as chaves de objeto dos arquivos carregados. Para saber mais sobre o formato do objeto de evento para o Amazon S3, consulte Processar notificações de eventos do Amazon S3 com o Lambda.
A função então usa o AWS SDK for Python (Boto3) para baixar os arquivos PDF especificados no objeto de evento para o diretório de armazenamento temporário local antes de criptografá-los usando a biblioteca pypdf
Por fim, a função usa o SDK do Boto3 para armazenar o arquivo criptografado no bucket do S3 de destino.
Copie e cole o código a seguir em um novo arquivo denominado requirements.txt
.
boto3 pypdf
Neste exemplo, o código da função tem apenas duas dependências que não fazem parte da biblioteca Python padrão: o SDK para Python (Boto3) e o pacote pypdf
que a função usa para realizar dos arquivos PDF.
nota
Uma versão do SDK para Python (Boto3) está incluída como parte do runtime do Lambda, portanto, o código seria executado sem adicionar o Boto3 ao pacote de implantação da função. Porém, para manter total controle das dependências da função e evitar possíveis problemas de desalinhamento de versões, a prática recomendada para o Python é incluir todas as dependências da função no pacote de implantação da função. Para saber mais, consulte Dependências de runtime em Python.
Copie e cole o código a seguir em um novo arquivo denominado template.yaml
.
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Resources: EncryptPDFFunction: Type: AWS::Serverless::Function Properties: FunctionName: EncryptPDF Architectures: [x86_64] CodeUri: ./ Handler: lambda_function.lambda_handler Runtime: python3.12 Timeout: 15 MemorySize: 256 LoggingConfig: LogFormat: JSON Policies: - AmazonS3FullAccess Events: S3Event: Type: S3 Properties: Bucket: !Ref PDFSourceBucket Events: s3:ObjectCreated:* PDFSourceBucket: Type: AWS::S3::Bucket Properties: BucketName: EXAMPLE-BUCKET EncryptedPDFBucket: Type: AWS::S3::Bucket Properties: BucketName: EXAMPLE-BUCKET-encrypted
O modelo do AWS SAM define os recursos que você cria para a aplicação. Neste exemplo, o modelo define uma função do Lambda usando o tipo AWS::Serverless::Function
e dois buckets do S3 usando o tipo AWS::S3::Bucket
. Os nomes dos buckets especificados no modelo são espaços reservados. Antes de implantar a aplicação usando o AWS SAM, você precisa editar o modelo para renomear os buckets com nomes globalmente exclusivos que atendam às regras de nomenclatura de buckets do S3. Essa etapa é explicada com mais detalhes em Implantar recursos usando o AWS SAM.
A definição do recurso da função do Lambda configura um gatilho para a função usando a propriedade de evento S3Event
. Esse gatilho faz com que a função seja invocada sempre que um objeto for criado no bucket de origem.
A definição da função também especifica uma política do AWS Identity and Access Management (IAM) a ser anexada ao perfil de execução da função. A política gerenciada da AWS AmazonS3FullAccess
concede à função as permissões necessárias para ler e gravar objetos no Amazon S3.
Copie e cole o código a seguir em um novo arquivo denominado test_pdf_encrypt.py
.
import boto3 import json import pytest import time import os @pytest.fixture def lambda_client(): return boto3.client('lambda') @pytest.fixture def s3_client(): return boto3.client('s3') @pytest.fixture def logs_client(): return boto3.client('logs') @pytest.fixture(scope='session') def cleanup(): # Create a new S3 client for cleanup s3_client = boto3.client('s3') yield # Cleanup code will be executed after all tests have finished # Delete test.pdf from the source bucket source_bucket = 'EXAMPLE-BUCKET' source_file_key = 'test.pdf' s3_client.delete_object(Bucket=source_bucket, Key=source_file_key) print(f"\nDeleted {source_file_key} from {source_bucket}") # Delete test_encrypted.pdf from the destination bucket destination_bucket = 'EXAMPLE-BUCKET-encrypted' destination_file_key = 'test_encrypted.pdf' s3_client.delete_object(Bucket=destination_bucket, Key=destination_file_key) print(f"Deleted {destination_file_key} from {destination_bucket}") @pytest.mark.order(1) def test_source_bucket_available(s3_client): s3_bucket_name = 'EXAMPLE-BUCKET' file_name = 'test.pdf' file_path = os.path.join(os.path.dirname(__file__), file_name) file_uploaded = False try: s3_client.upload_file(file_path, s3_bucket_name, file_name) file_uploaded = True except: print("Error: couldn't upload file") assert file_uploaded, "Could not upload file to S3 bucket" @pytest.mark.order(2) def test_lambda_invoked(logs_client): # Wait for a few seconds to make sure the logs are available time.sleep(5) # Get the latest log stream for the specified log group log_streams = logs_client.describe_log_streams( logGroupName='/aws/lambda/EncryptPDF', orderBy='LastEventTime', descending=True, limit=1 ) latest_log_stream_name = log_streams['logStreams'][0]['logStreamName'] # Retrieve the log events from the latest log stream log_events = logs_client.get_log_events( logGroupName='/aws/lambda/EncryptPDF', logStreamName=latest_log_stream_name ) success_found = False for event in log_events['events']: message = json.loads(event['message']) status = message.get('record', {}).get('status') if status == 'success': success_found = True break assert success_found, "Lambda function execution did not report 'success' status in logs." @pytest.mark.order(3) def test_encrypted_file_in_bucket(s3_client): # Specify the destination S3 bucket and the expected converted file key destination_bucket = 'EXAMPLE-BUCKET-encrypted' converted_file_key = 'test_encrypted.pdf' try: # Attempt to retrieve the metadata of the converted file from the destination S3 bucket s3_client.head_object(Bucket=destination_bucket, Key=converted_file_key) except s3_client.exceptions.ClientError as e: # If the file is not found, the test will fail pytest.fail(f"Converted file '{converted_file_key}' not found in the destination bucket: {str(e)}") def test_cleanup(cleanup): # This test uses the cleanup fixture and will be executed last pass
O script de teste automatizado executa três funções de teste para confirmar que a aplicação está funcionando corretamente:
-
O teste
test_source_bucket_available
confirma que o bucket de origem foi criado com sucesso carregando nele um arquivo PDF de teste. -
O teste
test_lambda_invoked
interroga o último fluxo de logs do CloudWatch Logs para a função para confirmar que, quando você carregou o arquivo de teste, a função do Lambda foi executada e reportada como bem-sucedida. -
O teste
test_encrypted_file_in_bucket
confirma que o bucket de destino contém o arquivotest_encrypted.pdf
criptografado.
Após a execução de todos esses testes, o script executa uma etapa adicional de limpeza para excluir os arquivos test.pdf
e test_encrypted.pdf
dos buckets de origem e de destino.
Como no modelo do AWS SAM, os nomes de bucket especificados nesse arquivo são espaços reservados. Antes de executar o teste, você precisa editar esse arquivo com os nomes reais dos buckets da aplicação. Essa etapa é explicada com mais detalhes em Testar a aplicação com o script automatizado
Copie e cole o código a seguir em um novo arquivo denominado pytest.ini
.
[pytest] markers = order: specify test execution order
Isso é necessário para especificar a ordem em que os testes do script test_pdf_encrypt.py
são executados.
Implantar a aplicação
Você pode criar e implantar os recursos para a aplicação de exemplo manualmente ou usando o AWS SAM. Em um ambiente de produção, recomendamos o uso de uma ferramenta de IaC, como o AWS SAM, para implantar integralmente as aplicações de tecnologia sem servidor com rapidez, repetidas vezes, sem usar processos manuais.
Neste exemplo, siga as instruções do console ou da AWS CLI para aprender a configurar cada recurso da AWS separadamente, ou avance para Implantar recursos usando o AWS SAM e implante a aplicação rapidamente usando alguns comandos da CLI.
Implantar os recursos manualmente
Para implantar a aplicação manualmente, realize as seguintes etapas:
-
Crie os buckets do Amazon S3 de origem e de destino
-
Criar uma função do Lambda que criptografa um arquivo PDF e salva a versão criptografada em um bucket do S3
-
Configure um gatilho do Lambda que invoca a função quando os objetos são carregados no bucket de origem
Siga as instruções dos parágrafos a seguir para criar e configurar os recursos.
Criar dois buckets do S3
Primeiro, crie dois buckets do S3. O primeiro bucket corresponde ao bucket de origem no qual você vai carregar as imagens. O segundo bucket é usado pelo Lambda para salvar o arquivo criptografado quando você invoca a função.
Criar um perfil de execução (AWS CLI apenas)
Um perfil de execução é um perfil do IAM que concede a uma função do Lambda permissão para acessar os recursos e Serviços da AWS. Quando você cria uma função usando o console do Lambda, o Lambda cria automaticamente um perfil de execução. Você só precisa criar um perfil manualmente se escolher implantar a aplicação usando o AWS CLI. Para conceder à função acesso de leitura e gravação ao Amazon S3, anexe a política gerenciada da AWS AmazonS3FullAccess
.
Crie o pacote de implantação de função
Para criar sua função, crie um pacote de implantação contendo o código de função e as dependências. Para essa aplicação, o código de função usa uma biblioteca separada para a criptografia dos arquivos PDF.
Para criar o pacote de implantação
-
Navegue até o diretório do projeto que contém os arquivos
lambda_function.py
erequirements.txt
que você criou ou baixou anteriormente do GitHub e crie um novo diretório denominadopackage
. -
Instale as dependências especificadas no arquivo
requirements.txt
no diretóriopackage
executando o comando a seguir.pip install -r requirements.txt --target ./package/
-
Crie um arquivo .zip contendo o código da aplicação e suas dependências. No Linux ou no MacOS, execute os comandos a seguir na interface da linha de comando.
cd package zip -r ../lambda_function.zip . cd .. zip lambda_function.zip lambda_function.py
No Windows, use sua ferramenta de zip preferida para criar o arquivo
lambda_function.zip
. Certifique-se de que o arquivolambda_function.py
e as pastas que contêm as dependências estejam todos na raiz do arquivo .zip.
Você também pode criar seu pacote de implantação usando um ambiente virtual Python. Consulte Trabalhar com arquivos .zip para funções do Lambda em Python
Criar a função do Lambda
Agora, você usa o pacote de implantação criado na etapa anterior para implantar a função do Lambda.
Configurar um gatilho do Amazon S3 para invocar a função
Para que a função do Lambda seja executada quando você carregar uma imagem no bucket de origem, é necessário configurar um gatilho para a função. Você pode configurar o acionador do Amazon S3 usando o console ou a AWS CLI.
Importante
Este procedimento configura o bucket do S3 para invocar a função sempre que um objeto é criado no bucket. Certifique-se de configurar isso somente no bucket de origem. Se a função do Lambda criar objetos no mesmo bucket que a invoca, a função poderá ser invocada continuamente em um loop
Implantar recursos usando o AWS SAM
Para implantar a aplicação de exemplo usando a CLI do AWS SAM, realize as etapas a seguir.
Certifique-se de que você instalou a última versão da CLI e de que o Docker
-
Edite o arquivo
template.yaml
para especificar o nome dos buckets do S3. Os buckets do S3 devem ter nomes globalmente exclusivos que atendam às regras de nomenclatura do S3.Substitua o nome do bucket
EXAMPLE-BUCKET
por um nome de sua escolha composto por letras minúsculas, números, pontos (.) e hifens (-). Para o bucket de destino, substituaEXAMPLE-BUCKET-encrypted
por<source-bucket-name>-encrypted
, onde<source-bucket>
é o nome que você escolheu para o bucket de origem. -
Execute o comando a seguir no diretório em que você salvou os arquivos
template.yaml
,lambda_function.py
erequirements.txt
.sam build --use-container
Esse comando reúne os artefatos de compilação da aplicação e os coloca no formato e no local adequados para implantá-los. Especificar a opção
--use-container
compila a função dentro de um contêiner do Docker semelhante ao Lambda. Nós o usamos aqui para que você não precise ter o Python 3.12 instalado na máquina local para que a compilação funcione.Durante o processo de compilação, o AWS SAM procura o código da função do Lambda no local que você especificou com a propriedade
CodeUri
no modelo. Neste caso, especificamos o diretório atual como o local (./
).Se houver um arquivo
requirements.txt
presente, o AWS SAM o usará para coletar as dependências especificadas. Por padrão, o AWS SAM cria um pacote de implantação .zip com o código e as dependências da função. Você também pode escolher implantar a função como uma imagem de contêiner usando a propriedade PackageType. -
Para implantar a aplicação e criar os recursos do Lambda e do Amazon S3 especificados no modelo do AWS SAM, execute o comando a seguir.
sam deploy --guided
Usar o sinalizador
--guided
significa que o AWS SAM mostrará instruções para guiar você no processo de implantação. Para a implantação, aceite as opções padrão pressionando Enter.
Durante o processo de implantação, o AWS SAM cria os seguintes recursos na sua Conta da AWS:
-
Uma pilha do AWS CloudFormation chamada
sam-app
-
Uma função do Lambda com o nome de
EncryptPDF
-
Dois buckets do S3 com os nomes que você escolheu quando editou o arquivo
template.yaml
do modelo do AWS SAM -
Um perfil de execução do IAM para sua função com o formato de nome
sam-app-EncryptPDFFunctionRole-
2qGaapHFWOQ8
Quando o AWS SAM termina de criar os recursos, você deve ver a seguinte mensagem:
Successfully created/updated stack - sam-app in us-west-2
Testando a aplicação
Para testar a aplicação, você carrega um arquivo PDF no bucket de origem e confirma que o Lambda cria uma versão criptografada do arquivo no bucket de destino. Neste exemplo, você pode testar isso manualmente usando o console ou a AWS CLI, ou usando o script de teste fornecido.
Para aplicações em produção, você pode usar os métodos e as técnicas de teste tradicionais, como testes de unidade, para confirmar o funcionamento correto do código da função do Lambda. A prática recomendada também é realizar testes como os do script de teste fornecido que realizam testes de integração com recursos reais baseados na nuvem. Os testes de integração na nuvem confirmam que a infraestrutura foi implantada corretamente e que os eventos fluem entre os diferentes serviços como esperado. Para saber mais, consulte Como testar funções e aplicações com tecnologia sem servidor.
Testar a aplicação manualmente
Você pode testar a função manualmente adicionando um arquivo PDF ao bucket de origem do Amazon S3. Quando você adiciona o arquivo ao bucket de origem, a função do Lambda deve ser invocada automaticamente e uma versão criptografada do arquivo deve ser armazenada no bucket de destino.
Testar a aplicação com o script automatizado
Para testar a aplicação usando o script de teste fornecido, primeiro certifique-se de que o módulo pytest
esteja instalado no ambiente local. Você pode instalar o pytest
executando o seguinte comando:
pip install pytest
Você também precisa editar o código no arquivo test_pdf_encrypt.py
para substituir os nomes de bucket de espaço reservado pelos nomes dos buckets de origem e destino do Amazon S3. Faça as seguintes alterações em test_pdf_encrypt.py
:
-
Na função
test_source_bucket_available
, substituaEXAMPLE-BUCKET
pelo nome do bucket de origem. -
Na função
test_encrypted_file_in_bucket
, substituaEXAMPLE-BUCKET-encrypted
por<source-bucket>-encrypted
, onde<source-bucket>
é o nome do bucket de origem. -
Na função
cleanup
, substituaEXAMPLE-BUCKET
pelo nome do bucket de origem e substituaEXAMPLE-BUCKET-encrypted
por≪source-bucket>-encrypted
, onde<source-bucket>
é o nome do seu bucket de origem.
Para executar os testes, faça o seguinte:
-
Salve um arquivo PDF denominado
test.pdf
no diretório que contém os arquivostest_pdf_encrypt.py
epytest.ini
. -
Abra um terminal ou um programa shell e execute o comando a seguir no diretório que contém os arquivos de teste.
pytest -s -v
Quando o teste for concluído, você verá uma saída como a seguinte:
============================================================== test session starts =========================================================
platform linux -- Python 3.12.2, pytest-7.2.2, pluggy-1.0.0 -- /usr/bin/python3
cachedir: .pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/pdf_encrypt_app/.hypothesis/examples')
Test order randomisation NOT enabled. Enable with --random-order or --random-order-bucket=<bucket_type>
rootdir: /home/pdf_encrypt_app, configfile: pytest.ini
plugins: anyio-3.7.1, hypothesis-6.70.0, localserver-0.7.1, random-order-1.1.0
collected 4 items
test_pdf_encrypt.py::test_source_bucket_available PASSED
test_pdf_encrypt.py::test_lambda_invoked PASSED
test_pdf_encrypt.py::test_encrypted_file_in_bucket PASSED
test_pdf_encrypt.py::test_cleanup PASSED
Deleted test.pdf from EXAMPLE-BUCKET
Deleted test_encrypted.pdf from EXAMPLE-BUCKET
-encrypted
=============================================================== 4 passed in 7.32s ==========================================================
Próximas etapas
Agora que você criou esta aplicação de exemplo, pode usar o código fornecido como base para criar outros tipos de aplicação de processamento de arquivos. Modifique o código no arquivo lambda_function.py
para implementar a lógica de processamento de arquivos para seu caso de uso.
Muitos casos de uso típicos de processamento de arquivos envolvem processamento de imagens. Ao usar o Python, as bibliotecas de processamento de imagens mais populares, como a pillow
Ao implantar os recursos com o AWS SAM, você precisa tomar algumas medidas adicionais para incluir a distribuição do fonte correto no pacote de implantação. Como o AWS SAM não instala dependências para uma plataforma diferente da plataforma da máquina de compilação, especificar a distribuição do fonte correto (arquivo .whl
) no arquivo requirements.txt
não funcionará se sua máquina de compilação usar um sistema operacional ou uma arquitetura diferente do ambiente de execução do Lambda. Em vez disso, você deve realizar um dos seguintes procedimentos:
-
Use a opção
--use-container
ao executarsam build
. Quando você especifica essa opção, o AWS SAM baixa uma imagem de base de contêiner compatível com o ambiente de execução do Lambda e cria o pacote de implantação da função em um contêiner do Docker usando essa imagem. Para saber mais, consulte Compilando uma função do Lambda dentro de um contêiner fornecido. -
Compile você mesmo o pacote de implantação .zip da função usando o binário de distribuição do fonte correto e salve o arquivo.zip no diretório que especificar como
CodeUri
no AWS SAM modelo. Para saber mais sobre a compilação de pacotes de implantação .zip para Python usando distribuições binárias, consulte Criar um pacote de implantação .zip com dependências e Criar pacotes de implantação .zip com bibliotecas nativas.