O blog da AWS

Adicionando domínios personalizados às MicroVMs do AWS Lambda com o Application Load Balancer

Por Frank Scarfo, Software Development Engineer na Amazon Web Services e Ben Freiberg, Senior Solutions Architect na Amazon Web Services.

As MicroVMs do AWS Lambda são um bloco de construção de computação sem servidor que fornece isolamento em nível de VM, desempenho de inicialização quase instantâneo e retenção de estado. Agora você pode dar a cada usuário ou trabalho seu próprio ambiente de execução para executar de forma segura código just-in-time, seja ele gerado por usuário ou por IA. Você faz isso sem gerenciar infraestrutura de virtualização ou escolher entre isolamento, velocidade e retenção de estado. As MicroVMs do Lambda são baseadas na virtualização Firecracker, a tecnologia que fundamenta o AWS Lambda.

Quando você executa uma carga de trabalho nas MicroVMs do AWS Lambda, cada MicroVM pode ser acessada em um endpoint gerado pelo serviço que se parece com 92cfc7f9-….lambda-microvm-….on.aws. Isso funciona, mas muitas equipes desejam expor suas MicroVMs sob um domínio que possuem, como 92cfc7f9-….microvms.example.com. Quando o cliente é um navegador, também desejam atender ao cross-origin resource sharing (CORS) sem alterar a aplicação dentro da MicroVM.

Ambos são alcançáveis hoje, inteiramente a partir de primitivas de balanceamento de carga e rede. Não há distribuição do Amazon CloudFront nem computação no caminho da solicitação. Tudo o que você precisa é de um Application Load Balancer (ALB) que encerra o TLS com seu certificado do AWS Certificate Manager (ACM), reescreve o cabeçalho Host e encaminha a solicitação pela AWS PrivateLink. Nesta publicação, você implantará esse padrão com o AWS Cloud Development Kit (AWS CDK), mapeará um wildcard de domínios personalizados para suas MicroVMs e deixará o ALB lidar com o CORS para você.

O exemplo completo e implantável está disponível como um padrão no Serverless Land. Este passo a passo se concentra no padrão de rede reutilizável. O exemplo também inclui uma pequena aplicação de demonstração que provisiona uma MicroVM e cria um token de acesso, ao qual fazemos referência, mas que não detalhamos aqui.

O que você vai construir

Ao final, você terá:

  • Um domínio personalizado wildcard como *.microvms.example.com, em que cada <uuid>.microvms.example.com é mapeado de forma transparente para a MicroVM correspondente.
  • Um ALB voltado para a internet que reescreve o cabeçalho Host da solicitação recebida para o endpoint real da MicroVM e encaminha as solicitações a ele de forma privada pela PrivateLink.
  • Cabeçalhos de preflight e resposta CORS tratados no ALB, sem nenhuma alteração no código em execução na MicroVM.

Chamar https://<uuid>.microvms.example.com/<path> (com os cabeçalhos de acesso à MicroVM descritos posteriormente) atinge a MicroVM correta, com seu domínio intacto de ponta a ponta.

Visão geral da solução

Confira o fluxo da solicitação abaixo:

Fluxo de solicitação de um navegador através do Application Load Balancer, que encerra o TLS e reescreve o cabeçalho Host, e então encaminha pela AWS PrivateLink para o serviço de MicroVM do Lambda.

O componente principal é a reescrita do cabeçalho host do ALB, introduzida em Reescrita de URL e cabeçalho host para Application Load Balancers. Uma regra de listener corresponde o host personalizado recebido com uma condição de regex, captura o ID da MicroVM a partir do rótulo mais à esquerda, e uma transformação host-header-rewrite reescreve o cabeçalho Host para <uuid>.lambda-microvm.<region>.on.aws antes de encaminhar. Como o front-end do serviço de MicroVM roteia com base no cabeçalho Host, a solicitação chega à MicroVM correta, enquanto o domínio do cliente permanece na barra de endereços do navegador durante todo o processo.

