DevOps

ECS e Lambda: Containers e Serverless na AWS Já leu

17 min de leitura

ECS e Lambda: Containers e Serverless na AWS
Gerenciar servidores EC2 diretamente — aplicar patches, monitorar uso de disco, escalar manualmente — é uma responsabilidade operacional que consome tempo e atenção que poderiam ser direcionados ao produto. Os serviços g

Gerenciar servidores EC2 diretamente — aplicar patches, monitorar uso de disco, escalar manualmente — é uma responsabilidade operacional que consome tempo e atenção que poderiam ser direcionados ao produto. Os serviços gerenciados de computação da AWS existem para transferir essa responsabilidade operacional para a nuvem.

O Amazon ECS gerencia clusters de containers, eliminando a necessidade de operar o plano de controle do orquestrador. O AWS Lambda vai além: abstrai completamente a infraestrutura, deixando o engenheiro responsável apenas pelo código da função e por quanto tempo ela pode executar.

A escolha entre EC2, ECS e Lambda não é uma progressão linear em que cada opção é "melhor" que a anterior. São ferramentas com trade-offs distintos, adequadas para tipos diferentes de workload. Entender esses trade-offs é o que permite fazer escolhas de arquitetura conscientes.


Amazon ECS: Containers sem Gerenciar o Orquestrador

O ECS é o serviço de orquestração de containers da AWS. Ele gerencia onde os containers rodam, garante que o número desejado de instâncias está em execução, integra com o Application Load Balancer para distribuição de tráfego e com o IAM para controle de acesso.

Conceitos Fundamentais do ECS

Cluster — o agrupamento lógico de recursos de computação onde as tasks são executadas. Um cluster pode conter instâncias EC2 ou usar o Fargate.

Task Definition — o blueprint de um container ou grupo de containers. Define a imagem Docker, quantidade de CPU e memória, variáveis de ambiente, volumes, configurações de rede e a IAM role da task.

Task — uma instância em execução de uma task definition. Equivalente a um pod no Kubernetes.

Service — um controlador que garante que um número determinado de tasks está sempre em execução. Gerencia atualizações com zero downtime, integra com load balancers e escala automaticamente.

ECS com Fargate: Serverless para Containers

O Fargate é o modo de execução do ECS que elimina completamente a necessidade de gerenciar instâncias EC2. Ao usar Fargate, o time define quanto de CPU e memória cada container precisa — a AWS aloca a infraestrutura necessária de forma invisível.

# ecs.tf — Infraestrutura completa do ECS com Fargate

# Cluster ECS
resource "aws_ecs_cluster" "principal" {
  name = "${var.project_name}-${var.environment}"

  configuration {
    execute_command_configuration {
      logging = "OVERRIDE"
      log_configuration {
        cloud_watch_log_group_name = aws_cloudwatch_log_group.ecs_exec.name
      }
    }
  }

  setting {
    name  = "containerInsights"
    value = "enabled"
  }

  tags = local.tags_comuns
}

# Log group para os logs dos containers
resource "aws_cloudwatch_log_group" "aplicacao" {
  name              = "/ecs/${var.project_name}-${var.environment}"
  retention_in_days = 30
  tags              = local.tags_comuns
}

resource "aws_cloudwatch_log_group" "ecs_exec" {
  name              = "/ecs/${var.project_name}-${var.environment}/exec"
  retention_in_days = 7
  tags              = local.tags_comuns
}

# IAM Role para as tasks ECS — permissões da aplicação
resource "aws_iam_role" "ecs_task" {
  name = "${var.project_name}-${var.environment}-ecs-task-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action    = "sts:AssumeRole"
      Effect    = "Allow"
      Principal = { Service = "ecs-tasks.amazonaws.com" }
    }]
  })
}

