CI/CDではテストや静的解析だけでなく、ビルドしたアプリのデプロイなども行います。この記事では、ViteでセットアップしたReactアプリをS3にデプロイするワークフローを CircleCI で構築する方法を紹介します。
GitHub CLI と CircleCI CLI を併用すれば、リポジトリ作成からデプロイまで30分程度で一通り通せます。
本記事の設定はハンズオン用です
手順をシンプルにするため、S3バケットをパブリック公開する構成にしています。セキュリティリスクがあるため、この設定をそのまま業務や本番環境のアプリに流用しないでください。(本番運用では CloudFront + OAC による配信を推奨します)
また、体験終了後は作成したAWSリソースの削除をお忘れなく!
前提条件
Node.jsがインストールされている環境で、 GitHub CLIとCircleCI CLIそして、AWS CLIを利用します。node --version などで導入済みかを確認してください。
アカウントは GitHub・CircleCI・AWS の3つを用意します。CircleCI は GitHub アカウントでサインアップでき、本記事の構成は Free プランで動作します(料金ページ)。AWS 側では S3 バケットと IAM ロールを作成する権限が必要です。
ステップ1. React アプリを作成する
まずはアプリをセットアップしましょう。Vite で React + TypeScript のプロジェクトを作成します。
npm create vite@latest demo-cci-react-app -- --template react-ts
cd demo-cci-react-app
npm install
開発サーバーを起動し、ブラウザで http://localhost:5173 を開いて Vite のデフォルト画面が表示されることを確認します。
npm run dev
ステップ2. GitHub リポジトリを作成する
GitHub CLI でローカルリポジトリを初期化し、そのままリモートリポジトリを作成してプッシュします。
git init
git add .
git commit -m "Initial commit"
gh repo create demo-cci-react-app --public --source=. --push
成功すると次のような出力になります。
✓ Created repository [your-username]/demo-cci-react-app on github.com
✓ Added remote git@github.com:[your-username]/demo-cci-react-app.git
ステップ3. CircleCI プロジェクトを作成する
CircleCI CLI でプロジェクトを作成し、GitHub リポジトリと連携します。CircleCI CLI で認証していない場合は、初回に circleci setup で認証を済ませてください。
<org-id> は CircleCI Web App の Organization Settings → Overview から取得できます。GitHub App 統合を使っている場合は <vcs-type> に circleci を指定します(公式ドキュメント)。
circleci project create <vcs-type> <org-id> --name demo-cci-react-app
Project 'demo-cci-react-app' successfully created in organization 'your-username'
You may view your new project at: https://app.circleci.com/projects/gh/your-username/demo-cci-react-app
ステップ4. S3 バケットを作成して静的ホスティングを有効化する
ホスティング用の S3 バケットを作成し、静的ウェブサイトとして公開できる状態にします。
バケットを作成する
バケット名は AWS 全体で一意になる必要があるため、末尾にランダムな文字列を付けてください。
export BUCKET_NAME=demo-cci-react-app-a3f8b2 # 実際のバケット名に置き換え
aws s3 mb s3://${BUCKET_NAME} --region ap-northeast-1
オブジェクト所有権を変更して ACL を有効化する
チュートリアルをシンプルにするための措置です。実際の運用ではAWS Amplifyを利用するか、S3バケットの前段にCloudFrontを用意し、OAC( Origin Access Control)を設定してください。
2023年4月以降に作成された S3 バケットは、デフォルトで「Bucket owner enforced」(ACL 無効)になっています。本記事ではデプロイ時に --acl public-read を使用するため、まずオブジェクト所有権を ObjectWriter に変更して ACL を有効化します。
aws s3api put-bucket-ownership-controls \
--bucket ${BUCKET_NAME} \
--ownership-controls Rules=[{ObjectOwnership=ObjectWriter}]
パブリックアクセスを許可する
S3 バケットはデフォルトで全パブリックアクセスがブロックされています。静的ウェブサイトとして公開するには、このブロックを解除します。
aws s3api put-public-access-block \
--bucket ${BUCKET_NAME} \
--public-access-block-configuration \
"BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false"
BlockPublicAcls と IgnorePublicAcls はオブジェクトレベルの ACL を許可し、BlockPublicPolicy と RestrictPublicBuckets はバケットポリシーによる公開を許可するブロック設定です。本記事では4つすべてを無効化します。
静的ウェブサイトホスティングを有効化する
--index-document には、ルートアクセス時に返すファイル名を指定します。
aws s3 website s3://${BUCKET_NAME} \
--index-document index.html
これで http://${BUCKET_NAME}.s3-website-ap-northeast-1.amazonaws.com のようなエンドポイント URL が生成されます。
ステップ5. IAM ロールを OIDC で作成する
CircleCI から S3 へアクセスするための IAM ロールを作成します。OIDC(OpenID Connect) を使うことで、静的なアクセスキーを発行せずに、ジョブ実行ごとの短期トークンで AWS リソースへアクセスできます。
まずAWS IAM に CircleCI 用の Identity Provider(IdP)を追加しましょう。続いてS3 アクセス権限を持つ IAM ロールを作成し、Trust Policy で CircleCI の特定 Organization からの認証のみを許可します。具体的な手順は次の記事にまとめてあります。
上記の記事が利用できない場合は、CircleCI 公式ドキュメント: OpenID Connect tokens と AWS ドキュメント: Creating a role for web identity or OpenID Connect Federation を参照してください。
ここで作成した IAM ロールの ARN(例: arn:aws:iam::123456789012:role/circleci-oidc)は、次のステップで CircleCI の環境変数として登録するため控えておきます。
ステップ6. CircleCI に環境変数を登録する
config.yml からデプロイに必要な情報を参照するため、Context を作成して環境変数を登録します。Context は複数のプロジェクトで環境変数を安全に共有するための仕組みです。
Context の作成には Organization 管理者権限が必要です。権限がない場合は管理者に依頼してください。
CircleCI Web App で Organization Settings → Contexts を開き、「Create Context」をクリックして aws-oidc という名前の Context を作成します。続いて、その Context に2つの環境変数を追加します。
| 変数名 | 値 | 説明 |
|---|---|---|
AWS_ROLE_ARN |
arn:aws:iam::123456789012:role/circleci-oidc |
ステップ5で作成した IAM ロールの ARN |
AWS_REGION |
ap-northeast-1 |
デプロイ先の AWS リージョン |
CLI から登録する場合は circleci context store-secret を使用します。各コマンドの実行後、対応する値を入力してください。
circleci context store-secret --org-id <org-id> aws-oidc AWS_ROLE_ARN
circleci context store-secret --org-id <org-id> aws-oidc AWS_REGION
ステップ7. config.yml を作成する
CircleCI のビルド・デプロイパイプラインを定義する .circleci/config.yml を作成します。
mkdir .circleci
touch .circleci/config.yml
以下のYAMLをコピーアンドペーストしてください。その上で、to: 's3://demo-cci-react-app-[ランダム文字列]'を作成したバケット名に差し替えましょう。
version: 2.1
orbs:
node: circleci/node@7.2.1
aws-s3: circleci/aws-s3@4.1.3
aws-cli: circleci/aws-cli@5.1.1
jobs:
build-react-app:
executor:
name: node/default
resource_class: small
steps:
- checkout
- node/install-packages:
pkg-manager: npm
- run:
name: Build React app
command: npm run build
- persist_to_workspace:
root: .
paths:
- dist
deploy-artifact:
executor:
name: aws-s3/default
resource_class: small
steps:
- attach_workspace:
at: .
- aws-cli/setup:
role_arn: ${AWS_ROLE_ARN}
region: ${AWS_REGION}
- aws-s3/sync:
from: dist
to: 's3://demo-cci-react-app-[ランダム文字列]'
arguments: |
--delete \
--acl public-read \
--cache-control "max-age=60"
workflows:
build-and-deploy:
jobs:
- build-react-app
- deploy-artifact:
context: aws-oidc
requires:
- build-react-app
filters:
branches:
only: main
このパイプラインは「React アプリのビルド」と「S3 へのデプロイ」の2ジョブ構成です。ビルド成果物は Workspace を通じてジョブ間で共有し、デプロイジョブでは OIDC 認証を使って S3 にアップロードします。
Orb のバージョンは本記事公開時点のものです。最新バージョンは Orb Registry で確認してください。
YAMLの実装を読み解く
この config.yml で行っている処理について、2つの視点から解説します。
1. 3つの Orb による効率化
CIやCDを実行する上での環境セットアップやアカウント連携に関するコマンドを、CircleCIでは Orbs という仕組みで簡略化しています。今回のサンプルでは、以下の3つのOrbを利用しました。
-
node: Node.js環境の構築とパッケージキャッシュを管理 -
aws-cli: AWS CLIの導入とOIDCによる一時認証を実行 -
aws-s3: ビルド成果物のS3同期(sync)を1コマンドで記述
2. ジョブの分割とWorkspace を使った連携
CIやCDでは、ジョブをできるだけタスク単位で分割することを推奨しています。今回のサンプルでは、アプリのビルドとS3へのデプロイの2つを行なっているので、build-react-appとdeploy-artifactの2ジョブが定義されています。
それぞれのジョブは独立した環境で動作します。そのため、Workspace を使ってファイルを共有しています。
build-react-app ジョブでビルドしたアプリはdist/ フォルダに保存されています。これを persist_to_workspace で保存し、deploy-artifact ジョブではattach_workspace を利用してdist/ を読み込み
しています。
ステップ8. デプロイを実行する
config.yml をコミット・プッシュすれば、CircleCI が自動でパイプラインを実行します。
git add .circleci/config.yml
git commit -m "Add CircleCI config"
git push origin main
パイプラインの実行を確認する
CircleCI Web App → Projects → demo-cci-react-app を開き、Pipelines タブで最新のパイプラインを確認します。build-react-app → deploy-artifact の順に走り、両方が SUCCESS(緑色)になれば成功です。
デプロイされたアプリを確認する
AWS S3 コンソールで作成したバケットを開き、「プロパティ」タブの「静的ウェブサイトホスティング」にあるエンドポイント URL をブラウザで開きます。
Vite のデフォルト画面が表示されれば成功です。
コードを変更して自動デプロイを確認する
最後に、コード変更が S3 まで届くことを確認します。src/App.tsx を次の内容に書き換えてください。
function App() {
return (
<div className="App">
<h1>Hello CircleCI!</h1>
<p>This app is automatically deployed to S3.</p>
</div>
)
}
export default App
コミットしてプッシュします。
git add src/App.tsx
git commit -m "Update app title"
git push origin main
CircleCI で新しいパイプラインが自動実行されます。完了したら S3 のエンドポイント URL を再読み込みし、「Hello CircleCI!」が表示されれば、Git プッシュをトリガーとした自動デプロイが動作している状態です。
まとめと次のステップ
Vite + React アプリを GitHub にプッシュするだけで、S3 へ自動デプロイされる環境が整いました。OIDC を採用したことで、「秘密鍵を管理しない安全な CI/CD」を実現しています。
今回の構成はあくまで「最小構成」です。本格的な運用を目指すなら、以下のようなトピックにもぜひ挑戦してみてください。
- セキュリティの強化: CloudFront + OAC を導入し、S3バケットへの直接アクセスを遮断する。
- 運用性の向上: S3 バージョニングを有効にし、いつでも以前のバージョンに切り戻せるようにする。
- CircleCI の活用:


