Uma das lacunas que eu queria fechar nos estudos para o SAA-C03 era o monitoramento de instâncias EC2. Não a parte teórica de “o CloudWatch coleta métricas e dispara alarmes”, essa parte eu já sabia. A lacuna era não ter feito isso do zero nenhuma vez: instância no ar, agente instalado, alarme configurado, email chegando. Ciclo completo.

Esse post documenta esse ciclo, incluindo os problemas que apareceram no caminho, que acabaram servindo de aprendizado.


Subindo a instância EC2

No painel da AWS, vou em EC2 e clico em Launch Instance.

  • Nome: ec2-cloudwatch-nginx-lab
  • Imagem: Ubuntu 24.04 LTS
  • Tipo: t3.micro
  • AZ: sa-east-1a (region mais próxima, menor latência para o lab)

Em Key pair, clico para gerar uma nova. Nome: aws-ec2-cloudwatch-nginx-lab, tipo RSA, formato .pem. O arquivo baixa automaticamente.

Em Network Settings, deixei o SSH aberto para qualquer IP. Não recomendo em produção, mas como é um lab que será deletado depois, prefiro ter essa flexibilidade para acessar de lugares diferentes. Marquei também Allow HTTP traffic e Allow HTTPS traffic.

Storage no padrão, 8 GB. Launch Instance.

Em pouquíssimo tempo a instância está up.

Instância EC2 ec2-cloudwatch-nginx-lab rodando no console AWS
Instância EC2 ec2-cloudwatch-nginx-lab rodando no console AWS


Conectando via SSH

Para conectar, abro o PowerShell do Windows dentro da pasta onde está a chave .pem e rodo:

ssh -i aws-ec2-cloudwatch-nginx-lab.pem ubuntu@SEU-DNS-PUBLICO

Se aparecer o erro UNPROTECTED PRIVATE KEY FILE, rode esses dois comandos no PowerShell:

icacls aws-ec2-cloudwatch-nginx-lab.pem /inheritance:r
icacls aws-ec2-cloudwatch-nginx-lab.pem /grant:r "%USERNAME%:(R)"

Se ainda reclamar, tenta executar o PowerShell como administrador.

No linux você pode usar o comando chmod:

chmod 400 aws-ec2-cloudwatch-nginx-lab.pem

Como alternativa, a AWS tem o EC2 Instance Connect, você abre o terminal da instância direto no navegador. É só acessar a instância, clicar em Connect e pronto. Bem prático quando você está numa máquina que não tem o .pem.


Instalando o Docker e subindo o Nginx

Conectado, primeiro atualizo o sistema:

sudo apt update && sudo apt upgrade -y

Instalo o Docker:

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker ubuntu

Já fiz isso no meu tutorial de Homelab no OCI, então é bem tranquilo.

Crio uma pasta para o lab:

mkdir ec2_teste
cd ec2_teste/

Crio o arquivo do docker-compose.yml:

touch docker-compose.yml
sudo nano docker-compose.yml

Conteúdo:

version: '3.8'
services:
  nginx-proxy:
    image: jc21/nginx-proxy-manager:latest
    container_name: nginx-proxy
    restart: unless-stopped
    ports:
      - "80:80"
      - "81:81"
      - "443:443"
    volumes:
      - ./npm-data:/data
      - ./npm-letsencrypt:/etc/letsencrypt

CTRL+O para salvar, CTRL+X para sair do nano.

Libero as portas no firewall:

sudo apt install iptables-persistent -y
sudo iptables -I INPUT 1 -p tcp -m multiport --dports 80,443,81 -j ACCEPT
sudo netfilter-persistent save

Subo o container:

sudo docker compose up -d

Abrindo o navegador com o IP público da instância, o Nginx Proxy Manager já está no ar.

Interface do Nginx Proxy Manager acessível via IP público da instância EC2
Interface do Nginx Proxy Manager acessível via IP público da instância EC2


Instalando e configurando o CloudWatch Agent

Com a instância rodando e o Nginx no ar, o próximo passo é monitorar os recursos dela com o CloudWatch.

Por padrão, a AWS já coleta algumas métricas básicas de CPU via hipervisor, mas disco e memória não aparecem sem o CloudWatch Agent instalado. Vamos resolver isso.

Instalando o agente

O pacote não está nos repositórios padrão do Ubuntu, então baixo o .deb direto da AWS:

wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
sudo dpkg -i amazon-cloudwatch-agent.deb

Configurando o agente

Com o agente instalado, rodo o wizard de configuração:

sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard

Respondi assim:

Pergunta Resposta
On which OS? 1 (Linux)
EC2 or on-premises? 1 (EC2)
Which user? 2 (root)
StatsD daemon? 2 (no)
Monitor metrics? 1 (yes)
CPU interval 4 (60s)
Disk metrics 1 (yes)
Memory metrics 1 (yes)
Resto Enter para aceitar os padrões

O wizard gera o arquivo de configuração automaticamente. Inicio o agente:

sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
  -a fetch-config -m ec2 -s \
  -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json

Erro 1: arquivo do collectd não encontrado

Apareceu esse erro:

open /usr/share/collectd/types.db: no such file or directory

O agente procura um arquivo do collectd que não vem instalado por padrão no Ubuntu 24.04. A solução é simples: criar o arquivo vazio para o agente parar de reclamar.

sudo mkdir -p /usr/share/collectd && sudo touch /usr/share/collectd/types.db

Rodei o comando de iniciar de novo e passou.

Erro 2: métricas não apareciam no CloudWatch

O agente estava rodando, mas o namespace CWAgent não aparecia no console. Fui verificar os logs:

sudo tail -50 /opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log

O erro era bem claro:

UnauthorizedOperation: not authorized to perform: ec2:DescribeTags

Aqui entra um conceito importante do modelo de segurança da AWS: tudo que não foi explicitamente permitido está bloqueado por padrão. Uma instância EC2 não tem acesso a nenhum outro serviço da conta simplesmente por existir. Para que o agente consiga enviar métricas para o CloudWatch, a instância precisa de uma IAM Role com as permissões corretas, e ela não vem configurada por padrão.

A alternativa seria colocar credenciais diretas na instância, mas isso é exatamente o que não se faz. Role é o caminho certo: as credenciais são temporárias, rotacionadas automaticamente e ficam fora do código.

Resolvi assim:

  1. IAM → Roles → Create role → AWS Service → EC2
  2. Anexei as policies:
    • CloudWatchAgentServerPolicy
    • AmazonEC2ReadOnlyAccess
  3. Nome da role: ec2-cloudwatch-role
  4. EC2 → instância → Actions → Security → Modify IAM role → seleciono ec2-cloudwatch-role
  5. Reinicio o agente:
sudo systemctl restart amazon-cloudwatch-agent

Dessa vez sem erros nos logs. Aguardei 2 minutos e o namespace CWAgent apareceu no CloudWatch com as métricas de disco e memória sendo coletadas.

CloudWatch Agent rodando com sucesso na instância EC2
CloudWatch Agent rodando com sucesso na instância EC2

Métricas de disco e memória visíveis no namespace CWAgent do CloudWatch
Métricas de disco e memória visíveis no namespace CWAgent do CloudWatch


Criando os alarmes

Com as métricas chegando, crio os alarmes. No console AWS:

CloudWatch → Alarms → All alarms → Create alarm

Clico em Select metric → EC2 → Per-Instance Metrics, busco minha instância e seleciono CPUUtilization.

Configurações:

  • Period: 1 minute
  • Condition: Greater than 80

Em Notification, crio um novo tópico SNS com meu email. A inscrição chega no email e confirmo.

Nome do alarme: CPU-alto-lab.

Repito o processo para disco: em CWAgent busco disk_used_percent e crio o alarme com threshold de 80%.


Testando o alarme de CPU

Hora de ver se funciona de verdade. Instalo o stress:

sudo apt install stress -y

Forço carga máxima de CPU nos 2 núcleos por 3 minutos:

stress --cpu 2 --timeout 180

No CloudWatch o gráfico sobe imediatamente. Em 1 a 2 minutos o alarme muda para In alarm e o email de alerta chega.

Alarme CPU-alto-lab em estado In Alarm no console CloudWatch
Alarme CPU-alto-lab em estado In Alarm no console CloudWatch

Email de alerta do CloudWatch recebido via SNS
Email de alerta do CloudWatch recebido via SNS

O ciclo fechou: instância no ar, agente coletando, alarme disparando, notificação chegando. O Erro 2 foi o que deu mais trabalho, mas também o que ficou. A negação implícita da AWS não é detalhe de configuração, é o modelo. Entender isso muda como você pensa permissões em cloud.