DevOps

AWS na Prática: EC2, VPC e IAM em Profundidade Já leu

16 min de leitura

AWS na Prática: EC2, VPC e IAM em Profundidade
Os artigos anteriores introduziram EC2, VPC e IAM como parte da prática com Terraform. Os recursos foram criados, o código funcionou. Mas criar recursos via Terraform sem entender profundamente os serviços subjacentes é

Os artigos anteriores introduziram EC2, VPC e IAM como parte da prática com Terraform. Os recursos foram criados, o código funcionou. Mas criar recursos via Terraform sem entender profundamente os serviços subjacentes é construir sobre uma fundação frágil — quando algo dá errado, quando o comportamento não é o esperado, quando é necessário tomar uma decisão de arquitetura, a falta de conhecimento profundo se manifesta como horas de debugging e decisões subótimas.

Este artigo aprofunda os três serviços mais fundamentais da AWS — EC2, VPC e IAM — com o nível de detalhe necessário para tomar decisões de arquitetura conscientes e diagnosticar problemas com eficiência.


EC2 em Profundidade

Famílias de Instâncias e Quando Usar Cada Uma

A AWS oferece centenas de tipos de instâncias organizados em famílias com propósitos distintos. Escolher o tipo errado significa pagar mais por menos performance ou ter um gargalo que não é o esperado.

As famílias mais relevantes para aplicações web e serviços de backend:

Família T — Burstable Performance. As instâncias T acumulam créditos de CPU durante períodos ociosos e os gastam durante picos. São econômicas para workloads com tráfego irregular — servidores de desenvolvimento, ambientes de staging, aplicações com tráfego variável. A geração atual é T4g (ARM Graviton) e T3. O modelo t3.medium (2 vCPU, 4GB RAM) é o ponto de partida mais comum para APIs de médio porte.

A armadilha das instâncias T é o CPU credit exhaustion: se a aplicação demanda CPU continuamente acima da baseline, os créditos se esgotam e a performance cai drasticamente. O CloudWatch expõe a métrica CPUCreditBalance — quando ela se aproxima de zero em uma instância T, é hora de migrar para uma família M.

Família M — General Purpose. Balanceamento equilibrado de CPU, memória e rede. A família M6i (Intel), M6a (AMD) e M6g (Graviton ARM) representa o padrão para servidores de aplicação em produção. A geração Graviton (sufixo g) oferece, em média, 20% mais performance por custo que as equivalentes x86.

Família C — Compute Optimized. Alta proporção de CPU em relação à memória. Indicada para servidores de aplicação que fazem processamento intensivo — codificação de vídeo, análise científica, servidores de jogos, processamento de machine learning para inferência. A família atual é C7g (Graviton).

Família R — Memory Optimized. Alta proporção de memória em relação à CPU. Indicada para bancos de dados in-memory, caches de grande volume, processamento de big data com Spark ou Hadoop. A família R7g é a geração atual.

Família I — Storage Optimized. Alto throughput de I/O com NVMe local. Indicada para bancos de dados que precisam de latência de storage extremamente baixa — Cassandra, MongoDB, Elasticsearch em alta escala. O armazenamento local é efêmero — perde os dados ao parar a instância.

Ciclo de Vida de uma Instância EC2

Entender os estados possíveis de uma instância é fundamental para diagnosticar comportamentos inesperados e automatizar corretamente:

pending → running → stopping → stopped → terminated
              ↓
         shutting-down → terminated

pending — a instância está sendo inicializada. O user_data está sendo executado. A instância ainda não está pronta para receber tráfego.

running — a instância está em execução. Cobrança ocorre neste estado.

stopping — o sistema operacional está sendo encerrado graciosamente. Em instâncias com armazenamento EBS, o volume é preservado.

stopped — a instância está desligada. O EBS é preservado. A cobrança de EC2 cessa, mas o EBS continua sendo cobrado. O IP público é liberado — ao reiniciar, um novo IP é atribuído, a menos que haja um Elastic IP.