resource "aws_iam_role_policy" "ecs_task" {
  name = "permissoes-aplicacao"
  role = aws_iam_role.ecs_task.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "LerSecretos"
        Effect = "Allow"
        Action = ["secretsmanager:GetSecretValue"]
        Resource = [
          "arn:aws:secretsmanager:${var.aws_region}:${data.aws_caller_identity.atual.account_id}:secret:${var.project_name}/*"
        ]
      },
      {
        Sid    = "AcessoS3"
        Effect = "Allow"
        Action = ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"]
        Resource = ["${aws_s3_bucket.assets.arn}/*"]
      },
      {
        Sid    = "LogsCloudWatch"
        Effect = "Allow"
        Action = [
          "logs:CreateLogStream",
          "logs:PutLogEvents"
        ]
        Resource = ["${aws_cloudwatch_log_group.aplicacao.arn}:*"]
      }
    ]
  })
}

# IAM Role de execução — permissões do ECS para gerenciar a task
resource "aws_iam_role" "ecs_execution" {
  name = "${var.project_name}-${var.environment}-ecs-execution-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action    = "sts:AssumeRole"
      Effect    = "Allow"
      Principal = { Service = "ecs-tasks.amazonaws.com" }
    }]
  })
}

resource "aws_iam_role_policy_attachment" "ecs_execution" {
  role       = aws_iam_role.ecs_execution.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}

# Permissão adicional para ler segredos durante o pull da task
resource "aws_iam_role_policy" "ecs_execution_secrets" {
  name = "ler-segredos-na-inicializacao"
  role = aws_iam_role.ecs_execution.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Action = ["secretsmanager:GetSecretValue"]
      Resource = [
        "arn:aws:secretsmanager:${var.aws_region}:${data.aws_caller_identity.atual.account_id}:secret:${var.project_name}/*"
      ]
    }]
  })
}

# Task Definition — define o container da aplicação
resource "aws_ecs_task_definition" "aplicacao" {
  family                   = "${var.project_name}-${var.environment}"
  requires_compatibilities = ["FARGATE"]
  network_mode             = "awsvpc"
  cpu                      = var.task_cpu
  memory                   = var.task_memory
  execution_role_arn       = aws_iam_role.ecs_execution.arn
  task_role_arn            = aws_iam_role.ecs_task.arn

  container_definitions = jsonencode([
    {
      name      = "minha-api"
      image     = var.app_image
      essential = true

      portMappings = [{
        containerPort = 3000
        protocol      = "tcp"
      }]

      # Variáveis de ambiente não sensíveis
      environment = [
        { name = "NODE_ENV",    value = var.environment },
        { name = "PORT",        value = "3000" },
        { name = "LOG_LEVEL",   value = "info" },
        { name = "APP_VERSION", value = var.app_version }
      ]

      # Segredos injetados a partir do Secrets Manager
      # O ECS busca o valor em tempo de execução e injeta como variável de ambiente
      secrets = [
        {
          name      = "DATABASE_URL"
          valueFrom = "arn:aws:secretsmanager:${var.aws_region}:${data.aws_caller_identity.atual.account_id}:secret:${var.project_name}/${var.environment}:DATABASE_URL::"
        },
        {
          name      = "JWT_SECRET"
          valueFrom = "arn:aws:secretsmanager:${var.aws_region}:${data.aws_caller_identity.atual.account_id}:secret:${var.project_name}/${var.environment}:JWT_SECRET::"
        }
      ]

      logConfiguration = {
        logDriver = "awslogs"
        options = {
          awslogs-group         = aws_cloudwatch_log_group.aplicacao.name
          awslogs-region        = var.aws_region
          awslogs-stream-prefix = "ecs"
        }
      }

      healthCheck = {
        command     = ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
        interval    = 30
        timeout     = 5
        retries     = 3
        startPeriod = 60
      }
    }
  ])

  tags = local.tags_comuns
}

# Application Load Balancer
resource "aws_lb" "aplicacao" {
  name               = "${var.project_name}-${var.environment}-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.load_balancer.id]
  subnets            = module.vpc.ids_subnets_publicas

  enable_deletion_protection = var.environment == "production"

  tags = local.tags_comuns
}

