mazyu36の日記

某SIer所属のクラウドエンジニアのブログ

モノレポなNext.jsのアプリケーションをAWS Amplifyでホスティングする(w/ AWS CDK)

タイトルの通りですが、Next.jsのアプリケーションをAWS Amplifyでホスティングする機会があったので試してみました。

また趣味ですがAmplify自体はCDKで実装しています。

目次

やること

今回Next.jsのアプリケーション例として、vercelのサンプルのブログスターターキットを使います。

vercel.com

上記のブログをAmplifyでホスティングします。

またAmplify自体はCDKで構築します。以下ドンピシャなAWS公式の動画があったのでこちらの手順を参考にさせてもらいました。

www.youtube.com

上記の動画からリンクされている記事(の和訳)である以下も参考になります。

aws.amazon.com

リポジトリ構成

以下のようにアプリとインフラ(CDK)をセットにしたモノレポにしてみます。

.
├── README.md
├── blog # Next.jsのサンプルアプリ
└── cdk # AmplifyのCDKプロジェクト

以下実装例です。

github.com

事前準備:Next.jsアプリ(ブログ)の作成

yarnでブログスターターキットを取得し、アプリを作成します。

yarn create next-app --example blog-starter .

適当に記事などを作成して、ローカルで起動してみます。 * 記事を作成する場合はblog/_posts/配下に .mdを作成。 * 画像はblog/public/assets配下に格納する。記事から画像を参照する場合は、/assets始まりで記載。

yarn dev

起動したらローカルホストで3000にアクセスします。

http://localhost:3000/

記事が見れたので特に問題なさそうです。

※ また以下の記事を参考にシンタックスハイライトの追加のみ実施。

blog-taupe-mu-63.vercel.app

CDK(Amplify)の実装

事前準備

今回はAmplifyのalphaモジュールを使います。

docs.aws.amazon.com

以下のようにalphaモジュールを追加します。

# cdk init
cdk init app --language typescript

# Amplifyのalpha モジュールを設定
npm i @aws-cdk/aws-amplify-alpha

プロジェクト構成

.
├── README.md
├── bin
│   └── cdk_amplify.ts
├── cdk.json
├── cdk.out
├── jest.config.js
├── lib
│   └── cdk_amplify-stack.ts # Stackを実装
├── node_modules
├── package-lock.json
├── package.json
├── parameter.ts # 環境依存パラメータの定義
├── test
│   └── cdk_amplify.test.ts
└── tsconfig.json

Stackの実装内容

alphaモジュールのAppを使用して実装していきます。

以下実装内容の全量です。今回はlib配下のStackに直接実装しています。

参考資料のAWS公式の動画やブログ記事のサンプルから変えている点を中心に、実装の詳細を説明します。

const amplifyApp = new App(this, "AmplifyAppBlog", {
      appName: props.appName,

      // ①GitHubをソースとして使用
      sourceCodeProvider: new GitHubSourceCodeProvider({
        owner: props.ownerName,
        repository: props.repositoryName,
        oauthToken: cdk.SecretValue.secretsManager(props.secretNameForGitHubToken)
      }),
      platform: Platform.WEB_COMPUTE,

      // ②環境変数の設定
      environmentVariables:
      {
        "AMPLIFY_MONOREPO_APP_ROOT": "blog",
        "AMPLIFY_DIFF_DEPLOY": "true"
      },

      // ③buildspecの設定
      buildSpec: codebuild.BuildSpec.fromObjectToYaml({
        version: 1,
        applications: [
          {
            appRoot: 'blog',
            frontend: {
              phases: {
                preBuild: {
                  commands: ['yarn install --frozen-lockfile'],
                },
                build: {
                  commands: ['yarn run build'],
                },
              },
              artifacts: {
                baseDirectory: '.next',
                files: ['**/*'],
              },
              cache: {
                paths: ['node_modules/**/*'],
              },
            }
          }
        ]
      })
    })

    amplifyApp.addBranch("main", { stage: "PRODUCTION" })

GitHubをソースとして使用

      sourceCodeProvider: new GitHubSourceCodeProvider({
        owner: props.ownerName,
        repository: props.repositoryName,
        oauthToken: cdk.SecretValue.secretsManager(props.secretNameForGitHubToken) // TokenはSecrets Managerで管理
      }),

今回はGitHubソースコードプロバイダーとして使います。

またGitHubトークンはSecrets Managerに事前に登録しておき、読み込む形としています。

なおシークレット名はパラメータで設定できるように実装しています。

環境変数の設定

      // ②環境変数の設定
      environmentVariables:
      {
        "AMPLIFY_MONOREPO_APP_ROOT": "blog", // アプリのルートを指定
        "AMPLIFY_DIFF_DEPLOY": "true" // 差分ビルドを有効化
      },

今回重要なのはAMPLIFY_MONOREPO_APP_ROOTです。