shutting-down / terminated — a instância está sendo destruída permanentemente. Por padrão, os volumes EBS com DeleteOnTermination=true são destruídos junto.

EBS: Tipos de Volume e Quando Usar

O Elastic Block Store é o armazenamento de bloco persistente da AWS. A escolha do tipo de volume afeta diretamente a performance e o custo:

gp3 — General Purpose SSD de terceira geração. O padrão recomendado para a maioria dos casos. Oferece 3.000 IOPS e 125 MB/s de throughput base, independentemente do tamanho do volume. IOPS e throughput adicionais podem ser provisionados separadamente. Mais econômico que o gp2 para volumes acima de 1TB.

io2 Block Express — SSD de alta performance com IOPS provisionadas. Usado em bancos de dados que precisam de latência consistente abaixo de 1ms e IOPS acima de 16.000. Significativamente mais caro que o gp3.

st1 — HDD de throughput otimizado. Para dados acessados sequencialmente com alto volume — logs, data warehouses, backups. Não adequado para sistemas operacionais ou bancos de dados transacionais.

sc1 — HDD de custo otimizado. O mais barato da linha. Para dados acessados raramente — arquivos de backup, dados frios.

# Exemplo de configuração de volumes no Terraform
resource "aws_instance" "banco_dados" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "r6g.xlarge"

  # Volume raiz — sistema operacional
  root_block_device {
    volume_type           = "gp3"
    volume_size           = 50
    encrypted             = true
    delete_on_termination = true
  }

  # Volume de dados — banco de dados
  ebs_block_device {
    device_name           = "/dev/sdb"
    volume_type           = "io2"
    volume_size           = 500
    iops                  = 10000
    encrypted             = true
    delete_on_termination = false  # preserva dados ao terminar a instância
  }
}

User Data e Instance Metadata Service

O user data é um script executado uma única vez quando a instância inicia pela primeira vez. É o mecanismo de bootstrapping — instalação de pacotes, configuração inicial, registro em sistemas de configuração.

#!/bin/bash
# user_data.sh — script de bootstrapping de uma instância de aplicação

set -euo pipefail

# Configura logging do user_data
exec > >(tee /var/log/user-data.log | logger -t user-data -s 2>/dev/console) 2>&1

echo "=== Iniciando bootstrapping ==="
echo "Instância: $(curl -s http://169.254.169.254/latest/meta-data/instance-id)"
echo "AZ: $(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone)"
echo "Tipo: $(curl -s http://169.254.169.254/latest/meta-data/instance-type)"

# Atualiza o sistema
apt-get update -y
apt-get upgrade -y

# Instala dependências
apt-get install -y docker.io awscli jq

# Configura Docker
systemctl enable docker
systemctl start docker
usermod -aG docker ubuntu

# Lê segredos do AWS Secrets Manager
SECRET=$(aws secretsmanager get-secret-value \
  --secret-id "minha-api/producao" \
  --region us-east-1 \
  --query SecretString \
  --output text)

# Cria o arquivo .env a partir dos segredos
echo "$SECRET" | jq -r 'to_entries[] | "\(.key)=\(.value)"' \
  > /opt/minha-api/.env
chmod 600 /opt/minha-api/.env

# Baixa e inicia a aplicação
ACCOUNT_ID=$(curl -s http://169.254.169.254/latest/meta-data/iam/info \
  | jq -r '.InstanceProfileArn' | cut -d: -f5)