resource "aws_lb_target_group" "aplicacao" {
  name        = "${var.project_name}-${var.environment}-tg"
  port        = 3000
  protocol    = "HTTP"
  vpc_id      = module.vpc.vpc_id
  target_type = "ip"  # Fargate usa IP, não instância

  health_check {
    enabled             = true
    healthy_threshold   = 2
    unhealthy_threshold = 3
    interval            = 30
    path                = "/health"
    matcher             = "200"
    timeout             = 5
  }

  deregistration_delay = 30

  tags = local.tags_comuns
}

resource "aws_lb_listener" "https" {
  load_balancer_arn = aws_lb.aplicacao.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS13-1-2-2021-06"
  certificate_arn   = aws_acm_certificate.principal.arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.aplicacao.arn
  }
}

# ECS Service — mantém tasks em execução e integra com o ALB
resource "aws_ecs_service" "aplicacao" {
  name            = "${var.project_name}-${var.environment}"
  cluster         = aws_ecs_cluster.principal.id
  task_definition = aws_ecs_task_definition.aplicacao.arn
  desired_count   = var.service_desired_count
  launch_type     = "FARGATE"

  # Configuração de deploy com zero downtime
  deployment_minimum_healthy_percent = 100
  deployment_maximum_percent         = 200

  deployment_circuit_breaker {
    enable   = true
    rollback = true  # Rollback automático se o deploy falhar
  }

  network_configuration {
    subnets          = module.vpc.ids_subnets_privadas
    security_groups  = [aws_security_group.aplicacao.id]
    assign_public_ip = false
  }

  load_balancer {
    target_group_arn = aws_lb_target_group.aplicacao.arn
    container_name   = "minha-api"
    container_port   = 3000
  }

  # Permite que o Terraform gerencie o task definition
  # sem interferir em deploys feitos via CLI ou pipeline
  lifecycle {
    ignore_changes = [task_definition, desired_count]
  }

  tags = local.tags_comuns
}

# Auto Scaling do serviço ECS
resource "aws_appautoscaling_target" "ecs" {
  max_capacity       = var.service_max_count
  min_capacity       = var.service_min_count
  resource_id        = "service/${aws_ecs_cluster.principal.name}/${aws_ecs_service.aplicacao.name}"
  scalable_dimension = "ecs:service:DesiredCount"
  service_namespace  = "ecs"
}

# Escala com base em CPU
resource "aws_appautoscaling_policy" "cpu" {
  name               = "escala-por-cpu"
  policy_type        = "TargetTrackingScaling"
  resource_id        = aws_appautoscaling_target.ecs.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
  service_namespace  = aws_appautoscaling_target.ecs.service_namespace

  target_tracking_scaling_policy_configuration {
    target_value       = 70.0
    scale_in_cooldown  = 300
    scale_out_cooldown = 60

    predefined_metric_specification {
      predefined_metric_type = "ECSServiceAverageCPUUtilization"
    }
  }
}

Deploying no ECS via GitHub Actions

# .github/workflows/deploy-ecs.yml
name: Deploy no ECS

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production

    steps:
      - uses: actions/checkout@v4

      - name: Configura credenciais AWS
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
          aws-region: us-east-1

      - name: Login no ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Constrói e publica imagem no ECR
        id: build
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/minha-api:$IMAGE_TAG .
          docker push $ECR_REGISTRY/minha-api:$IMAGE_TAG
          echo "image=$ECR_REGISTRY/minha-api:$IMAGE_TAG" >> $GITHUB_OUTPUT

      - name: Atualiza task definition com a nova imagem
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: infrastructure/task-definition.json
          container-name: minha-api
          image: ${{ steps.build.outputs.image }}

      - name: Deploy no ECS
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: ${{ steps.task-def.outputs.task-definition }}
          service: minha-api-production
          cluster: minha-api-production
          wait-for-service-stability: true
          wait-for-minutes: 10
          codedeploy-appspec: infrastructure/appspec.json

ECS Exec: Acesso ao Container sem SSH

O ECS Exec permite abrir uma sessão interativa em um container em execução usando o AWS Systems Manager — sem expor portas SSH, sem chaves de acesso:

# Abre um shell interativo em um container Fargate
aws ecs execute-command \
  --cluster minha-api-production \
  --task TASK_ID \
  --container minha-api \
  --interactive \
  --command "/bin/sh"

