Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

05 - Serverless (Funções como Serviço)

#cold-start#event-driven#faas#lambda#serverless
video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

Serverless (Funções como Serviço)

Serverless é o modelo em que o provedor de nuvem administra servidores, escalonamento e capacidade ociosa, e o time paga por execução real em vez de máquina alocada. O termo cobre duas famílias complementares: FaaS, funções como serviço, em que código é acionado por evento e executa em ambiente efêmero, e BaaS, backend como serviço, em que capacidades inteiras, como autenticação, armazenamento e filas, são consumidas prontas. A AWS Lambda é a referência histórica e comercial do modelo, com Azure Functions e Google Cloud Functions seguindo a mesma forma. Em produção, serverless brilha em cargas esparsas e orientadas a eventos, e cobra seu preço em restrições de tempo, memória e portabilidade que precisam ser assumidas no desenho, e não descobertas no incidente.

Conceito: FaaS e BaaS

No FaaS, a unidade de implantação é uma função: você envia código, declara gatilhos e o provedor instancia ambientes, escala até milhares de execuções paralelas e devolve a zero quando o tráfego cessa. No BaaS, você usa serviços gerenciados por API, como Cognito para identidade, S3 para objetos e DynamoDB para dados, e seu código próprio encolhe para o que é genuinamente específico do negócio. Sistemas serverless completos combinam os dois: funções orquestram regras, e serviços gerenciados sustentam o resto.

A promessa de escalar até zero é também a definição do perfil ideal de carga: o modelo transfere para o provedor a preocupação com capacidade ociosa e picos, e cobra por uso real. A contrapartida invisível no slide é que esse elasticismo depende de funções stateless, curtas e desenhadas para reentrância, e que qualquer componente de estado persistente precisa ser externo e com capacidade própria planejada.

Aspecto FaaS BaaS
Unidade entregue Função acionada por evento Serviço gerenciado pronto
Código próprio Lógica de negócio da função Configuração e integração
Escala Automática por invocação, até zero Automática, gerida pelo serviço
Exemplos AWS Lambda, Azure Functions Cognito, DynamoDB, S3, Firebase
Risco dominante Cold start e limites de execução Acoplamento ao provedor

Funcionamento Interno no AWS Lambda

A Lambda executa cada função em ambiente isolado, hoje baseado em Firecracker, microVM leve mantida pela AWS. O ciclo tem duas fases: init, em que o código é baixado, o runtime é iniciado e o código de inicialização roda uma vez, e invoke, em que a requisição é processada; o ambiente pode ser reaproveitado para chamadas seguintes, o que explica por que objetos globais sobrevivem entre invocações. A concorrência é o número de ambientes simultâneos, controlável com reserved concurrency para proteger sistemas de apoio e com provisioned concurrency para eliminar latência de preparo. Os limites de formato são parte do contrato: até 15 minutos de execução, memória de 128 MB a 10 GB com CPU proporcional, e teto de payload nos gatilhos síncronos e assíncronos.

# Função Lambda acionada por fila: padrão típico de processamento assíncrono
Resources:
  ProcessarPedidoFn:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.12
      MemorySize: 512          # memória dimensiona também vCPU e rede
      Timeout: 60              # abaixo do teto de 15 minutos
      Tracing: Active          # correlação de trace por invocação
      Events:
        Fila:
          Type: SQS
          Properties:
            Queue: !GetAtt PedidosQueue.Arn
            BatchSize: 10                        # agrupa eventos por execução
            MaximumBatchingWindowInSeconds: 5    # espera para formar lote
  PedidosQueue:
    Type: AWS::SQS::Queue
    Properties:
      RedrivePolicy:          # mensagens venenosas vão para a DLQ
        deadLetterTargetArn: !GetAtt PedidosDLQ.Arn
        maxReceiveCount: 5
  PedidosDLQ:
    Type: AWS::SQS::Queue

