ウェブアプリケーションのための AWS EC2 デプロイメントガイド
EC2 は、一貫したパフォーマンス、カスタム ソフトウェア スタック、予測可能な価格設定を必要とする Web アプリケーションにとって、AWS で最も柔軟なコンピューティング オプションであり続けています。クラウドネイティブの世界では ECS、EKS、Lambda が注目を集めていますが、EC2 では完全に制御できるサーバーが提供されます。コンテナ オーケストレーションの複雑さ、コールド スタートのレイテンシー、驚くべき呼び出しコストはありません。
このガイドでは、EC2 での実稼働 Node.js Web アプリケーションのデプロイについて説明します。インスタンスの選択、セキュリティ グループの構成、アプリケーションのデプロイ、Nginx リバース プロキシ、Cloudflare による SSL、CloudWatch によるモニタリング、EC2 デプロイを健全に保つ継続的なメンテナンス パターンについて説明します。
重要なポイント
- t3.large は、フルスタック Node.js + PostgreSQL デプロイメントの適切な開始点です
- Ubuntu 24.04 LTS を使用します — 2029 年までサポートされ、広く文書化されており、優れたパッケージの可用性
- Elastic IP は必須です - EC2 IP は、Elastic IP なしで停止/開始するたびに変更されます
- セキュリティ グループはステートフルです。必要なのは受信ルールのみです。アウトバウンドは通常、すべて許可です
- 導入 SSH キーを別の
.pemファイルに保存します。決して git にコミットしないでください- 可能な場合は、直接 SSH の代わりに EC2 インスタンス接続またはセッション マネージャーを使用します (ゼロキー管理)
- CloudWatch エージェントはメモリとディスクのメトリクスを提供します (デフォルトでは利用できません)
- リザーブドインスタンスまたは Savings Plan は、オンデマンドと比較して EC2 コストを 40 ~ 60% 削減します
インスタンスの選択
適切なインスタンス タイプはワークロードによって異なります。
| ワークロード | 推奨インスタンス | vCPU | RAM | 月額費用 |
|---|---|---|---|---|
| ライト (ブログ、小さなアプリ) | t3.small | 2 | 2GB | ~$18 |
| 中 (フルスタック アプリ) | t3.medium | 2 | 4GB | ~$35 |
| プロダクション (マルチサービス) | t3.large | 2 | 8GB | ~$70 |
| 重い (高トラフィック API) | c6i.xlarge | 4 | 8GB | ~$140 |
| メモリを大量に使用する (ML/キャッシュ) | r6i.large | 2 | 16GB | ~$120 |
5 つの Node.js アプリケーション (Next.js、NestJS、Docusaurus、2 つのブランド サイト) と Docker インフラストラクチャ (PostgreSQL、Redis、Authentik) を含むモノリポジトリの場合、t3.large が実行可能な最小構成です。 t3 ファミリは「バースト可能な」パフォーマンスを使用します。通常の動作中のパフォーマンスは優れていますが、CPU の使用率が高い状態が続くとスロットルが発生します。
一貫して CPU ワークロード (ビデオ処理、ML 推論、重い暗号化) が高い場合は、代わりに c6i (コンピューティング最適化) インスタンスを使用してください。
サーバーの初期セットアップ
Ubuntu 24.04 で EC2 インスタンスを起動した後:
# 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
セキュリティグループの設定
セキュリティ グループは EC2 インスタンスのファイアウォールです。慎重に設定してください。
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
Cloudflareプロキシドメインの場合、HTTP/HTTPSをCloudflare IP範囲に制限します。
# 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
これにより、Cloudflare の WAF および DDoS 保護をバイパスして、サーバーへの直接アクセスが防止されます。
アプリケーションのデプロイメント
# 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
Elastic IP と DNS
EC2 インスタンスのパブリック IP は、停止および起動するたびに変更されます。 Elastic IP は永続的な IP を提供します。
# 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
Cloudflareでは、これらのレコードをWebトラフィック用に「プロキシ済み」(オレンジ色の雲)に設定します。 Cloudflare プロキシは実際の EC2 IP を隠し、DDoS 保護を提供します。
ストレージ: EBS ボリューム管理
EC2 インスタンスにはルート EBS ボリュームが含まれます。運用環境では、ビルド アーティファクト、ログ、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
CloudWatch モニタリング
EC2 は、デフォルトで基本的な CPU とネットワークのメトリクスを提供します。メモリとディスクのメトリクスについては、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
自動バックアップ
S3 への自動 PostgreSQL バックアップを設定します。
# 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 -
IAM ロールの構成
AWS サービス (S3、CloudWatch、SES) にアクセスするために、IAM ロールを EC2 インスタンスにアタッチします。
{
"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": "*"
}
]
}
IAM ロールがインスタンスにアタッチされている場合、AWS SDK 呼び出しではインスタンスの認証情報が自動的に使用されます。環境変数にアクセス キーやシークレット キーは必要ありません。
よくある落とし穴と解決策
落とし穴 1: Elastic IP がない - 再起動時に IP が変更される
EC2 インスタンスを停止および開始すると (再起動ではありません)、新しいパブリック IP が割り当てられます。 Elastic IP がないと、DNS が壊れます。インスタンスを起動した直後に、Elastic IP を割り当てて関連付けます。
落とし穴 2: SSH アクセスがロックアウトされる
SSH キーを紛失した場合、またはセキュリティ グループの構成を誤って自分自身をロックアウトした場合は、AWS コンソールから EC2 Instance Connect (ブラウザベースの SSH) を使用するか、Session Manager (SSM エージェントのインストールが必要です。デフォルトで Ubuntu に付属しています) を使用してください。最後の手段として、ルート EBS ボリュームをデタッチして別のインスタンスにアタッチし、authorized_keys ファイルを修正して再アタッチします。
落とし穴 3: 導入中のディスク容量不足
.next ビルド キャッシュと node_modules は、開発中に大幅に増加します。 df -h を使用してディスク使用量を監視し、disk_used_percent > 80% に CloudWatch アラームを設定します。 ncdu コマンド (ncdu /) は、どのディレクトリがスペースを消費しているかを特定します。
落とし穴 4: Node.js OOM によるメモリ枯渇
Node.js にはデフォルトのメモリ制限 (64 ビットで 1.4 GB) があり、大規模なアプリケーションでは OOM クラッシュが発生する可能性があります。 PM2 エコシステム ファイルで node_args: '--max-old-space-size=1024' を設定して、メモリ使用量を明示的に制限します。この値を超えた場合に自動再起動するには、max_memory_restart をこれよりわずかに大きい値に設定します。
落とし穴 5: 負荷が継続した場合の T3 CPU スロットリング
T3 インスタンスは、バースト可能なパフォーマンスのために「CPU クレジット」を使用します。長時間にわたる CPU 使用率の高い操作 (大規模なビルド、大量のデータベース クエリ) によりクレジットが枯渇し、「ベースライン」パフォーマンスまでスロットルが発生します。 CloudWatch で CPUCreditBalance を監視します。クレジットが継続的に枯渇する場合は、c6i インスタンスにアップグレードするか、「無制限」モードを有効にしてください (ベースラインを超える CPU 時間あたりの追加コスト)。
よくある質問
EC2 または AWS Elastic Beanstalk などのマネージド サービスを使用する必要がありますか?
EC2 では、正確な Node.js バージョン、ファイル システム アクセス、Docker サイドカー コンテナを実行する機能、カスタム Nginx 構成などを完全に制御できます。 Elastic Beanstalk は基盤となるインフラストラクチャを管理しますが、オプションが制限され、トラブルシューティングが複雑になります。 Linux サーバー管理に慣れているチームにとって、PM2 + Nginx を備えた EC2 はマネージド プラットフォームよりもシンプルで予測しやすいものです。プラットフォームでスケーリングとヘルス管理を自動的に処理する場合は、Beanstalk を使用します。
EC2 でダウンタイムなしのデプロイメントを処理するにはどうすればよいですか?
PM2 pm2 reload コマンドは、クラスター モード プロセス (NestJS API) にゼロ ダウンタイムを提供します。 Next.js (フォーク モード) の場合は、最初に新しいバージョンをビルドしてから、PM2 をリロードします。 PM2 がプロセスを切り替えるのにかかる数秒間、Nginx は受信リクエストをキューに入れます (タイムアウトはわずかです)。真にダウンタイムをゼロにするには、ALB (Application Load Balancer) の背後で 2 つの EC2 インスタンスを使用し、一方にデプロイし、もう一方がトラフィックを処理します。
自動スケーリングを使用する必要があるのはどのような場合ですか?
自動スケーリングにより、ヘルスチェック、起動テンプレート、ロードバランサー、セッションアフィニティの考慮事項など、運用が大幅に複雑になります。トラフィックが予測可能なアプリケーションの場合、垂直方向のスケーリングを備えた適切なサイズの EC2 インスタンス (より大きなインスタンス タイプ) は、水平方向の自動スケーリングよりもシンプルで、多くの場合安価です。ベースラインの 5 倍を超えるトラフィックの急増があり、永続的に大規模なインスタンスを実行するコストが自動スケーリングの複雑さを超えている場合は、自動スケーリングを検討してください。
後で EC2 からコンテナに移行するにはどうすればよいですか?
まず、アプリケーションを Docker でコンテナ化します (アプリごとに Dockerfile を作成します)。 Docker Compose を使用してローカルでテストします。次に、ECS Fargate (サーバーレスコンテナ、よりシンプル) または EKS (Kubernetes、より強力だが複雑) のどちらかを選択します。段階的にコンテナ化する場合、移行は中断を伴いません。同じ Nginx/Cloudflare セットアップの背後でコンテナ化されたバージョンを実行し、動作を確認してからカットオーバーします。
実稼働環境で EC2 を実行する最もコスト効率の高い方法は何ですか?
ベースライン インスタンス用に 1 年間のリザーブド インスタンス (前払いまたは部分前払いなし) を購入します。オンデマンドより 40% 安くなります。アプリケーションが中断に対応できる場合、トラフィック急増時の容量を追加するには、スポット インスタンス (最大 90% 安価) を使用します。 CloudWatch の請求アラームを月次予算の 80% に設定すると、予期せぬコストの増加が早期に検出されます。実稼働 Web アプリケーションの場合、リザーブド インスタンスはコストと信頼性の最適なバランスを提供します。
次のステップ
EC2 上で本番 Web アプリケーションを実行するには、セキュリティ パッチ、ディスク管理、パフォーマンス監視、デプロイの自動化など、継続的な運用上の注意が必要です。 ECOSIRE は、自動バックアップ、CloudWatch モニタリング、ダウンタイムゼロの PM2 デプロイメントを備え、複数のドメインにわたる 5 つのアプリケーションを提供する実稼働 EC2 t3.large インスタンスを実行します。
AWS インフラストラクチャのコンサルティング、EC2 デプロイメントのセットアップ、または Node.js アプリケーションの完全な DevOps サポートが必要な場合でも、当社のサービスを探索 して、当社がどのようにお手伝いできるかをご確認ください。
執筆者
ECOSIRE Research and Development Team
ECOSIREでエンタープライズグレードのデジタル製品を開発。Odoo統合、eコマース自動化、AI搭載ビジネスソリューションに関するインサイトを共有しています。
関連記事
Cloud Hosting for ERP: AWS vs Azure vs Google Cloud
A detailed comparison of AWS, Azure, and Google Cloud for ERP hosting in 2026. Covers performance, cost, regional availability, managed services, and ERP-specific recommendations.
Cloud vs On-Premise ERP in 2026: The Definitive Guide
Cloud vs on-premise ERP in 2026: total cost analysis, security comparison, scalability, compliance, and the right deployment model for your business.
Docker Compose for Development: Local Infrastructure
Docker Compose for local development: PostgreSQL, Redis, Authentik, networking, health checks, volume management, and environment-specific configurations for TypeScript monorepos.