# Executa um comando específico
aws ecs execute-command \
  --cluster minha-api-production \
  --task TASK_ID \
  --container minha-api \
  --interactive \
  --command "node -e 'console.log(process.env.NODE_ENV)'"

# Lista as tasks em execução para obter o TASK_ID
aws ecs list-tasks \
  --cluster minha-api-production \
  --service-name minha-api-production \
  --query 'taskArns[*]' \
  --output text

AWS Lambda: Computação Orientada a Eventos

O Lambda executa código em resposta a eventos — uma requisição HTTP via API Gateway, um arquivo novo no S3, uma mensagem em uma fila SQS, um agendamento via EventBridge. A infraestrutura é completamente invisível: sem servidores para provisionar, sem capacidade para planejar, sem patches para aplicar.

O modelo de cobrança é por invocação e por duração de execução — medida em GB-segundos. Uma função que não é invocada não gera custo.

Estrutura de uma Função Lambda

// src/handlers/processar-pedido.js

const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager');
const { SQSClient, DeleteMessageCommand } = require('@aws-sdk/client-sqs');

// Clientes SDK inicializados fora do handler — reutilizados entre invocações
const secretsClient = new SecretsManagerClient({ region: process.env.AWS_REGION });
const sqsClient = new SQSClient({ region: process.env.AWS_REGION });

// Cache de configuração — evita buscar segredos a cada invocação
let config = null;

async function carregarConfig() {
  if (config) return config;

  const resposta = await secretsClient.send(
    new GetSecretValueCommand({
      SecretId: process.env.SECRET_ARN,
    })
  );

  config = JSON.parse(resposta.SecretString);
  return config;
}

// Handler principal — invocado pelo Lambda para cada evento
exports.handler = async (event, context) => {
  // context.callbackWaitsForEmptyEventLoop = false evita que o Lambda
  // aguarde conexões de banco abertas antes de encerrar
  context.callbackWaitsForEmptyEventLoop = false;

  const cfg = await carregarConfig();

  // O evento pode vir de diferentes fontes — este exemplo processa SQS
  const resultados = await Promise.allSettled(
    event.Records.map(record => processarMensagem(record, cfg))
  );

  // Identifica mensagens que falharam para que o SQS as reenvie
  const falhas = resultados
    .map((resultado, idx) => ({ resultado, record: event.Records[idx] }))
    .filter(({ resultado }) => resultado.status === 'rejected')
    .map(({ record }) => ({
      itemIdentifier: record.messageId,
    }));

  // Retorno com batchItemFailures permite reprocessar apenas as mensagens que falharam
  return { batchItemFailures: falhas };
};

async function processarMensagem(record, cfg) {
  const pedido = JSON.parse(record.body);

  console.log(JSON.stringify({
    level: 'info',
    msg: 'Processando pedido',
    pedidoId: pedido.id,
    requestId: record.messageId,
  }));

  // Lógica de processamento
  await validarPedido(pedido, cfg);
  await reservarEstoque(pedido, cfg);
  await confirmarPagamento(pedido, cfg);
  await notificarCliente(pedido, cfg);

  console.log(JSON.stringify({
    level: 'info',
    msg: 'Pedido processado com sucesso',
    pedidoId: pedido.id,
  }));
}

Infraestrutura do Lambda com Terraform

# lambda.tf

# Empacota o código da função
data "archive_file" "lambda_zip" {
  type        = "zip"
  source_dir  = "${path.module}/../src"
  output_path = "${path.module}/../dist/funcao.zip"
  excludes    = ["**/*.test.js", "**/node_modules/.cache/**"]
}

# IAM Role da função Lambda
resource "aws_iam_role" "lambda" {
  name = "${var.project_name}-${var.environment}-lambda-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action    = "sts:AssumeRole"
      Effect    = "Allow"
      Principal = { Service = "lambda.amazonaws.com" }
    }]
  })
}

resource "aws_iam_role_policy_attachment" "lambda_basico" {
  role       = aws_iam_role.lambda.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
}