Modelos de invocação e seus efeitos práticos:

  • Síncrona: API Gateway ou ALB chama e espera a resposta; latência e erro voltam direto ao cliente, e o teto de payload se aplica.
  • Assíncrona: S3, EventBridge ou SNS disparam, e a plataforma enfileira com retentativas configuráveis; falhas viram eventos em fila de destino.
  • Streams e filas: SQS, Kinesis e DynamoDB Streams entregam em lotes, e o processamento parcial exige marcadores de progresso para não duplicar efeito.

Os limites de plataforma moldam o desenho antes de qualquer linha de código:

  • 15 minutos por execução: jobs longos, como fechamento contábil pesado, não cabem; dividir em passos com Step Functions ou mover para containers.
  • Payload síncrono limitado: arquivos e imagens grandes não viajam no corpo da requisição; sobem em S3 e viajam como referência.
  • Memória de 128 MB a 10 GB: a alocação dimensiona CPU e rede junto, e tarefas de processamento intenso precisam de medição real.
  • Concorrência compartilhada da conta: um burst de uma função pode esgotar a cota e bloquear vizinhas; reserved concurrency protege o que é crítico.
  • Ambiente efêmero: arquivos escritos em /tmp somem entre invocações, e nada de sessão em memória sobrevive a escala.

Cold Start e Estado Externo

Cold start é a latência extra da fase init quando um ambiente novo precisa nascer: acontece no primeiro acesso após deploy, em picos que exigem escala nova e em funções ociosas cujo ambiente foi reciclado. O impacto aparece no p99, e não no p50: a maioria das invocações voa, e uma minoria penaliza. Mitigações existem e têm custo: provisioned concurrency mantém ambientes prontos cobrando 24 horas por dia, runtimes compilados e enxutos reduzem a fase init, e recursos como SnapStart da AWS aceleram linguagens de máquina virtual. Funções com VPC e conexões pesadas pioram o cenário, e precisam de desenho consciente.

A leitura operacional correta é tratar cold start como variável de capacidade, e não como defeito: mede-se a distribuição por runtime, identifica-se quais funções têm requisito de latência real, e paga-se provisão apenas para elas. Aplicar provisioned concurrency em toda função é a forma mais rápida de perder a vantagem econômica do modelo.

Como funções são efêmeras, todo estado mora fora: memória local e disco temporário somem entre invocações, e qualquer dado persistente vai para S3, DynamoDB, Redis ou banco. O padrão que mais pega desprevenidos é o pool de conexões: cada ambiente simultâneo abre suas conexões com o banco, e um pico de concorrência esgota o limite do PostgreSQL em segundos. A resposta é intermediar com proxy de conexão, como RDS Proxy, ou usar APIs HTTP sobre o banco, ou manter a concorrência limitada deliberadamente.

Causa de latência Sintoma observado Mitigação
Cold start de ambiente p99 alto em tráfego esparso Provisioned concurrency, runtime leve, SnapStart
Pool de conexões Esgotamento de conexões no banco RDS Proxy, limitação de concorrência
Cadeia síncrona de funções Latência soma de ponta a ponta Orquestração assíncrona com Step Functions
Payload grande em gatilho síncrono Erros de limite e custo de rede Armazenar em S3 e passar referência

Cobrança por Invocação

A conta do FaaS soma número de invocações e tempo de execução ponderado pela memória alocada, em GB-segundo, com escalas gratuitas generosas para cargas pequenas. Para tráfego esparso, sazonal ou de madrugada, a economia é real, porque não há capacidade ociosa paga. Para carga constante e alta, funções podem sair mais caras que containers dimensionados, e a comparação honesta exige simular o perfil de tráfego real. Detalhes que pesam no orçamento: funções conversadoras em cadeia multiplicam invocações, logs e tracing têm custo próprio, e excesso de memória alocada multiplica o GB-segundo sem ganho correspondente.

Padrões de custo frequentes em produção:

  • Função de uso geral gigante: tudo em uma função significa pagar memória máxima por tarefa simples; fragmentar por perfil economiza.
  • Lotes mal dimensionados: batch pequeno demais multiplica invocações; grande demais estoura timeout; o ajuste é medido, não adivinhado.
  • Chamadas entre regiões e zonas: tráfego cruzado soma na conta e na latência; desenho regional evita desperdício.

Padrões Event-Driven e Orquestração

