2
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?

More than 1 year has passed since last update.

AWS CDKでReactをCloudFront+S3にデプロイする

Last updated at Posted at 2021-08-16

この記事は AWS CDK v1 を使用しています。
2022/09/03 現在は AWS CDK v2 がリリースされており、内容が古くなっている可能性があります。

本記事でやること

  • AWS 上に CloudFront + S3 の構成 で静的ウェブサイトをホスティングします
  • create-react-app で作成した React のアプリケーションを題材にします
  • AWS CDK を使用して、CloudFront や S3 をプロビジョニングし、アプリケーションのデプロイまで行います
    AWS CDK を使用すると、アプリの S3 へのアップロードから CloudFront の Invalidation まで、デプロイ作業がすべて自動化できます!すごい!

cloudfront.png

いいからコードを!という方はこちらをご参照ください。

手順

AWS のアクセスキーの設定

aws コマンドを使用して、アクセスキーなどの AWS のアクセス情報を設定しておきます。
これにより、AWS CDK が AWS のサービスへアクセスできるようになります。
アクセスキーは AWS マネジメントコンソールの IAM から発行できます。

% aws configure --profile AWSプロファイル名
AWS Access Key ID [None]: XXXXXXXXXXXXXXXXXXXX # アクセスキーを指定
AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXX # シークレットアクセスキーを指定
Default region name [None]: ap-northeast-1 # お好みで。ここでは東京リージョンを指定
Default output format [None]: json

ここで指定した AWS プロファイル名は後程利用します。

React のアプリケーション作成

デプロイ対象の React アプリケーションを作成します。
こちらに変更を加えていきます。

% yarn create react-app --template typescript react-aws-cdk
% cd react-aws-cdk

依存ライブラリのインストール

必要なライブラリをインストールします。

yarn add -D \
  @aws-cdk/aws-cloudfront \
  @aws-cdk/aws-cloudfront-origins \
  @aws-cdk/aws-s3 \
  @aws-cdk/aws-s3-deployment \
  @aws-cdk/core \
  aws-cdk \
  ts-node \
  cross-env

