O blog da AWS

Visualizando ambientes usando funções AWS Lambda em contêineres

Por John Ritsema, Principal Solutions Architect.

Pipelines de integração contínua e entrega contínua (CI/CD) são mecanismos eficazes que permitem que as equipes transformem código-fonte em aplicações em execução. Quando um desenvolvedor faz uma alteração no código e a envia para um repositório remoto, um pipeline com uma série de etapas pode processar a alteração. Um pipeline integra uma alteração com a base de código completa, verifica o estilo e formatação, executa verificações de segurança e executa testes unitários. Como etapa final, ele compila o código em um artefato que pode ser implantado em um ambiente para consumo.

Ao usar o GitHub ou muitos outros provedores Git hospedados, um pull request ou merge request pode ser enviado para uma alteração de código específica. Isso cria um local focado para discussão e colaboração sobre a alteração antes que ela seja aprovada e mesclada em um branch de código compartilhado.

Um mecanismo poderoso para colaboração envolve implantar um pull request (PR) em um ambiente em execução. Isso permite que as partes interessadas visualizem as alterações ao vivo e vejam como elas ficariam. Criar um ambiente em execução rapidamente permite que os colegas de equipe forneçam feedback quase imediato, acelerando todo o processo de desenvolvimento.

Implantar PRs em ambientes efêmeros incentiva as equipes a fazer muitas pequenas alterações que podem ser visualizadas e testadas em paralelo. Isso evita ter que primeiro mesclar em um branch de origem comum e implantar em ambientes de longa duração que estão sempre ativos e incorrem em custos.

Criar esse mecanismo apresenta vários desafios, incluindo complexidade de configuração, tempo de criação do ambiente e custo do ambiente. Esta publicação aborda esses desafios mostrando como criar um pipeline de CI/CD para visualizar alterações em aplicações web em ambientes efêmeros, rápidos de provisionar, de baixo custo e que escalam para zero. Esta publicação percorre as etapas necessárias para configurar uma aplicação de exemplo.

Arquitetura de exemplo

Os conceitos nesta publicação podem ser implementados usando várias ferramentas e provedores Git hospedados que se conectam a pipelines de CI/CD. O código de exemplo compartilhado nesta publicação usa GitHub Actions para acionar um fluxo de trabalho. O fluxo de trabalho usa um pequeno módulo Terraform com Docker para compilar o código-fonte da aplicação em uma imagem de contêiner, enviá-la para o Amazon Elastic Container Registry (ECR) e criar uma função AWS Lambda com a imagem.

O contêiner em execução no Lambda é acessível a partir de um navegador web através de uma URL de função Lambda. Isso fornece um endpoint HTTPS dedicado para uma função.

Isso é usado em vez do AWS App RunnerAmazon ECS Fargate com um Application Load Balancer (ALB), ou Amazon EKS com ALB ingress devido à velocidade de provisionamento e baixo custo. URLs de função Lambda são ideais para ambientes de PR efêmeros usados ocasionalmente, pois podem ser provisionados rapidamente. O ambiente de computação de escala para zero do Lambda leva a custos mais baixos, pois as cobranças são incorridas apenas para solicitações HTTP reais. Isso é útil para PRs que podem ser revisados apenas ocasionalmente e depois ficarem ociosos até que o PR seja mesclado ou fechado.

Esta é a arquitetura de exemplo:

Configurando o exemplo

O projeto de exemplo mostra como implementar este exemplo. Ele consiste em uma aplicação web básica escrita em Node.js. Todo o código necessário para implementar a arquitetura está contido no diretório .github . Para habilitar ambientes efêmeros para um novo projeto, copie o diretório .github sem poluir os arquivos do seu projeto.

Existem dois recursos principais necessários para executar o Terraform dentro do GitHub Actions: uma função IAM da AWS e um local para armazenar o estado do Terraform. Credenciais da AWS são necessárias para dar ao pipeline permissão para provisionar recursos da AWS.