Serverless e arquitetura orientada a eventos nasceram juntas: os gatilhos nativos são eventos, e o desenho natural é de funções pequenas reagindo a fatos. Fan-out com SNS distribuindo para várias filas, buffer com SQS protegendo o processamento, e EventBridge roteando eventos entre domínios compõem o repertório básico. Para fluxos com passos, retentativa e decisão, Step Functions assume a orquestração com estado durável, retentativas declarativas e observabilidade do fluxo inteiro, em vez de construir máquina de estados dentro de uma função. Regra de ouro do desenho: funções curtas e de um propósito, orquestração externa para o fluxo, e idempotência no consumidor porque a entrega é at-least-once.

Cenários em que a escolha costuma acertar:

  • Cargas esporádicas e sazonais: relatórios noturnos, integrações de madrugada e picos de campanha pagam por uso real, sem servidor parado.
  • Webhooks e integrações externas: receber evento de parceiro, validar e encaminhar é o caso de uso nativo do modelo.
  • Processamento de arquivos e mídia: upload em S3 disparando transformação, thumbnail ou extração de dados, com escala proporcional ao volume.
  • Times sem equipe de plataforma: o provedor assume patching, capacidade e escalonamento, e o time foca na lógica de negócio.

Vendor Lock-in

O acoplamento ao provedor é estrutural em serverless: os gatilhos, as permissões, os limites e os serviços de apoio são específicos, e migrar significa reescrever integração. A pergunta útil não é como eliminar o lock-in, e sim onde ele é aceitável: para infraestrutura de commodity, como fila e armazenamento, o custo de troca é aceitável diante do ganho; para regra de negócio central, o acoplamento é risco real. A mitigação clássica é hexagonal no pequeno: a função é adaptador, a regra mora em módulo puro testável sem nuvem, e a infraestrutura fica declarada em IaC como Terraform ou CDK, para que o ambiente inteiro seja reproduzível. Formatos abertos como CloudEvents reduzem atrito na camada de evento, e runtimes portáveis evitam prisão de linguagem, mas a promessa de portabilidade total raramente sobe o teste da migração real.

Uma tática de mitigação incremental que funciona em produção: concentrar a dependência específica do provedor em adaptadores finos dentro de cada função, e manter bibliotecas de domínio compartilhadas independentes de SDK. Quando a migração acontecer, o esforço se limita a reescrever o anel externo de cada função, e o núcleo testado continua idêntico.

Erros Comuns e Boas Práticas

Sintoma em produção Causa provável Mitigação
p99 alto em função "rápida" Cold start em tráfego esparso Provisioned concurrency ou runtime enxuto
Banco cai em pico de tráfego Pool por ambiente sem limite RDS Proxy e concorrência reservada
Mensagens perdidas em erro de validação Sem DLQ e sem retentativa configurada DLQ por origem e alerta de profundidade
Timeout em job de fechamento Tarefa longa dentro da Lambda Extrair para Fargate ou Step Functions
Conta surpresa no fim do mês Invocações e memória sem revisão Alarme de custo e perfil de memória medido

Boas práticas consolidadas para o dia a dia:

  • Uma função, um propósito: funções curtas com responsabilidade única simplificam escala, permissão e análise de custo.
  • Idempotência desde o primeiro desenho: entrega duplicada é propriedade do modelo, não exceção.
  • Estado sempre fora do ambiente: qualquer dado em memória ou disco local é cache descartável, nunca fonte de verdade.
  • Toda fila tem DLQ e alerta: mensagem venenosa silenciosa é incidente que só aparece no relato de cliente.
  • IaC obrigatório: funções criadas no console são dívida instantânea, sem reprodução nem revisão.
  • Permissão mínima por função: papel dedicado com escopo estreito evita que um comprometimento alcance tudo.
  • Meça memória real e ajuste: perfil de uso com ferramenta própria costuma reduzir custo sem tocar em código.
  • Trate configuração fora do código: parâmetros em Parameter Manager ou Secrets Manager, nunca em variável commitada.
  • Escolha runtime pelo perfil da função: runtime enxuto reduz init; linguagem compilada ajuda nas funções de caminho quente.