🍘

React*S3*CloudFrontホスティングとGitHubActionsを使用したCI/CDパイプラインで効率化

2022/08/04に公開

始めに

今業務でCI周り触ることが多いので、Reactで開発するときのホスティングもGitHubActionsを活用して効率的にできないかということで触ってみました。

フロントエンドエンジニアではなく、不足等あるかもですが見てってください。

記事の内容

Reactで作成したアプリケーションをS3*CloudFrontで配信するための基盤構築します。
GitHubActionsでテスト→ビルド→デプロイ→キャッシュクリアをワークフローで流すことにより、より便利に開発できますっていうのを紹介します
また、開発段階で外部に公開したくない場合にIP制限をかける方法も併せて紹介します。

全体のフロー

GitHubにプッシュされたことをトリガーにGitHubActioinsでワークフローをキックします。
Slack通知の発行も組み込んで状況をSlackのbotで受け取れるようにしておきます。

flow

構築手順

1. Reactプロジェクトの作成とリポジトリ作成

みなさんよく行う部分だと思うので省略します。
CRAを使用して作成、リポジトリもGitHubで作成してください。

2. S3バケット作成

CloudFront経由での公開のみ許可するので、S3の静的サイトホスティング機能を使用しません。

  1. AWSコンソールにログインしてS3画面へ移動
  2. バケット作成ボタンを押下して設定画面へ進む
  3. バケット名を任意の名前でつける
  4. 設定はデフォルトで作成する
  5. CloudFrontで公開するテスト用のindex.htmlをバケット直下へローカルからアップロードしておく。

3. CloudFrontディストリビューションを作成

CloudFrontディストリビューションを作成します。OAIを持つクラウドフロントからのみアクセスできるよう設定を進めます。

  1. AWSコンソールにログインしてCloudFront画面へ移動
  2. ディストリビューションを作成ボタンを押下して作成画面へ進む
  3. オリジンドメインより作成した対象のバケットを選択する
  4. S3バケットアクセスよりOAIを設定するようにしてください。オリジンアクセスアイデンティティは作成して、バケットポリシーは自動更新にチェックをする。
  5. ビューワー、ビューワープロトコルポリシーはRedirect HTTP to HTTPSを選択する
  6. キャッシュキーとオリジンリクエストより、キャッシュポリシーはCaching Optimizedを選択する(キャッシュが24h-の設定です。カスタムしたい時はポリシーを作成しましょう)
  7. 代替ドメイン・SSL証明証設定は独自ドメインを使用する方は設定してください。(今回は省略します)
  8. デフォルトルートオブジェクトはindex.htmlを設定します。アクセスした時にindex.htmlを返すようになります。
  9. 標準ログ記録はオンにしておくことをお勧めします。オンにしてバケット選択&プレフィクス入力をしてください。
  10. 作成ボタンを押して作成してください(エラー等で不足がある場合はお手数ですがご対応ください。)

4. デプロイ用ワークフローファイルの作成

name: Build And Deploy
on:
  push:
    branches:
      - master

jobs:
  deploy:
    name: Build & Deploy
    runs-on: ubuntu-20.04
    timeout-minutes: 5

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup Node
        uses: actions/setup-node@v2
        with:
          node-version: 16.16.0
	  cache: npm

      - name: Package Install
        run: npm install

      - name: Build
        run: npm run build

      - 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: ${{ secrets.AWS_REGION }}

      - name: Deploy to S3
        run: aws s3 sync ./build s3://${{ secrets.AWS_S3_BUCKET_NAME }}

      - name: CloudFront Cache Clear
        run: CFID=$(aws cloudfront list-distributions --query "DistributionList.Items[].{Id:Id,Origin:Origins.Items[0].DomainName}[?contains(Origin,'${{ secrets.AWS_S3_BUCKET_NAME }}.s3')] | [0].Id" | sed 's/"//g')
          echo "aws cloudfront create-invalidation ${CFID}"
          aws cloudfront create-invalidation --distribution-id ${CFID} --paths "/*"

5. IP制限をかける

  1. CloudFront > サイドメニューより関数のページを開く
  2. 関数を作成ボタンを押下
  3. 許可するIPアドレスを追記して、以下のコードを関数コードとして使用する。
function handler(event) {
    var request = event.request;
    var clientIP = event.viewer.ip;

    // アクセス許可するIPを設定
    var IP_WHITE_LIST = [
     'xxx.xxx.xxx.xxx',
     'xxxx:xxxx:xxxx:xxxx:'
    ];

    // IPがIPV6で受け取っていたので部分一致で対応しました。
    // V4であれば完全一致で大丈夫です
    var flg = IP_WHITE_LIST.some(whiteListIp => {
        return clientIP.indexOf(whiteListIp) == 0? true: false;
    })

    if (flg) {
        return request;
    } else {
        var response = {
            statusCode: 403,
            statusDescription: 'Forbidden',
        }
        return response;
    }
}
  1. 変更後に発行することによって1-5分後に反映されると思います。
  2. ホワイトリストが有効になっているか実機等でご確認ください。

最後に

割とこのCICDパイプラインは気に入ってます。
テストであったり、Slackの通知先の切り替えや、バケットの切り替えなど各プロジェクト毎にカスタムして導入していただけるといいのかなと思います。

IP制限のところは他の方法もありますが、関数書くだけの簡易的でお安い、CloudFrontの関数機能を使用しました。

他にReactアプリのホスティングで良い方法があれば気になる、、、

Discussion