- 2022年10月24日
こんにちは、最近ユニフェイスに入社した田港です
今までの会社では 派遣で既存システムの一部の改修案件に携わることが多く、いつか一気通貫で 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 ごとに振り分けの処理を行う必要などありそう。。など少々課題は残ってそうです
そちらについてはいずれまた気が向いたら、、ということで今回はここまでです!
それではまた次の記事で 🤗