uniface.hub

uniface.hub

ユニフェイスの開発者ブログ


Title Azure AppService に Next.js + C# ASP.NET Core Web Api をデプロイする
  • 2022年10月24日
  • 田港洋賢
Azure AppService に Next.js + C# ASP.NET Core Web Api をデプロイする


こんにちは、最近ユニフェイスに入社した田港です

今までの会社では 派遣で既存システムの一部の改修案件に携わることが多く、いつか一気通貫で Web システムをつくってみたいなぁと考えていました

ユニフェイスでは早速その機会をいただき、大変ですがやりがいを感じながら仕事をさせていただいています
システム開発の醍醐味を味わいたい方は、ぜひユニフェイスの門を叩いてみてはいかがでしょうか?

さて、それではタイトルにあるとおりAzure AppService 上に Next.js + C# ASP.NET Core Web Apiをデプロイしていきたいと思います
題材に選んだ理由は以下です

  • Next.js を使ったことがないので試したい
  • Azure AppService のみでクライアントとサーバーの両方を立ち上げて運用できるのか試したい

また、必要になったリソースは以下の通りでした

  • dotnet6 sdk
  • docker
  • node.js + npm

まずはプロジェクトをつくる

WebApiプロジェクト作成

まずはC# ASP.NET Core Web Apiプロジェクトの雛型を用意しましょう
ASP.NET Coreには便利なdotnetコマンドがあるので、そちらを利用してプロジェクトを作成していきます
事前に.NET SDKのインストールが必要なので、以下の公式ドキュメントを参考にインストールします
.NET Tutorial | Hello World in 5 minutes (microsoft.com)

dotnetコマンドには React+WebApi のテンプレートが用意されており、シンプルに React のみを利用する場合にはそちらのコマンドを用意したほうが便利ですが、  今回は Next.js を利用してみたいので、一旦、C# WebApi のみを生成したいと思います

dotnetコマンドで何度かプロジェクトを作ったことはあるのですが、もちろんコマンドは覚えていない🤤ので、dotnet --helpを連打して、WebApi のみのテンプレートを生成する以下のコマンドを導き出しました
dotnet new WebApi --name {プロジェクト名}

これによりひとまず C# Web Api のプロジェクトができます
私の場合プロジェクト名をWebAppにしておきました
(最初にめんどくさがって--nameオプションを省略したらディレクトリがファイルの海になって泣きを見ました 😭)

Next.js のプロジェクト作成

次はNext.jsのプロジェクト生成です
Next.js の公式サイトで以下のコマンドを見つけました
typescript オプションもつけてます。型大好き🤤)
npx create-next-app@latest --typescript

コマンドを実行するとディレクトリ名とか聞かれるのでよしなに入力していきます
私の場合nextjs-appで作成しました  作成後のディレクトリ構成は以下のようになりました

WebApp
 |- nextjs-app
 ~~~
 |- WebApp.csproj


Next.js が初めてなのでチュートリアルにあるとおり、npm run devをやってみます
うおーうごいたー🎉

うごいた

ローカル環境で Next.js から WebApi に要求してみる

Next.js と WebApi を立ち上げて連携ができるか確認してみます
WebApi はデフォルトでhttp[s]://localhost:{port}/weatherforecastで天気予報を返してくれるので、それを取得してみましょう

Next.js は html の生成方式がいくつか利用できるようですが、Azure の環境変数注入でサイト内容を変更したりできそうな、SSR (サーバーサイドレンダリング)をやってみようと思います

SSR を行う場合、getServerSidePropsという関数を用意してあげるとレンダリング前にデータ取得ができるそうなので、ひとまずnextjs-app/pages/index.tsxに以下のコードを追記してみます
(データ取得にaxiosを使用しています)

import type { NextPage, GetServerSidePropsContext } from "next";
~~~

// ☆ SSRデータフェッチ
export async function getServerSideProps(context: GetServerSidePropsContext) {
  // 環境変数SERVER_PORTから接続先のポート番号を取得する
  const port = process.env.SERVER_PORT ?? "80";
  // WebApiからデータ取得
  const data = await axios
    .get(`https://localhost:${port}/weatherforecast`)
    .then((resp) => resp.data);
  return {
    props: {
      data,
    },
  };
}

