Azure FunctionsでNestJSを動作させる方法
あけましておめでとうございます。
2024年の振り返りやら今年の目標やらを書こうと思いつつ年が明けまして、とりあえず記事を投稿しておきたいと思います。
以前の記事でNestJS学び始めとして記事を書きました。
当然?ローカルで動かしているだけでは意味は無く、サーバー、私の場合はAzureにデプロイすることを目指してみます。
VMであれば上の記事がそのまま流用できるので何の問題も無いのですが、できればAzure AppServices、更に言えばAzure Functionsにインストールしたいところです。
で、調べてみたところ、NestJSが流行り始めたであろう2019~2020年頃に幾つか記事が書かれていました。
https://qiita.com/nikaera/items/e860a58620368c25ffbf
そしてこれらの記事では以下の「azure-func-http」というモジュールが使われており、このモジュールを組み込んでFunctionsにデプロイすれば動作する。というものになります。
https://github.com/nestjs/azure-func-http
ただこのモジュール、最新のNestJS環境には対応していないらしく、実行時にエラーが発生してしまいます。
このモジュールから派生した別のAzure Functions対応のモジュールも見つけたのですが、これもうまくいきませんでした。
ちなみにAWSではLambdaで動作することはわかっており、なおさらFunctionsで動かしたくなりました。
上のモジュールはGitHubで公開されていることもあり、私がNestJSを知り尽くしているのであれば上記のモジュールを修正して。。ということもできるかもしれませんが、残念ながらそこまでの知識は無いため、別の方法を探してみた結果がこれから説明するコンテナを用いたデプロイとなります。
事前準備としてDocker Desktopをインストールする必要があります。
それが終わりましたらいよいよ作業開始です。
まずはリソースグループを作成します。
az group create --name myResourceGroup --location japaneast
上のコマンドのうち、「myResourceGroup」がリソースグループ名になりますので好きな名前を設定してください。
あとは「--location」の後にはリージョンを設定します。今回は「japaneast」、つまり東日本リージョンを設定してますが、これも好みのリージョンを設定してください。
次にNestJSプロジェクトを「ローカル上に」作成します。 NestJS CLIのインストールも行っていますが、不要であれば「nest new」から実行してください。
npm install -g @nestjs/cli
nest new my-nest-app
無事作成できたら、そのプロジェクトのディレクトリに移動します。
cd my-nest-app
次にプロジェクトのルートディレクトリに 「Dockerfile」というファイル名(拡張子は無しでファイル名はそのまま「Dockerfile」です)のファイルを作成し、それをVSCodeで開いて上記の内容をコピペしてください。
# ベースイメージとしてNode.jsを使用
FROM node:20.10.0-slim
# 作業ディレクトリを設定
WORKDIR /app
# 依存関係をインストール
COPY package*.json ./
RUN npm install
# アプリケーションのソースコードをコピー
COPY . .
# アプリケーションを起動
CMD ["npm", "run", "start:prod"]
次に同じくプロジェクトのルートディレクトリに「docker-compose.yml」という名前でファイルを作成し、以下の内容を記述します。
version: '3.8'
services:
api:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
volumes:
- .:/app
command: npm run start:dev
Dockerコンテナのビルドと起動
以下のコマンドを実行して、Dockerコンテナをビルドし、起動します。
docker-compose up --build
色々と処理内容が表示され、以下のような表示になったらローカル環境での作業・確認はOKとなります。

ここからはAzureに対する作業となります。
Dockerイメージをビルドし、Azure Container Registry (ACR) にプッシュするのですが、まずはACRを作成します。
「myuniqueregistryname」がACRの名前なので好きな名前を付けてください。お試しであればそのままでも構いません。
az acr create --resource-group myResourceGroup --name myuniqueregistryname --sku Basic
リソースグループとACRがちゃんと作られていることを確認

次にDockerイメージのビルド。
docker build -t myuniqueregistryname.azurecr.io/my-nest-app:latest .

ACRにログインし、
az acr login --name myuniqueregistryname
ログイン成功

Dockerイメージをプッシュします
docker push myuniqueregistryname.azurecr.io/my-nest-app:latest
これも正常に完了していることを確認

ランタイムとバージョンを指定してAzure Functionsアプリを作成し、デプロイします。
その前にストレージアカウントを作成します。
az storage account create --name mystorageacct20241123 --resource-group myResourceGroup --location japaneast --sku Standard_LRS
次にApp Serviceプランを作成
az appservice plan create --name myAppServicePlan --resource-group myResourceGroup --location japaneast --sku B1 --is-linux
そしてAzure Functionsアプリを作成します。
このあたりのコマンドプロンプト上の結果については割愛しますが、それぞれ成功しているかどうかはコマンドプロンプトとAzureポータルをそれぞれ確認しておいてください。
az functionapp create --resource-group myResourceGroup --name myUniqueFunctionApp2024 --storage-account mystorageacct20241123 --plan myAppServicePlan --runtime node --runtime-version 20 --deployment-container-image-name myuniqueregistryname.azurecr.io/my-nest-app:latest
Azureポータルで今回作成したリソースグループを選択すると以下のように今まで作成したリソースが存在しています。

環境変数を設定します。
az functionapp config appsettings set --name myUniqueFunctionApp2024 --resource-group myResourceGroup --settings FUNCTIONS_WORKER_RUNTIME=node
次にAzure Container Registry (ACR)からDockerイメージをプルするための認証情報を設定するのですが、そのためにその認証情報を取得する必要があります。
まずはAzureポータルでACRを選択します。
①左側のメニューから「アクセスキー」を選択
②「管理者ユーザー」にチェックを入れる
③パスワード(passwordとpassword2どちらでも)の「表示」ボタンを押す
④ユーザー名とパスワードをメモ

以下のコマンドの「ユーザー名」と「パスワード」に上で取得したユーザー名とパスワードを設定し、実行してください。
az functionapp config appsettings set --name myUniqueFunctionApp2024 --resource-group myResourceGroup --settings DOCKER_REGISTRY_SERVER_URL=https://myuniqueregistryname.azurecr.io DOCKER_REGISTRY_SERVER_USERNAME=<ユーザー名> DOCKER_REGISTRY_SERVER_PASSWORD=<パスワード>
最後に認証情報を設定した後、Azure Functionsを再起動します。
az functionapp restart --name myUniqueFunctionApp2024 --resource-group myResourceGroup
これで、NestJSプロジェクトを作成し、Dockerを使用してAzure Functionsにデプロイするという手順は完了です。
FunctionsのURLにアクセスしてみましょう。
恐ろしく寂しい画面ではありますが、無事動作していることがわかります。

以上が、Functionsにデプロイするための手順となります。
もしこれ以降ソースコードを修正して再度デプロイしたい場合は「docker build」、「az acr login」、「docker push」、そしてFunctionsの再起動を行えばOKです。
独自にコードをゴリゴリ書き、もしくは最初に挙げた既存のモジュールを修正してFunctionsにデプロイしている人もいるかもしれませんが、一番簡単な方法としてはこれになるのではないでしょうか。
もし他の方法をご存じの方がいらっしゃいましたら是非教えていただければと思います。