1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

とりあえずReactをIaCを使ってCloudFrontにデプロイする

Last updated at Posted at 2024-12-31

はじめに

Reactアプリのデプロイ、CloudFormationでサクッと構築したい!という方に向けて、最小限の手順でデプロイする方法を紹介します。S3でホスティングして、CloudFrontで配信するシンプルな構成です。

必要なもの

  • AWS CLI
  • make コマンド
  • 以下のファイル:
    • CloudFormationテンプレート(cloudfront.yaml)
    • Makefile

デプロイ手順

1. デプロイ用ファイルを用意

cloudfront.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudFormation template for React App deployment with S3 and CloudFront'

Parameters:
  Environment:
    Type: String
    Default: dev
    AllowedValues:
      - dev
      - stg
      - prod
    Description: Environment name

Resources:
  S3Bucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    Properties:
      BucketName: !Sub react-app-${Environment}-${AWS::AccountId}-${AWS::Region}
      PublicAccessBlockConfiguration:
        BlockPublicAcls: false
        BlockPublicPolicy: true
        IgnorePublicAcls: false
        RestrictPublicBuckets: true
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      VersioningConfiguration:
        Status: Enabled
      WebsiteConfiguration:
        IndexDocument: index.html
        ErrorDocument: index.html
      OwnershipControls:
        Rules:
          - ObjectOwnership: BucketOwnerPreferred
      CorsConfiguration:
        CorsRules:
          - AllowedHeaders: ['*']
            AllowedMethods: [GET, HEAD]
            AllowedOrigins: ['*']
            MaxAge: 3600

  S3BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref S3Bucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: cloudfront.amazonaws.com
            Action: s3:GetObject
            Resource: !Sub ${S3Bucket.Arn}/*
            Condition:
              StringEquals:
                AWS:SourceArn: !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}

  CloudFrontOriginAccessControl:
    Type: AWS::CloudFront::OriginAccessControl
    Properties:
      OriginAccessControlConfig:
        Name: !Sub ${AWS::StackName}-OAC
        Description: Origin Access Control for S3
        SigningBehavior: always
        SigningProtocol: sigv4
        OriginAccessControlOriginType: s3

  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Origins:
          - DomainName: !Sub ${S3Bucket.RegionalDomainName}
            Id: S3Origin
            S3OriginConfig:
              OriginAccessIdentity: ''
            OriginAccessControlId: !Ref CloudFrontOriginAccessControl
        Enabled: true
        DefaultRootObject: index.html
        DefaultCacheBehavior:
          AllowedMethods:
            - GET
            - HEAD
            - OPTIONS
          CachedMethods:
            - GET
            - HEAD
            - OPTIONS
          TargetOriginId: S3Origin
          ForwardedValues:
            QueryString: false
            Cookies:
              Forward: none
          ViewerProtocolPolicy: redirect-to-https
          Compress: true
          DefaultTTL: 86400    # 1 day
          MinTTL: 0
          MaxTTL: 31536000    # 1 year
        CustomErrorResponses:
          - ErrorCode: 403
            ResponseCode: 200
            ResponsePagePath: /index.html
          - ErrorCode: 404
            ResponseCode: 200
            ResponsePagePath: /index.html
        ViewerCertificate:
          CloudFrontDefaultCertificate: true
        PriceClass: PriceClass_100
        HttpVersion: http2
        IPV6Enabled: true

Outputs:
  BucketName:
    Description: Name of S3 bucket to hold website content
    Value: !Ref S3Bucket

  BucketDomainName:
    Description: Domain name of S3 bucket
    Value: !GetAtt S3Bucket.DomainName

  CloudFrontDistributionId:
    Description: ID of CloudFront distribution
    Value: !Ref CloudFrontDistribution

  CloudFrontDomainName:
    Description: Domain name of CloudFront distribution
    Value: !GetAtt CloudFrontDistribution.DomainName

  WebsiteURL:
    Description: URL for website hosted on S3
    Value: !Sub https://${CloudFrontDistribution.DomainName}

まず、最小限必要なファイルを配置します。

your-project/
  ├── templates/
  │   └── cloudfront.yaml  # CloudFormationテンプレート
  ├── ui/
  │   └── chat-app/
  │       └── dist/        # Reactのビルドファイル
  └── Makefile

2. デプロイコマンドを実行

コマンドを実行させるために下のmakefileを作成して下さい。

# 環境変数
REGION ?= us-east-1
ENV ?= dev
STACK_NAME ?= react-app-stack
TEMPLATE_PATH ?= templates/cloudfront.yaml
DIST_PATH ?= ui/chat-app/dist

# バケット名を取得するヘルパー関数
define get_bucket_name
$(shell aws cloudformation describe-stacks \
    --stack-name $(STACK_NAME) \
    --region $(REGION) \
    --query 'Stacks[0].Outputs[?OutputKey==`BucketName`].OutputValue' \
    --output text)
endef

# UIデプロイ
.PHONY: ui-deploy
ui-deploy:
	aws cloudformation create-stack \
		--stack-name $(STACK_NAME) \
		--template-body file://$(TEMPLATE_PATH) \
		--region $(REGION) \
		--parameters ParameterKey=Environment,ParameterValue=$(ENV)

# UIスタック更新
.PHONY: ui-update
ui-update:
	aws cloudformation update-stack \
		--stack-name $(STACK_NAME) \
		--template-body file://$(TEMPLATE_PATH) \
		--region $(REGION) \
		--parameters ParameterKey=Environment,ParameterValue=$(ENV)

# UIスタック削除
.PHONY: ui-delete
ui-delete:
	aws cloudformation delete-stack \
		--stack-name $(STACK_NAME) \
		--region $(REGION)

# UIスタック状態確認
.PHONY: ui-status
ui-status:
	aws cloudformation describe-stacks \
		--stack-name $(STACK_NAME) \
		--region $(REGION)

# UIバケット名を取得
.PHONY: ui-get-bucket
ui-get-bucket:
	@echo "Retrieving bucket name..."
	@echo "Bucket name: $(call get_bucket_name)"

# UIS3同期
.PHONY: ui-sync
ui-sync:
	@echo "Syncing UI files to bucket..."
	aws s3 sync $(DIST_PATH) s3://$(call get_bucket_name)

# UIキャッシュ無効化
.PHONY: ui-invalidate
ui-invalidate:
	@echo "Invalidating CloudFront cache..."
	aws cloudfront create-invalidation \
		--distribution-id $$(aws cloudformation describe-stacks \
			--stack-name $(STACK_NAME) \
			--region $(REGION) \
			--query 'Stacks[0].Outputs[?OutputKey==`CloudFrontDistributionId`].OutputValue' \
			--output text) \
		--paths "/*"

# ヘルプ
.PHONY: help
help:
	@echo "UI関連コマンド:"
	@echo "  make ui-deploy            - UIのCloudFormationスタックをデプロイ"
	@echo "  make ui-deploy ENV=prod   - UI本番環境にデプロイ"
	@echo "  make ui-update           - UIスタックを更新"
	@echo "  make ui-delete           - UIスタックを削除"
	@echo "  make ui-status           - UIスタックの状態を確認"
	@echo "  make ui-sync             - UIビルドファイルをS3に同期"
	@echo "  make ui-invalidate       - CloudFrontのキャッシュを無効化"
	@echo ""
	@echo "環境変数:"
	@echo "  REGION         - AWSリージョン (デフォルト: us-east-1)"
	@echo "  ENV            - 環境名 (デフォルト: dev)"
	@echo "  STACK_NAME     - スタック名 (デフォルト: react-app-stack)"
	@echo "  TEMPLATE_PATH  - テンプレートパス (デフォルト: templates/cloudfront.yaml)"
	@echo "  DIST_PATH      - ビルドファイルパス (デフォルト: ui/chat-app/dist)"

たった3ステップでデプロイ完了します:

# 1. インフラをデプロイ
make ui-deploy

# 2. Reactのビルドファイルをアップロード
make ui-sync

# 3. CloudFrontのキャッシュを無効化
make ui-invalidate

以上です!CloudFormationのOutputsに表示されるURLにアクセスすれば、アプリが表示されます。

インフラの中身

CloudFormationテンプレートで作成されるリソースは以下の通りです:

  • S3バケット

    • Reactアプリのホスティング用
    • 暗号化有効
    • バージョニング有効
  • CloudFront

    • HTTPSリダイレクト
    • キャッシュ設定(デフォルト1日)
    • SPA対応(404/403を200にリダイレクト)

便利な運用コマンド

# スタックの状態確認
make ui-status

# バケット名の確認
make ui-get-bucket

# 環境の指定(dev/stg/prod)
make ui-deploy ENV=prod

# スタックの削除
make ui-delete

カスタマイズのポイント

  • 環境変数で設定変更が可能

    REGION       # AWSリージョン
    ENV          # 環境名(dev/stg/prod)
    STACK_NAME   # スタック名
    
  • CloudFormationテンプレートの主な設定箇所

    • バケット名のフォーマット
    • CloudFrontのキャッシュ設定
    • CORSの設定

まとめ

これで最小構成のデプロイ環境の構築は完了です。あとは必要に応じて:

  • Route 53でドメイン設定
  • WAFでセキュリティ設定
  • CodePipelineで自動デプロイ

などを追加していけば、本番環境としても使える構成になります。

CloudFormationを使うことで、環境の再現性が担保され、複数環境の管理も容易になります。まずはこのベース構成から始めて、必要な機能を追加していくアプローチがおすすめです。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?