AWS EC2 Deployment Guide for Web Applications

Complete AWS EC2 deployment guide: instance selection, security groups, Node.js deployment, Nginx reverse proxy, SSL, auto-scaling, CloudWatch monitoring, and cost optimization.

E
ECOSIRE Research and Development Team
|19 de março de 202611 min de leitura2.3k Palavras|

Guia de implantação do AWS EC2 para aplicativos da Web

O EC2 continua sendo a opção de computação mais flexível na AWS para aplicações web que precisam de desempenho consistente, pilhas de software personalizadas e preços previsíveis. Embora ECS, EKS e Lambda recebam mais atenção no mundo nativo da nuvem, o EC2 oferece um servidor que você controla completamente — sem complexidade de orquestração de contêineres, sem latência de inicialização a frio, sem custos de invocação surpresa.

Este guia aborda a implantação de um aplicativo web Node.js de produção no EC2: seleção de instância, configuração de grupo de segurança, implantação de aplicativo, proxy reverso Nginx, SSL com Cloudflare, monitoramento com CloudWatch e os padrões de manutenção contínua que mantêm uma implantação EC2 saudável.

Principais conclusões

  • t3.large é o ponto de partida certo para uma implantação full-stack de Node.js + PostgreSQL
  • Use Ubuntu 24.04 LTS — com suporte até 2029, amplamente documentado, excelente disponibilidade de pacotes
  • Elastic IP é obrigatório — seu IP do EC2 muda a cada parada/início sem ele
  • Os grupos de segurança têm estado — você só precisa de regras de entrada; a saída normalmente é permitida para todos
  • Armazene sua chave SSH de implantação em um arquivo .pem separado; nunca comprometa com o git
  • Use conexão de instância EC2 ou Session Manager em vez de SSH direto quando possível (gerenciamento de chave zero)
  • O agente CloudWatch fornece métricas de memória e disco (não disponíveis por padrão)
  • Instâncias reservadas ou planos de economia reduzem os custos do EC2 em 40-60% em comparação com sob demanda

Seleção de instância

O tipo de instância correto depende da sua carga de trabalho:

Carga de trabalhoInstância recomendadavCPUmemória RAMCusto/mês
Light (blog, pequeno aplicativo)t3.pequeno22 GB~$18
Médio (aplicativo full stack)t3.médio24 GB~$35
Produção (multisserviço)t3.grande28 GB~$70
Pesado (API de alto tráfego)c6i.xlarge48 GB~$140
Muita memória (ML/cache)r6i.grande216 GB~$120

Para um monorepo com 5 aplicativos Node.js (Next.js, NestJS, Docusaurus, 2 sites de marca) mais infraestrutura Docker (PostgreSQL, Redis, Authentik), um t3.large é a configuração mínima viável. A família t3 usa desempenho "expansível" — o desempenho é excelente durante a operação normal, mas a alta sustentação da CPU aciona o afogamento.

Para cargas de trabalho de CPU consistentemente altas (processamento de vídeo, inferência de ML, criptografia pesada), use instâncias c6i (otimizadas para computação).


Configuração inicial do servidor

Depois de iniciar sua instância EC2 com Ubuntu 24.04:

# Connect via SSH
ssh -i your-key.pem [email protected]

# Update system packages
sudo apt update && sudo apt upgrade -y

# Install essential tools
sudo apt install -y \
  git curl wget unzip \
  build-essential \
  nginx \
  certbot python3-certbot-nginx \
  docker.io docker-compose-plugin \
  htop ncdu iotop

# Install Node.js via NVM (allows easy version management)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install 22
nvm use 22
nvm alias default 22

# Install pnpm
curl -fsSL https://get.pnpm.io/install.sh | sh -
source ~/.bashrc

# Install PM2 globally
npm install -g pm2

# Install PM2 log rotation immediately
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 50M
pm2 set pm2-logrotate:retain 7
pm2 set pm2-logrotate:compress true

Configuração do grupo de segurança

O grupo de segurança é o firewall da sua instância EC2. Configure-o com cuidado:

Inbound Rules:
┌─────────┬──────────┬─────────────┬──────────────────────────────────┐
│ Type    │ Protocol │ Port Range  │ Source                           │
├─────────┼──────────┼─────────────┼──────────────────────────────────┤
│ SSH     │ TCP      │ 22          │ Your IP only (not 0.0.0.0/0!)    │
│ HTTP    │ TCP      │ 80          │ 0.0.0.0/0 (Cloudflare IPs only)  │
│ HTTPS   │ TCP      │ 443         │ 0.0.0.0/0 (Cloudflare IPs only)  │
└─────────┴──────────┴─────────────┴──────────────────────────────────┘

Note: Internal app ports (3000, 3001, 3002, etc.) should NOT be
      in the security group — traffic goes through Nginx only

Para domínios com proxy da Cloudflare, restrinja HTTP/HTTPS aos intervalos de IP da Cloudflare:

# Cloudflare IPv4 ranges — restrict port 80/443 source to these
103.21.244.0/22
103.22.200.0/22
103.31.4.0/22
104.16.0.0/13
104.24.0.0/14
108.162.192.0/18
131.0.72.0/22
141.101.64.0/18
162.158.0.0/15
172.64.0.0/13
173.245.48.0/20
188.114.96.0/20
190.93.240.0/20
197.234.240.0/22
198.41.128.0/17

Isso impede o acesso direto ao seu servidor, ignorando a proteção WAF e DDoS da Cloudflare.


Implantação de aplicativos

# Create application directory
sudo mkdir -p /opt/ecosire/app
sudo chown ubuntu:ubuntu /opt/ecosire/app

# Clone the repository
git clone https://github.com/your-org/your-repo.git /opt/ecosire/app
cd /opt/ecosire/app

# Create .env.local from template
cp .env.example .env.local
# Edit with production values
nano .env.local

# Install dependencies
pnpm install --frozen-lockfile

# Build everything
npx turbo run build

# Run database migrations
pnpm --filter @ecosire/db db:migrate

# Start infrastructure (PostgreSQL, Redis, Authentik)
docker compose -f infrastructure/docker-compose.dev.yml up -d

# Wait for services to be healthy
sleep 30

# Start Node.js applications
pm2 start ecosystem.config.cjs

# Save process list for reboot persistence
pm2 save

# Configure PM2 to start on system boot
pm2 startup
# Run the command it outputs

IP elástico e DNS

O IP público de uma instância do EC2 muda sempre que você a interrompe e inicia. Elastic IP fornece um IP permanente:

# In AWS Console:
# 1. EC2 > Network & Security > Elastic IPs
# 2. Allocate Elastic IP address
# 3. Associate it with your instance

# Your IP is now permanent — update Cloudflare DNS to point to it
# A record: ecosire.com → 13.223.116.181 (your Elastic IP)
# A record: api.ecosire.com → 13.223.116.181
# A record: auth.ecosire.com → 13.223.116.181

Na Cloudflare, defina esses registros como “Proxied” (nuvem laranja) para tráfego da web. O proxy Cloudflare oculta seu IP EC2 real, fornecendo proteção DDoS.


Armazenamento: gerenciamento de volume EBS

As instâncias EC2 incluem um volume EBS raiz. Para produção, você precisa de espaço suficiente para construir artefatos, logs e dados do Docker:

# Check current disk usage
df -h

# Check which directories are consuming space
ncdu /

# Typical space requirements for a 5-app monorepo:
# - /opt/ecosire/app: ~2GB (code + node_modules + .next builds)
# - Docker data (/var/lib/docker): ~5GB
# - PM2 logs (/var/log/pm2): ~1GB (with rotation)
# - System: ~5GB
# Total: ~13GB minimum, recommend 30GB+ root volume

# If you need to resize an EBS volume (no downtime needed):
# 1. In AWS Console: EC2 > Volumes > Modify Volume
# 2. After resize completes, grow the filesystem:
sudo growpart /dev/xvda 1
sudo resize2fs /dev/xvda1

Monitoramento CloudWatch

O EC2 fornece métricas básicas de CPU e rede por padrão. Para métricas de memória e disco, instale o agente CloudWatch:

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