resource "aws_iam_role_policy" "lambda" {
  name = "permissoes-funcao"
  role = aws_iam_role.lambda.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect   = "Allow"
        Action   = ["secretsmanager:GetSecretValue"]
        Resource = [aws_secretsmanager_secret.config.arn]
      },
      {
        Effect   = "Allow"
        Action   = [
          "sqs:ReceiveMessage",
          "sqs:DeleteMessage",
          "sqs:GetQueueAttributes"
        ]
        Resource = [aws_sqs_queue.pedidos.arn]
      }
    ]
  })
}

# A função Lambda
resource "aws_lambda_function" "processar_pedido" {
  filename         = data.archive_file.lambda_zip.output_path
  source_code_hash = data.archive_file.lambda_zip.output_base64sha256
  function_name    = "${var.project_name}-${var.environment}-processar-pedido"
  role             = aws_iam_role.lambda.arn
  handler          = "handlers/processar-pedido.handler"
  runtime          = "nodejs20.x"
  timeout          = 30
  memory_size      = 512

  vpc_config {
    subnet_ids         = module.vpc.ids_subnets_privadas
    security_group_ids = [aws_security_group.lambda.id]
  }

  environment {
    variables = {
      NODE_ENV   = var.environment
      SECRET_ARN = aws_secretsmanager_secret.config.arn
      REGION     = var.aws_region
    }
  }

  # Lambda Layers — dependências compartilhadas entre funções
  layers = [aws_lambda_layer_version.dependencias.arn]

  tracing_config {
    mode = "Active"  # Habilita rastreamento com AWS X-Ray
  }

  tags = local.tags_comuns
}

# Layer com as dependências npm
resource "aws_lambda_layer_version" "dependencias" {
  filename            = "${path.module}/../dist/camada-dependencias.zip"
  layer_name          = "${var.project_name}-dependencias"
  compatible_runtimes = ["nodejs20.x"]
  description         = "Dependências npm compartilhadas"
}

# Trigger SQS — invoca a função para cada mensagem na fila
resource "aws_lambda_event_source_mapping" "sqs" {
  event_source_arn                   = aws_sqs_queue.pedidos.arn
  function_name                      = aws_lambda_function.processar_pedido.arn
  batch_size                         = 10
  maximum_batching_window_in_seconds = 5
  function_response_types            = ["ReportBatchItemFailures"]

  scaling_config {
    maximum_concurrency = 50  # Limita concorrência para proteger o banco de dados
  }
}

# Dead Letter Queue — mensagens que falharam múltiplas vezes
resource "aws_sqs_queue" "pedidos_dlq" {
  name                      = "${var.project_name}-${var.environment}-pedidos-dlq"
  message_retention_seconds = 1209600  # 14 dias

  tags = local.tags_comuns
}

resource "aws_sqs_queue" "pedidos" {
  name                       = "${var.project_name}-${var.environment}-pedidos"
  visibility_timeout_seconds = 60  # Deve ser >= timeout do Lambda
  message_retention_seconds  = 86400

  redrive_policy = jsonencode({
    deadLetterTargetArn = aws_sqs_queue.pedidos_dlq.arn
    maxReceiveCount     = 3  # Tenta 3 vezes antes de mover para DLQ
  })

  tags = local.tags_comuns
}

Quando Usar ECS, Lambda ou EC2

A decisão entre os três modelos depende das características do workload:

EC2 direto — quando o time precisa de controle total sobre o sistema operacional, quando as aplicações têm requisitos específicos de kernel ou hardware, quando os workloads são altamente previsíveis e de longa duração, ou quando o custo de instâncias reservadas supera o overhead operacional. É o modelo com maior responsabilidade operacional e maior controle.

ECS com Fargate — quando o workload é baseado em containers e tem tráfego relativamente estável ou previsível. O tempo de inicialização do Fargate — alguns segundos — é adequado para serviços web que precisam de escalabilidade mas não de resposta em milissegundos. O modelo ideal para APIs REST, workers de background e microsserviços.

Lambda — quando o workload é orientado a eventos, tem picos imprevisíveis de tráfego, executa em menos de 15 minutos, ou quando o custo por invocação é mais econômico que manter containers em execução. O modelo ideal para processamento de filas, webhooks, automações agendadas, transformação de dados e APIs de baixo tráfego.

