Open13

MagicOnion+MemoryPack+YetAnotherHttpHandler on Azure Container Apps 構築のログ

fumiofumio

動機

  • だいぶん前にMagicOnionをローカル環境まで構築してほっぽっておいたので再度履修しておきたい
  • MemoryPack触ったことないので今のうちに
    • ここはMessagePack for C#もSourceGenerator対応されたので、余裕があれば対応して比較もしたい まだプレリリース状態のようなので本当に余裕あれば
  • YetAnotherHttpHandlerをリリース頂いたので、Unityでgrpc-dotnetをつかってみたい
    • 業務でUnity+Grpc.Coreのプロトタイプ経験はあるが、結構もうげんなりしていたので
  • 個人開発ならAzure使うだろし、今の業務でもDockerのそばで暮らしている感じなのでそろそろと思っていたところにAzure Container Apps に爆速でアプリをデプロイする を目にしてしまった

リポジトリ

https://github.com/fumiodev/MagicOnionTest

fumiofumio

MagicOnion を Ver.5.1.8 でセットアップ。
MagicOnion のunitypackage同梱のdllと、 YetAnotherHttpHandler で導入した Cysharp.Net.Http.YetAnotherHttpHandler.Dependencies.unitypackage 同梱のdllとで同じライブラリのDLL、 System.Runtime.CompilerServices.Unsafe.dll が重複していたので、MagicOnion側のを削除。

一旦使用する想定はないものの、MessagePack for C# を導入していない状態で MagicOnion のunitypackage をインポートすると、MagicOnion.Abstraction でMessagePack 名前空間のあれこれが結構参照されてしまっているようなので、ひとまずMessagePackも導入する。

このままだと、Grpc.Core を参照している箇所でエラーが出るので、 Scripting Define Symbol に USE_GRPC_NET_CLIENTUSE_GRPC_NET_CLIENT_ONLY を追加してRecompile走らせる。

これでエラーは発生しない状態までいけた。

fumiofumio

Unityプロジェクトと同じリポジトリ内に .NET 7 のConsole Applicationのプロジェクトを作成。
az containerapp upコマンドでDockerFile無しでのデプロイを試したいので、Docker Support は一旦 Disable

fumiofumio

作成したサーバー用プロジェクトに、NuGetで MagicOnion.Server 5.1.8 を追加。
この方法だと、 .gitignore が追加されていないので、dotnet new gitignore で追加。
ただ、Riderのコンソールだとうまくdotnetコマンドが実行できなかったので、別でPowershellを起動して叩いた。

fumiofumio

MemoryPack対応はmemorypack-supportを参照して対応。
MagicOnion.Serialization.MemoryPack、Server側はNuGetで引けるが、ClientとなるUnity側では前述でインポートしたMagicOnionのunitypackageには同梱されていないようので、直接Githubのソースを引っ張ってくる必要がありそう。
5.1.8のラベルがついたソースからsrc\MagicOnion.Serialization.MemoryPack 以下のファイルをUnityプロジェクトへコピーする。

これでコンパイルエラーなどはなくなった。

fumiofumio

上記から
Sever側は endpointOptions.Protocols = HttpProtocols.Http2;
Client側は handler.Http2Only = true;
で指定して、ローカルかつデバッグ実行でのUnaryは疎通確認OK

ASP.NET Core HTTPS 開発証明書 が認証されていないと、Sever側をビルドした後の起動時に怒られるので、

 dotnet dev-certs https --trust     

で認証しておく。(あくまでローカルでの確認用なので注意)
詳しい説明は、このあたりを参照
実際にデプロイしてインターネット越しでアクセスさせる場合は、DOTNET_GENERATE_ASPNET_CERTIFICATE をfalseにしておくなどが良さそう?

fumiofumio

一旦この状態で、デプロイしてどうなるかを試したいので、az containerapp up を実行してみる

Azure CLIをインストールして
https://learn.microsoft.com/ja-jp/cli/azure/install-azure-cli-windows?tabs=azure-cli

ログインして

az login

