CDKでNext.jsのスタンドアロンモードでビルドしたイメージを AWS App Runnerへデプロイする

2022.06.16

まえがき

前回は手動で、ECR、AWS App Runnerをデプロイしていましたが、CDKを使ってデプロイします。

動作環境

$ node --version
v16.15.1

$ cdk --version
2.28.1 (build ba233f0)

今回つかったサンプルはこちらです

CDKセップアップ

まだCDKのCLIがインストールされていないときはインストールとセットアップしておきます。

npm install -g aws-cdk
cdk bootstrap

CDKプロジェクトを作成

前回のサンプルにformationをディレクトリを作り、CDKのプロジェクトをつくることにします。

mkdir formation
cd formation
cdk init --language typescript 
npx npm-check-updates -u

バージョン類が古いので一括でバージョンアップしときます。

ECRのリポジトリを作成

AppRunnerの設定時に実際のイメージがないとCDKでエラーになります。デプロイを2回にわけます。

一旦リポジトリ作成までします。

./formation/lib/formation-stack.ts

import { Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import * as apprunner from "@aws-cdk/aws-apprunner-alpha";
import * as ecr from "aws-cdk-lib/aws-ecr";

export class FormationStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const repository = new ecr.Repository(this, "NextjsSampleRepository", {
      repositoryName: "nextjs-sample-repository",
    });
  }
}

一旦デプロイします

cdk deploy

出来上がったECRのリポジトリにデプロイする

コンソールに行きAWS ECRのリポジトリのデプロイ手順を確認する

aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin [YOUR NUMMBER].dkr.ecr.ap-northeast-1.amazonaws.com
docker build -t app-runner-nextjs .
docker tag app-runner-nextjs:latest [YOUR NUMMBER].dkr.ecr.ap-northeast-1.amazonaws.com/app-runner-nextjs:latest
docker push [YOUR NUMMBER].dkr.ecr.ap-northeast-1.amazonaws.com/app-runner-nextjs:latest

AppRunnerのライブラリーを追加する

CDK v1にあったようなL2のクラスはまだないようです。(v2.28.1) alphaですが、今回は @aws-cdk/aws-apprunner-alpha をつかってAppRunnerを設定します。

npm install @aws-cdk/aws-apprunner-alpha

AppRunnerにリソース作成

AppRunnerの追記をします。

./formation/lib/formation-stack.ts

import { Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import * as apprunner from "@aws-cdk/aws-apprunner-alpha";
import * as ecr from "aws-cdk-lib/aws-ecr";

export class FormationStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const repository = new ecr.Repository(this, "NextjsSampleRepository", {
      repositoryName: "nextjs-sample-repository",
    });
    const app = new apprunner.Service(this, "NextjsSampleAppRunner", {
      source: apprunner.Source.fromEcr({
        imageConfiguration: { port: 3000 },
        repository: repository,
        tagOrDigest: "latest",
      }),
    });
    new CfnOutput(this, "NextjsSampleAppRunnerUrl", {
      value: app.serviceUrl,
    });
  }
}

一旦デプロイします

cdk deploy

NestJsSampleStack.NextjsSampleAppRunnerUrl =xxxxx.ap-northeast-1.awsapprunner.com

のようにconsoleにURLが表示されるのでアクセスし確認できます

よくばり版

CDK deploy時に、イメージのデプロイ一緒にして楽にしたい方はDockerImageAssetを使うとよいかもです。 ECRリポジトリ作成、イメージビルド、イメージのpushまでやってくれます。

./formation/lib/formation-stack.ts

import { CfnOutput, Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import { DockerImageAsset, Platform } from "aws-cdk-lib/aws-ecr-assets";
import * as apprunner from "@aws-cdk/aws-apprunner-alpha";
import * as path from "path";

export class FormationStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    const asset = new DockerImageAsset(this, "NextjsSampleImage", {
      directory: path.join(__dirname, "../../"),
      platform: Platform.custom("linux/x86_64"),
    });
    const app = new apprunner.Service(this, "NextjsSampleAppRunner", {
      source: apprunner.Source.fromAsset({
        imageConfiguration: { port: 3000 },
        asset,
      }),
    });

    new CfnOutput(this, "NextjsSampleAppRunnerUrl", {
      value: app.serviceUrl,
    });
  }
}

今回はformationをネストにつくっているのでビルド時にformationまで拾ってしまうので、Dockerとtsconfigから除外します。

.dockerignore

formation

tsconfig.json

{
  ...
  "exclude": [
    "node_modules",
    "formation"
  ]
}

デプロイします

cdk deploy

NestJsSampleStack.NextjsSampleAppRunnerUrl =xxxxx.ap-northeast-1.awsapprunner.com

のようにconsoleにURLが表示されるのでアクセスし確認できます