しばやん雑記

Azure Serverless とメイドさんが大好きなフリーランスのプログラマーのブログ

Azure Functions SDK 3.0.4 以降を使う場合は依存関係に注意

今月頭にリリースされた Azure Functions SDK 3.0.4 では Function Runtime が持っているアセンブリを、Function のデプロイパッケージから除外するという実装が追加されています。

ASP.NET Core 周りの明らかに必要ないだろうというアセンブリがごっそり除外されるので、以下のツイートで紹介されているようにデプロイパッケージのサイズは大幅に縮んでいます。

今は 3.0.5 がリリースされていますが、WebJobsStartup が読み込まれないという致命的な問題が修正されているので 3.0.4 を使っている場合はアップデートしましょう。

不要なアセンブリを除外することでデプロイの高速化はもちろん見込めますし、Run From Package を使った実行時に不要なアセンブリを展開し、読み込む必要がないので全体的に良いことが多いです。

除外されるアセンブリのリストは GitHub にあり、ここにないファイルがデプロイされるようになっています。

テストとして HttpTrigger だけの Function V3 プロジェクトを作成して、SDK バージョンは 3.0.3 のままの状態で dotnet publish を実行しデプロイ用のファイルを生成しました。

ASP.NET Core 周りのアセンブリが大量に出力されているのがわかります。

次は SDK バージョンを 3.0.5 にアップデートした後に、同様にデプロイ用のファイルを作成しました。

大幅に減って、Azure Storage SDK と NCrontab の厳密名付きアセンブリだけになりました。NCrontab が含まれているのは意図した結果ではなさそうなのですが、気にしないことにします。

Run From Package 用として zip にするとサイズの差が歴然なことがわかるはずです。

これでデプロイと実行も効率的になり、嬉しいことしかないと思ってしまいそうですが罠があります。

今の SDK 実装には問題があり、単純にリストにある同名のアセンブリを出力結果からごっそり消しているだけなので、バージョンの違いなどは完全に無視されます。なので以下のような問題が発生しています。

Azure Functions の Extensions を特定の組み合わせでインストールすると、アセンブリが読み込めないというエラーが出るという問題ですが、デプロイ時のアセンブリ削除によって必要なバージョンのアセンブリも消されてしまっているのが原因です。

Function の開発時に参照されているアセンブリと同名のものがリストに載っていれば削除されるので、強制的に Function Runtime 側のバージョンに合わせられるという鬼のような挙動です。そして実行時には指定されたバージョンのアセンブリが存在しないので実行時エラーになるという話でした。

現在は発生しにくいですが将来的に JSON.NET がアップデートされた時、新機能を使うために明示的にパッケージ参照を追加すると Function Runtime と異なるバージョンになるので実行時エラーになるでしょう。割と怖い問題です。

ちなみに Durable Functions 2.2.0 以前のバージョンにも同じような問題がありましたが、Issue で指摘して直してもらったので最新バージョンにアップデートすると問題なく動作します。

回避策としては以下のように csproj へ _FunctionsSkipCleanOutput を追加すると、以前の SDK バージョンと同じ動作になります。

この場合はデプロイパッケージのサイズ削減は行われないので、アセンブリバージョン問題は発生しません。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <AzureFunctionsVersion>v3</AzureFunctionsVersion>
    <_FunctionsSkipCleanOutput>true</_FunctionsSkipCleanOutput>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.5" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

このあたりの問題はチームも認識しているようなので何時かは改善すると思いますが、何も考えずに SDK バージョンを上げると Azure 上にデプロイした時、急にエラーになって焦ることになるので注意しましょう。

せめてバージョンも見て同じものを削除する実装なら問題なかったはずなのですけどね。