🛠️

React(Vite)をGitHub ActionsでS3に自動デプロイ

に公開

はじめに

TreyLinkにてテックリードとして活動しているyh1110です。
React(Vite)で作成したWebアプリケーションを、GitHub Actionsを使ってS3へ自動デプロイするフローを構築したので、備忘録として記録しておきます。

前提

今回の記事では自動デプロイ(CD)のみ扱います。
実際のプロジェクトではPR作成時にCIを組んでフォーマットの成形やlinterを走らせたり、テストを実施しているのですが、詳細は別記事にて改めてまとめておきます。

準備

以下が必要になります。

  • GitHubリポジトリ
  • AWSアカウントとS3バケット、Cloud Front設定
  • IAMロールおよび権限設定

1. IAMロールの作成とOIDC設定

GitHub Actionsで安全にAWSへアクセスするため、OpenID Connect(OIDC)を使って認証します。
OIDCを利用することで特定リポジトリからのアクセスに限定したり、短命なクレデンシャルの仕組みを用いることができるため、仮にキーが漏洩しても影響範囲を狭められるといったメリットがあります。
IAMロールのアクセスIDとアクセスキーを直書きせずに済む仕組みはありがたいですね。

実際GitHubの公式でも、AWSアクセスを伴うActionsのワークフローを組む際のセキュリティ強化の方法として以下にまとめられています。
https://docs.github.com/ja/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-aws

OIDCの設定は以下です

  1. AWSマネジメントコンソールでIAMサービスを開きます。
  2. Identity Providerを作成し、GitHubのOIDCを設定します。
  3. IAMロールを作成し、信頼関係をGitHubのOIDCプロバイダーに設定します。
  4. 必要なポリシーとして、AmazonS3FullAccess(または適切な権限)を設定します。

詳しくは以下を参考にしてください

AWS公式
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html

分かりやすいQiitaの記事
https://qiita.com/Mouflon_127000/items/d4c2d942d552b0051cb1

2. GitHub Actionsの設定

私が携わっているチームでは、リポジトリの.github/workflowsdeploy-to-s3.ymlを作成しています。
以下実際のActionのコードです(一部変更を加えています)

name: Deploy to S3

on:
  push:
    branches:
      - main

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "latest"
          cache: "npm"
          cache-dependency-path: frontend/package-lock.json

      - name: Upgrade npm
        run: npm install -g npm@latest

      - name: Check npm version
        run: npm -v

      - name: Install dependencies
        working-directory: ./frontend
        run: npm ci

      - name: Create .env.production
        working-directory: ./frontend
        run: |
          echo "VITE_API_URL=${{ secrets.VITE_API_URL }}" > .env.production

      - name: Build project
        working-directory: ./frontend
        run: npm run build -- --mode production

        # AWSの認証情報設定(OIDC利用のiamロールを使用)
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Clear backup folder
        # バックアップ先(backup/)を空にする
        run: |
          aws s3 rm s3://project/backup/ --recursive

      - name: Move existing files to backup
        # mobile/以下をまるごとbackup/へ移動
        run: |
          aws s3 mv \
            s3://project/mobile/ \
            s3://project/backup/ \
            --recursive

      - name: Deploy to S3
        working-directory: ./frontend
        # ビルド成果物をmobile/配下に同期
        run: |
          aws s3 sync dist/ s3://project/mobile/ --delete

      - name: Invalidate CloudFront (optional)
        if: env.CLOUDFRONT_DISTRIBUTION_ID != ''
        env:
          CLOUDFRONT_DISTRIBUTION_ID: ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }}
          # cloudfrontのキャッシュを削除
        run: |
          aws cloudfront create-invalidation \
            --distribution-id $CLOUDFRONT_DISTRIBUTION_ID \
            --paths "/mobile/*"

  • VITE_API_URLAWS_ROLE_TO_ASSUMEAWS_REGIONCLOUDFRONT_DISTRIBUTION_ID をsecretsで管理しているので、実際に使用する場合はこの値を変更し、適宜組み替えて使用してください
  • dist/はViteのデフォルトのビルド出力先です。

内容詳細

流れとしては

  1. mainブランチにpushでActionsが起動
  2. .env.productionを作成してAPIのURLを書き込み
  3. すでにデプロイされているS3のバックアップ
  4. 新しいビルドをs3にデプロイ
  5. Cloud Frontキャッシュの無効化

となります。
上記のワークフローですが、テスト環境も同様にActionsを組んでおり、developブランチにpushされるとテスト環境用のCDが走る仕組みになっています。

  • Node.jsのセットアップ
- uses: actions/setup-node@v4
  with:
    node-version: "latest"
    cache: "npm"
    cache-dependency-path: frontend/package-lock.json

npmのキャッシュを有効化することでインストールの高速化を図っています。

  • OIDCを使用した認証情報設定部分
- uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
    aws-region: ${{ secrets.AWS_REGION }}

上述した通り、この部分でGitHub OIDCトークンを使って IAMロールを引き受け、一時クレデンシャルを発行しています。
長期キー(IAMのアクセスIDとアクセスキー)が不要となります。

  • s3へバックアップ
- name: Clear backup folder
# バックアップ先(backup/)を空にする
run: |
  aws s3 rm s3://project/backup/ --recursive

- name: Move existing files to backup
# mobile/以下をまるごとbackup/へ移動
run: |
  aws s3 mv \
    s3://project/mobile/ \
    s3://project/backup/ \
    --recursive

現状のプロジェクトではS3のbackup/に対して既存デプロイのバックアップを取っています。
この運用に関しては手動でデプロイを行っていた際の名残りですので、Actionsが確立された現在は上記フローは冗長なため、削除予定です。
代わりにS3バージョニング等の仕組みで置き換え予定です。

  • CloudFrontキャッシュの無効化
- name: Invalidate CloudFront (optional)
if: env.CLOUDFRONT_DISTRIBUTION_ID != ''
env:
  CLOUDFRONT_DISTRIBUTION_ID: ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }}
  # cloudfrontのキャッシュを削除
run: |
  aws cloudfront create-invalidation \
    --distribution-id $CLOUDFRONT_DISTRIBUTION_ID \
    --paths "/mobile/*"

Cloud Frontのキャッシュを削除しています。
この操作により、S3へデプロイした最新の状態が即時で反映されるようになります。

3. GitHubにPushして自動デプロイ

設定が完了したら、mainブランチにpushします。

git add .
git commit -m "Add deploy workflow"
git push origin main

GitHub Actionsがトリガーされ、自動でビルド・デプロイされます。

4. 動作確認

デプロイが完了したら、AWS S3バケットにアクセスしてファイルがアップロードされていることを確認します。
今回はS3バケットを静的ホスティングとして設定しているため、ブラウザからアプリケーションにアクセスして確認します。

おわりに

この設定を行うことで、GitHubリポジトリへの変更を即座にS3へ反映できるようになります。
S3のバックアップ周りのフローに関しては今後修正が必要になる箇所ですので、修正次第記事を更新します。
CIに関しては別記事で書き残しておく予定ですので、興味があればそちらも見ていただけると嬉しい気持ちになります。

また、私が所属している TreyLink では、不動産業界を中心にDXを推進するプロダクトの開発を行っています。
まだまだスタートアップの成長フェーズにあり、技術選定や開発フローの改善、企画などなど積極的に関われる環境です。
新しい挑戦を一緒に楽しんでくれるエンジニアを募集中ですので、興味があればぜひご連絡ください!

X (Twitter):TreyLink公式

Discussion