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 mars 202611 min de lecture2.4k Mots|

Guide de déploiement AWS EC2 pour les applications Web

EC2 reste l'option de calcul la plus flexible d'AWS pour les applications Web qui nécessitent des performances constantes, des piles logicielles personnalisées et une tarification prévisible. Alors qu'ECS, EKS et Lambda attirent davantage l'attention dans le monde cloud natif, EC2 vous offre un serveur que vous contrôlez entièrement : pas de complexité d'orchestration de conteneurs, pas de latence de démarrage à froid, pas de coûts d'appel surprises.

Ce guide couvre le déploiement d'une application Web Node.js de production sur EC2 : sélection d'instances, configuration du groupe de sécurité, déploiement d'applications, proxy inverse Nginx, SSL avec Cloudflare, surveillance avec CloudWatch et les modèles de maintenance continue qui maintiennent un déploiement EC2 sain.

Points clés à retenir

  • t3.large est le bon point de départ pour un déploiement full-stack Node.js + PostgreSQL
  • Utilisez Ubuntu 24.04 LTS — pris en charge jusqu'en 2029, largement documenté, excellente disponibilité des packages
  • Elastic IP est obligatoire — votre IP EC2 change à chaque arrêt/démarrage sans elle
  • Les groupes de sécurité sont avec état : vous n'avez besoin que de règles entrantes ; le trafic sortant est généralement tout autorisé
  • Stockez votre clé SSH de déploiement dans un fichier .pem distinct ; ne le confiez jamais à git
  • Utilisez la connexion d'instance EC2 ou Session Manager au lieu de SSH direct lorsque cela est possible (gestion de clé zéro)
  • L'agent CloudWatch vous donne des métriques de mémoire et de disque (non disponible par défaut)
  • Les instances réservées ou les plans d'épargne réduisent les coûts EC2 de 40 à 60 % par rapport à la demande.

Sélection d'instances

Le bon type d'instance dépend de votre charge de travail :

Charge de travailInstance recommandéeProcesseur virtuelRAMCoût/mois
Light (blog, petite application)t3.petit22 Go~18$
Moyen (application full-stack)t3.moyen24 Go~35$
Production (multiservices)t3.grand28 Go~70$
Lourd (API à fort trafic)c6i.xlarge48 Go~140$
Beaucoup de mémoire (ML/cache)r6i.large216 Go~120$

Pour un monorepo avec 5 applications Node.js (Next.js, NestJS, Docusaurus, 2 sites de marque) plus une infrastructure Docker (PostgreSQL, Redis, Authentik), un t3.large est la configuration minimale viable. La famille t3 utilise des performances « extensibles » : les performances sont excellentes en fonctionnement normal, mais un processeur élevé et soutenu déclenche une limitation.

Pour des charges de travail CPU constamment élevées (traitement vidéo, inférence ML, cryptographie lourde), utilisez plutôt des instances c6i (optimisées pour le calcul).


Configuration initiale du serveur

Après avoir lancé votre instance EC2 avec 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

Configuration du groupe de sécurité

Le groupe de sécurité est le pare-feu de votre instance EC2. Configurez-le soigneusement :

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

Pour les domaines proxy Cloudflare, limitez HTTP/HTTPS aux plages IP 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

Cela empêche l'accès direct à votre serveur, contournant ainsi la protection WAF et DDoS de Cloudflare.


Déploiement d'applications

# 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 et DNS élastiques

L'adresse IP publique d'une instance EC2 change à chaque fois que vous l'arrêtez et la démarrez. Elastic IP fournit une adresse 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

Dans Cloudflare, définissez ces enregistrements sur « Proxied » (nuage orange) pour le trafic Web. Le proxy Cloudflare masque votre adresse IP EC2 réelle, offrant ainsi une protection DDoS.


Stockage : gestion des volumes EBS

Les instances EC2 incluent un volume EBS racine. Pour la production, vous avez besoin de suffisamment d'espace pour les artefacts de build, les journaux et les données 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

Surveillance CloudWatch

EC2 fournit par défaut des métriques de base sur le processeur et le réseau. Pour les métriques de mémoire et de disque, installez l'agent 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

Sauvegardes automatisées

Configurez des sauvegardes PostgreSQL automatisées sur 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 -

Configuration du rôle IAM

Attachez un rôle IAM à votre instance EC2 pour accéder au service 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": "*"
    }
  ]
}

Avec le rôle IAM attaché à votre instance, les appels du SDK AWS utilisent automatiquement les informations d'identification de l'instance : aucune clé d'accès/clé secrète n'est nécessaire dans vos variables d'environnement.


Pièges courants et solutions

Piège 1 : Pas d'adresse IP Elastic – Modifications IP au redémarrage

L'arrêt et le démarrage (pas le redémarrage) d'une instance EC2 attribue une nouvelle adresse IP publique. Sans une adresse IP Elastic, votre DNS tombe en panne. Allouez et associez une IP Elastic immédiatement après le lancement de votre instance.

Piège 2 : accès SSH verrouillé

Si vous perdez votre clé SSH ou si vous vous verrouillez en configurant mal les groupes de sécurité, utilisez EC2 Instance Connect (SSH basé sur un navigateur) à partir de la console AWS ou Session Manager (nécessite l'installation de l'agent SSM, fourni par défaut avec Ubuntu). En dernier recours, détachez le volume EBS racine, attachez-le à une autre instance, corrigez le fichier authorised_keys et rattachez-le.

Piège 3 : manque d'espace disque lors du déploiement