Critério              EC2          ECS/Fargate     Lambda
─────────────────────────────────────────────────────────
Controle do SO        Total        Nenhum          Nenhum
Tempo de startup      Minutos      Segundos        Milissegundos*
Duração máxima        Ilimitada    Ilimitada       15 minutos
Custo ocioso          Alto         Médio           Zero
Escala a zero         Não          Não             Sim
Cold start            Não          Baixo           Sim
Complexidade operat.  Alta         Média           Baixa
Workload ideal        Stateful     APIs REST       Eventos

*Lambda tem cold start — a primeira invocação após um período ocioso inicializa o ambiente de execução, levando de centenas de milissegundos a alguns segundos dependendo do runtime e do tamanho do pacote.


Mitigando Cold Starts no Lambda

O cold start é o principal problema de latência do Lambda. Três estratégias eficazes para mitigá-lo:

Provisioned Concurrency — mantém instâncias pré-inicializadas prontas para responder sem cold start. Tem custo por hora de concorrência provisionada:

resource "aws_lambda_provisioned_concurrency_config" "aplicacao" {
  function_name                  = aws_lambda_function.api.function_name
  qualifier                      = aws_lambda_alias.producao.name
  provisioned_concurrent_executions = 5
}

Reduzir o tamanho do pacote — pacotes menores inicializam mais rápido. Lambda Layers separam dependências do código da aplicação, permitindo que o runtime carregue as dependências em paralelo.

Manter o runtime aquecido — para funções de menor criticidade, um EventBridge agendado pode invocar a função a cada minuto com um evento de "ping", evitando que o ambiente de execução seja desalocado:

resource "aws_cloudwatch_event_rule" "manter_aquecido" {
  name                = "manter-lambda-aquecido"
  schedule_expression = "rate(5 minutes)"
}

resource "aws_cloudwatch_event_target" "manter_aquecido" {
  rule = aws_cloudwatch_event_rule.manter_aquecido.name
  arn  = aws_lambda_function.api.arn
  input = jsonencode({ source = "warmup" })
}

O Que Vem a Seguir

O próximo artigo aprofunda os serviços de banco de dados gerenciados da AWS — RDS em alta disponibilidade com Multi-AZ, ElastiCache para caching com Redis, e as estratégias de backup e recuperação de desastre que garantem durabilidade dos dados.


Referências para Aprofundamento

Documentação oficial AWS - Amazon ECS Documentation — docs.aws.amazon.com — Documentação completa do ECS, incluindo guias de Fargate, task definitions, serviços e auto scaling. - AWS Lambda Documentation — docs.aws.amazon.com — Referência completa do Lambda, cobrindo runtimes, triggers, limites e boas práticas de performance. - AWS Lambda Power Tuning — github.com — Ferramenta open source que encontra automaticamente a configuração de memória que otimiza custo ou performance para uma função Lambda específica.

Boas práticas - ECS Best Practices Guide — docs.aws.amazon.com — Guia oficial de boas práticas do ECS, cobrindo networking, segurança, storage e auto scaling. - Lambda Operator Guide — docs.aws.amazon.com — Guia de operações do Lambda para produção, cobrindo monitoramento, troubleshooting e otimização de custos.

Comparações e arquitetura - Serverless Land — serverlessland.com — Portal da AWS com padrões de arquitetura serverless, exemplos de integração entre serviços Lambda, SQS, SNS e EventBridge e workshops práticos.

Comentários

Mais em DevOps

Grafana: Dashboards e Alertas que Fazem Sentido
Grafana: Dashboards e Alertas que Fazem Sentido

Um dashboard mal projetado é quase tão ruim quanto não ter dashboard. Quando...

Instalando e Rodando Seus Primeiros Containers
Instalando e Rodando Seus Primeiros Containers

O artigo anterior explicou o que são containers e por que eles existem. Este...

Geração de Infraestrutura com IA
Geração de Infraestrutura com IA

Gerar Terraform, manifestos Kubernetes e pipelines CI/CD a partir de descriçõ...