Next.jsをLambda+CloudFrontでデプロイする

node: v20.11.0
next.jsを作成する
$ npx create-next-app@latest
作成時に聞かれることは以下のように回答
✔ What is your project named? … test-app
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like to use `src/` directory? … Yes
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … No
以降の参考元
最初にDockerfileを作成する。
利用するパッケージマネージャーはnpm
を想定している。
# Base
FROM public.ecr.aws/docker/library/node:20.11.0-slim AS base
WORKDIR /app
COPY package*.json ./
# 依存関係を解決するためのステージ
FROM base as dependencies
RUN npm ci
# ビルドするためのステージ
FROM base AS build
COPY . .
RUN npm run build
# 本番環境で実行するためのステージ
FROM public.ecr.aws/docker/library/node:20.11.0-slim AS production
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.3 /lambda-adapter /opt/extensions/lambda-adapter
ENV PORT=3000
WORKDIR /app
COPY --from=dependencies /app/node_modules ./node_modules
# カスタムサーバーで起動するためstandaloneをルートディレクトリ直下に展開する
COPY --from=build /app/.next/standalone ./
COPY --from=build /app/.next/static ./.next/static
COPY --from=build /app/public ./public
COPY --from=build /app/package*.json ./
COPY --from=build /app/next.config.js ./next.config.js
COPY --from=build /app/run.sh ./run.sh
# Lambda上でNext.jsのキャッシュファイルを書き込みするための設定 cf.https://tmokmss.hatenablog.com/entry/20221213/1670891305
RUN ln -s /tmp/cache ./.next/cache
#start the application
ENTRYPOINT ["sh"]
CMD ["run.sh"]
次にrun.sh
を作成する
#!/bin/bash -x
[ ! -d '/tmp/cache' ] && mkdir -p /tmp/cache
exec node server.js
next.config.js
を以下のように編集しstandaloneでビルドするように設定する。ちなみにファイル名がnext.config.mjs
になっていたらnext.config.js
にリネームしておくこと。
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
tailingSlash: true,
output: 'standalone',
compress: true,
}
module.exports = nextConfig

.dockerignoreファイルを追加
.env.local
node_modules
.vscode
.next
.git
.gitignore
.aws-sam
samconfig.toml
.vscode
は人によって.idea
の人もいると思う。
また.aws-sam
とsamconfig.toml
はのちに出てくる生成ファイル
デプロイには直接関係ないけど、この後生成されるファイルのうち、.aws-sam
とsamconfig.toml
はgit管理する必要がないので、.gitignore
ファイルに以下を追加する。
# aws-sam
/.aws-sam/
samconfig.toml
template.yml
を作成
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: "test app project"
Globals:
Function:
Timeout: 60
Resources:
StreamingNextjsFunction:
Type: AWS::Serverless::Function
Properties:
MemorySize: 256
PackageType: Image
Architectures:
- arm64
Environment:
Variables:
AWS_LWA_INVOKE_MODE: response_stream
FunctionUrlConfig:
AuthType: NONE
InvokeMode: RESPONSE_STREAM
Metadata:
DockerTag: v1
DockerContext: ./
Dockerfile: Dockerfile
Outputs:
StreamingNextjsFunctionOutput:
Description: "Streaming Nextjs Function ARN"
Value: !GetAtt StreamingNextjsFunction.Arn
StreamingNextjsFunctionUrlOutput:
Description: "nextjs streaming response function url"
Value: !GetAtt StreamingNextjsFunctionUrl.FunctionUrl

$ sam build && sam deploy --guided
を叩いてLambda上にデプロイする。
この時、Dockerが使えること、aws configureにて有効なトークンが設定されている必要がある。
この後デプロイについて聞かれるが、主に変更するのはStock Name
とStreamingNextjsFunction Function Url has no authentication. Is this okay?
の時にyes
を選択する。
Setting default arguments for 'sam deploy'
=========================================
Stack Name [sam-app]: test-app
AWS Region [ap-northeast-1]:
#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
Confirm changes before deploy [y/N]:
#SAM needs permission to be able to create roles to connect to the resources in your template
Allow SAM CLI IAM role creation [Y/n]:
#Preserves the state of previously provisioned resources when an operation fails
Disable rollback [y/N]:
StreamingNextjsFunction Function Url has no authentication. Is this okay? [y/N]: y
Save arguments to configuration file [Y/n]:
SAM configuration file [samconfig.toml]:
SAM configuration environment [default]:
最後の出力結果のvalue
に記載のURLでLambda上に上がったWebアプリにアクセスできる。
-------------------------------------------------------------------------------------------------------
Outputs
-------------------------------------------------------------------------------------------------------
Key StreamingNextjsFunctionOutput
(中略)
Value https://abc123def456ghr789jkl.lambda-url.ap-northeast-1.on.aws/
-------------------------------------------------------------------------------------------------------

このままでは認証がないのでCloudFrontと繋げたい。
まずは関数URLの認証タイプをNONE
からAWS_IAM
に変更する。
CloudFrontを作成する。
Origin domainに先ほどのValueに記載されているURLを入れる。
WAFのセキュリティ保護は有効にする。
その後、作成する。
ここからは独自の設定。
CloudFrontのセキュリティタブにてWAFの設定をする。
Editを押した後、View details of your configration
をクリックする。
RulesタブでAdd rules
をクリック後、Add my own rules and rule groups
をクリックしてルールを追加する。
RuleTypeをIP setに変更して、RuleのNameは適当に入れる。IP setに事前に設定していたIPセットを選択する。
ここで一度Add ruleでルール追加する。
その後再びEditボタンを押して、If a request
のところをdoesn't match the statement(NOT)
に変更する。
最後にすでに追加されている3つのルールを一度削除しておく。

オリジンを編集する。
Create new OACを選択してOACを作成する。下にAWS CLIでLambdaの設定を変更するコマンドをローカルで叩く。この時FUNCTION _NAMEを入れること。
最後に「変更を保存」を押して完了する。