ReactアプリをDocker + AWS S3 + CloudFrontで配信する方法

はじめに

モダンなWebアプリケーションの開発において、Reactは最も人気のあるフロントエンドフレームワークの一つです。しかし、開発したアプリケーションを効率的に本番環境に展開するためには、適切なデプロイ戦略が必要です。この記事では、Docker、AWS S3、CloudFrontを組み合わせてReactアプリケーションを効率的に配信する方法について解説します。

この構成の主なメリットは以下の通りです:

  • 開発環境の一貫性: Dockerを使用することで、開発環境と本番環境の差異を最小限に抑えられます
  • 高可用性: AWS S3の高い耐久性とCloudFrontのグローバルCDNによる高速配信
  • コスト効率: 静的ファイルのホスティングは非常に低コスト
  • スケーラビリティ: トラフィックが増加しても自動的にスケール

前提条件

  • AWS アカウント
  • Docker がインストールされた環境
  • Node.js と npm
  • AWS CLI(設定済み)
  • 基本的な React アプリケーション

構成の概要

この記事で構築するシステムの全体像は以下のようになります:

  1. Docker: Reactアプリをビルドする環境を提供
  2. AWS S3: ビルドされた静的ファイルを保存
  3. CloudFront: コンテンツをCDN経由で高速配信
  4. Route 53(オプション): カスタムドメインの設定

実装手順

1. Docker環境の準備

まず、ReactアプリケーションをビルドするためのDockerfile を作成します。

# ビルド用のステージ
FROM node:16-alpine as build

WORKDIR /app

# 依存関係のインストール
COPY package.json package-lock.json ./
RUN npm ci

# ソースコードのコピーとビルド
COPY . ./
RUN npm run build

# 配信用の軽量ステージ
FROM nginx:alpine

# ビルドされたファイルをnginxのコンテンツディレクトリにコピー
COPY --from=build /app/build /usr/share/nginx/html

# nginxの設定(SPAのためのルーティング設定)
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

SPAのルーティングをサポートするための nginx.conf も作成します:

server {
    listen 80;
    
    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html;
    }
}

2. Dockerイメージのビルドとテスト

以下のコマンドでDockerイメージをビルドします:

docker build -t react-app .

ローカルでテストするには:

docker run -p 8080:80 react-app

これで http://localhost:8080 にアクセスして動作確認ができます。

3. AWS S3バケットの作成

AWS S3バケットを作成し、静的ウェブサイトホスティングを有効にします。

# S3バケットの作成
aws s3 mb s3://my-react-app-bucket

# 静的ウェブサイトホスティングの設定
aws s3 website s3://my-react-app-bucket --index-document index.html --error-document index.html

# パブリックアクセスの設定
aws s3api put-bucket-policy --bucket my-react-app-bucket --policy '{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-react-app-bucket/*"
    }
  ]
}'

4. ビルドされたファイルのアップロード

Dockerコンテナからビルドファイルを抽出してS3にアップロードします:

# 一時的にコンテナを実行して、ビルドファイルを抽出
CONTAINER_ID=$(docker create react-app)
mkdir -p ./build
docker cp $CONTAINER_ID:/usr/share/nginx/html ./build
docker rm $CONTAINER_ID

# S3にアップロード
aws s3 sync ./build/html s3://my-react-app-bucket --delete

または、CI/CDパイプラインを使用する場合は、このプロセスを自動化するスクリプトを作成できます。

5. CloudFrontディストリビューションの設定

AWSコンソールまたはAWS CLIを使用してCloudFrontディストリビューションを作成します:

aws cloudfront create-distribution \
    --origin-domain-name my-react-app-bucket.s3.amazonaws.com \
    --default-root-object index.html \
    --error-responses Quantity=1,Items=[{ErrorCode=404,ResponsePagePath=/index.html,ResponseCode=200,ErrorCachingMinTTL=10}]

注意: 実際のCloudFront設定はより複雑になるため、上記のコマンドは簡略化されています。AWS管理コンソールを使用して、より詳細な設定を行うことをお勧めします。

S3バケットから直接ファイルを配信するのではなく、CloudFrontを経由することで以下のメリットがあります:

  • グローバルなエッジロケーションによる高速配信
  • HTTPSの対応
  • カスタムドメインの利用
  • WAF(Web Application Firewall)による保護