package.json に以下のような差分が生じます。
@aws-cdk/* のパッケージはバージョンが同じでないと不整合を起こすことがあります。
この場合は大丈夫ですが、あとから依存ライブラリを追加するときは要注意です。

package.json
  "devDependencies": {
    "@aws-cdk/aws-cloudfront": "^1.118.0",
    "@aws-cdk/aws-cloudfront-origins": "^1.118.0",
    "@aws-cdk/aws-s3": "^1.118.0",
    "@aws-cdk/aws-s3-deployment": "^1.118.0",
    "@aws-cdk/core": "^1.118.0",
    "aws-cdk": "^1.118.0",
    "cross-env": "^7.0.3",
    "ts-node": "^10.2.0"
  }

AWS CDK のセットアップ

AWS CDK を使用するには、事前セットアップが必要です。
AWS CDK が使用する S3 バケットを作成するなどが行われるようです。

% yarn cdk bootstrap --profile AWSプロファイル名 aws://AWSアカウントID/リージョン名

# 例えば、以下のとき
# AWSプロファイル名: hoge
# AWSアカウントID: 999999999999
# リージョン名: ap-northeast-1
% yarn cdk bootstrap --profile hoge aws://999999999999/ap-northeast-1

AWS CDK のコードを作成

TypeScript の設定ファイルを作成します。
React と AWS CDK では異なる設定が必要なため、ここで作成しておきます。

./deployment/tsconfig.json
{
  "compilerOptions": {
    "target": "es2020",
    "moduleResolution": "node"
  }
}

app.ts は AWS CDK のエントリーポイントです。
AWS CloudFormation のスタック名を環境変数で指定できるようにしています。
作成するリソースの定義は後述の spa-stack.ts に記述していきます。

./deployment/app.ts
import * as cdk from "@aws-cdk/core";
import { exit } from "process";
import { SpaStack } from "./spa-stack";

const stackName = process.env["STACK_NAME"];
if (!stackName) {
  console.error("missing environment variable STACK_NAME");
  exit(1);
}

const app = new cdk.App();
new SpaStack(app, stackName, {});

spa-stack.ts に作成するリソースを定義します。
設定内容はコードのコメントを参照してください。

./deployment/spa-stack.ts
import * as cdk from "@aws-cdk/core";
import * as cloudfront from "@aws-cdk/aws-cloudfront";
import * as s3 from "@aws-cdk/aws-s3";
import * as s3deploy from "@aws-cdk/aws-s3-deployment";
import { Duration } from "@aws-cdk/core";
import * as origins from "@aws-cdk/aws-cloudfront-origins";

export type SpaStackProps = {};

export class SpaStack extends cdk.Stack {
  constructor(
    scope: cdk.Construct,
    id: string,
    props?: cdk.StackProps & SpaStackProps
  ) {
    super(scope, id, props);

    // 配信するコンテンツを配置する S3 バケット
    const originBucket = new s3.Bucket(this, "OriginBucket");

    // CloudFront のログを配置する S3 バケット
    const logBucket = new s3.Bucket(this, "LogBucket");

    const distribution = new cloudfront.Distribution(this, "Distribution", {
      defaultBehavior: {
        // 配信するコンテンツを指定する
        origin: new origins.S3Origin(originBucket),
        // HTTP でアクセスされたら HTTPS へリダイレクトする
        viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
      },
      // ログの出力先を設定する
      logBucket: logBucket,

      // ルートへのアクセスに対して返却するコンテンツを設定する
      defaultRootObject: "index.html",
      errorResponses: [
        // S3 に指定されたファイルが存在しないとき、S3 は 403 エラーを返すが、
        // CloudFront により エラーを index.html の返却に置き換える
        // (SPA では、どの画面についても、index.html により表示を行うため)
        // https://aws.amazon.com/jp/premiumsupport/knowledge-center/s3-website-cloudfront-error-403/
        {
          httpStatus: 403,
          responseHttpStatus: 200,
          responsePagePath: "/index.html",
          ttl: Duration.minutes(5),
        },
      ],

      // 日本を含む地域のエッジロケーションからコンテンツを配信する
      // https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/PriceClass.html
      priceClass: cloudfront.PriceClass.PRICE_CLASS_200,
    });

    // build ディレクトリを S3 にアップロードして、アプリケーションをデプロイする
    new s3deploy.BucketDeployment(this, "Deployment", {
      sources: [s3deploy.Source.asset("./build")],
      destinationBucket: originBucket,
      // CloudFront のキャッシュを削除し、コンテンツを最新化する
      distribution: distribution,
    });
  }
}

npm scripts にデプロイコマンドを追加し、実行する

package.json の scripts にデプロイ用のコマンドを追加します。
デプロイは cdk deploy コマンドであり、 オプション -a でエントリーポイントを指定します。
作成する CloudFormation スタック名は環境変数から取得することにしているので、コマンド内に指定しています。

package.json
   "start": "react-scripts start",
     "build": "react-scripts build",
     "test": "react-scripts test",
-    "eject": "react-scripts eject"
+    "eject": "react-scripts eject",
+    "deploy": "cross-env STACK_NAME=react-aws-cdk-dev cdk -a \"ts-node ./deployment/app.ts\" deploy"
   },

アプリケーションをビルドし、次にデプロイします。

% yarn build
% yarn deploy --profile AWSプロファイル名

デプロイ結果の確認

AWS マネジメントコンソールで CloudFront にアクセスし、URL を確認します。
deploy.png

おわりに

初めて AWS CDK を使いましたが、おもしろいです!
CloudFormation と比較すると…

  • プログラミング言語を使用するから、for も if も使い放題
    → 柔軟性・拡張性が高い
  • 各リソースのプロパティに AWS 様おすすめのデフォルト値が割り当てられている
    → 記述負荷が低く、意識せずともベストプラクティスに従える
  • s3-deployment などのリソース作成ではなく、ワークフローを担う機能を利用できる

今回は実施しませんでしたが、以下ができるとよりよそうです。

  • Lambda@Edge や CloudFront Functions で レスポンスに HTTP ヘッダーを付与する
  • ACM, Route53 と連携させて、ドメイン設定を行う

参考

作成にあたり公式ドキュメントの他、DevelopersIO様を参考にさせていただきました。
本記事では、比較的新しいクラスの cloudfront.Distribution を利用して、記述をブラッシュアップしました。
明示的に OAI (Origin Access Identity)を記述しなくても、しっかりと OAI が生成されていい感じです。

以上です。
ありがとうございました。

2
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
2
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?