Amplifyを使えば5分でNext.js環境を公開できちゃう

8 min読了の目安(約7800字TECH技術記事

はじめに

はい、詐欺タイトルです
何も知らない状態から Amplify を使って Next.js をデプロイするのに私は 1 日半ほど費やしました
ただ、デプロイするまでにいくつかあるつまづきポイントを超えるとほんとに簡単にデプロイして公開できるので、そのつまづきポイントをご紹介できれば

記事のターゲット

  • S3 + CloudFront に Next.js 環境をデプロイしたい人
  • Amplify 使ってみたい人
  • Amplify でのデプロイ は GitHub 連携じゃなくて CLI 使って手動でしたい人

概要

なんと! 今なら!! たったの 5 ステップで!!!

  1. Amplify CLI のインストール
  2. デプロイ権限がある IAM の作成
  3. amplify init
  4. amplify add hosting
  5. amplify publish

環境

関係ありそうなのだけ
@aws-amplify/cliはグローバルにインストールしてます

"@aws-amplify/cli": "4.41.2",
"next": "10.0.6",
"react": "17.0.1",
"react-dom": "17.0.1",
"typescript": "4.1.3",

Next.js は CSR でデプロイする前提です

いざ参る

1. Amplify CLI のインストール

npm install -g @aws-amplify/cliして AmplifyCLI をインストール

つまづきポイント 1

yarn で @aws-amplify/cliをインストールしない

最初に amplify の設定をするのにamplify initをする際に
自分のリポジトリ配下に@aws-amplify/cliを yarn でインストールしたらうまくいかなかった

amplify initの設定中に下記エラーが発生する
(node:98501) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'value' of undefined

issue を読み漁るとnpm install -g @aws-amplify/cliしてねとのこと

https://github.com/aws-amplify/amplify-cli/issues/839#issuecomment-618474964

2. デプロイ権限がある IAM の作成

今回は事前にデプロイ用の IAM が用意されていたのでやっていないが
amplify configureすると aws console からいくつかの手順を進めると AdministratorAccess 権限を持った User を作成してくれる親切設計

https://docs.amplify.aws/cli/start/install#configure-the-amplify-cli

3. amplify init

Amplify App を作るための初回設定
実行すると CLI 上で設定に必要な情報を登録する流れに

❯ amplify init
Initializing new Amplify CLI version...
Done initializing new version.
Scanning for plugins...
Plugin scan successful
Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project {プロジェクト名}
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using react
? Source Directory Path:  src
? Distribution Directory Path: out
? Build Command:  yarn build
? Start Command: yarn start
Using default provider  awscloudformation
? Do you want to use an AWS profile? No
? accessKeyId:  ***
? secretAccessKey:  ***
? region:  ap-northeast-1

accessKey と secretAccessKey はデプロイ権限を持ってるやつ

無事に終わるとルート直下にamplifyディレクトリが生成される

つまづきポイント 2

S3 にデプロイできるように Next.js の設定を変えるんや

Next.js を S3 の静的ホスティング(or CloudFront で CDN 配信)するには build 時のコマンドを変えとく
下記のように build コマンドを変更すると build されたファイルが/outディレクトリに生成される

package.json
"scripts": {
  "build": "next build && next export",
},

4. amplify add hosting

どのようにホスティングするか設定する

❯ amplify add hosting

? Select the plugin module to execute: Hosting with Amplify Console
? Choose a type: Manual deployment

無事に終わるとamplify/backend/hosting/amplifyhostingが追加される

つまづきポイント 3

Select the plugin module to executeで二つの選択肢が出てくるが、Hosting with Amplify Console を選ぶんや

❯ Hosting with Amplify Console (Managed hosting with custom domains, Continuous deployment)
  Amazon CloudFront and S3

え、当たり前のようにAmazon CloudFront and S3使うんしょって思ってそっち選ぶとAccessDenied の壁を超えられず
できた CloudFront や S3 のバケットの中を見る感じ、うまくできてるはずなんだけど・・

TODO: Amazon CloudFront and S3 でも公開できるようにする

amplify publish

amplify publishで設定した内容でデプロイしてくれる

番外編: CICD に組み込みたい(CircleCI 編)

  1. amplify initを Headless mode で定義する
  2. local でamplify initする
  3. local でamplify add hostingする
  4. config.ymlにデプロイ step を追加する

1. amplify initを Headless mode で定義する

CICD で実行できるように Headless mode なるものが Amplify CLI にはある
サンプルに書かれているものを shell で定義した

/scripts/amplify_init/main.sh
#!/bin/sh

set -e
IFS='|'

ACCESS_KEY=""
SECRET_KEY=""

usage () {
  cat <<EOM
Usage: $(basename "$0") [OPTION]...
  -h               Display help
  -a ACCESS_KEY    Access key of AWS
  -s SECRET_KEY    secret key of AWS
EOM

  exit 2
}

while getopts "a:s:h" OPT
do
  case $OPT in
    "a" ) ACCESS_KEY="$OPTARG";;
    "s" ) SECRET_KEY="$OPTARG";;
    '-h'|'--help'|* ) usage ;;
  esac