REGION=$(curl -s http://169.254.169.254/latest/meta-data/placement/region)

aws ecr get-login-password --region $REGION | \
  docker login --username AWS --password-stdin \
  $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com

docker run -d \
  --name minha-api \
  --restart unless-stopped \
  --env-file /opt/minha-api/.env \
  -p 3000:3000 \
  $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/minha-api:latest

echo "=== Bootstrapping concluído ==="

O Instance Metadata Service (IMDS) disponível em http://169.254.169.254/latest/meta-data/ expõe informações sobre a instância — ID, tipo, AZ, IP, IAM role — sem necessidade de credenciais. A versão IMDSv2 é mais segura e exige um token de sessão:

# IMDSv2 — recomendado para novas instâncias
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")

INSTANCE_ID=$(curl -s \
  -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/instance-id)

PRIVATE_IP=$(curl -s \
  -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/local-ipv4)

IAM_ROLE=$(curl -s \
  -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/iam/security-credentials/)

VPC em Profundidade

CIDR Planning: A Decisão Que É Difícil de Desfazer

O bloco CIDR de uma VPC é uma das poucas decisões em infraestrutura AWS que é genuinamente difícil de reverter. Uma vez que a VPC está criada e tem recursos dentro dela, alterar o CIDR principal exige destruir e recriar tudo.

Princípios para planejamento de CIDR:

Use blocos /16 para VPCs de produção. Um /16 oferece 65.536 endereços — espaço mais que suficiente para crescer sem desperdiçar. Blocos menores parecem econômicos mas criam problemas quando o sistema cresce.

Evite sobreposição com redes on-premises. Se a empresa tem ou planeja ter uma conexão VPN ou Direct Connect com data center próprio, os blocos CIDR não podem se sobrepor. Os ranges 10.0.0.0/8, 172.16.0.0/12 e 192.168.0.0/16 são os ranges privados RFC 1918 — planeje a alocação com antecedência.

Reserve espaço para crescimento. Se a organização tem múltiplas contas AWS com peering entre VPCs, todos os blocos CIDR precisam ser distintos.

Uma estrutura de alocação para uma organização com múltiplos ambientes:

10.0.0.0/8  — range organizacional completo

  10.0.0.0/16   — conta Production
    10.0.0.0/24  — subnet pública AZ-a
    10.0.1.0/24  — subnet pública AZ-b
    10.0.2.0/24  — subnet pública AZ-c
    10.0.10.0/24 — subnet privada app AZ-a
    10.0.11.0/24 — subnet privada app AZ-b
    10.0.12.0/24 — subnet privada app AZ-c
    10.0.20.0/24 — subnet privada db AZ-a
    10.0.21.0/24 — subnet privada db AZ-b
    10.0.22.0/24 — subnet privada db AZ-c

  10.1.0.0/16   — conta Staging
  10.2.0.0/16   — conta Development
  10.3.0.0/16   — conta Shared Services

Security Groups vs NACLs: Quando Usar Cada Um

Ambos controlam tráfego de rede, mas com filosofias e aplicações diferentes:

Security Groups são stateful e operam no nível de instâncias e interfaces de rede. Stateful significa que o tráfego de retorno de uma conexão iniciada de dentro é automaticamente permitido, independentemente das regras. Security Groups só têm regras de allow — sem regras de deny explícitas.

Network ACLs são stateless e operam no nível de subnets. Stateless significa que o tráfego de retorno precisa de uma regra explícita de allow. NACLs têm regras de allow e deny, processadas em ordem numérica.

Na prática, a maioria dos times usa Security Groups para controle de acesso e NACLs apenas para regras de bloqueio amplo — bloquear um range de IPs maliciosos, impedir comunicação entre subnets de ambientes diferentes:

# Security Group — controle de acesso granular por recurso
resource "aws_security_group" "aplicacao" {
  name   = "minha-api-app-sg"
  vpc_id = aws_vpc.principal.id

  # Apenas permite tráfego do load balancer — referência por SG
  ingress {
    from_port       = 3000
    to_port         = 3000
    protocol        = "tcp"
    security_groups = [aws_security_group.load_balancer.id]
    description     = "Tráfego do ALB"
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
    description = "Todo tráfego de saída"
  }
}

# NACL — bloqueio amplo no nível de subnet
resource "aws_network_acl" "privada" {
  vpc_id     = aws_vpc.principal.id
  subnet_ids = aws_subnet.privada[*].id

  # Bloqueia um range de IPs conhecido como malicioso
  ingress {
    rule_no    = 50
    protocol   = "-1"
    action     = "deny"
    cidr_block = "203.0.113.0/24"
    from_port  = 0
    to_port    = 0
  }

  # Permite todo o resto
  ingress {
    rule_no    = 100
    protocol   = "-1"
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 0
    to_port    = 0
  }

  egress {
    rule_no    = 100
    protocol   = "-1"
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 0
    to_port    = 0
  }
}

VPC Endpoints: Tráfego Interno sem Sair para a Internet

Por padrão, quando uma instância EC2 em uma subnet privada acessa o S3 ou o DynamoDB, o tráfego sai pela internet — passando pelo NAT Gateway e gerando custo de transferência de dados. VPC Endpoints eliminam esse custo e aumentam a segurança ao manter o tráfego dentro da rede da AWS:

# Gateway Endpoint para S3 — gratuito
resource "aws_vpc_endpoint" "s3" {
  vpc_id            = aws_vpc.principal.id
  service_name      = "com.amazonaws.us-east-1.s3"
  vpc_endpoint_type = "Gateway"
  route_table_ids   = aws_route_table.privada[*].id

  tags = {
    Name = "s3-endpoint"
  }
}

# Interface Endpoint para Secrets Manager — tem custo por hora
resource "aws_vpc_endpoint" "secrets_manager" {
  vpc_id              = aws_vpc.principal.id
  service_name        = "com.amazonaws.us-east-1.secretsmanager"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.privada[*].id
  security_group_ids  = [aws_security_group.vpc_endpoints.id]
  private_dns_enabled = true

  tags = {
    Name = "secretsmanager-endpoint"
  }
}

IAM em Profundidade

O Modelo de Avaliação de Políticas

O IAM avalia permissões seguindo uma lógica bem definida que é fundamental entender para diagnosticar erros de permissão:

1. Deny explícito em qualquer política? → DENY (fim)
2. Allow explícito em alguma política? → ALLOW
3. Nenhum dos dois? → DENY implícito (padrão)

As políticas avaliadas incluem: políticas de identidade (anexadas ao usuário, grupo ou role), políticas de resource (bucket S3, key KMS), SCPs do AWS Organizations e permission boundaries.

Políticas Inline vs Gerenciadas

Existem três tipos de políticas IAM com trade-offs distintos:

AWS Managed Policies — criadas e mantidas pela AWS. Cobrem casos de uso comuns como AmazonS3ReadOnlyAccess e AmazonSSMManagedInstanceCore. São convenientes mas frequentemente mais permissivas que o necessário — concedem acesso a ações que a aplicação não precisa.

Customer Managed Policies — criadas pelo time, reutilizáveis em múltiplas identidades. O padrão recomendado para políticas que seguem o princípio do mínimo privilégio.

Inline Policies — embutidas diretamente em um usuário, grupo ou role. Adequadas para permissões únicas que não fazem sentido ser reutilizadas.

Escrevendo Políticas com Mínimo Privilégio

A tendência natural ao começar com IAM é conceder permissões amplas — s3:* em vez de s3:GetObject — para evitar erros de permissão durante o desenvolvimento. Isso é tecnicamente correto mas perigoso em produção.

Uma política bem construída para uma aplicação que lê e escreve em um bucket S3 específico:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ListarBucket",
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetBucketLocation"
      ],
      "Resource": "arn:aws:s3:::minha-empresa-assets-production",
      "Condition": {
        "StringLike": {
          "s3:prefix": ["uploads/*", "publico/*"]
        }
      }
    },
    {
      "Sid": "OperacoesNosObjetos",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": [
        "arn:aws:s3:::minha-empresa-assets-production/uploads/*",
        "arn:aws:s3:::minha-empresa-assets-production/publico/*"
      ]
    },
    {
      "Sid": "LerSecretos",
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue"
      ],
      "Resource": "arn:aws:secretsmanager:us-east-1:123456789:secret:minha-api/*"
    },
    {
      "Sid": "LogsCloudWatch",
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents",
        "logs:DescribeLogStreams"
      ],
      "Resource": "arn:aws:logs:us-east-1:123456789:log-group:/minha-api/*"
    }
  ]
}

