Executando Scripts na Inicialização do EC2: Guia Completo de User Data

Você acabou de criar uma AMI base e precisa que cada nova instância já suba com Nginx instalado, configurado e rodando — sem intervenção manual. O recurso que resolve isso é o EC2 User Data, e entender exatamente onde ele executa, quando falha silenciosamente e quais armadilhas existem na prática faz toda a diferença entre um bootstrap confiável e horas de depuração.

TL;DR — Resumo Rápido

PontoDetalhe
O que é User DataScript shell (ou cloud-init) executado pelo cloud-init na primeira inicialização da instância
Quando executaPor padrão, apenas uma vez — no primeiro boot após o lançamento
Onde colar o scriptConsole AWS → EC2 → Launch Instance → seção 'Advanced Details' → campo 'User data'
Log de execução/var/log/cloud-init-output.log na instância
Permissão de execuçãoO script roda como root — sem necessidade de sudo
Limite de tamanho16 KB (em texto puro); scripts maiores devem ser carregados via S3

Como o EC2 User Data Funciona Internamente

O cloud-init é o agente responsável por processar o User Data. Ele está presente por padrão nas AMIs Amazon Linux 2, Amazon Linux 2023, Ubuntu e outras distribuições populares disponíveis no AWS Marketplace. O fluxo de execução segue uma sequência bem definida de estágios.