Em vez de usar credenciais estáticas de usuário IAM que devem ser rotacionadas e protegidas, assuma uma função IAM para obter credenciais temporárias. O estado remoto do Terraform é necessário para descartar o ambiente quando o PR é mesclado ou fechado. O projeto de exemplo usa um bucket do Amazon S3 para armazenar o estado do Terraform.

Você pode usar o módulo Terraform localizado em .github/setup para criar esses recursos necessários.

    1. Forneça o nome da sua organização GitHub e repositório no arquivo terraform.tfvars como parâmetros de entrada. Você pode substituir aws-samples pelo seu nome de usuário do GitHub:
      cat .github/setup/terraform.tfvars
      github_org  = "aws-samples"
      github_repo = "ephemeral-preview-containers-furl"
    2. Para provisionar os recursos usando o Terraform, execute:
      cd .github/setup
      terraform init && terraform apply


      Armazene o arquivo terraform.tfstate gerado com segurança para que você possa gerenciar esses recursos no futuro, se necessário.

    3. Coloque a Região, a função IAM gerada e o nome do bucket no arquivo de configuração localizado em .github/workflows/config.env. Este arquivo de configuração é lido e usado pelo fluxo de trabalho do GitHub Actions.
      export AWS_REGION="<add region from setup>"
      
      export AWS_ROLE="<add role from setup>"
      
      export TF_BACKEND_S3_BUCKET="<add bucket from setup>"

      Esta função IAM tem uma política inline que contém o conjunto mínimo de permissões necessárias para provisionar os recursos da AWS. Isso pressupõe que sua aplicação não interage com serviços externos como bancos de dados ou caches. Se sua aplicação precisar desse acesso adicional, você pode adicionar as permissões necessárias à política localizada aqui.

Executando um servidor web no Lambda

A aplicação web (HTTP) de exemplo inclui um Dockerfile que contém instruções para empacotar a aplicação web em uma imagem de contêiner baseada em processo. Uma extensão Lambda chamada Lambda Web Adapter permite que você execute este processo de servidor web padrão no Lambda. O fluxo de trabalho de CI/CD faz uma cópia do Dockerfile e adiciona a seguinte linha.

COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.6.0 /lambda-adapter /opt/extensions/lambda-adapter

Esta linha copia o binário executável do Lambda Web Adapter de uma imagem ECR pública e o grava no contêiner no diretório /opt/extensions/. Quando o contêiner inicia, o Lambda inicia a extensão Lambda Web Adapter. Isso traduz payloads de eventos Lambda de gatilhos baseados em HTTP em solicitações HTTP reais que ele envia por proxy para a aplicação web em execução dentro do contêiner. Esta é a arquitetura:

Por padrão, o Lambda Web Adapter assume que a aplicação web está escutando na porta 8080. No entanto, você pode alterar isso no Dockerfile definindo a variável de ambiente PORT.

A aplicação web em contêiner experimenta um cold start. No entanto, isso provavelmente não é uma grande preocupação, pois a aplicação será visualizada apenas internamente pelos colegas de equipe.

Pipeline de fluxo de trabalho

O job do GitHub Actions definido no fluxo de trabalho up.yml é acionado quando um PR é aberto ou reaberto contra o branch main do repositório. O seguinte é um resumo das etapas que o Job executa.

  1. Ler a configuração de .github/workflows/config.env
  2. Assumir a função IAM, que tem permissões mínimas para implantar recursos da AWS
  3. Instalar a CLI do Terraform
  4. Adicionar a extensão Lambda Web Adapter à cópia do Dockerfile
  5. Executar terraform apply para provisionar os recursos da AWS usando o bucket S3 para estado remoto do Terraform
  6. Obter o endpoint HTTPS do Terraform e adicioná-lo ao PR como um comentário

O seguinte trecho de código mostra as etapas principais (4-6) do fluxo de trabalho up.yml.

- name: Lambda-ify
  run: echo "COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.6.0 /lambda-adapter /opt/extensions/lambda-adapter" >> Dockerfile