const Home: NextPage<IProps> = (props: IProps) => {
  ~~~

とりあえず上記をindex.tsxに反映してみます
(環境変数 SERVER_PORT に WebApi のポート設定お忘れなく)

怒られた。。

怒られました
どうやら Next.js さんはhttps通信時の自己署名証明書を絶対許さないマンで有名みたいです。。
httpsで通信しようとしたため、その問題に引っかかったということですね

上記エラーを回避するためプロキシーを作成してくださっている方もいるようですが、今回は http 通信にしちゃって問題をうやむやにして進めます 🤫
getしているところのURLをhttps→httpにします
.get(http://localhost:${port}/weatherforecast)

ついでに、APIから取得したデータを表示するようにindex.tsxの コードを追加で修正します

~~~
const Home: NextPage<IProps> = (props: IProps) => {
    return (
        // 中略
        {props.data.map((wfc, idx) => (
            <div key={idx}>
            <label>{`${wfc.date} : ${wfc.summary} : ${wfc.temperatureC} : ${wfc.temperatureF}`}</label>
            </div>
        ))}
    )
}

さらに、WebApi の初期設定では http 通信を https にリダイレクトしてしまうので、そちらも無効化します
プロジェクトルートのprogram.csを以下のように修正します
(☆ コメント部分)

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
// ☆else文追加
else
{
    app.UseHttpsRedirection();
}
// ☆ 無効化
//app.UseHttpsRedirection();

app.UseAuthorization();

これでローカル環境で Next.js と WebApi の連携ができるようになったのを確認しました

疎通確認

Azure AppService にデプロイ

ローカルで動かせることは確認したので、いざデプロイです

さて、Azure AppService で動作させる際、Next.js と WebApi の両方を稼働させないといけませんが、  通常はランタイムを node か dotnet かどちらかを選ばないといけません

そこをどうにかしないことには動かしようがないため、その辺の融通が利きそうな docker でのデプロイを目論んでいこうと思います

dockerfile を書く

さて、dockerfile を書いていくのですが、、 docker も勉強したっきり何もかも忘れてしまっている😭
、、ので、  ググったり埃のかぶったファイルを引っ張り出して思い出していきます

幸いなことにMicrosoft の公式ドキュメントが用意されていたのでありがたく参考にさせていただきました
ASP.NET Core 向けの Docker イメージ

上記により、以下のdockerfileが生み出されました(長い。。)

# ビルド用ステージ
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
## nodeとnpmをインストール
RUN apt update -y && apt install nodejs npm -y

## /sourceディレクトリを作成してプロジェクト内のファイルを全コピー & C#プロジェクトを公開ビルド
WORKDIR /source
COPY . .
RUN dotnet publish -c release -o /app

## Next.jsのプロジェクトをビルド
WORKDIR /source/nextjs-app
RUN npm i
RUN npm run build


# 実行用イメージ作成ステージ
FROM mcr.microsoft.com/dotnet/aspnet:6.0
## nodeとnpmをインストール
RUN apt update -y && apt install nodejs npm -y

## /appディレクトリを作成し、ビルドイメージからプロジェクトをコピーする
WORKDIR /app
COPY --from=build /source/nextjs-app/. /app/nextjs-app/.
COPY --from=build /source/startup.sh .
COPY --from=build /app .

## Next.jsアプリのデフォルト公開ポートをEXPOSE
EXPOSE 3000

## Next.jsとC# Web Apiの両方をスタートするシェルスクリプトをエントリポイントに設定
ENTRYPOINT ["/app/startup.sh"]

また、docker イメージのエントリポイントにするシェルスクリプトは以下のように書いています  Next.js と C# WebApi を立ち上げるだけのシンプルなやつですね

#!/bin/bash

# Next.jsをバックグラウンドでスタート
cd /app/nextjs-app
npm run start &

# C# WebApiをフォアグラウンドでスタート
cd /app
dotnet WebApp.dll

上記の dockerfile をプロジェクトルートに配置したら以下のコマンドを実行して docker イメージを作成します
docker build . -t webapp:latest

以下のようにイメージが作成できました  SIZE が少し大きいですね。。(伏線)

PS > docker image ls
REPOSITORY  TAG       IMAGE ID       CREATED              SIZE
webapp      latest    216558155ad4   About a minute ago   1.57G

以下のコマンドで起動を確認できますが、ローカルで確認したのと同じ結果になるので画像は省略します
(localhost:8080 でつながるとおもいます)
docker run -p 8080:3000 webapp

Azure Container Registry に docker イメージを登録

Azure で docker イメージを利用するためにはいくつか方法があるようですが、とりあえず今回は Azure Container Registry(ACR)を利用しようと思います
(Docker Hub も利用可能なようなので、そちらでも十分だとは思います)

以下の Microsoft の公式ドキュメントに作成方法が載っていたので参考にしましょう
クイック スタート:Azure portal を使用して Azure コンテナー レジストリを作成する

ただ、上記のクイックスタートには AzurePowerShell モジュールのインストールが必要なように書いていますが、  以下のようにタグ付けさえしていれば特に問題なくプッシュできたので、面倒な人はインストールしなくていいと思います😜
(おそらくprivate なコンテナを作成するとAzurePowerShellインストール・ログインが必要なんだとおもいます)

タグ付け
docker tag {イメージ名} {ACRコンテナ名}.azurecr.io/{タグ名}
プッシュ
docker push {ACRコンテナ名}.azurecr.io/{タグ名}

Azure にデプロイ設定する

ここまでできたらあとは Azure にデプロイするだけですね
またまたMicrosoft の公式サイトに記事があったので、以下に従って Azure App Service を作成していきます
Azure リソースを作成する

その結果、以下のようにデプロイ確認できました
ローカルで動作させたときと見た目は代わり映えしませんが、URL は azure のものになっていますね
それにhttpsで動作しています

デプロイしましたー

以上でAzure AppService に Next.js + C# ASP.NET Core Web Api をデプロイは成功ですね!
お疲れさまでした💯

ついでなので docker イメージファイルを小さくしてみる

デプロイに使用した docker イメージですが、1.5GB ほどと少々大きめでしたので、  もちっと小さくするべくベースとなるイメージを変更してみようと思います(伏線回収)

最初に使用したベースイメージは dotnet のものでしたので、今度は node のものにしてみます
Docker Hub で node の公式イメージを確認したところ、タグ盛沢山で正直どれ選べばいいかわからん 😭

、、のですがとりあえず alpine の lts っぽいものを選んでおこうと思います
node:lts-alpine3.16

そして、node のイメージなので dotnet のインストールが必要になるため、以下の Microsoft 公式ドキュメントを参考にインストールします
Alpine に .NET SDK または .NET ランタイムをインストールする

できあがったのが以下の dockerfile です(また長い。。)

# ベースステージ
FROM node:node:lts-alpine3.16 as base
## dotnetインストールに必要なパッケージを取得
RUN apk update
RUN apk add bash icu-libs krb5-libs libgcc libintl libssl1.1 libstdc++ zlib curl
## dotnetインストールスクリプトを取得
WORKDIR /work
RUN curl -LO https://dot.net/v1/dotnet-install.sh
RUN chmod +x ./dotnet-install.sh

# ビルドステージ
FROM base AS build
## dotnetインストールし、パスを通す
RUN ./dotnet-install.sh
ENV PATH $PATH:/root/.dotnet
## C# WebApiをビルド
WORKDIR /source
COPY . .
RUN dotnet publish -c release -o /app
## Next.jsをビルド
WORKDIR /source/nextjs-app
RUN npm i
RUN npm run build

# 実行ステージ
FROM base
## dotnetランタイムのみをインストールし、パスを通す
RUN ./dotnet-install.sh --runtime aspnetcore
ENV PATH $PATH:/root/.dotnet
## ビルドステージからビルドした実行ファイル群をコピー
WORKDIR /app
COPY --from=build /source/nextjs-app/. /app/nextjs-app/.
COPY --from=build /source/startup.sh .
COPY --from=build /app .
## Next.jsアプリのデフォルト公開ポートをEXPOSE
EXPOSE 3000
## Next.jsとC# Web Apiの両方をスタートするシェルスクリプトをエントリポイントに設定
ENTRYPOINT ["/app/startup.sh"]

ビルドしてサイズを確認すると、、

PS > docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
webapp       latest    af3782d1f1e1   18 seconds ago   460MB

460MB なので、だいぶ小さくなりましたね!

あとがき

Azure へデプロイしたあとすんなり動くか心配でしたが、思ったより Azure さんが賢いらしくて公開ポートも勝手に見つけてくれたようで助かりました!

ただ、今回は Next.js のみ公開したのでよかったのですが、WebApi も公開する場合は docker コンテナ内にプロキシを用意して要求 URL ごとに振り分けの処理を行う必要などありそう。。など少々課題は残ってそうです

そちらについてはいずれまた気が向いたら、、ということで今回はここまでです!
それではまた次の記事で 🤗