DEV Community

Cover image for EC2: A Espinha Dorsal da AWS (e Onde Todo Mundo Erra)
Felipe Duque
Felipe Duque

Posted on

EC2: A Espinha Dorsal da AWS (e Onde Todo Mundo Erra)

Lembro a primeira instância EC2 que subi. t2.micro, free tier, orgulho desproporcional ao tamanho da máquina. Terminei o tutorial, fechei o notebook, e esqueci ela ligada. Voltei três meses depois. Não tinha feito nada de útil naquele tempo todo — só ficou lá, rodando sozinha, sem propósito, virando linha na fatura que eu nem sabia que existia.

Foi minha primeira lição de verdade sobre EC2, e ainda é a mais repetida entre quem começa: instância esquecida é a forma mais boba de gastar dinheiro em nuvem. Se já aconteceu com você, bem-vindo ao clube. Se ainda não aconteceu, é questão de tempo.

O que é o EC2, afinal

EC2 (Elastic Compute Cloud) é o serviço de servidor virtual da AWS. Você não compra hardware, não instala nada fisicamente, só aluga capacidade de processamento — CPU, memória, rede, e opcionalmente disco — por hora ou por segundo, dependendo do tipo de instância e da forma de pagamento escolhida.

Toda instância nasce de uma AMI (Amazon Machine Image): sistema operacional, pacotes pré-instalados, configuração inicial, tudo empacotado. Você escolhe uma AMI da própria AWS, do Marketplace, ou uma sua, customizada e salva a partir de uma instância que você já ajustou do seu jeito.

Uma vez no ar, a instância é sua até você decidir parar (stop), terminar (terminate), ou — like eu, ano passado — deixar rodando pra sempre sem perceber.

Instance Types

O nome de uma instância parece código de nave espacial, mas segue uma lógica simples. Pega m6i.xlarge: m é a família, 6 é a geração, i indica o tipo de processador (Intel, nesse caso), e xlarge é o tamanho.

As famílias existem porque workload nenhum é igual ao outro, e usar a errada é queimar dinheiro pagando por recurso que sua aplicação nunca vai usar.

Uso geral (T, M) — o equilíbrio entre CPU e memória. A família T é burstable (chegamos nos créditos de CPU já já), boa pra carga variável e ambiente de dev/teste. A família M é o "meio-termo" sério, boa pra maioria das aplicações web e microsserviços sem gargalo específico.

Otimizada pra computação (C) — proporção alta de CPU por memória. Processamento em lote, encoding de vídeo, modelagem científica, servidor de jogos.

Otimizada pra memória (R, X, High Memory) — quando o gargalo é RAM, não CPU. Banco de dados in-memory, cache distribuído, processamento analítico de big data. A família X chega a terabytes de RAM, pra workload tipo SAP HANA.

Otimizada pra armazenamento (I, D, H) — disco local NVMe rapidíssimo, físico, na própria máquina. Banco de dados transacional de alta performance, data warehouse, processamento distribuído tipo Hadoop.

Computação acelerada (P, G, Inf, Trn, F1) — GPU ou chip dedicado. P e Trn pra treinar modelo de machine learning, G pra inferência e workload gráfico, Inf pra inferência otimizada em custo, F1 pra FPGA.

Ainda tem o sufixo a (AMD) e g (Graviton, o processador ARM da própria AWS). Instância Graviton costuma sair de 20 a 40% mais barata pra performance equivalente, mas exige compatibilidade com ARM — hoje a maioria das linguagens e frameworks modernos aguenta numa boa, mas vale testar antes de jogar produção inteira de uma vez.

Tips 'n Tricks: não escolha instância "no olho". Rode o CloudWatch por uma semana no ambiente atual (ou no de teste), olhe utilização real de CPU e memória, e só depois decida a família. "Achismo" de sizing é a razão de metade das contas de AWS estarem infladas por aí.

CPU Credits

Esse aqui é o item que mais gente ignora até tomar um susto em produção. As instâncias da família T (T3, T3a, T4g) são burstable: têm uma performance base garantida — geralmente uma fração do vCPU completo — e acumulam crédito quando estão usando menos CPU do que essa base. Quando a demanda sobe, a instância gasta esses créditos (em um peido) pra rodar acima do baseline, por um tempo.

E aí o que acontece quando o saldo zera? Depende do modo:

  • Standard: a instância volta a ser limitada à performance base. Sua aplicação simplesmente fica lenta, sem aviso bonito, só devagar mesmo.
  • Unlimited (padrão em T3/T3a/T4g): a instância continua rodando acima do baseline, e você paga uma tarifa extra por vCPU-hora usado além do crédito acumulado.

Sugestão: T-family é ótima pra ambiente de desenvolvimento, teste, ou aplicação com pico esporádico. Pra workload sustentado em alta CPU o tempo inteiro, você paga o pior dos dois mundos — ou throttle, ou sobretaxa. Nesse caso, migre pra M's ou C's Family.

Storage / Volumes

Brother, a coisa mais louca pra mim foi o dia que descobri que um disco na AWS, na real, não é um disco físico dentro de um servidor, mas sim um servidor de storage inteiro, disponibilizado na rede interna da AWS, que você attach no seu EC2. Isso é louco demais!

Vamos além da configuração básica que já vem no "click-ops"?

Instância EC2 sozinha não guarda nada de forma confiável — pra isso existe uma família inteira de serviços de armazenamento na AWS, cada um resolvendo um problema diferente. Confundir um com o outro é comum, principalmente entre quem está começando.

EBS (Elastic Block Store) — o disco que você anexa a uma instância, como um HD virtual. É a opção padrão pra boot volume e pra qualquer coisa que precise de acesso em nível de bloco, baixa latência, um disco por instância (com raras exceções de multi-attach). Dentro do EBS, a escolha do tipo de volume muda conforme a utilidade: gp3 serve pro dia a dia, é a opção padrão pra quase tudo; io1/io2 entra quando você tem um banco de dados crítico que não pode tolerar variação de performance; st1 é pra quando o gargalo é volume de dado sequencial grande, tipo processamento de log ou big data; sc1 é o armazenamento de bloco mais barato, pra dado que quase ninguém acessa mas precisa continuar existindo.

Instance Store — disco físico, colado na própria máquina que hospeda sua instância. Serve como scratch space ou cache: rápido igual raio, mas efêmero — se a instância para, os dados somem.

EFS (Elastic File System) — sistema de arquivos de rede (NFS), acessível por várias instâncias EC2 ao mesmo tempo. Exemplo clássico: um cluster de servidores WordPress (alguém usa ainda hoje?) atrás de um load balancer, todos precisando enxergar a mesma pasta de uploads de mídia. Ou um ambiente de renderização onde múltiplas máquinas precisam compartilhar o mesmo diretório de projeto. A lógica aqui é poder usar o mesmo disco por mais de uma instância, mesmo em zonas diferentes. Em teoria, EBS io1, io2 e Block Express também podem, mas só na mesma zona — não servem a esse propósito.

FSx — sistemas de arquivo gerenciados, cada variante compatível com um motor específico:

  • FSx for Windows File Server — SMB e integração com Active Directory. Exemplo: migrar uma aplicação Windows legada que depende de compartilhamento de rede corporativo, sem reescrever nada.
  • FSx for Lustre — throughput altíssimo, feito pra HPC. Exemplo: treinar modelo de machine learning lendo dataset gigante direto do S3 em paralelo, ou processamento genômico.
  • FSx for NetApp ONTAP — pra quem já usa NetApp on-premises e quer migrar pra nuvem sem perder recursos como snapshot e clonagem.
  • FSx for OpenZFS — mesma lógica, pra quem depende de recursos específicos do ZFS.

Keypairs

Toda instância Linux nasce sem senha, só com um par de chaves: a pública fica instalada na instância (~/.ssh/authorized_keys), a privada (o arquivo .pem) é baixada por você uma única vez, na criação. A AWS não guarda cópia da privada. Perdeu o .pem? Perdeu o acesso direto — tem workaround, mas nenhum é rápido nem elegante. (Sinceramente, aí foi mlk em!?)

Em instância Windows, a mesma chave privada descriptografa a senha de administrador, no lugar do SSH.

SSH com keypair aberto na internet (porta 22 liberada pro mundo) é uma das formas mais comuns de instância comprometida virar minerador de criptomoeda por conta alheia — chegamos nesse caso real mais adiante.

Sugestão: prefira EC2 Instance Connect (SSH via navegador, chave temporária) ou, melhor ainda, AWS Systems Manager Session Manager, que dá acesso ao shell sem abrir porta 22, com controle via IAM e log completo no CloudTrail.