- name: Deploy to ephemeral environment 
  id: furl
  working-directory: ./.github/workflows
  run: |
    terraform init \
      -backend-config="bucket=${TF_BACKEND_S3_BUCKET}" \
      -backend-config="key=${ENVIRONMENT}.tfstate"

    terraform apply -auto-approve \
      -var="name=${{ github.event.repository.name }}" \
      -var="environment=${ENVIRONMENT}" \
      -var="image_tag=${GITHUB_SHA}"

    echo "Url=$(terraform output -json | jq '.endpoint_url.value' -r)" >> $GITHUB_OUTPUT

- name: Add HTTPS endpoint to PR comment
  uses: mshick/add-pr-comment@v1
  with:
    message: |
      :rocket: Code successfully deployed to a new ephemeral containerized PR environment!
      ${{ steps.furl.outputs.Url }}
    repo-token: ${{ secrets.GITHUB_TOKEN }}
    repo-token-user-login: "github-actions[bot]"
    allow

O arquivo main.tf (no mesmo diretório) inclui infraestrutura como código (IaC) que é responsável por criar um repositório ECR, compilar e enviar a imagem do contêiner para ele e criar uma função Lambda com base na imagem. O seguinte é um trecho da configuração do Terraform. Você pode ver como isso pode ser configurado de forma concisa.

provider "docker" {
  registry_auth {
    address  = format("%v.dkr.ecr.%v.amazonaws.com", data.aws_caller_identity.current.account_id, data.aws_region.current.name)
    username = data.aws_ecr_authorization_token.token.user_name
    password = data.aws_ecr_authorization_token.token.password
  }
}

module "docker_image" {
  source = "terraform-aws-modules/lambda/aws//modules/docker-build"

  create_ecr_repo = true
  ecr_repo        = local.ns
  image_tag       = var.image_tag
  source_path     = "../../"
}

module "lambda_function_from_container_image" {
  source = "terraform-aws-modules/lambda/aws"

  function_name              = local.ns
  description                = "Ephemeral preview environment for: ${local.ns}"
  create_package             = false
  package_type               = "Image"
  image_uri                  = module.docker_image.image_uri
  architectures              = ["x86_64"]
  create_lambda_function_url = true
}

output "endpoint_url" {
  value = module.lambda_function_from_container_image.lambda_function_url
}

O Terraform gera o endpoint HTTPS. O fluxo de trabalho o escreve de volta ao PR como um comentário para que os colegas de equipe possam clicar no link para visualizar as alterações:

O fluxo de trabalho leva cerca de 60 segundos para criar uma nova aplicação web em contêiner isolada em um ambiente efêmero que pode ser visualizado.

Colaboração em pull request

A captura de tela a seguir mostra um exemplo de PR enquanto o autor colabora com sua equipe. Após implementar este exemplo, quando um novo PR chega, as alterações são implantadas em um novo ambiente efêmero. As partes interessadas podem usar o link para visualizar como as alterações ficam e fornecer feedback.

Uma vez que as alterações são aprovadas e mescladas no branch principal, o fluxo de trabalho down.yml do GitHub Actions descarta o ambiente. Isso significa que o ambiente efêmero é desprovisionado, incluindo recursos como a função Lambda e o repositório ECR.

Conclusão

Esta publicação discute alguns dos benefícios de usar ambientes efêmeros em pipelines de CI/CD. Ele mostra como implementar um pipeline usando GitHub Actions e URLs de função Lambda para ambientes rápidos, de baixo custo e efêmeros.

Com este exemplo, você pode implantar PRs rapidamente, e o custo é baseado em solicitações HTTP feitas ao ambiente. Não há custos de computação incorridos enquanto um PR está aberto e ninguém está visualizando o ambiente. As únicas cobranças são por invocações do Lambda, enquanto as partes interessadas estão interagindo ativamente com o ambiente. Quando um PR é mesclado ou fechado, a infraestrutura de nuvem é descartada. Você pode encontrar todo o código de exemplo referenciado nesta publicação aqui.

Para mais recursos de aprendizado sobre Serverless, visite Serverless Land.


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

Autores

John Ritsema é Principal Solutions Architect na Amazon Web Services.

Tradutores

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. Sua área de interesse são tecnologias serverless.
https://www.linkedin.com/in/nicolastarzia
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/