Por que não o CloudFront? Por que não um redirecionamento do ALB?

  • O CloudFront também pode reescrever o Host/SNI em direção à origem, mas uma única distribuição possui origens estáticas. Mapear um wildcard de IDs de MicroVM através de uma distribuição exigiria uma CloudFront Function para calcular a origem por solicitação. A transformação do ALB realiza a mesma reescrita para todo o wildcard sem nenhum código.
  • Uma ação de redirecionamento do ALB apenas emite uma resposta HTTP 301 Moved Permanently/302 Found. O navegador a seguiria, e a barra de endereços mostraria então a URL .on.aws, o que quebra nosso design, pois não é um domínio personalizado real. A transformação (não um redirecionamento) é o que torna o domínio personalizado transparente.

Passo a passo

O exemplo é uma aplicação AWS CDK. A configuração está sob a chave microvm-custom-domains em cdk.json (zona hospedada, base wildcard, a base do endpoint para reescrever, o nome do serviço PrivateLink e a origem CORS). Defina esses valores e depois implante. As seções abaixo explicam o que a stack cria e por quê.

Pré-requisitos

1. Criar a rede e o endpoint PrivateLink

Uma pequena VPC (duas Zonas de Disponibilidade, o mínimo para um ALB voltado para a internet) hospeda o ALB e um endpoint de interface de VPC para o serviço de MicroVM gerenciado pela AWS. Não há gateways NAT, pois nada aqui precisa de saída, o que mantém a pegada reduzida.

// Interface (PrivateLink) endpoint to the AWS managed MicroVM service.
const endpoint = new ec2.InterfaceVpcEndpoint(this, 'MicroVmEndpoint', {
  vpc,
  service: new ec2.InterfaceVpcEndpointService(cfg.microvmVpceServiceName, 443),
  subnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
});

2. Descobrir os endereços IP privados do endpoint em tempo de implantação

Um grupo de destino IP do ALB precisa dos endereços IP das ENIs privadas do endpoint de interface (um por Zona de Disponibilidade). O CloudFormation não expõe esses IPs como um atributo utilizável, então a stack os resolve durante a implantação com um AwsCustomResource que lê as próprias ENIs do endpoint pelo ID (DescribeNetworkInterfaces em vpcEndpointNetworkInterfaceIds).

Este é o único recurso de computação que o pacote implanta, ele é executado apenas durante o cdk deploy, e nunca está no caminho da solicitação.

3. Solicitar um certificado TLS wildcard

O ACM emite um certificado wildcard validado por DNS para *.microvms.example.com, validado através da zona hospedada que você importou. O ALB apresenta esse certificado para cada domínio personalizado sob o wildcard.

4. Criar o ALB e o grupo de destino da MicroVM

O ALB voltado para a internet tem um listener HTTPS:443 usando o certificado wildcard. O grupo de destino contém os IPs das ENIs do endpoint como destinos IP, acessados via HTTPS:443.

  • Criptografado em trânsito. Um certificado do AWS Certificate Manager (ACM) fornecido pelo cliente é usado para encerrar de forma segura a criptografia entre o cliente e o ALB. O ALB reinicia o TLS para o serviço de MicroVM, para que o tráfego permaneça criptografado através da rede.
  • Destinos baseados em IP. O grupo de destino usa destinos baseados em IP com os endereços IP locais dos endpoints de VPC.
  • Verificação de integridade 200,403,404. As verificações de integridade do balanceador de carga não são autenticadas, então o endpoint da MicroVM responde a elas com 403. Um 403 aqui significa “o endpoint está acessível”, não “a autenticação está com problema”, então o verificador o trata como saudável.
const targetGroup = new elbv2.ApplicationTargetGroup(this, 'MicroVmTargets', {
  vpc,
  protocol: elbv2.ApplicationProtocol.HTTPS,
  port: 443,
  targetType: elbv2.TargetType.IP,
  targets: targetIps.map((ip) => new elbv2t.IpTarget(ip, 443)),
  healthCheck: {
    protocol: elbv2.Protocol.HTTPS,
    path: '/',
    healthyHttpCodes: '200,403,404',
  },
});