Next.jsのアプリが入っているのはソースコードプロジェクトのルートではなく、blog/配下なので環境変数でパスとして指定します

またAMPLIFY_DIFF_DEPLOYtrueにすることで、フロントエンドの差分ビルドが有効になります。差分がないときはビルドがスキップされます。

③buildspecの設定

      buildSpec: codebuild.BuildSpec.fromObjectToYaml({
        version: 1,
        // applicationsでリスト化してAPのルートを指定(モノレポ用の設定)
        applications: [
          {
            // APのルートを指定(モノレポ用の設定)
            appRoot: 'blog',
            frontend: {
              phases: {
                preBuild: {
                  // yarnに変更
                  commands: ['yarn install --frozen-lockfile'],
                },
                build: {
                  // yarnに変更
                  commands: ['yarn run build'],
                },
              },
              artifacts: {
                baseDirectory: '.next',
                files: ['**/*'],
              },
              cache: {
                paths: ['node_modules/**/*'],
              },
            }
          }
        ]
      })

モノレポ用の設定として、applicationsで階層化し、appNameソースコードプロジェクト上アプリを配置しているパスを指定します。この時appNameのパスは②のAMPLIFY_MONOREPO_APP_ROOTと一致させます。

また、今回Next.jsのアプリ作成時にyarnを使用しているため、コマンドを全般的に置き換えています。

環境依存パラメータの定義

parameter.tsに環境依存パラメータを定義するようにしています。今回はBLEAのサンプルの実装方法を参考にしてみました。

import { Environment } from 'aws-cdk-lib';

// Interfaceを定義
export interface AppParameter {
  env?: Environment,
  stackName: string,
  appName: string;
  ownerName: string;
  repositoryName: string;
  secretNameForGitHubToken: string;
}

// 各環境のパラメータを定義。例はdev
export const devParameter: AppParameter = {
  // 使用する環境のアカウントID、リージョン
  env: {
    // account: '',
    region: 'ap-northeast-1'
  },
  stackName: 'mazyu36-amplify',  // Stack名を指定

  // ここから下はAmplify Appで使用する値
  appName: 'nextjs-blog',  // Amplify Appの名称
  ownerName: 'mazyu36', // 参照するGitHubリポジトリのユーザー名
  repositoryName: 'cdk-amplify', // Next.jsアプリを入れるGitHubリポジトリ名
  secretNameForGitHubToken: 'github-token'  // GitHubトークンを格納するSecrets Managerのシークレット名
}

上記はbin/cdk_amplify.tsで使用します。

devParameterをインポートして、propsとして各パラメータを渡すだけです(これもBLEAの実装方法を参考にしています)

#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { CdkAmplifyStack } from '../lib/cdk_amplify-stack';
import { devParameter } from '../parameter';  // パラメータをimport

const app = new cdk.App();
new CdkAmplifyStack(app, 'CdkAmplifyStack', {
  // 環境はパラメータで指定があればそちらを使用、なければデフォルト
  env: {
    account: devParameter.env?.account || process.env.CDK_DEFAULT_ACCOUNT,
    region: devParameter.env?.region || process.env.CDK_DEFAULT_REGION,
  },
  stackName: devParameter.stackName, // Stack名を指定

  // ここから下はAmplify Appで使用する値
  appName: devParameter.appName,
  ownerName: devParameter.ownerName,
  repositoryName: devParameter.repositoryName,
  secretNameForGitHubToken: devParameter.secretNameForGitHubToken,


});

デプロイの実施

GitHubトークンを取得

まずはGitHubソースコードプロバイダーとして使うためのトークンの発行を行います。

手順としては以下になります。

docs.github.com

スコープとしては、参考資料に従いadmin:repo_hookにします。

Secrets Managerにトークンを登録

発行したGitHubトークンをSecrets Managerに登録します。

「そのほかのシークレットタイプ」にして、トークンを貼り付けます。

その後シークレットの名前を入力します。CDK上で指定するシークレット名と一致させる必要があります。

CDKデプロイの実施

cdk deployでAmplifyのアプリを作成します。完了すると以下のようにリソースが作成されます。

Next.jsアプリのデプロイ

私が実施したところ、初回のデプロイは手動でトリガーしないと実施されませんでした。

カスタムリソースなどで頑張ってもいいですが、初回だけのためにそこまで頑張るのも微妙かと思い、上記画面の「ビルドの実行」を押下して対処しました。

ビルドを開始すると以下のようにプロビジョニングから実施されます。

デプロイが完了すると以下のようになります。

動作確認

デプロイ完了後、払い出されたドメインにアクセスします。

アクセスしてNext.jsのアプリが表示されれば問題なしです。これでAmplifyによるホスティングの設定および動作確認完了です。

終わりに

Amplifyでアプリをホスティングするだけであれば、非常に簡単にできます。便利ですね。

Amplify初心者なので、他機能もキャッチアップしてもっと活用していきたいです。