AWS ESC(Fargate)でNext.jsプロジェクトを自動ビルド&デプロイする方法
概要
AWS ESC(Fargate)で Next.js プロジェクトを、コンテナ環境に自動ビルド及び自動デプロイを行うポイントについて記載します。
前提
- 同一 Git リポジトリ内で Dockerfile と Next.js のソースを管理している。
- node_modules は.gitignore で git 管理対象外となっている。(package.json で管理)
- ECS(Fargate)の環境自体は準備できていて、CodePipeline での自動デプロイはできる。
参考
Dockerfile の準備
次のような構成で Dockerfile を配置します。
ポイントとしては、開発時に使用する Dockerfile をプロジェクトルートディレクトリに配置し、自動デプロイ用の Dockerfile は別フォルダで管理するという点です。
ローカル開発時には、docker-commpose.yml の設定を参照して、ローカルのボリュームをマウントし、dev モードで動作させていますが、検証/本番環境時ではビルドイメージの中に、ソースファイルを入れ、build と start を行っています。
Next.js のプロジェクトディレクトリは、/src
に格納されている前提とします。
プロジェクト構成
.
├ docker
│ └prod
│ └Dockerfile :本番/検証環境用Dockerfile
├ src :Nextプロジェクトソース用フォルダ
├ Dockerfile :ローカル開発用Dockerfile
├ docker-compose.yml :ローカル開発用docker-composeファイル
└ buildspec.yml : CodeBuild用設定ファイル
FROM node:17-alpine
WORKDIR /usr/app
version: '3'
services:
app:
build: ./
volumes:
- ./src:/usr/app
command: sh -c "yarn dev"
ports:
- "3000:3000"
FROM node:17-alpine
WORKDIR /usr/app
COPY ./src /usr/app/
RUN yarn install && yarn build
EXPOSE 3000
CMD ["yarn", "start"]
-
COPY ./src /usr/app/
でプロジェクトソースをコンテナ内に配置します。 -
RUN yarn install
でpackage.json
に記載のライブラリを取得します。 -
yarn build
でプロダクト用ビルドを行います。 -
EXPOSE 3000
で 3000 番ポートを公開します。 -
CMD ["yarn", "start"]
でコンテナ起動時に、自動で Web サーバが立ち上がり Listen になるようにします。
buildspec.yml の準備
buildspec.yml で、プロジェクトルートディレクトリの Dockerfile は使用せずに、docker/prod
ディレクトリ配下のファイルを使用しています。(cp ./docker/prod/Dockerfile ./Dockerfile
で起き変えています)
もし、環境ごとに使用するファイルを切り替える場合は、パス内のprod
の部分についてCodeBuildプロジェクト
の設定で、環境変数を定義して、それを参照するとよいでしょう。(環境変数を定義したうえで${project_env}
のような形で参照)
今回は、本番と検証で Dockerfile 内容は共通である前提(そのほうがいいと思いますが)としています。
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws --version
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin <aws_account_id>.dkr.ecr.ap-northeast-1.amazonaws.com
- REPOSITORY_URI=<aws_account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/samplerepo
- 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
- 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":"container01","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
files: imagedefinitions.json
※<aws_account_id>
部分には AWS アカウント ID を設定します。リージョンが違う場合はそこも変更します。
※REPOSITORY_URI
は ECR のリポジトリの URI を設定します。
※container01
の部分は、Docker イメージを参照するサービスのタスク定義のコンテナ名とします。
参考情報
buildspec.yml
のcommands
のところで、リポジトリ URI を、REPOSITORY_URI=<aws_account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/samplerepo
で直接指定していますが、コミットされたブランチによって、デプロイ先環境を切り替えるような Pipeline を組む場合、本記載を行ごと削除して、CodePipeline 側の環境変数(CodeDeploy 側の環境変数ではない)でREPOSITORY_URI
を定義することで、Pipeline 単位で使用するリポジトリを切り替えることができます。
またimagedefinitions.json
に対して受け渡しているコンテナ名"name":"container01"
についても、printf '[{"name":"%s","imageUri":"%s"}]' $CONTAINER_NAME $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
のように書き換え、CodePipeline 側の環境変数CONTAINER_NAME
を定義して引き渡すようにすることで、動的に切り替えができます。
注意
ビルドプロセスの中で、docker Hub からイメージを PULL している場合、IP アドレス単位で時間当たりの PULL 数が制限されており、AWS の CodeBuild の IP アドレスが共有であるために、Limit に引っかかり PULL できない場合があります。その場合、CodeBuild で認証するプロセスを組み込む必要があります。
以下に対応方法を記載しましたので、参考にしてください。
タスク定義の作成
タスク定義を作成する際に、コンテナの定義
のポートマッピング
で3000
を指定します。
※EC2 タイプの場合ホストポートとコンテナポートのマッピングを指定できますが、Fargate の場合、ホストポートとコンテナポートは同一のポート番号しか指定できません。
サービスの作成
ターゲットグループに設定するセキュリティグループでの TCP3000 番ポートの許可
ECS のサービスを作成する際に、ターゲットグループに対してセキュリティグループを設定しますが、その際に使用するセキュリティグループにインバウンドで3000
番のポートを許可するように設定します。
ロードバランス用のコンテナの 3000 番ポートとプロダクションリスナーポートをマッピング
ロードバランス用のコンテナに、タスク定義で作成した3000
番ポートを公開しているコンテナを指定します。
また、その際にプロダクションリスナーポート
で80:HTTP
(ALB 側でリスナーにするポート)を指定します。
あとは、CodePileline で Git リポジトリからソースを取得し、CodeBuild を行い、対象のサービスに対してデプロイするように設定すれば OK です。
まとめ
- プロジェクトのルートディレクトリ直下の Dockerfile で、プロダクションビルドが走るように設定する。
- コンテナは 3000 番ポートで Listen するようにする。
- コンテナのターゲットグループでインバウンド 3000 番ポートを許可する。
- サービス作成時に、ALB のリスナーポート(80:HTTP/443:HTTPS)とコンテナのポート(3000)をマッピングする。
Discussion