はじめに
この記事はcreate-react-appで作ったSPAをAmazon S3のウェブホスティングを使用して外部公開する際の環境変数の設定方法をまとめた記事です。
S3へデプロイしているサービスから環境変数を読み込む際、下記2点の壁がありました。
- CDにGithubActionsを使用しているため
npm build
時に.env
ファイルが無視されてしまう(.gitignoreに含めているため) - AWS側でSystemManagerやSecretsManagerを使おうにもS3からの読み取りができない
じゃあどうしたらいいの?に対する答えをこの記事でまとめていきます。
記事を書こうと思ったきっかけ
筆者は現在Fjord Bootcampで自作サービスの開発を行っているのですが、サービスのデプロイ先としてS3のウェブホスティングを使用しています。
はじめにで書いたように、本番環境で環境変数を読み込むにあたって壁にぶつかったため、Googleで調べてみましたが、力及ばずで解決策を見つけることができませんでした。
自分と同様の壁にぶつかった方がこの記事を読むことで、問題を解決できればという思いで執筆しました。
解決策
一番はじめに解決策からまとめますが、その方法は、
GithubActionsのworkflowにて.envファイルを生成するです。
- Github Actions Secretsに環境変数を設定する
- workflow内でrunを使って1を書き込んだファイルを生成する
CDパイプライン内でビルド実行前に.env
ファイルを作っちゃおうということです。
実践
手順については先述のとおりです。
順番に詳細をまとめていきます。
尚、今回は複数の環境変数をセットする想定で解説をします。
1. GithubActionsの環境変数を用意
- リポジトリ内のSettingsタブをクリック
- サイドバー内、Security→ Secrets and variables→ Actionsをクリック
- New repository secretをクリック
- 環境変数をセット
Note
create-react-appの場合は環境変数の接頭辞としてREACT_APP
をつける必要があります。
※同様の手順でREACT_APP_FOO
にfoo、REACT_APP_BAR
にbarを設定したものとします。
2. workflowファイルの編集
まずはじめに環境変数を仕込む前のworkflowファイルがこちらです。
name: build and deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install dependencies
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
run: aws s3 sync ./build s3://tasting-note --delete
- name: Clear cache
run: aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_CF_DISTRIBUTION_ID }} --paths "/*"
mainブランチにマージされたタイミングでS3へのデプロイとCloud frontのキャッシュクリアを走らせています。
このworkflowに.env
ファイルを仕込むstepを追加していきます。
(今回は本番環境用のファイルなのでファイル名は.env.production
でいきます。)
実装方針
1.で設定した環境変数を.env.production
というファイル名でテキスト出力します。
今回は環境変数が複数ある想定なのでcat + ヒアドキュメントをリダイレクトさせてファイルを作成します。
複数行のテキスト出力について
少し脱線しますがテキスト出力の方法についてはこちらの記事を参考にしました。
2年前の記事なので情報としては少し古いですが、Linuxコマンドのような枯れた技術であれば問題ないだろうという判断です。
それでは本題に戻ってstepを追加していきます。
- name: Prepare .env.production file
run: |
cat << EOF > .env.production
REACT_APP_HOGE=${{ secrets.REACT_APP_HOGE }}
REACT_APP_FOO=${{ secrets.REACT_APP_FOO }}
REACT_APP_BAR=${{ secrets.REACT_APP_BAR }}
EOF
コマンドの詳細な説明は前述の記事に任せるとして何をしているのかを簡単にまとめると、ヒアドキュメントの内容をcatで出力したものを>
(リダイレクト)を使用して.env.production
に書き込んでいます。
1.でセットした環境変数はworkflowファイル内から${{ secrets.HOGE }}
のようにして読み込むことができます。
このstepによって作成される.env.production
ファイルの中身は以下になります。
REACT_APP_HOGE=hoge
REACT_APP_FOO=foo
REACT_APP_BAR=bar
これをbuild前に仕込むことで本番環境において環境変数の読み込みが可能となります。
最終的なworkflowファイルは下記です。
name: build and deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Prepare .env.production file
run: |
cat << EOF > .env.production
REACT_APP_HOGE=${{ secrets.REACT_APP_HOGE }}
REACT_APP_FOO=${{ secrets.REACT_APP_FOO }}
REACT_APP_BAR=${{ secrets.REACT_APP_BAR }}
EOF
- name: Install dependencies
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
run: aws s3 sync ./build s3://tasting-note --delete
- name: Clear cache
run: aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_CF_DISTRIBUTION_ID }} --paths "/*"
この方法のデメリット
-
検証が難しい
ローカル環境であればconsole.log
を仕込んで値の確認ができますが、本番環境の場合はそれができないため、出たとこ勝負するしかありません。
例えば外部公開していないステージング環境をつくって、本番環境へデプロイする前に確認をする方法も考えられますが、S3の場合はBasic認証などの機能がない(もしあったらごめんなさい😥)ため、クローズドな環境で検証を行うことができません。
くれぐれも秘匿情報を本番環境のコンソールに表示させて検証をするといったことがないように気をつけましょう。 -
管理が大変
環境変数が増えてきた場合にActionsSecretsとworkflowファイルの2つの変更が必要になるため管理が煩雑で変更漏れのリスクも一定発生してしまいます。
まとめ
記事を作成しておいてなんですが、この方法がベストプラクティスと思えないのが本音です。
他の方法を思いつけなかったのでこの実装を行いましたが、他に良い方法があればそっと教えていただけると嬉しいです😌