graph TD A["EC2 Instance Launch"] --> B["cloud-init inicia"] B --> C["Busca User Data via endpoint de metadados"] C --> D{"Tipo de conteúdo?"} D -->|"#!/bin/bash"| E["Executa como script shell (root)"] D -->|"#cloud-config"| F["Processa como cloud-config YAML"] E --> G["stdout/stderr → /var/log/cloud-init-output.log"] F --> G G --> H["Marca estágio como concluído"] H --> I["Reboots subsequentes: script NÃO reexecuta"] style A fill:#FF9900,color:#000 style H fill:#2E8B57,color:#fff style I fill:#DC143C,color:#fff
  1. Instance Launch: A instância é criada a partir de uma AMI e recebe o User Data via metadados (http://169.254.169.254/latest/user-data).
  2. cloud-init detect: O agente identifica o tipo de conteúdo — script shell (#!/bin/bash), cloud-config YAML, ou multi-part MIME.
  3. Execução do script: Para scripts shell, o cloud-init grava o conteúdo em um arquivo temporário e executa como root.
  4. Log de saída: Todo stdout e stderr são capturados em /var/log/cloud-init-output.log.
  5. Marcação de conclusão: O cloud-init registra o estágio como concluído. Em reboots subsequentes, o script não é reexecutado por padrão.
Pense no User Data como uma carta lacrada entregue à instância no momento do nascimento. Ela é aberta e lida uma única vez. Se você quiser que a carta seja relida a cada boot, precisa instruir explicitamente o cloud-init a fazer isso.

Onde Colar o Script de User Data no Console AWS

A localização do campo muda dependendo do fluxo de lançamento que você está usando. O console atual usa o assistente unificado de lançamento.

Passo a passo no Console (Launch Instance Wizard)

  1. Acesse o console EC2: Services → EC2 → Instances → Launch instances.
  2. Configure AMI, tipo de instância, par de chaves e configurações de rede normalmente.
  3. Role até a seção 'Advanced details' (última seção antes do resumo).
  4. Localize o campo 'User data' no final da seção.
  5. Selecione 'Input is already base64 encoded' apenas se você estiver colando conteúdo já codificado. Para scripts em texto puro, deixe desmarcado.
  6. Cole seu script diretamente no campo de texto.
graph LR A["Console EC2"] --> B["Launch Instances"] B --> C["AMI + Tipo + Chave"] C --> D["Rede e Storage"] D --> E["Advanced Details (rolar até o final)"] E --> F["Campo: User data"] F --> G["Colar script #!/bin/bash ..."] G --> H["Launch Instance"] style F fill:#FF9900,color:#000 style H fill:#2E8B57,color:#fff

Script de User Data para Instalar o Nginx — Amazon Linux 2023

O script abaixo instala o Nginx, habilita o serviço para iniciar automaticamente e sobe o servidor. Cada linha tem uma razão de existir — não é boilerplate.

#!/bin/bash
# Redireciona stdout e stderr para o log do cloud-init
exec > >(tee /var/log/user-data.log | logger -t user-data -s 2>/dev/console) 2>&1

# Atualiza o índice de pacotes antes de instalar
dnf update -y

# Instala o Nginx
dnf install -y nginx

# Habilita o Nginx para iniciar automaticamente após reboots
systemctl enable nginx

# Inicia o Nginx imediatamente
systemctl start nginx

# Confirma o status no log para facilitar depuração
systemctl status nginx

O redirecionamento na segunda linha é crítico em produção. Sem ele, erros de instalação de pacotes aparecem apenas no cloud-init-output.log padrão, que nem sempre é o primeiro lugar que você olha. Com o redirecionamento, você tem um log dedicado em /var/log/user-data.log e as mensagens também vão para o syslog.

Variante para Ubuntu / Debian

#!/bin/bash
exec > >(tee /var/log/user-data.log | logger -t user-data -s 2>/dev/console) 2>&1

apt-get update -y
apt-get install -y nginx
systemctl enable nginx
systemctl start nginx
systemctl status nginx

Executando User Data via AWS CLI

Para automação e pipelines de infraestrutura, o console não é o caminho. O CLI é mais reproduzível e auditável.

aws ec2 run-instances \
  --image-id ami-0c02fb55956c7d316 \
  --instance-type t3.micro \
  --key-name minha-chave-ec2 \
  --security-group-ids sg-0123456789abcdef0 \
  --subnet-id subnet-0123456789abcdef0 \
  --user-data file://script-nginx.sh \
  --region us-east-1

O parâmetro --user-data file://script-nginx.sh lê o script do arquivo local e o envia automaticamente codificado em base64 para a API. Você não precisa codificar manualmente. Substitua os valores de --image-id, --key-name, --security-group-ids e --subnet-id pelos seus recursos reais.

Verificando a Execução do Script na Instância

Depois que a instância subir, a primeira coisa a verificar é o log do cloud-init. Não o status do Nginx — o log. Se o script falhou antes de chegar no systemctl start, o serviço simplesmente não vai estar rodando, sem nenhum erro óbvio no console EC2.

# Conecte via SSH e verifique o log principal do cloud-init
sudo cat /var/log/cloud-init-output.log

# Se você adicionou o redirecionamento customizado, verifique também:
sudo cat /var/log/user-data.log

# Verifica se o Nginx está rodando
systemctl status nginx

# Testa a resposta HTTP localmente na instância
curl -s http://localhost | head -20

Você também pode verificar o log do cloud-init diretamente pelo Console AWS sem precisar de SSH: EC2 → Instances → selecione a instância → Actions → Monitor and troubleshoot → Get system log. O System Log captura a saída do console serial, que inclui os logs do cloud-init durante o boot.

Diagnóstico: O Script Rodou mas o Nginx Não Está de Pé

Esse é o cenário clássico de misdiagnóstico. A instância sobe, você testa curl http://<ip-publico> e recebe timeout. A primeira suspeita é sempre o script — mas na maioria dos casos o script funcionou perfeitamente.

graph TD A["curl timeout na porta 80"] --> B{"Security Group libera TCP/80?"} B -->|"Não"| C["Adicionar regra inbound TCP 80 no SG"] B -->|"Sim"| D{"Instância tem IP público?"} D -->|"Não"| E["Associar Elastic IP ou usar subnet pública"] D -->|"Sim"| F["SSH na instância"] F --> G["cat /var/log/cloud-init-output.log"] G --> H{"Erro no log?"} H -->|"Sim"| I["Corrigir script e relancar instância"] H -->|"Não"| J["systemctl status nginx"] J --> K["Nginx rodando normalmente"] style C fill:#DC143C,color:#fff style E fill:#DC143C,color:#fff style I fill:#DC143C,color:#fff style K fill:#2E8B57,color:#fff

O fluxo de diagnóstico correto:

  1. Verifique o Security Group primeiro. O Nginx escuta na porta 80. Se o Security Group da instância não tem uma regra de entrada (inbound) liberando TCP/80 de 0.0.0.0/0 (ou do seu IP), o tráfego nunca chega à instância. O script pode ter rodado perfeitamente e o Nginx estar saudável — o problema é a camada de rede.
# Verifica as regras de inbound do Security Group
aws ec2 describe-security-groups \
  --group-ids sg-0123456789abcdef0 \
  --query 'SecurityGroups[*].IpPermissions' \
  --region us-east-1
  1. Verifique se a instância tem IP público. Uma instância em subnet privada, ou lançada sem IP público associado, não é acessível diretamente pela internet independentemente do Security Group.
# Verifica o IP público da instância
aws ec2 describe-instances \
  --instance-ids i-0123456789abcdef0 \
  --query 'Reservations[*].Instances[*].PublicIpAddress' \
  --region us-east-1
  1. Só então verifique o script. Se rede e IP estão corretos, acesse via SSH e inspecione /var/log/cloud-init-output.log. Erros de pacote como No package nginx available indicam que o repositório não foi atualizado antes da instalação, ou que o nome do pacote está errado para a distribuição.

Na prática, 70% dos casos de 'User Data não funcionou' são Security Group bloqueando a porta 80, não falha no script.

Modificando o User Data de uma Instância Existente

Você só pode modificar o User Data de uma instância que está parada (stopped), não terminada. Isso é uma restrição da API — a tentativa em uma instância rodando retorna erro.

# Primeiro, pare a instância
aws ec2 stop-instances \
  --instance-ids i-0123456789abcdef0 \
  --region us-east-1

# Aguarde a instância parar completamente
aws ec2 wait instance-stopped \
  --instance-ids i-0123456789abcdef0 \
  --region us-east-1

# Modifica o User Data (o conteúdo deve estar em base64 ou usar file://)
aws ec2 modify-instance-attribute \
  --instance-id i-0123456789abcdef0 \
  --user-data file://novo-script.sh \
  --region us-east-1

Importante: modificar o User Data não faz o script reexecutar automaticamente no próximo boot. Por padrão, o cloud-init já marcou o estágio como concluído. Para forçar a reexecução, você precisa limpar o estado do cloud-init antes de reiniciar a instância:

# Execute dentro da instância antes de reiniciar
sudo cloud-init clean --logs
sudo reboot

Use isso com cuidado em instâncias de produção — cloud-init clean remove todos os registros de execução anterior, o que pode causar comportamentos inesperados dependendo do que o User Data original configurou.

IAM: Quando o Script Precisa Acessar Recursos AWS

Se o seu script de bootstrap precisa baixar artefatos do S3, buscar segredos do Secrets Manager ou chamar qualquer API AWS, o script em si não precisa de credenciais hardcoded — ele precisa que a instância tenha um IAM Instance Profile com as permissões necessárias.

# Exemplo: script que baixa configuração do S3 durante o bootstrap
#!/bin/bash
dnf install -y nginx aws-cli

# O aws-cli usa automaticamente as credenciais do Instance Profile
aws s3 cp s3://meu-bucket-config/nginx.conf /etc/nginx/nginx.conf

systemctl enable nginx
systemctl start nginx

A política mínima necessária para o Instance Profile nesse caso:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::meu-bucket-config/nginx.conf"
    }
  ]
}