Elastic IP

Toda instância recebe um IP público dinâmico, que muda toda vez que você para e sobe a instância de novo. O Elastic IP resolve isso: é um IPv4 público fixo, associado à sua conta (não à instância), remapeável pra outra instância quando quiser — útil pra failover manual.

Desde 2024 a AWS cobra por hora todo endereço IPv4 público, associado ou não — antes só cobrava o Elastic IP ocioso. IP público hoje tem custo sempre, e Elastic IP parado é dinheiro escorrendo silenciosamente.

Sugestão: libere Elastic IP que não está mais em uso. Item clássico de "gordura" em fatura de conta antiga.

User Data Scripts

User Data é um script (geralmente bash, via cloud-init) colado na criação da instância, que roda automaticamente, como root, na primeira inicialização. Instala pacote, configura serviço, registra a instância — sem precisar entrar manualmente depois.

Fica acessível de dentro da instância via metadata: http://169.254.169.254/latest/user-data. E aqui mora um detalhe que muita gente esquece: nunca coloque segredo (senha, chave de API, token) direto no user data em texto plano. Qualquer processo com acesso à instância consegue ler o metadata.

Sugestão: use user data só pra bootstrap. Segredo de verdade fica no Secrets Manager, buscado em runtime, nunca hardcoded no script.

Vale mencionar o IMDSv2: exige token de sessão pra cada requisição, ao invés do IMDSv1, que respondia sem autenticação. Fecha uma classe inteira de ataque via SSRF. Se sua instância ainda está com IMDSv1 habilitado, esse é o próximo item da sua lista.

Purchasing Options

Aqui é onde a conta da AWS realmente é decidida — a mesma instância pode custar 3x, 5x, até 10x diferente dependendo de como você compra.

  • On-Demand — sem compromisso, paga por segundo/hora. O mais caro, o mais flexível.
  • Reserved Instances (RI) — compromisso de 1 ou 3 anos, desconto de até 60%. Standard (atributos fixos, maior desconto) ou Convertible (pode trocar de família, desconto menor).
  • Savings Plans — sucessor mais flexível do RI. Compromisso de gasto por hora, não de instância específica. Compute Savings Plans valem pra qualquer família/região/serviço; EC2 Instance Savings Plans são mais restritos, com desconto maior.
  • Spot Instances — capacidade ociosa da AWS, desconto de até 90%. A AWS pode retomar com 2 minutos de aviso. Ideal pra workload tolerante a falha — batch, renderização, CI/CD.
  • Dedicated Hosts — servidor físico dedicado, com visibilidade de socket e núcleo. Existe por licenciamento (BYOL) ou compliance.
  • Dedicated Instances — hardware dedicado, sem a visibilidade e controle de posicionamento do Dedicated Host.
  • Capacity Reservations — reserva capacidade garantida numa zona de disponibilidade, sem compromisso de longo prazo.

Sugestão: a estratégia madura raramente usa uma opção só. Baseline previsível vai de Savings Plans, pico sazonal vai de On-Demand, workload tolerante a interrupção vai de Spot.

Algumas Dicas Extras

  • Sempre marque "delete on termination" com intenção, não por padrão.
  • Tag em tudo. Instância sem tag de dono/projeto é a razão pela qual ninguém sabe se pode desligar aquele t2.large rodando desde 2022.
  • REVISE SUAS SECURITY GROUPS ANTES DE DORMIR! Porta 22 ou 3389 aberta pra 0.0.0.0/0 é convite formal pra bot de scan automatizado.

Caso Real

Em fevereiro de 2018, a empresa de segurança RedLock descobriu que o ambiente de nuvem da Tesla havia sido comprometido. O console de administração de um cluster Kubernetes rodando sobre a infraestrutura AWS da empresa estava exposto na internet, sem senha. Um invasor usou esse acesso pra chegar nas credenciais da conta AWS da Tesla, e a partir daí, subiu instâncias e rodou software de mineração de criptomoeda usando a capacidade computacional (e o cartão de crédito) da própria Tesla.

Não foi uma falha sofisticada. Foi um painel de administração exposto, sem autenticação, dando de bandeja acesso a recursos de computação que qualquer um podia ligar e desligar à vontade.

