2025年5月30日

AWS ECSでNext.js Webアプリを構築する手順書

0. 準備

  • AWSアカウントの作成と初期設定(IAMユーザー、AWS CLIのセットアップなど)が完了していること。
  • ローカル環境にDockerがインストールされていること。
  • Next.jsアプリケーションのソースコードがあること(ない場合は簡単なものを作成します)。

1. VPCの作成

AWSマネジメントコンソールでVPCを作成します。「VPCなどを作成」ウィザードを使用すると、Public Subnet、Private Subnet、ルートテーブル、インターネットゲートウェイ、NATゲートウェイを一度に作成できて便利です。

  1. AWSマネジメントコンソールにログインし、VPCサービスを開きます。
  2. 左側のナビゲーションペインから「お使いのVPC」を選択し、「VPCを作成」をクリックします。
  3. 作成するリソース」で「VPCなど」を選択します。
    • 名前タグの自動生成: 任意(例: my-nextjs-vpc
    • IPv4 CIDR ブロック: 例: 10.0.0.0/16
    • アベイラビリティーゾーン (AZ) の数: 2つ以上を選択することを推奨します(高可用性のため)。
    • パブリックサブネットの数: 選択したAZの数と同じ数を設定します(例: 2)。
    • プライベートサブネットの数: 選択したAZの数と同じ数を設定します(例: 2)。
    • NATゲートウェイ: 「1 AZ あたり」または必要に応じて選択します。これにより、プライベートサブネットのECSタスクがECRや外部サービスにアクセスできるようになります。
      • コスト削減のため、テスト環境では「なし」を選択し、後述するVPCエンドポイントを利用することも検討できますが、NATゲートウェイの方が汎用性は高いです。
    • VPC エンドポイント: S3ゲートウェイエンドポイントはデフォルトで作成されます。ECRとの通信のために、後ほどcom.amazonaws.region.ecr.apicom.amazonaws.region.ecr.dkrcom.amazonaws.region.logsのインターフェイスエンドポイントを作成することを推奨します(特にNATゲートウェイを使用しない場合やコストを抑えたい場合)。
    • DNSホスト名: 有効化
    • DNS解決: 有効化
  4. VPCを作成」をクリックします。
  5. 作成が完了したら、作成された各リソース(サブネットID、ルートテーブルIDなど)を確認しておきます。

2. Security Group作成

2.1. ALBのためのSecurity Group作成

  1. VPCサービスのナビゲーションペインから「セキュリティグループ」を選択し、「セキュリティグループを作成」をクリックします。
  2. 基本詳細:
    • セキュリティグループ名: 例: my-alb-sg
    • 説明: 例: Security group for Application Load Balancer
    • VPC: ステップ1で作成したVPCを選択します。
  3. インバウンドルール:
    • ルールを追加」をクリックします。
    • タイプ: HTTP
    • プロトコル: TCP
    • ポート範囲: 80
    • ソース: 任意の場所IPv4 (0.0.0.0/0) または、テスト用に特定のIPアドレス/範囲を指定します。
      • 本番環境では、CloudFrontなどからのアクセスのみに制限することを検討してください。
    • 説明: 例: Allow HTTP access from anywhere
  4. アウトバウンドルール:
    • デフォルトではすべてのトラフィックが許可されています。必要に応じて変更してください。
  5. セキュリティグループを作成」をクリックします。作成されたセキュリティグループID(sg-xxxxxxxx)を控えておきます。

2.2. ECSのためのSecurity Group作成

  1. 再度「セキュリティグループを作成」をクリックします。
  2. 基本詳細:
    • セキュリティグループ名: 例: my-ecs-sg
    • 説明: 例: Security group for ECS tasks
    • VPC: ステップ1で作成したVPCを選択します。
  3. インバウンドルール:
    • ルールを追加」をクリックします。
    • タイプ: カスタムTCP
    • プロトコル: TCP
    • ポート範囲: 3000 (Next.jsアプリケーションがリッスンするポートに合わせてください)
    • ソース: 「カスタム」を選択し、2.1で作成したALBのセキュリティグループID(my-alb-sgのID)を入力または選択します。
    • 説明: 例: Allow access from ALB on port 3000
  4. アウトバウンドルール:
    • デフォルトではすべてのトラフィックが許可されています。これにより、ECSタスクはECRからイメージをプルしたり、NATゲートウェイ経由で外部APIにアクセスしたりできます。
  5. セキュリティグループを作成」をクリックします。作成されたセキュリティグループIDを控えておきます。

3. ECSのクラスター作成

  1. Elastic Container Service (ECS) サービスを開きます。
  2. 左側のナビゲーションペインから「クラスター」を選択し、「クラスターの作成」をクリックします。
  3. クラスターテンプレートの選択: 「ネットワーキングのみ」(AWS Fargateを使用する場合)を選択します。
    • EC2インスタンスでECSをホストする場合は、「EC2 Linux + ネットワーキング」などを選択しますが、Fargateの方が管理が容易です。
  4. クラスターを設定:
    • クラスター名: 例: my-nextjs-cluster
    • ネットワーキング:
      • VPC: ステップ1で作成したVPCを選択します。
      • サブネット: ステップ1で作成したプライベートサブネットを2つ以上選択します。
    • CloudWatch Container Insights: 必要に応じて有効化します。
  5. 作成」をクリックします。

4. ECRの作成

  1. Elastic Container Registry (ECR) サービスを開きます。
  2. リポジトリ」を選択し、「リポジトリを作成」をクリックします。
  3. リポジトリの設定:
    • 可視性設定: プライベート
    • リポジトリ名: 例: my-nextjs-app
    • タグのイミュータビリティ: 無効 または 有効(推奨は 有効 ですが、開発中は 無効 の方が便利かもしれません)
    • イメージスキャン設定: 「プッシュ時にスキャン」を 有効 にすることを推奨します。
    • KMS暗号化: 必要に応じて設定します。
  4. リポジトリを作成」をクリックします。
  5. 作成されたリポジトリのURI(例: アカウントID.dkr.ecr.リージョン.amazonaws.com/my-nextjs-app)を控えておきます。

5. Dockerで起動するNext.jsアプリの作成

5.1. Next.jsアプリケーションの準備

既存のNext.jsアプリケーションがある場合はそれを使用します。ない場合は、簡単なアプリケーションを作成します。

npx create-next-app@latest my-nextjs-app
cd my-nextjs-app

5.2. Next.jsの本番用ビルドするDockerfile作成

プロジェクトのルートディレクトリに Dockerfile という名前のファイルを作成し、以下の内容を記述します。

  • node:18-alpine のバージョンは、お使いのNext.jsやNode.jsのバージョンに合わせて調整してください。

  • Next.jsのStandalone Output機能を利用すると、より軽量なイメージを作成できます。その場合はDockerfileの内容が変わります。

    FROM node:18-alpine AS alpine
    
    FROM alpine AS base
    
    FROM base AS builder
    
    COPY package.json package-lock.json ./
    
    RUN npm install
    
    WORKDIR /app
    COPY . .
    
    RUN npm run build
    
    
    # Add lockfile and package.json's of isolated subworkspace
    FROM base AS installer
    
    
    WORKDIR /app
    
    FROM alpine AS runner
    WORKDIR /app
    
    # Don't run production as root
    RUN addgroup --system --gid 1001 nodejs
    RUN adduser --system --uid 1001 nextjs
    USER nextjs
    
    
    # Automatically leverage output traces to reduce image size
    # https://nextjs.org/docs/advanced-features/  output-file-tracing
    COPY --from=builder --chown=nextjs:nodejs /app/.next/  standalone ./
    COPY --from=builder --chown=nextjs:nodejs /app/.next/  static ./.next/static
    COPY --from=builder --chown=nextjs:nodejs /app/public ./  public
    
    CMD ["node", "server.js"]
    

    この場合、next.config.js に以下を追加する必要があります。

    /** @type {import('next').NextConfig} */
    const nextConfig = {
      output: 'standalone',
    }
    
    module.exports = nextConfig
    

6. ECRにイメージをプッシュ

  1. AWS CLIでECRにログインします。 ターミナルで以下のコマンドを実行します。アカウントIDリージョンはご自身のものに置き換えてください。

    aws ecr get-login-password --region リージョン | docker login --username AWS --password-stdin アカウントID.dkr.ecr.リージョン.amazonaws.com
    

    成功すると "Login Succeeded" と表示されます。

  2. Dockerイメージをビルドします。 Dockerfile があるディレクトリで以下のコマンドを実行します。リポジトリURIはステップ4で控えたものです。

    docker build -t リポジトリURI:latest .
    

    例: docker build -t アカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/my-nextjs-app:latest .

  3. DockerイメージをECRにプッシュします。

    docker push リポジトリURI:latest
    

    例: docker push アカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/my-nextjs-app:latest

7. ECSのタスク定義の作成

  1. ECSサービスのナビゲーションペインから「タスク定義」を選択し、「新しいタスク定義の作成」をクリックします。
  2. 起動タイプの互換性の選択: 「AWS Fargate」を選択します。
  3. タスク定義を設定:
    • タスク定義ファミリー: 例: my-nextjs-task
    • ネットワークモード: awsvpc (Fargateでは必須)
    • タスク実行IAMロール:
      • 初めての場合は「新しいロールの作成」を選択します。ECSがECRからイメージをプルしたり、CloudWatch Logsにログを送信したりするための権限 (AmazonECSTaskExecutionRolePolicy) がアタッチされたロールが作成されます。
      • 既存の適切なロールがあればそれを選択します。
    • オペレーティングシステムファミリー: Linux
    • タスクサイズ:
      • タスクCPU (vCPU): 例: 0.5 vCPU
      • タスクメモリ (GB): 例: 1 GB
      • Next.jsアプリケーションの規模に応じて調整してください。
  4. コンテナの定義:
    • コンテナの追加」をクリックします。
    • 標準:
      • コンテナ名: 例: my-nextjs-container
      • イメージ: ステップ6でECRにプッシュしたイメージのURI(例: アカウントID.dkr.ecr.リージョン.amazonaws.com/my-nextjs-app:latest)を入力します。
      • プライベートリポジトリ認証: 無効(タスク実行ロールで認証されるため)
      • メモリ制限:
        • ソフト制限: タスクメモリより小さい値を設定(例: 768 MiB)。
        • ハード制限: タスクメモリと同じか、それより小さい値を設定(例: 1024 MiB)。
      • ポートマッピング:
        • コンテナポート: 3000 (DockerfileでEXPOSEしたポート、Next.jsがリッスンするポート)
        • プロトコル: tcp
        • ホストポートはFargateのawsvpcモードでは指定不要です。
        • App Protocol: HTTP (ALBがgRPCなど特定のプロトコルを想定しない場合)
      • ヘルスチェック: 必要に応じて設定します。
      • 環境変数: 必要に応じて設定します(例: NODE_ENV=production, PORT=3000 など)。
        • PORT=3000 はDockerfileやNext.jsの起動コマンドで指定されていれば不要です。
    • ログ記録:
      • Auto-configure CloudWatch Logs: 有効のままにしておくことを推奨します。
      • ロググループ: 例: /ecs/my-nextjs-task
      • ログストリームプレフィックス: 例: ecs
    • その他の設定は必要に応じて行い、「追加」をクリックします。
  5. ストレージ、モニタリングなど: 必要に応じて設定します。
  6. 作成」をクリックします。

8. ECSのサービス作成

  1. ECSサービスのナビゲーションペインから「クラスター」を選択し、ステップ3で作成したクラスター(例: my-nextjs-cluster)をクリックします。
  2. サービス」タブを開き、「作成」をクリックします。
  3. コンピューティング設定:
    • 起動タイプ: FARGATE
    • プラットフォームバージョン: LATEST を選択します。
  4. デプロイ設定:
    • アプリケーションタイプ: サービス
    • タスク定義:
      • ファミリー: ステップ7で作成したタスク定義ファミリー(例: my-nextjs-task)を選択します。
      • リビジョン: latest または特定のリビジョンを選択します。
    • サービス名: 例: my-nextjs-service
    • サービスタイプ: レプリカ
    • 必要なタスク数: 実行したいタスクの数(例: 2 で高可用性を確保)。
  5. デプロイオプション:
    • 最小ヘルス率: 例: 50
    • 最大率: 例: 200
    • デプロイ戦略はローリングアップデートがデフォルトです。
  6. ネットワーキング:
    • VPC: ステップ1で作成したVPCが選択されていることを確認します。
    • サブネット: ステップ1で作成したプライベートサブネットを2つ以上選択します(クラスター作成時に選択したものと同じ)。
    • セキュリティグループ: ステップ2.2で作成したECS用のセキュリティグループ(例: my-ecs-sg)を選択します。
    • パブリックIP: 無効 (ALB経由でアクセスするため)。
  7. ロードバランシング:
    • ロードバランサーの種類: Application Load Balancer を選択します。
    • 新しいロードバランサーを作成する:
      • ロードバランサー名: 例: my-nextjs-alb
    • コンテナを選択してロードバランスする:
      • コンテナ名:ポート: ステップ7で定義したコンテナとポート(例: my-nextjs-container:3000:tcp)を選択します。
    • リスナー:
      • プロトコル: HTTP
      • ポート: 80
    • ターゲットグループ:
      • 新しいターゲットグループを作成する:
        • ターゲットグループ名: 例: my-nextjs-tg
        • ターゲットタイプ: IPアドレス (Fargateの場合)
        • プロトコル: HTTP
        • パスパターン: / (またはアプリケーションのルートパス)
        • ヘルスチェックパス: アプリケーションのヘルスチェック用エンドポイント(例: /api/health など。なければ / でも可)
        • ヘルスチェックプロトコル: HTTP
        • その他ヘルスチェック設定(ポート: トラフィックポート、間隔、タイムアウトなど)を適切に設定します。
  8. サービス検出(オプション): 必要に応じて設定します。
  9. サービス Auto Scaling(オプション): 必要に応じて設定します。
  10. 作成」をクリックします。サービスの作成には数分かかることがあります。

ECRからのイメージ取得に失敗する場合の確認点

  • タスク実行IAMロールの権限:
    • ECSタスク実行ロールに AmazonECSTaskExecutionRolePolicy がアタッチされているか確認します。このポリシーにはECRからのイメージプル権限 (ecr:GetAuthorizationToken, ecr:BatchCheckLayerAvailability, ecr:GetDownloadUrlForLayer, ecr:BatchGetImage) が含まれています。
  • NATゲートウェイまたはVPCエンドポイント:
    • ECSタスクがプライベートサブネットに配置されている場合、ECRリポジトリにアクセスするためにインターネットへのアウトバウンド接続が必要です。
      • NATゲートウェイ: プライベートサブネットのルートテーブルがNATゲートウェイを指していることを確認します。
      • VPCエンドポイント (AWS PrivateLink):
        • ECR用インターフェイスエンドポイント (com.amazonaws.region.ecr.api, com.amazonaws.region.ecr.dkr) を作成し、VPC内のプライベートサブネットに関連付けます。これにより、トラフィックはAWSネットワーク内で完結し、NATゲートウェイが不要になる場合があります(ECRアクセスに関してのみ)。
        • CloudWatch Logs用インターフェイスエンドポイント (com.amazonaws.region.logs) も同様に設定すると、ログ送信もプライベート接続になります。
        • S3ゲートウェイエンドポイントも必要です(DockerイメージレイヤーはS3に保存されるため)。通常、VPC作成時にデフォルトで作成されます。
    • セキュリティグループのアウトバウンドルールが、ECRやS3へのHTTPS (ポート443) トラフィックを許可していることを確認します(デフォルトでは全許可なので問題ないことが多いです)。

9. ALBのDNS名でWebアプリにアクセス

  1. EC2サービスのナビゲーションペインから「ロードバランサー」を選択します。

  2. ステップ8で作成されたALB(例: my-nextjs-alb)を選択します。

  3. 説明」タブにある「DNS名」(例: my-nextjs-alb-xxxxxxxxxx.リージョン.elb.amazonaws.com)をコピーします。

  4. WebブラウザのアドレスバーにこのDNS名を貼り付けてアクセスし、Next.jsアプリケーションが表示されることを確認します。

    • 最初はターゲットグループのヘルスチェックが完了するまで時間がかかることがあります。
    • アクセスできない場合は、以下の点を確認してください。
      • ALBのセキュリティグループ(my-alb-sg)のインバウンドルールでHTTP (80) が許可されているか。
      • ECSタスクのセキュリティグループ(my-ecs-sg)のインバウンドルールでALBのセキュリティグループからのTCP (3000) が許可されているか。
      • ターゲットグループのヘルスチェックが成功しているか。ECSサービスのイベントログやCloudWatch Logsでコンテナのエラーを確認します。
      • Next.jsアプリケーションがコンテナ内でポート3000で正しく起動しているか。