Top View


Author kta-m

Next.jsをAWSにデプロイするための実践的な環境を作るぞ!

2020/12/19

Next.jsをデプロイするぞ!

いま、Next.jsを使って開発を行なっています。 Next.jsのデプロイ先としては、開発元のVercelが提供するプラットフォームVercel Platformがあります。

公式が言うにはVercel Platformが推奨ということにはなっていますが、今回はバックエンド側もAWS上にあるので、フロントエンドもAWSにあったほうが何かと便利!ということでAWS上にデプロイすることにしました。

Serverless Next.js Componentを使うぞ!

デプロイにはServerless Next.js Componentを使います。

Serverless Componentsは、Serverless Frameworkを介してサーバーレスなアプリケーションのよくある構成を設定ファイルだけでいい感じにデプロイしてくれる便利なヤツです。

Serverless Next.js ComponentはそのNext.jsデプロイ用コンポーネントです。CloudFrontとAWS Lambda@Edgeにデプロイしてくれます。

シンプルにデプロイするぞ!

デプロイするだけなら超簡単です。

Next.jsのプロジェクトルートで、serverless.ymlという名前のファイルを作ります。 {version_here}にはバージョンを入れます。2020/12/19時点で最新版は1.18.0です。

myNextApplication: # 名前はなんでもいい
  component: "@sls-next/serverless-component@{version_here}"

そして、おもむろに以下のコマンドを叩きます。 AWS_PROFILEに与えるのはAWS CLIのプロファイル名です。 必要なパーミッションは公式のREADMEを参照してください。

$ AWS_PROFILE={profile_name_here} npx serverless

なんとこれだけでデプロイできてしまいます!

設定がなさ過ぎて不安になりますが、本当にこれだけです。 すごいぞ!Serverless Components!

デプロイ先をstaging / productionで切り替えるぞ!

ここから実運用ができるよう頑張っていくわけですが、 まずしないといけないのが、デプロイ先をstaging / productionで切り替えることです。

問題は2つ。

  • 設定ファイルの名前はserverless.ymlのみで、オプションで指定することができない。
  • デプロイ時にできる.serverlessディレクトリにCloudFrontの設定等が保存されているが、こちらも名前が固定なのでデプロイ先を切り替えると上書きされてなくなってしまう。

これらを解決するために、ちょっと泥臭いですがデプロイ用のスクリプトを書きました。 書いてあるとおりですが、以下のことをしています。

  • staging/production用の設定ファイルを都度プロジェクトルートにコピー
  • .serverlessディレクトリの中身をS3に退避、復元
#!/bin/bash

set -e

export APP_NAME=sample-app
export AWS_PROFILE=$APP_NAME-$STAGE
export BUCKET_NAME=$APP_NAME-config-$STAGE

# 念のためクリーンアップ
if [ -e ./serverless.yml ]; then
    rm ./serverless.yml
fi
if [ -d ./.serverless ]; then
    rm -rf ./.serverless
fi
if [ -d ./.serverless_nextjs ]; then
    rm -rf ./.serverless_nextjs
fi

# serverlessの設定ファイルをコピー
cp ./bin/serverless/serverless.$STAGE.yml ./serverless.yml

# S3からデプロイ設定ファイルを復元
aws s3 cp s3://$BUCKET_NAME/.serverless ./.serverless --recursive

# デプロイ
npx serverless

# S3にデプロイ設定ファイルを退避
aws s3 cp ./.serverless s3://$BUCKET_NAME/.serverless --recursive

# 後片付け
rm ./serverless.yml
rm -rf ./.serverless
rm -rf ./.serverless_nextjs

ディレクトリ構造は以下です。

APP_ROOT
├── ...(その他いろいろ)
└── bin
    ├── serverless
    |   ├── serverless.stg.yml    # staging用
    |   └── serverless.prd.yml    # production用
    └── deploy.sh                 # ↑のスクリプト

AWS CLIのプロファイル名は$APP_NAME-$STAGEと合うように設定しておいてください。 また、事前に$APP_NAME-config-$STAGEの名前でS3バケットを用意してください。

こうしておけば、例えばstaging環境にデプロイするときは以下のコマンドを叩けばOKです。 package.jsonに設定しておけば便利です。

$ STAGE=stg ./bin/deploy.sh

設定をカスタマイズするぞ!

リソース名を固定する

なにも設定しなければ、S3のバケット名やLambda@Edgeの関数名がランダムになってしまいます。 何のリソースなのか分かるよう、名前を指定しておきましょう。{app_name_here}にはアプリ名を入れます。 IAMロールも作られますが、なぜかこれは名前を固定する設定がありませんでした。。

myNextApplication: # 名前はなんでもいい
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    bucketName: "{app_name_here}-stg"
    name: 
      apiLambda: "{app_name_here}-api-func-stg"
      defaultLambda: "{app_name_here}-default-func-stg"

Lambda@Edgeのスペックを指定する

SSRをする場合、ある程度の規模のアプリケーションになるとLambdaの初回起動時の処理が重くなり、デフォルトだとタイムアウトして503が返ってくることがありました。 アプリケーションの内容に合わせて適切にスペックを指定しましょう。

myNextApplication: # 名前はなんでもいい
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    memory:
      defaultLambda: 2048
      apiLambda: 1024
    timeout:
      defaultLambda: 10
      apiLambda: 10

stagingにBasic認証やIP制限をかける

staging環境のアクセスは制限したいですよね。 アクセスの前にアクセス制限の処理をするLambda@Edgeを実行するようにしましょう。 Serverless ComponentsにはLambdaを作る機能はないので、別途作ってLambda@Edge関数のARNを指定します。

Lambda@Edgeを作る際には以下の点に注意しましょう。

  • リージョンはus-east-1(バージニア北部)
  • 指定するARNには$LATESTやエイリアスが使えないので、バージョンを発行する必要がある
myNextApplication: # 名前はなんでもいい
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    cloudfront:
      defaults:
        lambda@edge:
          viewer-request: arn:aws:lambda:us-east-1:xxxxxxxxxx:function:BasicAuthentication:1

たとえばBasic認証をかける場合は次の記事が参考になると思います。 CloudfrontとS3でBasic認証をかける - Qiita

ただ、Basic認証に関してはServerless Next.js Componentの1.19.0-alpha.3からserverless.ymlの設定で実現できるようになっているようです。

コードサイズを削減する(experimental)

デフォルト設定では、node_modulesの内容がSSRを行う画面ごとにまるっと入ってしまうため、SSRの画面が増えるとわりと簡単にLambdaのコードサイズ制限50MBをおびやかします。 SSRの画面はなるべく減らしましょうという話ではあるのですが、減らすのもなかなか大変です。特に認証が絡んでくると。

useServerlessTraceTargetを使うとnode_modulesの中で実際に使われているものだけが入るようで、大幅にサイズが減ります。が、まだ "experimental" なのでご注意ください。

myNextApplication: # 名前はなんでもいい
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    useServerlessTraceTarget: true

また、これを使う際には環境変数NODE_ENVproductionを入れておく必要があります。 プロジェクトルートに.env.productionファイルを作り、以下のように書きましょう(next.jsの機能です)。

NODE_ENV=production

まとめ

お試しでやってみるだけであればほぼ設定なしでいけますが、本番利用にはちょっとコツがいりました。 みなさまのNext.jsの本番利用が捗れば幸いです。

kta-m

kta-m

先進技術部門 IoTチーム チームリーダー