IAM Roles para EC2: O Fluxo de Credenciais

Quando uma instância EC2 tem uma IAM Role associada via Instance Profile, as credenciais temporárias são renovadas automaticamente pelo serviço de metadados. A aplicação nunca lida com Access Keys estáticas:

// A AWS SDK usa automaticamente as credenciais da Instance Role
// Não é necessário configurar ACCESS_KEY_ID ou SECRET_ACCESS_KEY
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');

// O SDK detecta automaticamente as credenciais nesta ordem:
// 1. Variáveis de ambiente (AWS_ACCESS_KEY_ID, etc.)
// 2. Arquivo ~/.aws/credentials
// 3. Instance Metadata Service (IMDS) — usado em EC2 com IAM Role
// 4. ECS Task Role — usado em containers ECS
const s3 = new S3Client({ region: 'us-east-1' });

async function uploadArquivo(buffer, chave) {
  await s3.send(new PutObjectCommand({
    Bucket: process.env.S3_BUCKET,
    Key: chave,
    Body: buffer,
    ServerSideEncryption: 'AES256',
  }));
}

Auditando Permissões com IAM Access Analyzer

O IAM Access Analyzer é um serviço que identifica recursos com acesso externo não intencional e políticas excessivamente permissivas:

# Cria um analyzer para a conta AWS atual
aws accessanalyzer create-analyzer \
  --analyzer-name minha-empresa-analyzer \
  --type ACCOUNT \
  --region us-east-1