const listener = alb.addListener('Https', {
  port: 443,
  protocol: elbv2.ApplicationProtocol.HTTPS,
  certificates: [certificate],
  // Default action for anything that doesn't match our host regex.
  defaultAction: elbv2.ListenerAction.fixedResponse(404, {
    contentType: 'text/plain',
    messageBody: 'Unknown custom domain',
  }),
});

5. Adicionar a regra de reescrita do cabeçalho host

Uma regra de listener corresponde a <uuid>.microvms.example.com com uma condição de regex e reescreve o cabeçalho Host para <uuid>.lambda-microvm.<region>.on.aws com uma transformação host-header-rewrite. A regex captura o rótulo mais à esquerda (o ID da MicroVM) e o reutiliza na substituição.

No momento da escrita, os constructs L2 do CDK ainda não modelam condições de host com regex ou transformações, então o exemplo acessa o CfnListenerRule subjacente para configurá-las:

const escapedBase = customDomainBase.replace(/[.]/g, '\\.');
const matchRegex = `^(.+)\\.${escapedBase}$`;     // capture <uuid>
const replaceWith = `$1.${microvmEndpointBase}`;   // <uuid>.lambda-microvm.<region>.on.aws

const cfnRule = forwardingRule.node.defaultChild as elbv2.CfnListenerRule;

cfnRule.conditions = [{ field: 'host-header', regexValues: [matchRegex] }];

cfnRule.addPropertyOverride('Transforms', [
  {
    Type: 'host-header-rewrite',
    HostHeaderRewriteConfig: { Rewrites: [{ Regex: matchRegex, Replace: replaceWith }] },
  },
]);

6. Apontar o Route 53 para o ALB

Registros alias A e AAAA wildcard (*.microvms.example.com) apontam para o ALB, de modo que cada subdomínio personalizado da MicroVM seja resolvido para ele.

7. Implantar

Execute os seguintes comandos para instalar as dependências e depois implantar a aplicação.

npm install
npx cdk deploy

Lidando com CORS no ALB

Se seus clientes forem navegadores chamando a MicroVM de outra origem, o CORS é tratado inteiramente no ALB, sem nenhuma alteração na aplicação dentro da MicroVM.

O listener usa atributos de modificação de cabeçalho do ALB para inserir os cabeçalhos Access-Control-Allow-* em toda resposta. Uma regra de prioridade mais alta responde às solicitações de preflight OPTIONS na borda com uma resposta rápida 204. Caso contrário, as solicitações de preflight chegariam à origem e seriam rejeitadas sem um token de acesso.

// Insert CORS headers on every response on this listener.
const cfnListener = listener.node.defaultChild as elbv2.CfnListener;
cfnListener.addPropertyOverride('ListenerAttributes', [
  { Key: 'routing.http.response.access_control_allow_origin.header_value',  Value: cfg.corsAllowOrigin },
  { Key: 'routing.http.response.access_control_allow_methods.header_value', Value: 'GET,POST,PUT,DELETE,OPTIONS,PATCH,HEAD' },
  { Key: 'routing.http.response.access_control_allow_headers.header_value', Value: 'x-aws-proxy-auth,x-aws-proxy-port,content-type,authorization' },
  { Key: 'routing.http.response.access_control_expose_headers.header_value', Value: 'content-type,content-length' },
  { Key: 'routing.http.response.access_control_max_age.header_value',        Value: '86400' },
]);

// Answer OPTIONS preflights at the ALB.
new elbv2.ApplicationListenerRule(this, 'CorsPreflightRule', {
  listener,
  priority: 10,
  conditions: [elbv2.ListenerCondition.httpRequestMethods(['OPTIONS'])],
  action: elbv2.ListenerAction.fixedResponse(204, { contentType: 'text/plain', messageBody: '' }),
});

Como o ALB adiciona esses cabeçalhos tanto à resposta de preflight 204 quanto à resposta encaminhada da MicroVM, uma chamada cross-origin de um navegador tem sucesso sem nenhuma alteração na aplicação. Defina corsAllowOrigin como * para testes rápidos e fixe-o em seu próprio site para qualquer coisa além de uma demonstração.