done

REACTCONFIG="{\
\"SourceDir\":\"src\",\
\"DistributionDir\":\"out\",\
\"BuildCommand\":\"yarn build\",\
\"StartCommand\":\"yarn start\"\
}"
AWSCLOUDFORMATIONCONFIG="{\
\"configLevel\":\"project\",\
\"useProfile\":false,\
\"profileName\":\"default\",\
\"accessKeyId\":\"$ACCESS_KEY\",\
\"secretAccessKey\":\"$SECRET_KEY\",\
\"region\":\"ap-northeast-1\"\
}"
# project-nameには任意のproject名
AMPLIFY="{\
\"projectName\":\"project-name\",\
\"envName\":\"dev\",\
\"defaultEditor\":\"code\"\
}"
FRONTEND="{\
\"frontend\":\"javascript\",\
\"framework\":\"react\",\
\"config\":$REACTCONFIG\
}"
PROVIDERS="{\
\"awscloudformation\":$AWSCLOUDFORMATIONCONFIG\
}"

amplify init \
--amplify $AMPLIFY \
--frontend $FRONTEND \
--providers $PROVIDERS \
--yes

ほぼやってることはサンプルコードと一緒だが AWS のアクセスキーとシークレットキーを CircleCI の Context から受け取れるように引数でもらってる

2. amplify initamplify add hosting

前段で記載した通り
プロジェクトに設定情報を保存するため、ローカルで必ず一度実行する
amplify initに関しては定義した shell を実行すれば良き

config.ymlにデプロイ step を追加する

deploy_stg:
  executor: default
  steps:
    - checkout
    - attach_workspace:
        at: .
    - run:
        name: Install amplify cli
        command: curl -sL https://aws-amplify.github.io/amplify-cli/install | bash && $SHELL
    - run:
        name: Update path
        command: echo 'export PATH="$HOME/.amplify/bin:$PATH"' >> $BASH_ENV
    - run:
        name: Init amplify
        # contextから環境変数は取得する
        command: sh scripts/amplify_init/main.sh -a "$AWS_ACCESS_KEY_ID" -s "$AWS_SECRET_ACCESS_KEY"
    - run:
        name: Deploy
        command: amplify publish --yes

つまづきポイント 4

Amplify cli のインストールはcurl -sL https://aws-amplify.github.io/amplify-cli/install | bash && $SHELLやぞ
あと、インストールした後は path 通すんやで

つまづきポイント 5

image: cimg/node:14.15.4を使ってると Next の build がこけるから
yarn add sharpしとく

cimg/node:14.15.4のコンテナ内で build すると下記のようなエラーを吐く

> Build error occurred
Error: Cannot find module 'sharp'
Require stack:
- /home/circleci/src/node_modules/next/dist/build/index.js
- /home/circleci/src/node_modules/next/dist/cli/next-build.js
- /home/circleci/src/node_modules/next/dist/bin/next
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15)
    at Function.resolve (internal/modules/cjs/helpers.js:94:19)
    at /home/circleci/src/node_modules/next/dist/build/index.js:11:1029
    at /home/circleci/src/node_modules/next/dist/build/tracer.js:1:1331
    at NoopTracer.withSpan (/home/circleci/src/node_modules/@opentelemetry/api/build/src/trace/NoopTracer.js:47:16)
    at ProxyTracer.withSpan (/home/circleci/src/node_modules/@opentelemetry/api/build/src/trace/ProxyTracer.js:36:34)
    at traceFn (/home/circleci/src/node_modules/next/dist/build/tracer.js:1:1301)
    at /home/circleci/src/node_modules/next/dist/build/index.js:11:365
    at async /home/circleci/src/node_modules/next/dist/build/tracer.js:1:1441 {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    '/home/circleci/src/node_modules/next/dist/build/index.js',
    '/home/circleci/src/node_modules/next/dist/cli/next-build.js',
    '/home/circleci/src/node_modules/next/dist/bin/next'
  ]
}
error Command failed with exit code 1.

yarn add sharpせえよとのこと

https://github.com/vercel/next.js/blob/canary/errors/install-sharp.md

番外編: Amplify コンソールは Basic 認証がめっちゃ楽

Cloud Front + S3 環境だと Cloud Front 側で Basic 認証の設定するような Lambda を実装してたけど、Amplify コンソールでは画面ぽちぽちですぐ Basic 認証できる