LoginSignup
1
1

Next.jsをCodePipelineでECSに自動デプロイする

Last updated at Posted at 2024-04-11

動機

Next.js のソースコードをプッシュしたら、自動でビルド、ECS へのデプロイを行う仕組みを CodePipeline で作りたかったのだけど、AWS の記事は UI が違っていたりして、とても苦労しました。

そこではじめて CodePipeline を触る人のためになればと思い、構築方法を残します。

自分では Next.js を使っていましたが、どのフレームワークでもほぼ同じはずです。
また環境変数のようにソースコードでも AWS 側でも設定できるものは、今回は分かりやすさのためにソースコード側で行うようにしました。

流れ

デプロイまでの流れは以下の通りです。

  1. CodeCommit にソースコードを push する
  2. CodeCommit の変更を検出して CodeBuild が Docker イメージをビルドを開始する
  3. Docker イメージを ECR に push する
  4. ECR から ECS にデプロイ

また CodeCommit と CodeBuild が成果物を保存するのに S3 を使っています。

前提

Docker や Node.js や AWS CLI はあらかじめインストールしておきます。
私は WSL2 で作業していますが、たぶんどの OS でもあまり変わらないと思います。

ここではローリングアップデートでの自動デプロイを紹介します。
ブルーグリーンデプロイとはファイルや設定が異なるので注意してください。

React のプロジェクトを作成

アプリのひな形を作成

ここではデフォルトの my-app という名前で作成します。
TypeScript、ESLint、Tailwind CSS などのオプションは全部デフォルト値のままにしました。

npx create-next-app@latest
cd my-app

ファイル追加

Dockerfilebuildspec.yaml をルートディレクトリ(package.jsonと同じところ)に追加します。

Dockerfile

とりあえず動いていますが、もっと良い書き方があるかもしれません。

Dockerfile
FROM node:20-alpine AS build

WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine

WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY --from=build /app/.next ./.next
COPY --from=build /app/public ./public

EXPOSE 3000
USER node
CMD ["npm", "start"]

buildspec.yaml

REPOSITORY_NAME には、この後出て来る ECR のリポジトリ名を入れます。
同様に CONTAINER_NAME には ECS で使うコンテナ名を入れます。

buildspec.yaml
version: 0.2

env:
  variables:
    REPOSITORY_NAME: my-ecr-repo
    CONTAINER_NAME: my-container
phases:
  pre_build:
    commands:
      - echo $REPOSITORY_NAME
      - echo $CONTAINER_NAME
      - AWS_ACCOUNT_ID=$(echo ${CODEBUILD_BUILD_ARN} | cut -f 5 -d :)
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
      - REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$REPOSITORY_NAME
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}
  build:
    commands:
      - echo Build started on `date`
      - docker build -t $REPOSITORY_URI:latest .
      - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
  post_build:
    commands:
      - echo Build completed on `date`
      - docker push $REPOSITORY_URI:latest
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - printf '[{"name":"%s","imageUri":"%s"}]' $CONTAINER_NAME $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
    files: imagedefinitions.json

CodeCommit

リポジトリを作成します。
image.png

image.png

リポジトリを作成したら git clone を行います。
私は HTTPS (GRC) を使っていますが、リポジトリが空だと各方式での git clone のやり方が書いてあるので、迷わないと思います。

私は多要素認証があるので一手間(get-session-token)かかっています。下記のページにあるスクリプトを利用させてもらってます。

aws sts get-session-token --serial-number arn:aws:iam::111111111111:mfa/mkt1234 --profile mkt1234 --token-code 112233 | source set-aws-session-token
git clone codecommit::ap-northeast-1://my-repository

先ほど作成した Next.js のファイルを追加し push します。

git add .
git commit -m "add app"
git push

アップロードされました。
image.png

ECR (Elastic Container Registry)

リポジトリを作成します。作成する名前は buildspec.yaml に書いた REPOSITORY_NAME と同じものにします。

image.png

VPC

以前書いた記事を参考に作成します。
ここでは my-vpc という名前の VPC を作成します。
セキュリティグループのインバウンドルールに 80 番ポートを足すのを忘れないでください。

ECS

下記ページを参考にクラスターとタスク定義とサービスを作成します。

クラスター

my-cluster という名前でクラスターを作成します。

タスク定義

タスク定義のコンテナ名を buildspec.yaml に書いた CONTAINER_NAME と同じものにします。
イメージ URI は作成した ECR のリポジトリ名の latest にしておきます。

image.png

image.png

サービス

既存のクラスターを my-cluster にします。
サービス名は my-service にします。
ネットワーキングとロードバランシングについては上記のページを参考にしてください。

ここでいったんデプロイが始まり失敗しますが、ECR にイメージが無いのが原因なので気にしないでください。

ポリシー作成

CodeBuild に最小限の権限を付与するために、IAM で新規にポリシーを作成します。
ポリシーは複雑なので JSON で作成します。

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "CodeBuildPolicy",
			"Effect": "Allow",
			"Action": [
                "logs:CreateLogGroup",
				"logs:CreateLogStream",
				"ecr:GetAuthorizationToken",
				"ecr:UploadLayerPart",
				"ecr:PutImage",
				"ecr:CompleteLayerUpload",
				"ecr:InitiateLayerUpload",
				"ecr:BatchCheckLayerAvailability",
				"s3:GetObject",
				"s3:PutObject"
			],
			"Resource": "*"
		}
	]
}

image.png

image.png

ロール作成

CodeBuild 用のロールを作成します。

IAM でロールを作成します。先ほど作成したポリシーを使います。

image.png

image.png

my-codebuild-role という名前を付けます。
image.png

CodePipeline

パイプラインを作成します。
image.png

CodeCommit

image.png

image.png

CodeBuild

3ページ目のビルド設定はちょっと複雑です。
「プロジェクトを作成する」をクリックするといったん別のウィンドウが開きます。
image.png

image.png

image.png

image.png

image.png

image.png

CodeDeploy

image.png

この設定でパイプラインを作成します。

結果

image.png

更新

CodeCommit の master ブランチに変更が発生するたびに、自動的にビルド→デプロイが実行されます。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1