Teste de ponta a ponta

Primeiro, inicie uma MicroVM do Lambda e crie um token de acesso (siga Crie sua primeira MicroVM do Lambda). Quando estiver em execução, o serviço fornece a você um endpoint gerado que se parece com:

012345678-9abc-defg.lambda-microvm.us-east-2.on.aws

Para obter o equivalente do domínio personalizado, substitua o sufixo do endpoint (.lambda-microvm.<region>.on.aws) pela sua base wildcard: .microvms.example.com. Tudo antes desse sufixo é preservado exatamente:

012345678-9abc-defg.microvms.example.com

A regra de reescrita do ALB captura o que precede o sufixo e o reanexa à base real do endpoint, de modo que o mapeamento se mantenha para todo o wildcard. Você nunca registra nada individualmente por MicroVM.

Com seu token em mãos, chame o domínio personalizado derivado:

curl "https://012345678-9abc-defg.microvms.example.com/<path>" \
  -H "X-aws-proxy-auth: <token>" \
  -H "X-aws-proxy-port: 8080"

A solicitação viaja até o ALB, que encerra o TLS, reescreve o cabeçalho host e encaminha pela PrivateLink para a MicroVM. A resposta retorna sob seu domínio.

A arquitetura de referência também inclui uma demonstração de página única e um endpoint POST /api/provision que executa ou reutiliza uma MicroVM e cria um token de curta duração. Com ele, você pode testar o fluxo sem configurar a criação de tokens você mesmo. Ele até realiza essa troca de sufixo para você e devolve uma URL de domínio personalizado pronta para uso. Consulte o repositório para essa parte.

Considerações importantes

  • A autenticação ainda é responsabilidade do cliente. Este padrão apenas reescreve o Host. O cliente ainda deve fornecer um token de acesso válido e não expirado em X-aws-proxy-auth. Isso é deliberado. Os tokens de MicroVM são específicos por MicroVM e de curta duração, então incorporá-los à infraestrutura seria frágil e inseguro.
  • Fixação de região. A PrivateLink é regional, então o ALB, o endpoint e o serviço de MicroVM devem estar todos na mesma Região.
  • Reforço para produção. Se você adaptar o endpoint de provisionamento do exemplo, coloque autenticação e limitação de taxa na frente dele, fixe o CORS à sua origem e delimite o IAM ao mínimo. O caminho de provisionamento do exemplo é intencionalmente aberto para demonstração e não é seguro para produção como está escrito.
  • Custo. Você paga pelo ALB e pelo endpoint de interface (por hora, além do processamento de dados), além do uso da MicroVM do Lambda. Não há distribuição do CloudFront nem computação por solicitação no caminho de dados.

Limpeza

Execute o seguinte comando no mesmo diretório de onde você implantou a aplicação.

npx cdk destroy

Isso remove o ALB, os grupos de destino, o endpoint, o certificado, a VPC e os registros do Route 53 criados pela stack.

Conclusão

Você pode expor as MicroVMs do AWS Lambda com domínios personalizados wildcard de propriedade do cliente usando um Application Load Balancer e a AWS PrivateLink. A chave é a reescrita do cabeçalho host do ALB. Como o serviço de MicroVM roteia solicitações com base no cabeçalho Host, uma única regra de reescrita pode mapear de forma transparente um wildcard inteiro de domínios personalizados para suas MicroVMs. O CORS também é tratado na borda. Toda a configuração depende apenas de primitivas de rede, sem distribuição do CloudFront nem computação no caminho da solicitação.

Para experimentar você mesmo, implante a arquitetura de referência e revise a publicação de lançamento da reescrita de URL e cabeçalho host do ALB para saber mais sobre o recurso de transformação.


Este conteúdo foi traduzido da publicação original do blog em inglês (disponível aqui).

Biografia do Autores

Frank Scarfo é Software Development Engineer na Amazon Web Services.
Ben Freiberg é Senior Solutions Architect na Amazon Web Services.

Biografia do 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