Next.JSをAzureのWeb App Serviceにデプロイする

最近、新しいアプリを作ってみたらデプロイに苦労したのでメモを残しておく。 2023.9.13時点で、Next.JSのアプリは、VSCode拡張機能や、Web App Serviceから自動設定できるGitHub連携のデフォルトのGitHub Actionでは、Next.JSのデプロイに時間がかかりすぎたり、うまくデプロイできないといった問題が生じている。原因としては、非常に膨大なファイル数をアップロード/デプロイしているからの様子。例えばnode_modules以下のファイル群など。

対策としては、Next.JSをStandaloneモードでビルドし、必要最小限のファイルに絞ってデプロイすればよいのだが、ビルドにいろいろ手を加える必要がある。

Standaloneモードでビルドするには、next.config.jsのoutputにstandaloneを設定すればよい。またdistDirでビルド結果の出力先を指定しておくほうが、デプロイに必要なファイルを集めやすかった。そのため、buildに出力するとする。

/** @type {import('next').NextConfig} */
const nextConfig = {
  distDir: 'build',
  output: 'standalone',
}

module.exports = nextConfig

次に、package.jsonのscriptにpostbuildを追加する。postbuildはbuild終了時に実行したいコマンドを定義することができる。Next.JSではCDEに配置すべきファイルということでビルド時にいくつかのファイルは分けられているらしい。CDEを活用する場合は違った設定を考えたほうが良いのだろう。(ここではそこまで考えない)

また、実行時に不要なモジュールはdevDependenciesへ変更している。

{
  "name": "next-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "postbuild": "cp -r public build/standalone && mkdir -p build/standalone/public/_next && cp -r build/static build/standalone/public/_next/",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "next": "13.4.19",
    "react": "18.2.0",
    "react-dom": "18.2.0"
  },
  "devDependencies": {
    "@types/node": "20.6.0",
    "@types/react": "18.2.21",
    "@types/react-dom": "18.2.7",
    "eslint": "8.49.0",
    "eslint-config-next": "13.4.19",
    "typescript": "5.2.2"
  }
}

最後に、GitHub Actionsのymlファイルを以下のように直す。

# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy Node.js app to Azure Web App - next-sample-app

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up Node.js version
        uses: actions/setup-node@v3
        with:
          node-version: '20.x'

      - name: npm install, build, and test
        run: |
          npm install
          npm run build --if-present
          npm run test --if-present
          npm prune --production
          cd build/standalone
          zip -qr ../../package.zip .
          cd ../../
        env:
          DATABASE_URL: ${{ secrets.AZURE_DATABASE_URL }}

      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v3
        with:
          name: node-app
          path: package.zip

      - name: Clean
        run: |
          rm -rf build
          rm -rf package.zip
  
  deploy:
    runs-on: ubuntu-latest
    needs: build
    environment:
      name: 'Production'
      url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

    steps:
      - name: Download artifact from build job
        uses: actions/download-artifact@v3
        with:
          name: node-app

      - name: 'Deploy to Azure Web App'
        id: deploy-to-webapp
        uses: azure/webapps-deploy@v2
        with:
          app-name: 'next-sample-app'
          slot-name: 'Production'
          publish-profile: ${{ secrets.XXXX }}
          package: package.zip

ポイントだけ説明する。

buildすると、build/standaloneフォルダに必要なファイル一式がそろうようにしている。(postbuildで必要なファイルのコピーを追加したため。) それをpackage.zipに圧縮している。

          cd build/standalone
          zip -qr ../../package.zip .
          cd ../../

アップロード/ダウンロード時間を軽減にはかなり効く。実際速くなっている。

本記事では、AzureのWeb App Serviceのデプロイを対象に説明したが、少しアレンジすれば、ほかの環境へのデプロイでも参考になるのではないかとは思う。

以上。

2023/10/29追記

スタンドアローンモードでビルド、実行するため、Web App Serviceのスタートアップコマンドは以下に変更する必要があった。

node server.js

Web App Serviceの場合、Webの管理画面から、「設定 > 構成 > 全般設定 > スタートアップ」で設定する必要があった。 変更していないままデプロイするとコマンドが見つからずエラーが起きる。

2024/2/10追記

Next.JSのデフォルトポートは3000だが、Web App Serviceのデフォルトは8080なので、対応させる必要がある。 これは「構成」からアプリケーション設定にPORTを3000として追加するだけでよい。