Nunca coloque credenciais AWS (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) diretamente no User Data. O conteúdo do User Data é acessível via endpoint de metadados da instância sem autenticação adicional por qualquer processo rodando na máquina.

Próximos Passos e Recursos Oficiais

O User Data resolve bem o caso de bootstrap simples em instâncias individuais. Para frotas de instâncias com configurações complexas, considere evoluir para AWS Systems Manager State Manager ou ferramentas de gerenciamento de configuração como Ansible integrado ao EC2. Para pipelines de criação de AMIs com software pré-instalado, o EC2 Image Builder é a abordagem mais robusta — o User Data deixa de ser necessário quando o software já está na imagem.

Glossário

TermoDefinição
User DataScript ou configuração fornecida no lançamento da instância EC2, processada pelo cloud-init durante o primeiro boot
cloud-initAgente de inicialização padrão em distribuições Linux na nuvem; responsável por processar User Data e configurar a instância
Instance ProfileContêiner IAM que associa um IAM Role a uma instância EC2, permitindo que processos na instância chamem APIs AWS
AMI (Amazon Machine Image)Template imutável contendo o sistema operacional e software base usado para lançar instâncias EC2
Security GroupFirewall stateful no nível da instância que controla tráfego de entrada e saída por protocolo, porta e origem/destino

Comentários

Postagens mais visitadas deste blog

Variáveis de Ambiente no Lambda: Configuração, Acesso e Criptografia com KMS

Entendendo Instâncias T3 Burstáveis: CPU Credits e Por Que Seu Servidor Fica Lento

Monitoramento de Memória RAM no EC2: Por que o CloudWatch Agent é Obrigatório