Aula 03 · Orquestração

Orquestração de containers

Do “rodar containers” para “manter o estado desejado”: o que um orquestrador faz e o Amazon ECS com Fargate na conta do AWS Academy, do primeiro service ao ECS Exec, ao troubleshooting, ao auto scaling e à limpeza.

6 tópicos5 exercíciosECS · Fargate · ECR · CloudWatchAWS Academy Learner LabGitHub Codespaces
Tópicos
    3.1

    Orquestração de containers

    Conceito

    Por que orquestrar?

    1Um containerdocker run

    Você escolhe o host, a porta e reinicia na mão quando cai.

    Funciona para testar
    →
    2Uma aplicaçãodocker compose up

    Vários serviços num host, com rede e volumes declarados em YAML.

    Valida a stack em ambiente local
    →
    3Produçãodezenas de hosts · centenas de containers

    Um host sozinho é ponto único de falha e tem limite de CPU e memória.

    Precisa de um orquestrador
    Perguntas que aparecem em produção
    Onde rodar?Qual servidor tem CPU e memória livres para este container?
    E se cair?O processo morreu ou o servidor saiu do ar às 3h da manhã. Quem sobe de novo?
    E o pico de acesso?Como ir de 3 para 30 réplicas e voltar, sem intervenção manual?
    Como trocar a versão?Atualizar sem derrubar o serviço e voltar atrás se der errado.
    Como se encontram?IPs mudam a cada container novo. Como um serviço acha o outro e divide a carga?
    E a configuração?Variáveis, senhas e certificados diferentes por ambiente, sem colocar na imagem.

    Imperativo × declarativo

    Imperativo: você diz como

    terminal
    docker run -d --name web1 app:v2
    docker run -d --name web2 app:v2
    docker run -d --name web3 app:v2
    # web2 caiu? alguém precisa perceber e rodar de novo

    Cada passo é um comando. Se algo muda depois, ninguém corrige sozinho.

    Declarativo: você diz o quê

    yaml
    replicas: 3
    image: app:v2
    # o orquestrador garante 3 réplicas o tempo todo

    Você descreve o estado final. O orquestrador compara com o que está rodando e corrige a diferença, para sempre.

    Estado desejadoo que você declarou
    →
    Observao que está rodando
    →
    Comparaencontra a diferença
    →
    Agecria, remove, move
    ↺

    Esse ciclo, chamado loop de reconciliação, roda sem parar. É o mesmo princípio do Swarm (Aula 02), do ECS e do Kubernetes, e também da infraestrutura como código e do GitOps.

    O que todo orquestrador entrega

    CapacidadeO que fazNo ECSNo Kubernetes
    SchedulingEscolhe onde cada container roda, conforme CPU, memória e regrasPlacement do ECS / Fargatekube-scheduler
    Auto-reparoContainer ou nó caiu: sobe outro no lugarServiceReplicaSet / Deployment
    EscalaAumenta e reduz réplicas, manual ou automaticamenteService Auto ScalingHPA
    Service discovery e balanceamentoUm endereço estável na frente de réplicas que mudamALB, Service ConnectService, Ingress
    Deploy sem downtimeTroca de versão gradual e rollbackRolling update, blue/greenRollingUpdate
    Configuração e segredosInjeta variáveis e credenciais nos containersTask definition + Secrets ManagerConfigMap, Secret

    Orquestradores de containers: não gerenciados

    Containers
    App 1
    App 2
    Bins / Libs
    Bins / Libs
    Container
    Container
    containerd
    Sistema operacional
    VM
    Virtualizador
    Sistema operacional
    Hardware
    Você opera todas as camadas, inclusive o control plane do orquestrador
    Você instala e opera o orquestrador
    KubernetesPadrão de mercado, projeto da CNCF
    Docker SwarmNativo do Docker, simples
    HashiCorp NomadOrquestra containers e binários
    Red Hat OpenShiftKubernetes corporativo
    OKDDistribuição comunitária do OpenShift
    Rancher (SUSE)Gerencia vários clusters Kubernetes
    Apache MesosPioneiro, hoje praticamente sem uso

    Liberdade total de configuração, em troca de operar upgrades, alta disponibilidade, backup do etcd e segurança do control plane.

    Orquestradores de containers: gerenciados

    Containers
    App 1
    App 2
    Bins / Libs
    Bins / Libs
    Container
    Container
    containerd
    Sistema operacional
    VM
    Virtualizador
    Sistema operacional
    Hardware
    Camadas e control plane operados pelo provedor · você cuida das aplicações
    O provedor opera o orquestrador para você
    Amazon ECSOrquestrador próprio da AWS
    Amazon EKSKubernetes gerenciado na AWS
    Google GKEKubernetes gerenciado no Google Cloud
    Google Cloud RunContainers serverless
    Azure AKSKubernetes gerenciado na Azure
    Azure Container AppsContainers serverless na Azure
    Oracle OKEKubernetes gerenciado na OCI
    OpenShift gerenciadoROSA (AWS) e ARO (Azure)
    Mirantis MKEKubernetes corporativo
    Rancher (SUSE)Plataforma para gerenciar clusters

    Menos operação e SLA do provedor, em troca de menos controle e de dependência do fornecedor. Nesta aula: Amazon ECS (orquestrador da AWS). Na Aula 04: Kubernetes (k3d, local). Na Aula 05: Amazon EKS.

    3.2

    Orquestrando containers com Amazon ECS

    Conceito

    Orquestração com Amazon ECS

    Do conceito à prática: o ECS é o orquestrador de containers da AWS. Aqui você vê que problema ele resolve, quem decide (o control plane), quem executa (o data plane) e como cada peça mantém a sua aplicação no ar.

    O que é o ECS e qual problema ele resolve

    O Amazon Elastic Container Service (ECS) é um serviço de orquestração de containers totalmente gerenciado pela AWS: ajuda a implantar, gerenciar e escalar aplicações em containers, com as boas práticas de configuração e operação da AWS já embutidas.

    O problema

    • Em produção, alguém precisa decidir em qual máquina cada container roda
    • Repor o que cai, escalar quando a carga sobe e publicar versões sem derrubar a aplicação
    • Ligar rede, balanceamento, logs, métricas e permissões a cada container

    O que o ECS faz

    • Você declara o estado desejado (quantas tasks, qual imagem, quais recursos) e o ECS mantém
    • Os schedulers escolhem onde rodar e repõem tasks; o service faz rolling update e auto scaling
    • Integra com ECR, Elastic Load Balancing, CloudWatch e IAM

    O que você não precisa fazer

    • Instalar e operar um control plane: ele é da AWS
    • Pagar pela orquestração: paga só a capacidade usada
    • No Fargate, nem cuidar de servidores

    É o loop de reconciliação do tópico 3.1 entregue como serviço. Referências: What is Amazon ECS? e Amazon ECS pricing.

    Como funciona o control plane do ECS

    O ECS tem três camadas: provisionamento (como você fala com ele), controller (o control plane, com os schedulers) e capacidade (onde os containers rodam). O control plane é da AWS: você não instala, não opera e não escala.

    1. Você descreve o que quer pela API do ECS: com infraestrutura como código (Terraform, CloudFormation, Pulumi, CDK) ou direto pelo console, pela CLI ou por um SDK, seja você ou o seu pipeline de CI/CD. Task definition, service, desiredCount: tudo vira chamada à API.
    2. O control plane guarda o estado do cluster. Os schedulers usam esse mesmo estado para decidir onde colocar cada task. O service scheduler, por padrão, espalha as tasks entre zonas de disponibilidade.
    3. Na capacidade, um agente executa as ordens: no Fargate, o agente do Fargate, gerenciado pela AWS; nas Managed Instances, instâncias EC2 que a AWS opera por você; no EC2, o agente do ECS, que roda em cada instância sua e faz a comunicação com o ECS.
    4. Os agentes devolvem o estado das tasks e dos recursos, e o ciclo recomeça.

    Referências: What is Amazon ECS?, Schedule your containers on Amazon ECS e Amazon ECS container agent.

    Você · CI/CD Console CLI SDK CDK Terraform CloudFormation Pulumi Control plane ECS API do ECS RunTask, CreateService… Estado do cluster tasks e services Schedulers service scheduler RunTask (avulsas) EventBridge (agendadas) Capacidade Fargateagente do Fargatetasktask Managed Instancesoperado pela AWStasktask EC2agente do ECStasktask 1234 você não opera o control plane, nem paga pela orquestração

    Onde as tasks rodam

    O control plane decide; o data plane executa. É nele que cada task recebe CPU, memória e rede, e que os containers sobem, rodam e param. Você escolhe onde: Fargate, Managed Instances ou EC2. O que muda entre eles é quem cuida da máquina.

    Fargate × Managed Instances × EC2: quem cuida de quê

    Nos três, você entrega a mesma coisa: imagem, task definition e service. O que muda é quem cuida das máquinas onde as tasks rodam.

    Fargateserverless: sem máquinas à vista
    Managed Instancesinstâncias EC2 operadas pela AWS
    EC2instâncias EC2 operadas por você
    O que roda
    (imagem, task definition, service)
    Você
    Você
    Você
    Escolha da máquina
    AWSvocê só pede vCPU e memória por task
    AWS ou vocêa AWS escolhe, ou você filtra por vCPU, memória, GPU
    Vocêtipo de instância no launch template
    Quantidade de máquinas
    AWSnão há máquinas para escalar
    AWSsobe e desliga instâncias conforme as tasks
    VocêAuto Scaling group + capacity provider
    Sistema operacional e patches
    AWSinvisível para você
    AWSBottlerocket; instâncias trocadas a cada 14 dias
    VocêAMI, agente do ECS e atualizações
    Hardware e virtualização
    AWS
    AWS
    AWS
    você cuidaAWS cuidavocê escolhe se quer cuidar

    Nos exercícios, vamos praticar com o ECS no Fargate. As três dão capacidade ao mesmo tipo de cluster ECS e podem até conviver nele, via capacity providers. Referência: ECS Managed Instances.

    Data plane: Fargate

    Você não vê máquina nenhuma: informa CPU, memória, rede e IAM da task, e a AWS escolhe onde rodar.

    • Cada task tem sua própria fronteira de isolamento: não divide kernel, CPU, memória nem interface de rede com outra task
    • Rede awsvpc: cada task ganha uma ENI com IP privado na sua VPC, com security group próprio
    • A platform version (kernel + runtime) é da AWS. Task nova sempre sobe na revisão mais recente, já corrigida; se surgir uma falha de segurança, a AWS aposenta as tasks na revisão vulnerável
    • Fargate Spot: mais barato, mas a task pode ser interrompida com aviso de 2 minutos

    Referências: Architect for AWS Fargate.

    Control plane do ECS · AWSinfra da AWStaskcontainerkernel, CPU, memóriae ENI só desta taskENI · IPtaskcontainerkernel, CPU, memóriae ENI só desta taskENI · IPtaskcontainerkernel, CPU, memóriae ENI só desta taskENI · IPsua VPCagente do Fargate

    Data plane: Managed Instances

    Instâncias EC2 de verdade, mas operadas pela AWS: ela provisiona, escala, aplica patches e faz a manutenção.

    • O tipo de instância é escolhido automaticamente (o de uso geral mais econômico) ou por atributos: vCPU, memória, fabricante da CPU, aceleradores como GPU
    • Por padrão, várias tasks por instância, para aproveitar melhor a máquina. Se precisar de isolamento forte, dá para rodar uma task por instância
    • Bottlerocket, sem SSH, sistema de arquivos raiz imutável e SELinux. A instância é drenada após 14 dias e as tasks do service vão para instâncias novas
    • Rede awsvpc ou host. Cobrança pela instância EC2 inteira

    Referências: Architect for Amazon ECS Managed Instances.

    Control plane do ECS · AWSgerenciado pela AWSprovisiona · escala · patchessua VPCuma ENI por task (awsvpc) ou a rede da instância (host)EC2 · Bottlerockettasktasktaskvárias tasks por instânciadrenada após 14 diasEC2 · Bottlerockettasktasktipo escolhido pela AWSou por atributos (ex.: GPU)

    Data plane: EC2

    Uma container instance é uma instância EC2 sua, rodando o agente do ECS e registrada no cluster. O agente faz a comunicação entre a instância e o ECS.

    • Você cuida da gestão e manutenção: AMI (a otimizada para ECS já vem com Docker), patches, tipo de instância e quantidade
    • A capacidade vem de um Auto Scaling group ligado ao cluster por um capacity provider. Dá para ter vários grupos, cada um com um tipo de instância
    • Várias tasks por instância. Rede awsvpc (a recomendada), bridge (padrão no Linux), host ou none
    • Spot reduz custo, mas a instância pode ser retomada com aviso de 2 minutos

    Referências: Amazon EC2 container instances, Amazon ECS container agent, Task networking options for EC2.

    Control plane do ECS · AWSsua conta · ASGvocê: AMI, patches, escalasua VPCawsvpc (recomendado), bridge (padrão no Linux), host ou noneEC2 · AMI otimizada p/ ECSagente do ECS · Dockertasktasktaskcontainer instanceEC2 · AMI otimizada p/ ECSagente do ECS · Dockertasktaskcontainer instance

    Do Docker para o ECS

    ECSEquivalente que você já conhecePara que serve
    Task definitionAs flags do docker run / um serviço do ComposeDescreve imagem, recursos, portas, variáveis e logs. É versionada (app:1, app:2…)
    TaskUm container rodandoInstância de uma task definition
    Servicedeploy.replicas + restart policyMantém o número desejado de tasks, faz rolling update e registra no load balancer
    ClusterO host DockerOnde as tasks rodam (Fargate, Managed Instances ou EC2)
    ECRDocker HubRegistry privado da conta AWS

    Funcionamento do ECS

    Como as peças se ligam: a task definition e a imagem do ECR dão origem às tasks, que rodam no data plane (Fargate, Managed Instances ou EC2); o load balancer entrega o tráfego a elas e cada task envia seus logs ao CloudWatch.

    Cluster agrupamento lógico
    Service mantém N tasks rodando
    Task definition as tasks são cópias da mesma receita
    Taskcontainer app
    Taskcontainer app
    Taskcontainer app
    as tasks rodam no data plane
    Data plane onde os containers rodam
    Fargateserverless, isolada por task
    Managed InstancesEC2 operado pela AWS
    EC2
    EC2
    EC2suas instâncias
    EC2
    EC2
    receita
    Task definition imagem, CPU, memória, portas, variáveis, logs, IAM
    imagem
    Amazon ECR registry de imagens da AWS
    tráfego
    Application Load Balancer um endereço na frente das tasks
    logs
    CloudWatch Logs stdout/stderr via log driver awslogs
    Prática
    Exercício 3.2.1

    Primeiro serviço no ECS com Fargate

    ~40 min

    Criar um cluster ECS e um service (app) com 2 tasks no Fargate rodando a imagem do nginx publicada no Amazon ECR, usando a conta do AWS Academy Learner Lab. O exercício não depende das aulas anteriores.

    AWS Academy Learner Lab

    As permissões de IAM são limitadas: o exercício usa a role pronta LabRole. Trabalhe na região do lab (normalmente us-east-1) e faça a limpeza do exercício 3.6.1 no fim da parte de ECS.

    1. No AWS Academy, abra o Learner Lab e clique em Start Lab. Aguarde a bolinha ao lado de AWS ficar verde.

      Clique em ⓘ AWS Details e, em AWS CLI, clique em Show. Lá estão os três valores: aws_access_key_id, aws_secret_access_key e aws_session_token.

      Copie o bloco abaixo, troque cada COLE_AQUI pelo valor correspondente do Learner Lab e cole tudo no terminal. Ele grava as credenciais, a região e já deixa a variável AWS_REGION pronta:

      terminal
      mkdir -p ~/.aws
      cat > ~/.aws/credentials <<'EOF'
      [default]
      aws_access_key_id=COLE_AQUI
      aws_secret_access_key=COLE_AQUI
      aws_session_token=COLE_AQUI
      EOF
      printf '[default]\nregion = us-east-1\n' > ~/.aws/config
      export AWS_REGION=us-east-1

      Use a região que aparece em AWS Details (normalmente us-east-1). Se preferir, cole direto o bloco [default] inteiro do Learner Lab no lugar das quatro linhas entre cat e EOF.

      Credenciais temporárias

      Elas expiram quando a sessão do lab termina (cerca de 4 horas). Ao clicar em Start Lab de novo, pegue os valores novos e repita este passo. Não publique essas chaves em repositório, chat ou print.

    2. Baixe o instalador da AWS CLI v2:

      terminal
      curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o awscliv2.zip

      Descompacte e instale:

      terminal
      unzip -q awscliv2.zip && sudo ./aws/install

      Confira a versão e valide as credenciais:

      terminal
      aws --version && aws sts get-caller-identity --no-cli-pager
    3. Monte o nome do registry e do repositório:

      terminal
      ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
      REGISTRY=$ACCOUNT.dkr.ecr.$AWS_REGION.amazonaws.com
      REPO=$REGISTRY/app

      Crie o repositório no ECR:

      terminal
      aws ecr create-repository --repository-name app --no-cli-pager

      Autentique o Docker no ECR:

      terminal
      aws ecr get-login-password | docker login --username AWS --password-stdin $REGISTRY
    4. Crie a pasta do exercício e entre nela:

      terminal
      mkdir -p ~/labs/ecs && cd ~/labs/ecs

      Crie o Dockerfile. Ele troca a página padrão do nginx por uma mensagem com o nome da disciplina e a versão da imagem. A versão entra como ARG, com 1.27-alpine como padrão:

      terminal
      cat > Dockerfile <<'EOF'
      ARG NGINX_VERSION=1.27-alpine
      FROM nginx:${NGINX_VERSION}
      ARG NGINX_VERSION
      RUN echo "<h1>Container Technologies</h1><p>nginx:${NGINX_VERSION}</p>" > /usr/share/nginx/html/index.html
      EOF

      Construa a imagem v1 (nginx:1.27-alpine):

      terminal
      docker build -t $REPO:v1 .

      Publique no ECR:

      terminal
      docker push $REPO:v1
    5. Descubra o ARN da LabRole:

      terminal
      ROLE=$(aws iam get-role --role-name LabRole --query Role.Arn --output text)

      Crie o grupo de logs:

      terminal
      aws logs create-log-group --log-group-name /ecs/app --no-cli-pager

      Crie a task definition:

      terminal
      cat > taskdef.json <<EOF
      {
        "family": "app",
        "networkMode": "awsvpc",
        "requiresCompatibilities": ["FARGATE"],
        "cpu": "256",
        "memory": "512",
        "executionRoleArn": "$ROLE",
        "taskRoleArn": "$ROLE",
        "containerDefinitions": [{
          "name": "nginx",
          "image": "$REPO:v1",
          "essential": true,
          "cpu": 256,
          "memory": 512,
          "memoryReservation": 256,
          "linuxParameters": { "initProcessEnabled": true },
          "portMappings": [{ "containerPort": 80, "protocol": "tcp" }],
          "healthCheck": {
            "command": ["CMD-SHELL", "wget -q --spider http://localhost/ || exit 1"],
            "interval": 30,
            "timeout": 5,
            "retries": 3,
            "startPeriod": 10
          },
          "logConfiguration": {
            "logDriver": "awslogs",
            "options": {
              "awslogs-group": "/ecs/app",
              "awslogs-region": "$AWS_REGION",
              "awslogs-stream-prefix": "nginx"
            }
          }
        }]
      }
      EOF
      Para saber mais: o que é cada campo da task definition
      Nível da task
      CampoO que é
      familyNome da task definition. Cada register-task-definition cria uma nova revisão: app:1, app:2…
      networkModeawsvpc: cada task ganha a própria interface de rede (ENI) com IP na VPC. É o único modo aceito no Fargate
      requiresCompatibilitiesOnde a task pode rodar: FARGATE, EC2 ou MANAGED_INSTANCES. O ECS valida os campos de acordo
      cpu / memoryTamanho da task: 256 = 0,25 vCPU e 512 MiB. No Fargate é sobre isso que a AWS cobra, e só algumas combinações são aceitas
      executionRoleArnRole usada pelo agente do ECS antes de o container subir: baixar a imagem do ECR e enviar logs ao CloudWatch
      taskRoleArnRole usada pela aplicação dentro do container para chamar serviços da AWS. Também é usada pelo ECS Exec para abrir a sessão pelo SSM. No Learner Lab, as duas são a LabRole
      Nível do container (containerDefinitions)
      CampoO que é
      nameNome do container dentro da task. É ele que aparece no console e que se usa no --container do ECS Exec
      imageImagem a rodar: a do seu repositório no ECR com a tag v1 (nginx 1.27 com a mensagem da disciplina)
      essentialtrue: se este container parar, a task inteira para e o service sobe outra
      cpuParte da CPU da task reservada ao container, em unidades (1024 = 1 vCPU)
      memoryLimite rígido (hard) em MiB: se o container passar disso, é encerrado por falta de memória (OOM)
      memoryReservationLimite flexível (soft) em MiB: a memória reservada para o container. Ele pode usar mais quando precisar, até o memory (hard). Precisa ser menor que o memory
      linuxParameters.initProcessEnabledPõe um processo init no container, que recolhe processos órfãos. Recomendado para o ECS Exec; o Exec em si é ligado no service (--enable-execute-command, passo 8)
      portMappingsPorta em que o container escuta (80, TCP). No awsvpc, é a mesma porta no IP da task
      healthCheck.commandComando rodado dentro do container. CMD-SHELL executa via shell; saída 0 = saudável. A nginx:alpine já traz o wget
      healthCheck.interval / timeoutA cada 30 s roda o teste, e cada tentativa tem até 5 s para responder
      healthCheck.retriesDepois de 3 falhas seguidas, o container fica UNHEALTHY e o service substitui a task
      healthCheck.startPeriodCarência de 10 s após o container subir: falhas nesse período não contam. Valores aceitos: interval 5–300 s, timeout 2–60 s, retries 1–10, startPeriod 0–300 s
      logConfigurationDriver awslogs: envia o stdout/stderr ao CloudWatch Logs, no grupo /ecs/app, região e prefixo do stream (nginx/…)

      Registre a task definition:

      terminal
      aws ecs register-task-definition --cli-input-json file://taskdef.json --query taskDefinition.taskDefinitionArn --no-cli-pager
    6. Descubra a VPC padrão, duas subnets e crie um security group:

      terminal
      VPC=$(aws ec2 describe-vpcs --filters Name=isDefault,Values=true --query 'Vpcs[0].VpcId' --output text)
      SUBNETS=$(aws ec2 describe-subnets --filters Name=vpc-id,Values=$VPC --query 'Subnets[0:2].SubnetId' --output text | tr '\t' ',')
      SG=$(aws ec2 create-security-group --group-name app-sg --description "app nginx" --vpc-id $VPC --query GroupId --output text)

      Libere a porta 80 no security group:

      terminal
      aws ec2 authorize-security-group-ingress --group-id $SG --protocol tcp --port 80 --cidr 0.0.0.0/0 --no-cli-pager

      Confira os valores:

      terminal
      echo "VPC=$VPC SUBNETS=$SUBNETS SG=$SG"
    7. Crie o cluster com o Container Insights com observabilidade aprimorada (enhanced) ligado, para ter métricas de CPU, memória, rede e quantidade de tasks por cluster, service e task no CloudWatch:

      terminal
      aws ecs create-cluster --cluster-name ecs-labs \
        --settings name=containerInsights,value=enhanced --no-cli-pager
    8. Crie o service app com 2 tasks no Fargate e o ECS Exec habilitado (você vai usá-lo no exercício 3.4.1):

      terminal
      aws ecs create-service --cluster ecs-labs --service-name app \
        --task-definition app --desired-count 2 --launch-type FARGATE \
        --enable-execute-command \
        --network-configuration "awsvpcConfiguration={subnets=[$SUBNETS],securityGroups=[$SG],assignPublicIp=ENABLED}" --no-cli-pager

      Aguarde estabilizar:

      terminal
      aws ecs wait services-stable --cluster ecs-labs --services app

      Confira o estado:

      terminal
      aws ecs describe-services --cluster ecs-labs --services app --query 'services[0].{desejado:desiredCount,rodando:runningCount}' --no-cli-pager

      Confira o health check das tasks:

      terminal
      aws ecs describe-tasks --cluster ecs-labs --tasks $(aws ecs list-tasks --cluster ecs-labs --service-name app --query 'taskArns' --output text) --query 'tasks[].healthStatus' --no-cli-pager
    9. Descubra o IP público de uma task:

      terminal
      TASK=$(aws ecs list-tasks --cluster ecs-labs --service-name app --query 'taskArns[0]' --output text)
      ENI=$(aws ecs describe-tasks --cluster ecs-labs --tasks $TASK --query "tasks[0].attachments[0].details[?name=='networkInterfaceId'].value" --output text)
      IP=$(aws ec2 describe-network-interfaces --network-interface-ids $ENI --query 'NetworkInterfaces[0].Association.PublicIp' --output text)

      Veja qual imagem está respondendo:

      terminal
      curl -s http://$IP | grep -o 'nginx:[0-9.]*-alpine'

      Mostre o endereço e abra no navegador para ver a página:

      terminal
      echo http://$IP

      Veja a versão no cabeçalho da resposta:

      terminal
      curl -sI http://$IP | grep -i server
    Resultado esperado
    • ECR: a imagem v1 aparece no repositório (aws ecr describe-images --repository-name app).
    • Service: desejado: 2, rodando: 2, com as duas tasks HEALTHY. Logo após subir, o status pode ficar alguns segundos como UNKNOWN.
    • Acesso: o primeiro curl mostra nginx:1.27-alpine e o segundo, Server: nginx/1.27.x. No navegador aparece Container Technologies e a versão logo abaixo.
    • Recursos: no console, o container nginx mostra limite de memória hard/soft de 512 / 256 MiB e CPU 256.
    • Métricas: alguns minutos depois, os dados do cluster aparecem em CloudWatch → Container Insights.
    O que aprendemos com este exercício?
    • O fluxo completo no ECS: imagem no ECR → task definition → cluster → service no Fargate.
    • A task definition é a receita: CPU e memória, limites, health check, porta, logs e roles.
    • O service mantém o número de tasks pedido, saudáveis e cada uma com seu próprio IP (awsvpc).
    • Credenciais temporárias, LabRole e Container Insights: o mesmo tipo de ambiente de um projeto real na AWS.
    Guarde as variáveis

    REPO, SUBNETS e SG são usadas nos exercícios seguintes de ECS (3.3.1 a 3.6.1). Se abrir outro terminal, rode de novo o export AWS_REGION do passo 1 e as variáveis dos passos 3, 5 e 6 (os comandos que criam recursos vão avisar que eles já existem).

    3.3

    ECS: operando o service

    Conceito

    O service como orquestrador

    Uma task sozinha é como um docker run: se ela cair, ninguém sobe outra. O service é a camada que recebe o estado desejado e trabalha sem parar para mantê-lo.

    Auto-reparo

    Se uma task para ou falha no health check, o service sobe outra para manter o desiredCount.

    Escala

    Mude o desiredCount na mão ou use Service Auto Scaling por CPU, memória ou requisições por alvo do ALB. Os tipos de política estão no tópico 3.5.

    Deploy

    Cada mudança gera uma nova revisão da task definition. O rolling update troca as tasks aos poucos e o circuit breaker faz rollback se a nova versão não subir.

    Observabilidade

    Logs no CloudWatch Logs (awslogs), métricas no Container Insights e eventos do service.

    O loop de reconciliação do service

    O scheduler do service compara o tempo todo o que você pediu (desiredCount) com o que existe (runningCount e pendingCount) e age na diferença.

    • Faltam tasks: sobe novas. Sobram tasks: para as excedentes
    • Distribui as tasks entre as subnets e zonas de disponibilidade informadas
    • Cada decisão vira um evento do service: has started 1 tasks, has reached a steady state
    • Estratégia REPLICA (N cópias, a usada no lab) ou DAEMON (uma por instância, só em EC2)

    É o mesmo princípio do Swarm (Aula 02) e do Kubernetes: você declara o estado, o orquestrador converge.

    Estado desejado desiredCount = 2 Scheduler compara desejado × atual Estado atual runningCount = 1 Ação inicia 1 task nova loop contínuo evento: 1 task parou ✕

    Ciclo de vida de uma task

    Toda task passa pelos mesmos estados. O campo lastStatus mostra onde ela está e o desiredStatus, para onde o ECS quer levá-la.

    • PROVISIONING cria a interface de rede (ENI); PENDING espera recursos disponíveis; ACTIVATING baixa a imagem, cria os containers, configura a rede e registra no target group do load balancer
    • Ao parar, a task registra o motivo em stoppedReason e stopCode: é a primeira coisa a olhar quando algo falha
    • O stopCode traz a categoria do erro, como CannotPullContainer, ResourceInitializationError ou OutOfMemoryError. Referência: Amazon ECS task lifecycle
    terminal
    aws ecs describe-tasks --cluster ecs-labs --tasks $TASK \
      --query 'tasks[0].{status:lastStatus,codigo:stopCode,motivo:stoppedReason}' --no-cli-pager
    subindo PROVISIONINGPENDINGACTIVATINGRUNNING cria a ENIespera recursosimagem, rede, ALBatendendo parando DEACTIVATINGSTOPPINGDEPROVISIONINGSTOPPED sai do ALBSTOPSIGNAL → SIGKILLlibera a ENIstoppedReason o service sobe uma substituta se a task parar sem você pedir

    Quem decide que uma task está doente

    SinalOnde se configuraO que acontece
    Container essencial saiu"essential": true na task definitionO processo principal terminou (erro ou fim): a task inteira para
    Health check do containerhealthCheck na task definition (comando dentro do container)Após retries falhas seguidas, a task fica UNHEALTHY e o service a substitui
    Health check do load balancerTarget group do ALB/NLB (caminho, porta, códigos esperados)O alvo sai do tráfego e, se o service usa o load balancer, a task é substituída

    Período de carência

    healthCheckGracePeriodSeconds no service: por quanto tempo, depois que a task sobe, o scheduler ignora falhas nos health checks do load balancer, do VPC Lattice e do container. O padrão é 0. Essencial para apps que demoram a iniciar.

    startPeriod

    A mesma ideia dentro do health check do container: falhas nesse intervalo não contam para os retries.

    Liveness × readiness

    No ECS, o health check do container faz o papel de liveness (reinicia). O do load balancer, o de readiness (tira do tráfego). O Kubernetes separa os dois em probes (Aula 05).

    Referência: Amazon ECS service definition parameters (healthCheckGracePeriodSeconds).

    Estratégias de deploy

    O service troca a versão da aplicação de quatro jeitos. Todas são do controlador padrão do ECS; o que muda é como as tasks e o tráfego passam da versão antiga para a nova.

    EstratégiaComo troca a versãoQuando usar
    Rolling (padrão)Substitui as tasks aos poucos, dentro de min/maxA maioria dos services; é a usada no lab
    Blue/greenSobe o ambiente novo inteiro ao lado e vira o tráfego de uma vezRollback rápido, validação antes de expor
    CanaryManda uma pequena fatia do tráfego para a versão nova e, se estiver tudo bem, o restanteTestar com usuários reais e pouco risco
    LinearAumenta o tráfego da versão nova em degraus iguais ao longo do tempoExposição gradual e previsível

    Blue/green, canary e linear rodam as duas versões ao mesmo tempo (o uso de recursos pode dobrar durante o deploy) e precisam de algo que divida o tráfego: Application ou Network Load Balancer, Service Connect ou VPC Lattice. Também existem os controladores CodeDeploy e external. Referência: Amazon ECS deployment types.

    Rolling update (padrão)

    O service substitui as tasks antigas por novas, respeitando dois limites em relação ao desiredCount:

    • minimumHealthyPercent: mínimo de tasks saudáveis durante a troca (arredondado para cima)
    • maximumPercent: máximo de tasks rodando ao mesmo tempo (arredondado para baixo)
    • Com 4 tasks e 125% de máximo, cabe 1 task extra: troca uma por vez. Com 200%, sobem as 4 novas antes de parar as antigas
    • Falhas detectadas pelo circuit breaker ou por alarmes do CloudWatch

    Referência: Deploy Amazon ECS services by replacing tasks.

    service · desiredCount 4 · min 100% · max 125%v1v2task v2task v1task v2task v1task v2task v1task v2task v11ª troca: sobe 1 task v2 (5 rodando = 125%); saudável, para 1 v12ª troca: o mesmo, uma por vez3ª troca: nunca menos de 4 saudáveis4ª troca: as 4 tasks estão na v2recomeçasem load balancer obrigatório: o próprio service troca as tasks

    Blue/green

    Dois ambientes completos: o blue (versão atual) e o green (versão nova).

    • O green sobe inteiro ao lado do blue e pode receber tráfego de teste antes da produção
    • A produção vira para o green de uma vez
    • No bake time, as duas versões continuam rodando: se algo der errado, o rollback é voltar o tráfego para o blue
    • Lifecycle hooks (Lambda ou pausa) permitem validar em cada etapa

    Referência: Amazon ECS blue/green deployments.

    UsuáriosALBlistenerblue · v1tasktasktaskgreen · v2tasktasktask100%0%0%100%1 · green sobe ao lado e recebe tráfego de teste2 · a produção vira para o green de uma vez3 · bake time: blue fica de reserva e depois é desligado

    Canary

    Um blue/green em dois passos de tráfego, para testar com usuários reais e pouco risco.

    • Passo 1: uma fatia pequena vai para o green (o exemplo da AWS usa 10%)
    • Canary bake time: espera e observa métricas e alarmes
    • Passo 2: o restante do tráfego vai para o green de uma vez
    • A AWS recomenda começar com 5% a 10% e avaliar por 10 a 30 minutos

    Referência: Amazon ECS canary deployments.

    UsuáriosALBlistenerblue · v1tasktasktaskgreen · v2tasktasktask100%90%0%0%10%100%1 · green sobe e é testado, sem tráfego de produção2 · 10% vai para o canary · canary bake time3 · tudo bem? o restante vai de uma vez4 · bake time e o blue é desligado

    Linear

    O tráfego passa para a versão nova em degraus iguais, com uma espera entre cada um.

    • Step percent: quanto do tráfego muda a cada degrau (de 3% a 100%)
    • Step bake time: espera entre os degraus (de 0 a 1.440 minutos)
    • Com 10% por degrau: 10%, 20%, 30%… até 100% no green. A animação usa 25% para ficar visível
    • Mais lento que blue/green e canary, mas com exposição gradual e previsível

    Referência: Amazon ECS linear deployments.

    UsuáriosALBlistenerblue · v1tasktasktaskgreen · v2tasktasktask100%75%50%25%0%0%25%50%75%100%1 · green sobe e é testado2 · degraus iguais de 25%, com step bake time entre eles3 · 100% no green4 · bake time e o blue é desligado

    Circuit breaker e alarmes: rollback automático

    Dois jeitos de o ECS perceber que um deploy deu errado e voltar sozinho para a versão anterior:

    • Circuit breaker: conta as tasks da versão nova que não chegam a RUNNING e, depois, as que falham nos health checks (load balancer, Cloud Map ou container)
    • O limite padrão é 50% do desiredCount, com mínimo de 3 e máximo de 200. Bateu o limite, o deploy vira FAILED e não lança mais tasks
    • Com rollback=true, o ECS volta para o último deploy COMPLETED
    • Alarmes do CloudWatch: se um alarme ligado ao deploy for para ALARM (ex.: erros 5xx), o deploy também falha, mesmo com as tasks saudáveis. O que detectar primeiro, vence
    terminal
    --deployment-configuration \
     "deploymentCircuitBreaker={enable=true,rollback=true},alarms={alarmNames=[app-5xx],enable=true,rollback=true}"

    Use o circuit breaker para tasks que não sobem e os alarmes para métricas da aplicação. Referências: How the deployment circuit breaker detects failures e How CloudWatch alarms detect Amazon ECS deployment failures.

    deploy novo · app:2task v2PENDING✕não subiutask v2PENDING✕não subiutask v2PENDING✕não subiufalhas0 / 31 / 32 / 33 / 3limite: 3rolloutStateIN_PROGRESSFAILEDCloudWatchalarme do deployOKse for a ALARM,também falharollbackúltimo deploy bom · app:1COMPLETEDIN_PROGRESS (rollback)task v1task v1task v1task v1app:1 seguiuatendendo otempo todo1 · as tasks da app:2 não chegam a RUNNING: cada uma conta uma falha2 · falhas = limite: deploy FAILED, nenhuma task nova é lançada3 · rollback automático para o último deploy COMPLETED

    Como uma task para: encerramento gracioso

    Ao escalar para baixo, fazer deploy ou parar uma task, o ECS não mata o container de uma vez. Ele dá tempo para a aplicação terminar o que está fazendo.

    • Com load balancer: a task sai do target group e espera o deregistration_delay (padrão 300 s) para drenar as conexões
    • Depois, o ECS manda o sinal de parada da imagem (STOPSIGNAL do Dockerfile; o padrão é SIGTERM)
    • Se o processo não sair em stopTimeout (padrão 30 s, máximo 120 s), recebe SIGKILL
    • A aplicação deve tratar esse sinal: parar de aceitar requisições e fechar as abertas. A imagem do nginx, por exemplo, define STOPSIGNAL SIGQUIT, que faz o nginx encerrar de forma graciosa

    Sem load balancer, como no lab, o sinal de parada chega logo no início. Referência: Amazon ECS task lifecycle.

    linha do tempo da parada 1 · drena no ALB deregistration_delay 2 · app encerra stopTimeout fim parada pedida STOPSIGNAL SIGKILL a task sai do target group:novas requisições vão para outrasa app para de aceitar etermina as requisições abertas padrões: deregistration_delay 300 sstopTimeout 30 s (máximo 120 s) app que sai antes do SIGKILL = deploy e scale-in sem erro para o usuário

    Observar e operar o service

    Eventos do service

    O diário do scheduler: tasks iniciadas, paradas, falhas de health check, deploy concluído. O primeiro lugar para olhar.

    CloudWatch Logs

    O stdout/stderr de cada container, pelo log driver awslogs, num stream por task.

    Container Insights

    Com o modo enhanced: CPU, memória, rede e quantidade de tasks por cluster, service, task e container.

    ParaComando
    Ver o estado e os últimos eventosaws ecs describe-services --cluster ecs-labs --services app --query 'services[0].events[0:5].message'
    Escalar na mãoaws ecs update-service --cluster ecs-labs --service app --desired-count 3
    Refazer o deployaws ecs update-service --cluster ecs-labs --service app --force-new-deployment
    Esperar estabilizaraws ecs wait services-stable --cluster ecs-labs --services app
    Saber por que uma task parouaws ecs describe-tasks --cluster ecs-labs --tasks $TASK --query 'tasks[0].stoppedReason'
    Acompanhar os logsaws logs tail /ecs/app --follow
    Prática
    Exercício 3.3.1

    Auto-reparo, escala e nova versão no ECS

    ~30 min

    Ver o service do ECS reconciliar o estado desejado, escalar, publicar a v2 com rolling update e ler os logs.

    1. Pare uma task "na mão":

      terminal
      aws ecs stop-task --cluster ecs-labs --task $(aws ecs list-tasks --cluster ecs-labs --service-name app --query 'taskArns[0]' --output text) --reason "teste de auto-reparo" > /dev/null

      Aguarde um pouco:

      terminal
      sleep 30

      Acompanhe o service repor:

      terminal
      aws ecs describe-services --cluster ecs-labs --services app --query 'services[0].events[0:4].message' --no-cli-pager
    2. Escale para 3 tasks:

      terminal
      aws ecs update-service --cluster ecs-labs --service app --desired-count 3 > /dev/null

      Aguarde estabilizar:

      terminal
      aws ecs wait services-stable --cluster ecs-labs --services app

      Confira:

      terminal
      aws ecs describe-services --cluster ecs-labs --services app --query 'services[0].runningCount' --no-cli-pager
    3. Construa a v2 com o mesmo Dockerfile, trocando só a versão do nginx para 1.28-alpine:

      terminal
      docker build --build-arg NGINX_VERSION=1.28-alpine -t $REPO:v2 .

      Publique no ECR:

      terminal
      docker push $REPO:v2

      Gere uma task definition apontando para a imagem v2:

      terminal
      sed 's/:v1"/:v2"/' taskdef.json > taskdef-v2.json

      Registre a nova revisão:

      terminal
      aws ecs register-task-definition --cli-input-json file://taskdef-v2.json --query taskDefinition.revision --no-cli-pager

      Atualize o service para usá-la, com circuit breaker ligado:

      terminal
      aws ecs update-service --cluster ecs-labs --service app --task-definition app \
        --deployment-configuration "deploymentCircuitBreaker={enable=true,rollback=true}" > /dev/null

      Aguarde estabilizar:

      terminal
      aws ecs wait services-stable --cluster ecs-labs --services app

      Confira a task definition em uso:

      terminal
      aws ecs describe-services --cluster ecs-labs --services app --query 'services[0].taskDefinition' --no-cli-pager
    4. Descubra o IP de uma task:

      terminal
      TASK=$(aws ecs list-tasks --cluster ecs-labs --service-name app --query 'taskArns[0]' --output text)
      ENI=$(aws ecs describe-tasks --cluster ecs-labs --tasks $TASK --query "tasks[0].attachments[0].details[?name=='networkInterfaceId'].value" --output text)
      IP=$(aws ec2 describe-network-interfaces --network-interface-ids $ENI --query 'NetworkInterfaces[0].Association.PublicIp' --output text)

      Confira a nova versão na página e no cabeçalho:

      terminal
      curl -s http://$IP | grep -o 'nginx:[0-9.]*-alpine'; curl -sI http://$IP | grep -i server

      Veja os logs no CloudWatch:

      terminal
      aws logs tail /ecs/app --since 15m | tail -8
    Resultado esperado
    • Os eventos mostram has started 1 tasks logo depois do stop-task: o service reconciliou.
    • Com a escala, runningCount vai a 3.
    • O service passa a usar app:2: a página mostra nginx:1.28-alpine e o cabeçalho, Server: nginx/1.28.x; os logs no CloudWatch mostram os acessos do nginx ("GET / HTTP/1.1" 200).
    O que aprendemos com este exercício?
    • O service reconcilia: parou uma task, outra sobe sozinha, e os eventos registram isso.
    • Escalar é mudar o desiredCount.
    • Nova versão = nova revisão da task definition + rolling update, com o circuit breaker protegendo contra uma versão quebrada.
    • Os logs no CloudWatch mostram a troca de versão acontecendo.
    Desafio

    Coloque um Application Load Balancer na frente do service (target group do tipo ip, porta 80, health check em /) para ter um único endereço em vez do IP de cada task.

    3.4

    ECS: acesso ao container e troubleshooting

    Conceito

    Como acessar um container no Fargate?

    No Fargate não existe uma máquina para você entrar: a AWS cuida do host. E a imagem não deveria ter servidor SSH. Para olhar dentro de um container em execução, o caminho é o ECS Exec.

    Sem SSH

    Não há instância visível nem chave para conectar. Abrir a porta 22 e colocar sshd na imagem aumentaria a superfície de ataque.

    ECS Exec

    Roda um comando ou abre um shell dentro do container da task, pelo AWS Systems Manager (SSM). Pela CLI com aws ecs execute-command ou pelo botão Connect no console.

    Para que serve

    Investigar: ver arquivos, variáveis, processos, testar a rede de dentro da task. Não é para mudar a aplicação na mão; a mudança vem de uma imagem nova.

    RequisitoOnde
    Exec ligado na task--enable-execute-command no service (ou no run-task). Só vale para tasks novas
    Permissão para abrir o canalTask role com ssmmessages:CreateControlChannel, CreateDataChannel, OpenControlChannel e OpenDataChannel. No Learner Lab, a LabRole
    Permissão para quem conectaSeu usuário ou role com ecs:ExecuteCommand
    ClienteAWS CLI com o Session Manager plugin instalado (ou o console)
    Saída de rede para o SSMIP público, NAT gateway ou VPC endpoint do ssmmessages
    O comando existir na imagem/bin/sh não existe em imagens distroless
    Sistema de arquivos gravávelO agente do SSM grava arquivos no container: readonlyRootFilesystem não é suportado

    Vamos praticar com o ECS no Fargate: o exercício 3.2.1 já criou o service com o ECS Exec ligado e, no exercício 3.4.1, você entra no container. Referência: Monitor Amazon ECS containers with ECS Exec.

    ECS Exec: como funciona

    1. Você roda aws ecs execute-command ou clica em Connect no console.
    2. A API do ECS confere a sua permissão (ecs:ExecuteCommand) e se a task tem o Exec ligado. Se estiver tudo certo, pede uma sessão ao SSM Session Manager.
    3. Dentro da task, o agente do SSM, que o Fargate coloca sozinho no container, mantém um canal de saída com o SSM (HTTPS, porta 443).
    4. O SSM liga as duas pontas: o que você digita chega ao shell do container, e a resposta volta, tudo criptografado.

    Nenhuma porta de entrada é aberta na task: quem inicia a conexão é o agente, de dentro para fora. Os comandos podem ser registrados no CloudWatch Logs ou no S3 pela configuração do cluster.

    Você AWS CLI + Session Manager plugin, ou Connect API do ECS confere IAM e Exec SSM Session Manager ssmmessages Task no Fargate container nginx agente SSM /bin/sh 1234 :443 sem SSH e sem porta de entrada aberta na task

    Troubleshooting: por onde começar

    Quando o service não fica estável ou a aplicação não responde, siga do mais geral para o mais específico:

    1 · Eventoso que o scheduler tentou fazer
    →
    2 · stoppedReasonpor que a task parou
    →
    3 · Logso que a aplicação disse
    →
    4 · MétricasCPU e memória no limite?
    →
    5 · ECS Execolhar dentro do container
    Sintoma (stopCode / mensagem)Causa comum, segundo a documentação
    CannotPullContainer: “Check that the role has the permissions to pull images”A execution role não pode baixar do ECR, ou a imagem não existe no repositório
    CannotPullContainer: “connection issue between the task and Amazon ECR”A task não alcança o ECR: em subnet pública, falta assignPublicIp=ENABLED; em subnet privada, falta NAT gateway ou VPC endpoint
    CannotPullContainer: “failed to resolve ref”A imagem ou a tag da task definition não existe mais no repositório
    ResourceInitializationError: “failed to initialize logging driver”O grupo de logs da task definition não existe no CloudWatch Logs
    ResourceInitializationError: “cannot pull secrets…”Sem rede até o Secrets Manager ou o Parameter Store, ou a execution role sem permissão para o secret
    OutOfMemoryErrorO container passou do limite de memória (memory)
    Task fica em PENDINGEstá esperando recursos disponíveis para rodar
    Task RUNNING, mas o curl não respondeSecurity group sem a porta, task sem IP público ou aplicação escutando em outra porta

    Tasks paradas aparecem no console por apenas 1 hora. Pela CLI: aws ecs list-tasks --cluster ecs-labs --desired-status STOPPED e, no describe-tasks, os campos stopCode, stoppedReason e o reason de cada container. Referência: Amazon ECS stopped tasks error messages.

    Troubleshooting com o ECS Exec

    Quando eventos, logs e métricas não bastam, entre no container e investigue de dentro, com a mesma rede e as mesmas variáveis da aplicação.

    terminal
    aws ecs execute-command --cluster ecs-labs --task $TASK --container nginx --interactive --command "/bin/sh"

    O que olhar lá dentro

    • ps: o processo principal está rodando?
    • wget -qO- http://localhost/: a app responde de dentro?
    • env: as variáveis chegaram?
    • nginx -t: a configuração é válida?
    • cat /etc/resolv.conf e nslookup: o DNS resolve?

    Quando o Exec não conecta

    • “The execute command failed”: a task role sem as permissões ssmmessages, o agente do SSM não está rodando, ou há VPC endpoint do ECS sem o do ssmmessages
    • Confira no describe-tasks: enableExecuteCommand deve ser true e o agente ExecuteCommandAgent, RUNNING
    • Ligou o Exec depois? Ele só vale para tasks novas: force um novo deploy
    • Na sua máquina: AWS CLI 2.3.6 ou mais nova e o Session Manager plugin instalado

    Para conferir todos os pré-requisitos de uma vez, a AWS recomenda o script ECS Exec Checker (precisa de jq). A sessão cai após 20 minutos parada e os comandos rodam como root. Referência: Troubleshoot Amazon ECS Exec issues. Lembre: em imagens sem shell (distroless), use a variante :debug ou um container auxiliar.

    Prática
    Exercício 3.4.1

    ECS Exec e troubleshooting na prática

    ~25 min

    Entrar no container de uma task em execução com o ECS Exec, investigar de dentro e, depois, provocar um erro de propósito para ler o stopCode e o stoppedReason de uma task parada.

    1. Ajuste o service para 2 réplicas:

      terminal
      aws ecs update-service --cluster ecs-labs --service app --desired-count 2 --no-cli-pager

      Aguarde estabilizar:

      terminal
      aws ecs wait services-stable --cluster ecs-labs --services app

      Guarde uma das tasks e confira se ela está pronta para o ECS Exec: exec deve ser true e o agente, RUNNING:

      terminal
      TASK=$(aws ecs list-tasks --cluster ecs-labs --service-name app --query 'taskArns[0]' --output text)
      aws ecs describe-tasks --cluster ecs-labs --tasks $TASK --query 'tasks[0].{exec:enableExecuteCommand,agente:containers[0].managedAgents[0].lastStatus}' --no-cli-pager
    2. Antes de tentar conectar, confira os pré-requisitos com o ECS Exec Checker, um script mantido pela AWS. Instale o Session Manager plugin (usado pela AWS CLI para abrir a sessão) e o jq, que o script exige:

      terminal
      curl -s "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb" -o session-manager-plugin.deb && sudo dpkg -i session-manager-plugin.deb
      terminal
      sudo apt-get install -y jq

      Rode o checker para o cluster e a task (ele recebe o ID da task, a parte final do ARN):

      terminal
      bash <( curl -Ls https://raw.githubusercontent.com/aws-containers/amazon-ecs-exec-checker/main/check-ecs-exec.sh ) ecs-labs ${TASK##*/}

      Leitura do resultado: verde está ok, amarelo é recomendação (o Exec funciona mesmo assim) e vermelho precisa ser corrigido antes de conectar. Ele confere a AWS CLI e o plugin, a configuração do cluster e da task, o agente, as permissões IAM e os VPC endpoints.

    3. Entre no container nginx da task:

      terminal
      aws ecs execute-command --cluster ecs-labs --task $TASK --container nginx --interactive --command "/bin/sh"

      Dentro do container, investigue: versão do nginx, a página servida, a resposta local e os processos. Depois saia:

      sh
      nginx -v
      cat /usr/share/nginx/html/index.html
      wget -qO- http://localhost/
      ps
      exit

      No console, o botão Connect do container abre a mesma sessão.

    4. Agora provoque um erro: gere uma revisão da task definition com uma tag que não existe (v9):

      terminal
      sed 's/:v1"/:v9"/' ~/labs/ecs/taskdef.json > ~/labs/ecs/taskdef-erro.json

      Registre essa revisão e guarde o ARN:

      terminal
      REV=$(aws ecs register-task-definition --cli-input-json file://$HOME/labs/ecs/taskdef-erro.json --query taskDefinition.taskDefinitionArn --output text)

      Rode uma task avulsa com ela, fora do service:

      terminal
      ERR=$(aws ecs run-task --cluster ecs-labs --launch-type FARGATE --task-definition $REV \
        --network-configuration "awsvpcConfiguration={subnets=[$SUBNETS],securityGroups=[$SG],assignPublicIp=ENABLED}" \
        --query 'tasks[0].taskArn' --output text)

      Aguarde a task parar:

      terminal
      aws ecs wait tasks-stopped --cluster ecs-labs --tasks $ERR
    5. Leia por que ela parou:

      terminal
      aws ecs describe-tasks --cluster ecs-labs --tasks $ERR --query 'tasks[0].{codigo:stopCode,motivo:stoppedReason}' --no-cli-pager

      Liste as tasks paradas do cluster (o console só mostra por 1 hora):

      terminal
      aws ecs list-tasks --cluster ecs-labs --desired-status STOPPED --no-cli-pager

      Remova a revisão com erro:

      terminal
      aws ecs deregister-task-definition --task-definition $REV > /dev/null
    Resultado esperado
    • Pronta: exec: true e agente: RUNNING.
    • Checker: nenhum item em vermelho; alguns amarelos (recomendações) são normais.
    • Dentro do container: nginx -v mostra nginx/1.28.x e a página, nginx:1.28-alpine: o service está na v2 desde o exercício 3.3.1.
    • Erro provocado: a task para sem chegar a RUNNING, e o stopCode e o stoppedReason apontam falha ao baixar a imagem (CannotPullContainer), porque a tag v9 não existe no ECR.
    O que aprendemos com este exercício?
    • O ECS Exec Checker valida os pré-requisitos antes de conectar: CLI, plugin, permissões, agente e rede.
    • O ECS Exec dá um shell dentro do container sem SSH e sem abrir porta, pelo SSM.
    • Task que nem chega a rodar não tem container para entrar: a investigação vai pelo stopCode e pelo stoppedReason.
    • Uma task avulsa (run-task) permite testar algo sem afetar o service.
    3.5

    ECS: tipos de políticas de scaling

    Conceito
    Service Auto Scaling

    Tipos de políticas de scaling

    Em vez de mudar o desiredCount na mão, o ECS pode ajustá-lo sozinho. São cinco formas de decidir quando e quanto escalar: target tracking, step scaling, scheduled, predictive e queue-based.

    Como o ECS escala um service

    Sinalmétrica do CloudWatch, horário agendado ou previsão
    →
    Application Auto Scalingavalia as políticas do service
    →
    desiredCountsempre entre min e max
    →
    Service ECSsobe ou para tasks para chegar lá

    Scalable target

    Registre o service com register-scalable-target: dimensão ecs:service:DesiredCount, min-capacity e max-capacity. Toda política age dentro desses limites.

    Reativas × proativas

    Reativas respondem à métrica atual: target tracking, step e queue-based. Proativas escalam antes da carga chegar: scheduled e predictive.

    Cooldown

    Tempo de espera para a última ação surtir efeito. Scale-out continua se a carga subir mais. Scale-in é conservador e fica bloqueado até o cooldown acabar.

    As métricas do ECS chegam ao CloudWatch a cada 1 minuto: ECSServiceAverageCPUUtilization, ECSServiceAverageMemoryUtilization e, com ALB, ALBRequestCountPerTarget.

    Target tracking: o termostato

    Você escolhe uma métrica e um valor-alvo, por exemplo CPU média em 60%. O ECS cria e gerencia os alarmes e calcula quantas tasks adicionar ou remover para manter a métrica perto do alvo.

    • Escala para fora rápido e proporcional; para dentro, aos poucos
    • Pode ter várias políticas, uma por métrica: escala para fora se qualquer pedir e para dentro só se todas concordarem
    • Sem dados suficientes, não escala
    • Não edite os alarmes que ele cria

    Recomendação da AWS: é a política mais simples e o ponto de partida para quase todo service.

    CPU média do service alvo 60% tasks 2224 5543 scale-out rápido ↑ scale-in gradual ↓
    Parâmetros para configurar · aws application-autoscaling put-scaling-policy --policy-type TargetTrackingScaling
    target-tracking.json
    {
      "TargetValue": 60.0,
      "PredefinedMetricSpecification": {
        "PredefinedMetricType": "ECSServiceAverageCPUUtilization"
      },
      "ScaleOutCooldown": 60,
      "ScaleInCooldown": 300,
      "DisableScaleIn": false
    }
    TargetValue
    valor-alvo da métrica: 60 = CPU média em 60%
    PredefinedMetricType
    ECSServiceAverageCPUUtilization, ECSServiceAverageMemoryUtilization ou ALBRequestCountPerTarget (este pede ResourceLabel do target group)
    ScaleOutCooldown
    segundos até poder escalar para fora de novo
    ScaleInCooldown
    segundos até poder escalar para dentro de novo
    DisableScaleIn
    true deixa a política só adicionar tasks

    Step scaling: degraus por alarme

    Você cria os alarmes do CloudWatch e define os step adjustments: quanto maior a violação do limite, maior o degrau.

    • Ajuste por número de tasks (ChangeInCapacity), por percentual (PercentChangeInCapacity) ou valor exato (ExactCapacity)
    • Políticas e alarmes separados para scale-out e scale-in
    • Controle fino de velocidade: métrica de alta resolução e alarme com poucos pontos

    Use quando precisar reagir mais rápido ou com degraus diferentes. Para CPU e requisições por alvo, a AWS recomenda target tracking.

    CPU média 100%90%80% 70%30%0% +4 tasks +2 tasks +1 task sem ação −1 task alarme A: CPU ≥ 70% alarme B: CPU ≤ 30% CPU 87% → +2 tasks
    Parâmetros para configurar · aws application-autoscaling put-scaling-policy --policy-type StepScaling
    step-scaling.json
    {
      "AdjustmentType": "ChangeInCapacity",
      "MetricAggregationType": "Average",
      "Cooldown": 60,
      "StepAdjustments": [
        { "MetricIntervalLowerBound": 0,
          "MetricIntervalUpperBound": 10, "ScalingAdjustment": 1 },
        { "MetricIntervalLowerBound": 10,
          "MetricIntervalUpperBound": 20, "ScalingAdjustment": 2 },
        { "MetricIntervalLowerBound": 20, "ScalingAdjustment": 4 }
      ]
    }
    AdjustmentType
    ChangeInCapacity (± tasks), PercentChangeInCapacity (± %) ou ExactCapacity (valor fixo)
    StepAdjustments
    os degraus. Os limites são relativos ao threshold do alarme: com alarme em 70%, 0–10 = 70–80% (+1), 10–20 = 80–90% (+2), acima de 20 = 90%+ (+4)
    ScalingAdjustment
    quantas tasks somar; negativo na política de scale-in
    Cooldown
    segundos de espera entre ações
    Alarme do CloudWatch
    você cria com put-metric-alarm (--threshold 70) e aponta --alarm-actions para o ARN da política

    Scheduled scaling: por horário

    Ações agendadas mudam o min-capacity e o max-capacity em horários fixos, com at(), rate() ou cron() e fuso horário próprio.

    • Ideal para carga previsível: horário comercial, fechamento do mês, campanha com data marcada
    • Combina com target tracking: a agenda define a faixa e a política dinâmica escala dentro dela
    • Configurado pela CLI ou API; não aparece como política no console
    tasks (faixa min–max) 08:00 → 6 a 12 19:00 → 1 a 2 00h06h12h18h24h 1612 faixa da agenda tasks reais (target tracking)
    Parâmetros para configurar · aws application-autoscaling put-scheduled-action
    scheduled-action.json
    {
      "ScheduledActionName": "comercial",
      "Schedule": "cron(0 8 ? * MON-FRI *)",
      "Timezone": "America/Sao_Paulo",
      "ScalableTargetAction": {
        "MinCapacity": 6,
        "MaxCapacity": 12
      },
      "StartTime": "2026-10-01T00:00:00Z",
      "EndTime": "2026-12-31T23:59:59Z"
    }
    Schedule
    at() uma vez, rate() por intervalo (ex. rate(6 hours)) ou cron() com 6 campos: minuto, hora, dia, mês, dia da semana e ano
    Timezone
    fuso do horário; sem ele, vale UTC
    ScalableTargetAction
    os novos MinCapacity e MaxCapacity a partir daquele momento
    StartTime / EndTime
    opcionais: período em que a agenda vale
    Ação de volta
    crie uma segunda ação às 19:00 (cron(0 19 ? * MON-FRI *)) com min 1 e max 2

    Predictive scaling: aprende com o histórico

    Analisa até 14 dias da métrica para achar padrões diários e semanais e gera uma previsão hora a hora das próximas 48 horas, atualizada a cada 6 horas.

    • Precisa de pelo menos 24 h de dados; o ideal são duas semanas
    • Começa em forecast only: só prevê, para você avaliar. Depois, forecast and scale
    • Escala no início de cada hora; SchedulingBufferTime antecipa a subida para a task estar pronta
    • Sozinho não faz scale-in: combine com target tracking. Com várias políticas, vale o maior desiredCount

    Bom para tráfego cíclico e aplicações que demoram para inicializar.

    histórico · até 14 dias previsão · 48 h agora sobe antes do pico carga real carga prevista tasks pela previsão scale-in feito pelo target tracking
    Parâmetros para configurar · aws application-autoscaling put-scaling-policy --policy-type PredictiveScaling
    predictive.json
    {
      "MetricSpecifications": [{
        "TargetValue": 60,
        "PredefinedMetricPairSpecification": {
          "PredefinedMetricType": "ECSServiceCPUUtilization"
        }
      }],
      "Mode": "ForecastOnly",
      "SchedulingBufferTime": 300,
      "MaxCapacityBreachBehavior": "HonorMaxCapacity"
    }
    MetricSpecifications
    par de métricas (carga + escala) e o alvo. Pares prontos: ECSServiceCPUUtilization, ECSServiceMemoryUtilization ou ALBRequestCount; ou métricas customizadas
    Mode
    ForecastOnly só prevê; ForecastAndScale passa a escalar
    SchedulingBufferTime
    segundos de antecedência para subir as tasks antes da hora prevista
    MaxCapacityBreachBehavior
    HonorMaxCapacity respeita o max; IncreaseMaxCapacity (com MaxCapacityBuffer) deixa a previsão subir o max

    Queue-based scaling: pelo tamanho da fila

    Para workers que consomem uma fila do Amazon SQS. Escalar pelo tamanho da fila sozinho exagera; o certo é escalar pelo backlog por task: mensagens na fila ÷ tasks rodando.

    • Alvo = latência aceitável ÷ tempo médio por mensagem. Ex.: 10 s ÷ 0,1 s = 100 mensagens por task
    • Target tracking com metric math: m1 / m2, sem código extra
    • RunningTaskCount exige Container Insights ligado no cluster
    • Use task scale-in protection para não parar uma task no meio de uma mensagem
    Produtores apps, APIs Fila SQS m1 = 1.500 mensagens Service (workers) m2 = 10 tasks · +5 novas metric math no target tracking backlog por task = m1 / m2 = 1.500 / 10 = 150 alvo = 10 s / 0,1 s = 100 → 150 > 100: escala para fora 1.500 / 15 tasks = 100 ✓ de volta ao alvo
    Parâmetros para configurar · aws application-autoscaling put-scaling-policy --policy-type TargetTrackingScaling
    queue-backlog.json
    {
      "TargetValue": 100,
      "CustomizedMetricSpecification": {
        "Metrics": [
          { "Id": "m1", "ReturnData": false,
            "MetricStat": { "Stat": "Sum", "Metric": {
              "Namespace": "AWS/SQS",
              "MetricName": "ApproximateNumberOfMessagesVisible",
              "Dimensions": [
                { "Name": "QueueName", "Value": "pedidos" }
              ] } } },
          { "Id": "m2", "ReturnData": false,
            "MetricStat": { "Stat": "Average", "Metric": {
              "Namespace": "ECS/ContainerInsights",
              "MetricName": "RunningTaskCount",
              "Dimensions": [
                { "Name": "ClusterName", "Value": "ecs-labs" },
                { "Name": "ServiceName", "Value": "worker" }
              ] } } },
          { "Id": "e1", "Expression": "m1 / m2", "ReturnData": true }
        ]
      }
    }
    TargetValue
    backlog aceitável por task: latência aceitável ÷ tempo por mensagem (10 s ÷ 0,1 s = 100)
    m1
    mensagens visíveis na fila SQS (Sum)
    m2
    tasks rodando no service (Average); exige Container Insights
    e1
    a expressão m1 / m2, única com ReturnData: true: é ela que a política persegue
    min-capacity 0
    opcional no scalable target: o worker zera quando a fila esvazia

    Qual política usar

    PolíticaGatilhoAlarmesUse quando
    Target trackingMétrica fora do alvoCriados pelo ECSQuase sempre: CPU, memória ou requisições por alvo do ALB
    Step scalingAlarme violado, degrau pelo tamanho da violaçãoCriados por vocêPrecisa de degraus próprios ou reação mais rápida
    ScheduledData e hora (at, rate, cron)NenhumPicos com hora marcada
    PredictivePrevisão a partir do históricoNenhumTráfego cíclico e app que demora a subir
    Queue-basedBacklog por task de uma fila SQSCriados pelo ECS (target tracking) ou por você (step)Workers assíncronos

    Combine

    Agenda ou previsão para se antecipar, target tracking para o imprevisto. Com várias políticas ativas, vale o maior desiredCount.

    Durante o deploy

    O scale-in fica desligado enquanto um deployment do ECS está em andamento; o scale-out continua.

    Escala até zero

    Com min-capacity 0 o service pode zerar quando não há trabalho, útil para workers de fila.

    Referência: Automatically scale your Amazon ECS service.

    Prática
    Exercício 3.5.1

    Auto scaling do service: target tracking e agenda

    ~25 min

    Registrar o service app no Application Auto Scaling, criar uma política de target tracking por CPU e uma ação agendada, e ver o ECS mudar o desiredCount sozinho.

    Permissão

    Na primeira vez, o Application Auto Scaling cria a service-linked role AWSServiceRoleForApplicationAutoScaling_ECSService, o que exige iam:CreateServiceLinkedRole. Se o registro do passo 2 falhar por permissão, avise o professor.

    1. Volte o service para 1 task, para ver a escala acontecer:

      terminal
      aws ecs update-service --cluster ecs-labs --service app --desired-count 1 > /dev/null

      Aguarde estabilizar:

      terminal
      aws ecs wait services-stable --cluster ecs-labs --services app
    2. Registre o service como scalable target, com mínimo de 1 e máximo de 4 tasks:

      terminal
      aws application-autoscaling register-scalable-target --service-namespace ecs \
        --scalable-dimension ecs:service:DesiredCount --resource-id service/ecs-labs/app \
        --min-capacity 1 --max-capacity 4 --no-cli-pager

      Confira os limites:

      terminal
      aws application-autoscaling describe-scalable-targets --service-namespace ecs --resource-ids service/ecs-labs/app \
        --query 'ScalableTargets[0].{min:MinCapacity,max:MaxCapacity}' --no-cli-pager
    3. Crie a configuração de target tracking: manter a CPU média do service em 50%:

      terminal
      cat > ~/labs/ecs/cpu50.json <<'EOF'
      {
        "TargetValue": 50.0,
        "PredefinedMetricSpecification": {
          "PredefinedMetricType": "ECSServiceAverageCPUUtilization"
        },
        "ScaleOutCooldown": 60,
        "ScaleInCooldown": 60
      }
      EOF

      Crie a política:

      terminal
      aws application-autoscaling put-scaling-policy --service-namespace ecs \
        --scalable-dimension ecs:service:DesiredCount --resource-id service/ecs-labs/app \
        --policy-name cpu50 --policy-type TargetTrackingScaling \
        --target-tracking-scaling-policy-configuration file://$HOME/labs/ecs/cpu50.json > /dev/null

      Veja os alarmes do CloudWatch que o ECS criou para ela:

      terminal
      aws application-autoscaling describe-scaling-policies --service-namespace ecs --resource-id service/ecs-labs/app \
        --query 'ScalingPolicies[].{politica:PolicyName,alarmes:Alarms[].AlarmName}' --no-cli-pager
    4. Crie uma ação agendada para daqui a 2 minutos que sobe o mínimo para 3 tasks. Calcule o horário (em UTC, o fuso padrão das ações agendadas):

      terminal
      QUANDO=$(date -u -d '+2 minutes' +%Y-%m-%dT%H:%M:%S)

      Crie a ação:

      terminal
      aws application-autoscaling put-scheduled-action --service-namespace ecs \
        --scalable-dimension ecs:service:DesiredCount --resource-id service/ecs-labs/app \
        --scheduled-action-name pico --schedule "at($QUANDO)" \
        --scalable-target-action MinCapacity=3,MaxCapacity=4 --no-cli-pager

      Confira o agendamento:

      terminal
      aws application-autoscaling describe-scheduled-actions --service-namespace ecs --resource-id service/ecs-labs/app \
        --query 'ScheduledActions[].{nome:ScheduledActionName,quando:Schedule}' --no-cli-pager
    5. Espere a ação rodar:

      terminal
      sleep 180

      Veja as atividades de escala:

      terminal
      aws application-autoscaling describe-scaling-activities --service-namespace ecs --resource-id service/ecs-labs/app \
        --query 'ScalingActivities[0:3].{status:StatusCode,descricao:Description}' --no-cli-pager

      Aguarde o service estabilizar e confira:

      terminal
      aws ecs wait services-stable --cluster ecs-labs --services app
      aws ecs describe-services --cluster ecs-labs --services app --query 'services[0].{desejado:desiredCount,rodando:runningCount}' --no-cli-pager
    6. Devolva o mínimo para 1. Com a CPU baixa, o target tracking passa a remover tasks aos poucos:

      terminal
      aws application-autoscaling register-scalable-target --service-namespace ecs \
        --scalable-dimension ecs:service:DesiredCount --resource-id service/ecs-labs/app \
        --min-capacity 1 --max-capacity 4 --no-cli-pager
    Resultado esperado
    • Scalable target: min: 1, max: 4.
    • Target tracking: a política cpu50 aparece com dois alarmes, um para scale-out e outro para scale-in, criados e gerenciados pelo ECS.
    • Agenda: depois do horário, as atividades mostram a ação agendada e o service fica com desejado: 3, rodando: 3, mesmo sem carga.
    • Scale-in: com o mínimo de volta em 1, o número de tasks cai aos poucos nos minutos seguintes. O scale-in é conservador de propósito.
    O que aprendemos com este exercício?
    • O auto scaling mexe no desiredCount, sempre entre o mínimo e o máximo do scalable target.
    • No target tracking, você dá o alvo e o ECS cria e gerencia os alarmes.
    • Uma ação agendada muda a faixa mínimo/máximo por horário, mesmo sem carga.
    • O scale-out é rápido; o scale-in é gradual de propósito.
    Desafio

    Troque o target tracking por uma política de step scaling: crie um alarme no CloudWatch para CPUUtilization do service acima de 70% e uma política com StepAdjustments que some 1 task. Os parâmetros estão no slide de step scaling.

    3.6

    ECS: conclusão e limpeza

    Conceito

    Por que usar o ECS

    Totalmente gerenciado

    Você implanta, gerencia e escala containers sem operar um control plane. As boas práticas de configuração e operação da AWS já vêm embutidas.

    Sem custo de orquestração

    O ECS não cobra pela orquestração: você paga só a capacidade. No Fargate, vCPU, memória e storage que a task pede, do pull da imagem até ela terminar, arredondado para o segundo.

    Capacidade sob medida

    Fargate (serverless), Managed Instances, EC2 ou servidores próprios com ECS Anywhere, no mesmo modelo de task definition e service.

    Integrado à AWS

    ECR para as imagens, Elastic Load Balancing para o tráfego, CloudWatch para logs, métricas e alarmes, IAM com uma role por task.

    Escala e deploy seguros

    Service Auto Scaling com cinco tipos de política, rolling update com circuit breaker e rollback por alarme.

    Isolamento e acesso

    No Fargate, cada task tem sua própria fronteira de isolamento: não divide kernel, CPU, memória nem interface de rede com outra. Para investigar, o ECS Exec, sem SSH.

    Referências: What is Amazon ECS?, Amazon ECS pricing e Amazon ECS task IAM role.

    O que você construiu com o ECS

    ECRimagem nginx v1 e v2
    →
    Task definitionlimites, health check, logs
    →
    Service no Fargate2 tasks, auto-reparo
    →
    Operaçãorolling update, ECS Exec
    →
    Auto scalingtarget tracking e agenda

    Em todos os passos, você declarou o estado desejado e o ECS convergiu: o mesmo princípio que aparece a seguir no Kubernetes.

    ECS × Kubernetes: o ECS é mais simples e totalmente AWS. O Kubernetes (e o Amazon EKS) é um padrão aberto e portável, com o ecossistema da CNCF, mas com mais conceitos para operar. É o assunto da Aula 04.

    Prática
    Exercício 3.6.1

    Limpando o ambiente do ECS

    ~10 min

    Remover da conta tudo o que foi criado nos exercícios de ECS: auto scaling, service, cluster, task definitions, security group, repositório e logs.

    1. Os recursos consomem os créditos do Learner Lab mesmo com a sessão encerrada. Comece pelo auto scaling: remover o scalable target apaga também as políticas e as ações agendadas dele (pule se não fez o exercício 3.5.1):

      terminal
      aws application-autoscaling deregister-scalable-target --service-namespace ecs \
        --scalable-dimension ecs:service:DesiredCount --resource-id service/ecs-labs/app --no-cli-pager
    2. Zere o service:

      terminal
      aws ecs update-service --cluster ecs-labs --service app --desired-count 0 > /dev/null

      Remova o service:

      terminal
      aws ecs delete-service --cluster ecs-labs --service app --force > /dev/null

      Aguarde a remoção:

      terminal
      aws ecs wait services-inactive --cluster ecs-labs --services app
    3. Remova o cluster:

      terminal
      aws ecs delete-cluster --cluster ecs-labs > /dev/null

      Remova todas as revisões da task definition:

      terminal
      for r in $(aws ecs list-task-definitions --family-prefix app --query 'taskDefinitionArns[]' --output text); do aws ecs deregister-task-definition --task-definition $r > /dev/null; done
    4. Remova o security group:

      terminal
      aws ec2 delete-security-group --group-id $SG --no-cli-pager

      Remova o repositório do ECR, com as imagens:

      terminal
      aws ecr delete-repository --repository-name app --force > /dev/null

      Remova o grupo de logs:

      terminal
      aws logs delete-log-group --log-group-name /ecs/app --no-cli-pager
    5. Confira que não sobrou nada:

      terminal
      aws ecs list-clusters --query 'clusterArns' --no-cli-pager
      aws ecr describe-repositories --query 'repositories[].repositoryName' --no-cli-pager
    Resultado esperado
    • O wait services-inactive termina sem erro e o cluster ecs-labs some.
    • As duas últimas listas voltam vazias (ou sem ecs-labs e app).
    • Se o delete-security-group reclamar de dependência, espere um minuto (as interfaces de rede das tasks ainda estão sendo liberadas) e rode de novo.
    O que aprendemos com este exercício?
    • Na nuvem, recurso esquecido custa: a limpeza faz parte do trabalho.
    • Existe uma ordem: auto scaling → service → cluster → task definitions → rede → registry → logs.
    • Remover o scalable target leva junto as políticas e as ações agendadas.
    • Conferir no fim, com as listas vazias, é a prova de que a conta ficou limpa.