# Create configuration
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard
// /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
{
  "agent": {
    "metrics_collection_interval": 60,
    "run_as_user": "cwagent"
  },
  "metrics": {
    "append_dimensions": {
      "AutoScalingGroupName": "${aws:AutoScalingGroupName}",
      "ImageId": "${aws:ImageId}",
      "InstanceId": "${aws:InstanceId}",
      "InstanceType": "${aws:InstanceType}"
    },
    "metrics_collected": {
      "mem": {
        "measurement": ["mem_used_percent"],
        "metrics_collection_interval": 60
      },
      "disk": {
        "measurement": ["used_percent"],
        "metrics_collection_interval": 60,
        "resources": ["/", "/opt/ecosire"]
      }
    }
  },
  "logs": {
    "logs_collected": {
      "files": {
        "collect_list": [
          {
            "file_path": "/var/log/pm2/ecosire-api.err.log",
            "log_group_name": "/ec2/ecosire/api-errors",
            "log_stream_name": "{instance_id}"
          },
          {
            "file_path": "/var/log/nginx/ecosire-error.log",
            "log_group_name": "/ec2/ecosire/nginx-errors",
            "log_stream_name": "{instance_id}"
          }
        ]
      }
    }
  }
}
# Start the agent
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
  -a fetch-config \
  -m ec2 \
  -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json \
  -s

Backups automatizados

Configure backups automatizados do PostgreSQL para S3:

# Create backup script
cat > /opt/ecosire/scripts/backup-db.sh << 'EOF'
#!/bin/bash
set -e

DATE=$(date +%Y-%m-%d-%H%M%S)
BACKUP_FILE="/tmp/ecosire-db-${DATE}.sql.gz"
S3_BUCKET="s3://your-backups-bucket/postgres"

# Dump the database (connects via Docker network)
docker exec ecosire-postgres pg_dump \
  -U ecosire \
  -d ecosire_dev \
  --no-owner \
  --no-privileges \
  | gzip > "$BACKUP_FILE"

# Upload to S3
aws s3 cp "$BACKUP_FILE" "$S3_BUCKET/"

# Clean up local file
rm "$BACKUP_FILE"

# Delete backups older than 30 days from S3
aws s3 ls "$S3_BUCKET/" \
  | awk '{print $4}' \
  | sort \
  | head -n -30 \
  | xargs -I {} aws s3 rm "$S3_BUCKET/{}" 2>/dev/null || true

echo "Backup complete: ${DATE}"
EOF

chmod +x /opt/ecosire/scripts/backup-db.sh

# Schedule daily backups at 3 AM UTC
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/ecosire/scripts/backup-db.sh >> /var/log/db-backup.log 2>&1") | crontab -

Configuração de função IAM

Anexe uma função IAM à sua instância EC2 para acesso ao serviço AWS (S3, CloudWatch, SES):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::your-products-bucket",
        "arn:aws:s3:::your-products-bucket/*",
        "arn:aws:s3:::your-backups-bucket",
        "arn:aws:s3:::your-backups-bucket/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "ses:SendEmail",
        "ses:SendRawEmail"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "cloudwatch:PutMetricData",
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "*"
    }
  ]
}

Com a função do IAM anexada à sua instância, as chamadas do AWS SDK usam credenciais de instância automaticamente, sem necessidade de chave de acesso/chave secreta nas variáveis ​​de ambiente.


Armadilhas e soluções comuns

Armadilha 1: Sem Elastic IP – alterações de IP na reinicialização

Interromper e iniciar (não reinicializar) uma instância do EC2 atribui um novo IP público. Sem um Elastic IP, seu DNS quebra. Aloque e associe um Elastic IP imediatamente após iniciar sua instância.

Armadilha 2: acesso SSH bloqueado

Se você perder sua chave SSH ou se bloquear configurando incorretamente grupos de segurança, use o EC2 Instance Connect (SSH baseado em navegador) no console AWS ou o Session Manager (requer o agente SSM instalado, que vem com o Ubuntu por padrão). Como último recurso, desconecte o volume raiz do EBS, anexe-o a outra instância, corrija o arquivoauthorized_keys e reconecte.

Armadilha 3: Ficar sem espaço em disco durante a implantação

O cache de compilação .next e node_modules crescem substancialmente durante o desenvolvimento. Monitore o uso do disco com df -h e defina um alarme do CloudWatch em disk_used_percent > 80%. O comando ncdu (ncdu /) identifica quais diretórios estão consumindo espaço.

Armadilha 4: Esgotamento de memória do Node.js OOM

O Node.js tem um limite de memória padrão (1,4 GB em 64 bits) que pode causar travamentos de OOM em aplicativos grandes. Defina node_args: '--max-old-space-size=1024' em seu arquivo do ecossistema PM2 para limitar explicitamente o uso de memória. Defina max_memory_restart um pouco acima para reiniciar automaticamente se for excedido.

Armadilha 5: Aceleração da CPU T3 sob carga sustentada