下記に従って、az containerapp を利用できるようにして
https://learn.microsoft.com/ja-jp/azure/container-apps/containerapp-up#set-up

az containerapp up でDockerFileなしでのデプロイ実行

 az containerapp up \
      --name <CONTAINER_APP_NAME> \
      --source . \
      --ingress external  \
      --target-port 5000

リソースグループ、そしてその中に、コンテナー レジストリ、Container Apps 環境、Log Analytics ワークスペースが作成された。
(ちなみに上記だと作られるリソースグループのリージョンが West US 2 なので注意。)

が、Container Apps本体の作成前に、そもそものビルドでエラー。

  Determining projects to restore...
  Skipping project "/Shared/Shared.csproj" because it was not found.
  Skipping project "/Shared/Shared.csproj" because it was not found.
/app/SampleService.cs(3,7): error CS0246: The type or namespace name 'MagicOnionTest' could not be found (are you missing a using directive or an assembly reference?) [/app/Server.csproj]
/app/SampleService.cs(7,46): error CS0246: The type or namespace name 'IConnectionTestService' could not be found (are you missing a using directive or an assembly reference?) [/app/Server.csproj]
/app/SampleService.cs(7,71): error CS0246: The type or namespace name 'IConnectionTestService' could not be found (are you missing a using directive or an assembly reference?) [/app/Server.csproj] | 1 | Please build your app locally before publishing. | https://docs.microsoft.com/en-us/azure/app-service/configure-language-dotnetcore?pivots=platform-linux

Unityの Assets\Scripts\Shared 以下に配置している共有用のディレクトリが上手く読み込めていないっぽい。
MagicOnion公式サンプルの形で、Shared用のcsprojを用意して回避できるか試す。

fumiofumio

どうもうまくいかなかった(DockerFileでそれっぽくCOPYしてRestoreかけてもダメ)なので、
コマンド1つでうまくデプロイされてほしいところだが、一旦ほっておき、
SharedのDLLをdotnet publishして、直接Server以下にコピーし、csprojで参照させることにした。
(まぁこの程度ならGitHub ActionsなりのCIでカバーできる範疇なのでギリ許容、とする...)

fumiofumio

DockerFile置いたままだが、上記のコマンドに、すでに生成されてあるリソースグループとContainer Apps 環境を追加で指定してデプロイ。

 az containerapp up \
      --name <CONTAINER_APP_NAME> \
      --source . \
      --resource-group <RESOURCE_GROUP_NAME>
      --environment <ENVIRONMENT_NAME>
      --ingress external  \
      --target-port 5000

デプロイ後に、Client側で表示されるURLと指定ポートに書き換えて実行するも、
やはりTLS/SSL周りでダメなのか上手くリクエストが到達しないっぽく、下記のエラーになる。
(Azure Container Apps上のログストリームも、起動時以降ログが追記されない)

HttpRequestException: error trying to connect: tcp connect error: 接続済みの呼び出し先が一定の時間を過ぎても正しく応答しなかったため、接続できませんでした。または接続済みのホストが応答しなかったため、確立された接続は失敗しました。 (os error 10060)
fumiofumio

ちなみにContainer Appsの挙動だが、上記コマンドでデプロイすると、HTTPスケールになっている。
レプリカの最小が0になっているので、最後のリクエストからしばらくたてば下記のように0になって自動で停止する。
この状態なら課金されないはず。

また、レプリカの最大が10ということで、これを変更したところ、ドキュメントどおりリビジョンの再作成が走った。

いちおうコストも確認したが、リクエスト自体が発生していない扱いになっている+リビジョン再作成は課金対象外で、
課金されていたのはレジストリに対してのみの模様。ありがたい。

リビジョン周りの参考
https://techblog.ap-com.co.jp/entry/2022/09/08/120000

fumiofumio

ちょっと放置していたので料金の確認。
レジストリのみの金額なものの、Container Appsを利用していたのは10/23辺りのみなので、Task vCPU Durationで課金されているのは不明。
(とはいえ、検証の範囲であれば十分安価)