6. カスタムドメインの設定(オプション)

既にRoute 53でドメインを管理している場合は、CloudFrontディストリビューションにカスタムドメインを設定できます:

  1. AWS Certificate Manager(ACM)でSSL証明書を取得
  2. CloudFrontディストリビューションに証明書を関連付け
  3. Route 53でDNSレコードを作成(CloudFrontディストリビューションを指すように)

7. CI/CDパイプラインの構築(オプション)

GitHub ActionsやAWS CodePipelineなどを使用して、コードの変更が自動的にデプロイされる仕組みを構築すると便利です。

GitHubリポジトリのワークフローの例(.github/workflows/deploy.yml):

name: Deploy React App

on:
  push:
    branches: [ main ]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Set up Docker
      uses: docker/setup-buildx-action@v1
      
    - name: Build Docker image
      run: docker build -t react-app .
      
    - name: Extract build files
      run: |
        CONTAINER_ID=$(docker create react-app)
        mkdir -p ./build
        docker cp $CONTAINER_ID:/usr/share/nginx/html ./build
        docker rm $CONTAINER_ID
      
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ap-northeast-1
    
    - name: Deploy to S3
      run: aws s3 sync ./build/html s3://my-react-app-bucket --delete
      
    - name: Invalidate CloudFront cache
      run: |
        aws cloudfront create-invalidation \
          --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} \
          --paths "/*"

キャッシュ戦略とデプロイの最適化

キャッシュコントロール

効率的なキャッシュ戦略の実装は非常に重要です:

# index.htmlは短いキャッシュ期間に設定
aws s3 cp ./build/html/index.html s3://my-react-app-bucket/index.html \
  --cache-control "max-age=60"

# 静的アセット(ハッシュ値を含むファイル名)は長期キャッシュに設定
aws s3 sync ./build/html/static s3://my-react-app-bucket/static \
  --cache-control "max-age=31536000" \
  --exclude "*" \
  --include "*.js" \
  --include "*.css"

CloudFrontのキャッシュ無効化

新しいバージョンをデプロイした後、CloudFrontのキャッシュを無効化する必要があります:

aws cloudfront create-invalidation \
  --distribution-id DISTRIBUTION_ID \
  --paths "/*"

トラブルシューティング

CORS設定

API呼び出しで問題が発生した場合は、S3バケットのCORS設定を確認してください:

aws s3api put-bucket-cors --bucket my-react-app-bucket --cors-configuration '{
  "CORSRules": [
    {
      "AllowedOrigins": ["*"],
      "AllowedMethods": ["GET"],
      "AllowedHeaders": ["*"]
    }
  ]
}'

SPAルーティングの問題

CloudFrontでSPAのルーティングを適切に処理するために、404エラーをindex.htmlにリダイレクトする設定が必要です。CloudFrontディストリビューションのエラーページ設定で、404エラーを/index.htmlにリダイレクトするように設定します。

本番環境の考慮事項

セキュリティ

  • バケットポリシーを最小権限の原則に基づいて設定
  • CloudFrontとS3の間のOAI(Origin Access Identity)を使用
  • WAF(Web Application Firewall)の導入を検討

コスト最適化

  • S3ライフサイクルポリシーの設定
  • CloudFrontのPrice Classの適切な選択
  • リザーブドキャパシティの検討(大規模なトラフィックの場合)

パフォーマンス最適化

  • Reactアプリのコード分割
  • 画像の最適化
  • Gzipなどの圧縮設定

まとめ

この記事では、Docker、AWS S3、CloudFrontを組み合わせたReactアプリケーションの効率的なデプロイ方法について解説しました。この方法を使用することで、高可用性と低コストを両立させた堅牢なデプロイメントパイプラインを構築できます。

また、CI/CDパイプラインを導入することで、開発からデプロイまでのプロセスを自動化し、より高速な開発サイクルを実現できます。

最後に、本番環境でのセキュリティやパフォーマンスの最適化についても忘れずに検討しましょう。これらはエンドユーザーのエクスペリエンスに直接影響する重要な要素です。

コメント

タイトルとURLをコピーしました