Acontece com mais frequência do que se imagina, geralmente em escala bem menor: instância pequena, esquecida, com SSH aberto pro mundo, vira minerador em questão de horas, e o dono só descobre quando a fatura chega com quatro dígitos a mais.

Receita de bolo de banana

# --------------------------------------------------------------
# EC2 "specialist-grade" — instância de produção
# --------------------------------------------------------------

# AMI sempre atualizada via SSM Parameter, nunca hardcoded
data "aws_ssm_parameter" "al2023_ami" {
  name = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"
}

# Acesso via SSM Session Manager, não via SSH/keypair
resource "aws_iam_role" "ec2_ssm_role" {
  name = "ec2-ssm-role"

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

resource "aws_iam_role_policy_attachment" "ssm_managed" {
  role       = aws_iam_role.ec2_ssm_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_iam_instance_profile" "ec2_profile" {
  name = "ec2-ssm-instance-profile"
  role  = aws_iam_role.ec2_ssm_role.name
}

# Sem regra de ingress SSH aberta pro mundo
resource "aws_security_group" "app_sg" {
  name        = "app-sg"
  description = "Sem SSH/RDP exposto - acesso via SSM"
  vpc_id      = var.vpc_id

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description     = "HTTPS apenas via Load Balancer"
    from_port       = 443
    to_port         = 443
    protocol        = "tcp"
    security_groups = [var.alb_security_group_id]
  }

  tags = { Name = "app-sg" }
}

resource "aws_instance" "app" {
  ami                    = data.aws_ssm_parameter.al2023_ami.value
  instance_type          = "t3.medium"
  subnet_id              = var.private_subnet_id
  vpc_security_group_ids = [aws_security_group.app_sg.id]
  iam_instance_profile   = aws_iam_instance_profile.ec2_profile.name

  # key_name omitido de propósito -- acesso via SSM, não SSH direto

  # Modo unlimited -- evita throttle silencioso em pico de carga
  credit_specification {
    cpu_credits = "unlimited"
  }

  # IMDSv2 obrigatório -- fecha a classe de ataque via SSRF
  metadata_options {
    http_endpoint               = "enabled"
    http_tokens                 = "required"
    http_put_response_hop_limit = 1
  }

  root_block_device {
    volume_type            = "gp3"
    volume_size             = 30
    iops                     = 3000
    throughput               = 125
    encrypted                = true
    kms_key_id               = var.kms_key_arn
    delete_on_termination    = true

    tags = { Name = "app-root-volume" }
  }

  monitoring = true # CloudWatch detailed monitoring, granularidade de 1 min

  user_data = base64encode(templatefile("${path.module}/templates/bootstrap.sh.tpl", {
    environment = var.environment
    app_version = var.app_version
  }))

  user_data_replace_on_change = true # força recriação se o script de bootstrap mudar

  tags = {
    Name        = "app-${var.environment}"
    Environment = var.environment
    Owner       = "platform-team"
    ManagedBy   = "terraform"
  }

  lifecycle {
    create_before_destroy = true
    ignore_changes        = [ami] # evita replace acidental por AMI nova sem revisão
  }
}

# Elastic IP só se a aplicação realmente exigir IP fixo
resource "aws_eip" "app_eip" {
  count    = var.assign_elastic_ip ? 1 : 0
  instance = aws_instance.app.id
  domain   = "vpc"

  tags = { Name = "app-eip-${var.environment}" }
}
Enter fullscreen mode Exit fullscreen mode

As decisões que separam isso de um tutorial: AMI puxada via SSM (sempre patched, nunca ID fixo apodrecendo), sem key_name (acesso via Session Manager), credit_specification explícito, IMDSv2 forçado, EBS criptografado com IOPS/throughput definidos à parte do tamanho, lifecycle.ignore_changes na AMI, e Elastic IP condicional em vez de sempre criado.

Concluindo

EC2 parece simples até você perceber que cada escolha — tipo de instância, modo de crédito, tipo de disco, forma de acesso, IP, bootstrap, contrato de compra — impacta performance, segurança e o boleto do fim do mês. Não existe configuração padrão certa pra todo mundo, existe a certa pro seu workload, e isso só se descobre com dado real, não achismo.

Terminei de escrever isso e fui conferir se ainda tenho Elastic IP esquecido rodando por aí. Tinha um.

Ship safe, folks.

Top comments (0)