# Lista findings — recursos com acesso externo não intencional
aws accessanalyzer list-findings \
  --analyzer-arn arn:aws:access-analyzer:us-east-1:123456789:analyzer/minha-empresa-analyzer \
  --filter '{"status": {"eq": ["ACTIVE"]}}' \
  --query 'findings[*].{Resource:resource,Type:resourceType,Principal:principal}' \
  --output table

# Valida uma política antes de aplicá-la
aws accessanalyzer validate-policy \
  --policy-document file://politica.json \
  --policy-type IDENTITY_POLICY \
  --query 'findings[*].{Tipo:findingType,Mensagem:findingDetails}' \
  --output table

O Que Vem a Seguir

O próximo artigo aprofunda os serviços gerenciados de computação da AWS — ECS para containers, Lambda para funções serverless e como escolher entre as duas abordagens para diferentes tipos de workload.


Referências para Aprofundamento

Documentação oficial AWS - Amazon EC2 Documentation — docs.aws.amazon.com — Documentação completa do EC2, incluindo guias de tipos de instância, armazenamento EBS e ciclo de vida de instâncias. - Amazon VPC Documentation — docs.aws.amazon.com — Referência completa da VPC, cobrindo CIDR planning, security groups, NACLs e VPC endpoints com exemplos detalhados. - AWS IAM Documentation — docs.aws.amazon.com — Documentação do IAM, incluindo referência de políticas, guia do modelo de avaliação e boas práticas de segurança.

Segurança e boas práticas - AWS Security Best Practices — docs.aws.amazon.com — Pilar de segurança do AWS Well-Architected Framework, com recomendações detalhadas para IAM, redes e proteção de dados. - IAM Access Analyzer — docs.aws.amazon.com — Documentação do IAM Access Analyzer com guia de uso para identificar acessos não intencionais e validar políticas.

Ferramentas - AWS Policy Simulator — policysim.aws.amazon.com — Ferramenta oficial da AWS para testar e debugar políticas IAM sem precisar aplicá-las em produção.

Comentários

Mais em DevOps

Cultura DevOps: Maturidade, Postmortems e Melhoria Contínua
Cultura DevOps: Maturidade, Postmortems e Melhoria Contínua

É possível instalar todas as ferramentas descritas nos artigos anteriores — K...

Capstone: Provisionando a Infraestrutura Completa
Capstone: Provisionando a Infraestrutura Completa

Em projetos reais, a infraestrutura raramente é provisionada de uma vez. Ela...

Terraform + Ansible: Do Provisionamento à Configuração
Terraform + Ansible: Do Provisionamento à Configuração

Os artigos anteriores apresentaram o Terraform e o Ansible como ferramentas d...