As instâncias T3 usam "créditos de CPU" para desempenho expansível. Operações estendidas de alta CPU (construções grandes, consultas pesadas ao banco de dados) esgotam os créditos, causando limitação no desempenho da "linha de base". Monitore CPUCreditBalance no CloudWatch. Se os créditos se esgotarem consistentemente, atualize para uma instância c6i ou ative o modo "ilimitado" (custo adicional por hora de CPU acima da linha de base).


Perguntas frequentes

Devo usar o EC2 ou um serviço gerenciado como o AWS Elastic Beanstalk?

O EC2 oferece controle total: a versão exata do Node.js, acesso ao sistema de arquivos, capacidade de executar contêineres secundários do Docker e configuração personalizada do Nginx. O Elastic Beanstalk gerencia a infraestrutura subjacente, mas restringe suas opções e adiciona complexidade à solução de problemas. Para uma equipe confortável com o gerenciamento de servidores Linux, o EC2 com PM2 + Nginx é mais simples e previsível do que as plataformas gerenciadas. Use o Beanstalk se quiser que a plataforma lide com o dimensionamento e o gerenciamento de integridade automaticamente.

Como lidar com implantações com tempo de inatividade zero no EC2?

O comando PM2 pm2 reload fornece tempo de inatividade zero para processos em modo cluster (API NestJS). Para Next.js (modo fork), crie primeiro a nova versão e depois recarregue o PM2. Durante os poucos segundos que o PM2 leva para alternar processos, o Nginx enfileira as solicitações recebidas (com um pequeno tempo limite). Para um tempo de inatividade verdadeiramente zero, use duas instâncias EC2 atrás de um ALB (Application Load Balancer) e implante em uma enquanto a outra atende o tráfego.

Quando devo usar o escalonamento automático?

O escalonamento automático adiciona complexidade operacional significativa — verificações de integridade, modelos de inicialização, balanceadores de carga e considerações de afinidade de sessão. Para aplicações com tráfego previsível, uma instância EC2 de tamanho adequado com escalonamento vertical (tipo de instância maior) é mais simples e geralmente mais barata do que o escalonamento automático horizontal. Considere o escalonamento automático quando você tiver picos de tráfego superiores a 5 vezes a linha de base e o custo de execução de uma instância permanentemente maior exceder a complexidade do escalonamento automático.

Como faço para migrar do EC2 para contêineres posteriormente?

Comece conteinerizando seu aplicativo com Docker (escreva um Dockerfile para cada aplicativo). Teste-o localmente com Docker Compose. Em seguida, escolha entre ECS Fargate (contêineres sem servidor, mais simples) ou EKS (Kubernetes, mais poderoso, porém complexo). A migração não causa interrupções se você conteinerizar de forma incremental – execute a versão em contêiner atrás da mesma configuração Nginx/Cloudflare, verifique o comportamento e, em seguida, faça a transição.

Qual é a maneira mais econômica de executar o EC2 em produção?

Compre uma instância reservada de 1 ano (sem adiantamento ou adiantamento parcial) para sua instância de linha de base — 40% mais barata do que sob demanda. Para obter capacidade adicional durante picos de tráfego, use instâncias spot (até 90% mais baratas) se seu aplicativo puder lidar com interrupções. Configure um alarme de faturamento do CloudWatch em 80% do seu orçamento mensal para que aumentos inesperados de custos sejam detectados antecipadamente. Para aplicativos Web de produção, as Instâncias Reservadas oferecem o melhor equilíbrio entre custo e confiabilidade.


Próximas etapas

A execução de uma aplicação web de produção no EC2 requer atenção operacional contínua – patches de segurança, gerenciamento de disco, monitoramento de desempenho e automação de implantação. ECOSIRE executa uma instância de produção EC2 t3.large que atende 5 aplicativos em vários domínios, com backups automatizados, monitoramento CloudWatch e implantações PM2 com tempo de inatividade zero.

Se você precisa de consultoria de infraestrutura AWS, configuração de implantação EC2 ou suporte completo de DevOps para seu aplicativo Node.js, explore nossos serviços para ver como podemos ajudar.

E

Escrito por

ECOSIRE Research and Development Team

Construindo produtos digitais de nível empresarial na ECOSIRE. Compartilhando insights sobre integrações Odoo, automação de e-commerce e soluções de negócios com IA.

Converse no WhatsApp