Le cache de build .next et node_modules augmentent considérablement au cours du développement. Surveillez l'utilisation du disque avec df -h et définissez une alarme CloudWatch sur disk_used_percent > 80 %. La commande ncdu (ncdu /) identifie les répertoires qui consomment de l'espace.

Piège 4 : épuisement de la mémoire du MOO Node.js

Node.js a une limite de mémoire par défaut (1,4 Go sur 64 bits) qui peut provoquer des plantages de MOO sur les applications volumineuses. Définissez node_args: '--max-old-space-size=1024' dans votre fichier d'écosystème PM2 pour limiter explicitement l'utilisation de la mémoire. Réglez max_memory_restart légèrement au-dessus pour redémarrer automatiquement s'il est dépassé.

Piège 5 : limitation du processeur T3 sous une charge soutenue

Les instances T3 utilisent des « crédits CPU » pour des performances extensibles. Les opérations étendues nécessitant beaucoup de CPU (constructions volumineuses, requêtes de base de données lourdes) épuisent les crédits, provoquant une limitation des performances « de base ». Surveillez CPUCreditBalance dans CloudWatch. Si les crédits sont constamment épuisés, effectuez une mise à niveau vers une instance c6i ou activez le mode « illimité » (coût supplémentaire par heure CPU au-dessus de la ligne de base).


Questions fréquemment posées

Dois-je utiliser EC2 ou un service géré comme AWS Elastic Beanstalk ?

EC2 vous donne un contrôle total : la version exacte de Node.js, l'accès au système de fichiers, la possibilité d'exécuter des conteneurs side-car Docker et la configuration Nginx personnalisée. Elastic Beanstalk gère l'infrastructure sous-jacente mais limite vos options et ajoute de la complexité au dépannage. Pour une équipe à l'aise avec la gestion de serveur Linux, EC2 avec PM2 + Nginx est plus simple et plus prévisible que les plateformes gérées. Utilisez Beanstalk si vous souhaitez que la plate-forme gère automatiquement la mise à l'échelle et la gestion de la santé.

Comment gérer les déploiements sans temps d'arrêt sur EC2 ?

La commande PM2 pm2 reload fournit un temps d'arrêt nul pour les processus en mode cluster (API NestJS). Pour Next.js (mode fork), créez d'abord la nouvelle version, puis rechargez PM2. Pendant les quelques secondes nécessaires à PM2 pour changer de processus, Nginx met en file d'attente les requêtes entrantes (avec un petit délai d'attente). Pour un temps d'arrêt vraiment nul, utilisez deux instances EC2 derrière un ALB (Application Load Balancer) et déployez sur l'une pendant que l'autre dessert le trafic.

Quand dois-je utiliser la mise à l'échelle automatique ?

La mise à l'échelle automatique ajoute une complexité opérationnelle significative : vérifications de l'état, modèles de lancement, équilibreurs de charge et considérations d'affinité de session. Pour les applications avec un trafic prévisible, une instance EC2 correctement dimensionnée avec une mise à l'échelle verticale (type d'instance plus grand) est plus simple et souvent moins chère qu'une mise à l'échelle automatique horizontale. Envisagez la mise à l'échelle automatique lorsque vous rencontrez des pics de trafic supérieurs à 5 fois la ligne de base et que le coût d'exécution d'une instance de plus grande taille en permanence dépasse la complexité de la mise à l'échelle automatique.

Comment migrer ultérieurement d'EC2 vers des conteneurs ?

Commencez par conteneuriser votre application avec Docker (écrivez un Dockerfile pour chaque application). Testez-le localement avec Docker Compose. Choisissez ensuite entre ECS Fargate (conteneurs sans serveur, plus simple) ou EKS (Kubernetes, plus puissant mais complexe). La migration ne perturbe pas si vous conteneurisez de manière incrémentielle : exécutez la version conteneurisée derrière la même configuration Nginx/Cloudflare, vérifiez le comportement, puis effectuez la transition.

Quel est le moyen le plus rentable d'exécuter EC2 en production ?

Achetez une instance réservée d'un an (pas d'avance ni d'avance partielle) pour votre instance de base – 40 % moins cher qu'à la demande. Pour une capacité supplémentaire lors des pics de trafic, utilisez des instances Spot (jusqu'à 90 % moins chères) si votre application peut gérer les interruptions. Configurez une alarme de facturation CloudWatch à 80 % de votre budget mensuel afin que les augmentations de coûts inattendues soient détectées rapidement. Pour les applications Web de production, les instances réservées offrent le meilleur équilibre entre coût et fiabilité.


Prochaines étapes

L'exécution d'une application Web de production sur EC2 nécessite une attention opérationnelle continue : correctifs de sécurité, gestion des disques, surveillance des performances et automatisation du déploiement. ECOSIRE exécute une instance de production EC2 t3.large desservant 5 applications sur plusieurs domaines, avec des sauvegardes automatisées, une surveillance CloudWatch et des déploiements PM2 sans temps d'arrêt.

Que vous ayez besoin de conseils en infrastructure AWS, de configuration de déploiement EC2 ou d'une prise en charge DevOps complète pour votre application Node.js, explorez nos services pour voir comment nous pouvons vous aider.

E

Rédigé par

ECOSIRE Research and Development Team

Création de produits numériques de niveau entreprise chez ECOSIRE. Partage d'analyses sur les intégrations Odoo, l'automatisation e-commerce et les solutions d'entreprise propulsées par l'IA.

Discutez sur WhatsApp