ECS自動デプロイ構成でNext.jsに環境変数を渡して使用する
概要
環境ごとに接続先情報や Secret など、使用したい値が異なる場合、ECS 環境でサービスごとなどで、環境変数をアプリケーション側に受け渡して使用します。
なお、セキュリティの関係でソースリポジトリ(Git)上にそれらの情報は持たないものとします。
前提構成
- Next.js
- ECS(Fargate)
- GitHub
- CodePipeline
- CodeBuild
パターン検討
ECS のタスク定義内のコンテナ定義の中で、環境変数を定義するパターンと、コンテナのビルド時にコンテナ側に環境変数を渡しておく方法が考えられます。
この時、注意点があり、Next.js のサーバサイド処理内で使用する環境変数は、タスク定義で環境変数を渡すことができますが、クライアント側(クライアントで動作する JS)内で使用できるNEXT_PUBLIC_
から開始される環境変数については、ビルド時に設定されるものなので、コンテナ起動後に使用されるタスク定義で環境変数を設定しても使用できません。
そのようなNEXT_PUBLIC_
変数に関しては、ビルド時に、.env.production
ファイルを生成して、その際に環境変数を渡すといった考慮が必要になります。
※なお、Dockerfile のENV
設定でNEXT_PUBLIC_
変数を渡しても、サーバサイドでは使用できますが、クライアント側では使用できません。
タスク定義に設定してサーバサイド処理に環境変数を渡す
- AWS コンソールより
ECS
を選択し、タスク定義
で設定するタスクを選択します。 -
タスク定義
-コンテナ定義
の環境変数
に対象の値を設定します。
以下のような方法で、サーバサイド処理内から呼び出せます。
環境変数 TEST を呼び出す場合
process.env.TEST
CodePipeline の環境変数に設定して、ビルド時に.env 系ファイルに環境変数を渡す(クライアント側へ渡す環境変数も含む)
1. CodePipeline の環境変数を設定する
CodePipeline のビルド時の環境変数の設定は、CodePipeline 初期設定時にビルドステージを追加する
で設定を行うか、作成後に追加する場合は、AWS コンソールからCodePipeline
-パイプライン
に進み、対象のパイプラインを選択して、編集
を押下して、Build
のステージを編集する
-編集アイコン
押下で環境変数などの変更が行えます。
※設定後、子画面の完了
ボタン →Build ステージの完了
ボタン → パイプラインの保存
ボタンまで押下しないと反映されませんので注意してください。
2. buildspec.yml に CodePipeline の環境変数を.env 系ファイルに追記する処理を追加する
CodeBuild 用の定義buildspec.yml
にecho NEXT_PUBLIC_FOO=$HOGE >> ./src/.env.production
といった形で、ビルド前に env ファイルを環境変数を使用して生成する処理を入れるようにします。
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- cp ./docker/prod/Dockerfile ./Dockerfile
- echo NEXT_PUBLIC_FOO=$HOGE >> ./src/.env.production
- docker build -t $REPOSITORY_URI:latest .
- docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
これで、ビルド時に環境変数を Next.js のクライアント用のNEXT_PUBLIC_
変数に渡すことができるようになります。
補足:パラメータストアの利用
AWS のパラメータストアを使用するとこで、環境変数として CodePipeline やタスク定義に直接 Secret などの情報を記載しなくても、安全に値を取り出すことができます。
AWS Systems Manager
-パラメータストア
で、パラメータを登録しておき、それを、CodePipeline やタスク定義側から参照する形になります。
タスク定義でパラメータストアを参照する場合
ECS
のタスク定義
-コンテナ定義
の環境変数
に設定する際に、ValueFrom
の区分を選択し、パラメータ側で設定したName
を設定します。
なお、この場合、ECS のタスク実行ロールに対して、IAM
-ロール
で、AmazonSSMReadOnlyAccess
を付与する必要があります。
※パラメータ単位(パラメータのパス階層等)で制御する場合は IAM ポリシーで前方一致制御などを定義する必要があります。
ビルド時にパラメータストアを参照する場合
ビルド時にパラメータストアを参照する場合は、parameter-store
に対して、以下のように定義することで、参照できます。
※この例では、パラメータストアに登録された/sample/test/hoge
を変数HOGEFUGA
に格納して、echo HOGEFUGA=$HOGEFUGA >> ./src/.env.production
で Next.js 側に引き渡しています。
version: 0.2
env:
parameter-store:
HOGEFUGA: "/sample/test/hoge"
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...v1
- aws --version
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin <ACCOUNT ID>.dkr.ecr.ap-northeast-1.amazonaws.com
- REPOSITORY_URI=<REPOSITORY_URI>
- COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
- IMAGE_TAG=${COMMIT_HASH:=latest}
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- cp ./docker/prod/Dockerfile ./Dockerfile
- echo HOGEFUGA=$HOGEFUGA >> ./src/.env.production
- docker build -t $REPOSITORY_URI:latest .
- docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker images...
- docker push $REPOSITORY_URI:latest
- docker push $REPOSITORY_URI:$IMAGE_TAG
- echo Writing image definitions file...
- printf '[{"name":"<CONTAINER NAME>","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
files: imagedefinitions.json
なお、この場合、codebuild のサービスロール(CodeBuild 設定した時に作成 or 選択したもの)に対して、IAM
-ロール
で、AmazonSSMReadOnlyAccess
を付与する必要があります。
※パラメータ単位(パラメータのパス階層等)で制御する場合は IAM ポリシーで前方一致制御などを定義する必要があります。
Discussion