NDW

アプリ開発やトラブルシューティング等のノウハウ、キャンプや登山の紹介や体験談など。

1. システムエンジニアリング Azure 基盤技術

Azure Functionsの.NET Core3.1から.NET5への移行

投稿日:

はじめに

.NET5への移行概要

  • Azure Functionsの実行方法(モデル)が.NET isolated processに変更されました。
  • .NET Core 3.1から.NET5に移行する手順の概要を次に示します。
    前述の実行モデルの変更に伴う改修が主となり、プロジェクトの形式変更、使用パッケージの.NET isolated process版への置き換えが必要になります。
    なお、HTTP以外のService BusやBlobトリガーの場合は、移行前後での改修は不要のようです。
    No. 概要 説明
    1 プロジェクト
    定義の変更
    使用するフレームワークを.NET5、出力の種類をコンソールアプリケーション(Exe)に変更にします。
    2 パッケージの変更 Azure Functionsランタイムやトリガー用のパッケージを.NET isolated process用のパッケージ(Microsoft.Azure.Functions.Worker.*)に置き換えます。
    3 起動方法の変更 ASP.NET Coreアプリ等のようにホストビルダを使用してプロセスを初期化・実行します。(既存プロジェクトでスタートアップクラスを実装している場合は、DIサービス登録等の処理をこちらに移植する必要があります。)
    4 メソッド定義の変更 実行対象のメソッドに付与していたFunctionName属性をFunction属性に変更します。メソッド引数にILoggerを指定している場合は、FunctionContextから取得するよう変更します。
    5 HTTPトリガー
    使用時の変更
    HTTPトリガーを使用する場合、
    引数・戻り値の型をHttpRequestData, HttpResponseDataに変更します。
    6 ローカル実行用の
    ホスト定義の変更
    local.settings.jsonのワーカーランタイム(FUNCTIONS_WORKER_RUNTIME)を.NET isolated process(“dotnet-isolated”)に変更します。

.NET5への移行詳細

プロジェクト定義の変更

  • プロジェクトのプロパティを開きます。
    「対象のフレームワーク」を「.NET 5.0」、「出力の種類」を「クラスライブラリ」から「コンソールアプリケーション」に変更します。
  • プロジェクトファイル(.csproj)を直接編集する場合、次のようにTargetFramework/OutputType要素を編集します。
    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <TargetFramework>net5.0</TargetFramework> <!-- 変更箇所 -->
        <AzureFunctionsVersion>v3</AzureFunctionsVersion>
        <OutputType>Exe</OutputType>  <!-- 変更箇所 -->
      </PropertyGroup>
      <ItemGroup>
      ...
    

パッケージの変更

  • 既存のイン・プロセス・モデル用のパッケージを、アウト・オブ・プロセス・モデル用のものに置き換える必要があります。
    種類 イン・プロセス・モデル アウト・オブ・プロセス・モデル
    ランタイム Microsoft.NET.Sdk.Functions Microsoft.Azure.Functions.Worker
    Microsoft.Azure.Functions.Worker.Sdk
    トリガー Microsoft.Azure.WebJobs.Extensions.* Microsoft.Azure.Functions.Worker.Extensions.*
  • NuGetパッケージマネージャを起動してパッケージを置き換えます。
    NuGetパッケージマネージャの起動方法は、[ツール]-[NuGetパッケージマネージャー]-[ソリューションのNuGetパッケージの管理]、またはプロジェクトの[依存関係]-[パッケージ]を右クリックして[NuGetパッケージの管理]を選択します。
  • プロジェクトファイル(.csproj)を直接編集する方法もありますが、使用パッケージの最新安定バージョンが分からないので、前述のNuGetパッケージマネージャを使用する方法をお薦めします。
    (次の例ではHTTPトリガーを使用しています。)
    <Project Sdk="Microsoft.NET.Sdk">
      ...
      <ItemGroup>
        <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.6.0" />
        <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />
        <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.3.0" />
        ...
      </ItemGroup>
      ...
    

起動方法の変更

  • イン・プロセス・モデルの場合、Azure Functionsプロセスの起動をランタイムが行うため、プロセス初期化処理は不要でした。プロセス初期化時に必要となるDIサービスの登録は、ランタイムがスタートアップクラスを実行することで実現します。
    アウト・オブ・プロセス・モデルの場合、ホストビルダ(IHostBuilder)を使用してアプリ自身でプロセスの初期化やDIサービス登録処理の呼び出しを実装する必要があります。
  • アプリの実行開始場所(エントリポイント)となるメソッドMain()を定義し、ホストビルダでConfigureFunctionsWorkerDefaults()を実行します。
    構成情報を追加する場合はConfigureAppConfiguration()、依存関係を注入(DI)する場合はConfigureServices()を使用します。
    class Program
    {
        public static Task Main()
        {
            var host = new HostBuilder()
                .ConfigureFunctionsWorkerDefaults() // 必須
                // 構成情報を追加する場合
                .ConfigureAppConfiguration((context, config) =>
                {
                    var env = context.HostingEnvironment;
                    config
                        .SetBasePath(Directory.GetCurrentDirectory())
                        .AddJsonFile("appsettings.json", optional: true)
                        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
                })
                // 依存関係を注入する場合
                .ConfigureServices((context, services) =>
                {
                    var configuration = context.Configuration;
                    services.AddHttpClient(); // 例としてHttpClientをDI
                })
                .Build();
            return host.RunAsync();
        }
    }
    

メソッド定義の変更

  • 関数のメソッドに付与されたFunctionName属性をFunction属性に変更します。
    なお、FunctionName属性の定義(FunctionNameAttribute)は、Microsoft.Azure.Functions.Worker.Extensions.Abstractionsパッケージにあります。HTTPやServiceBusトリガーを使用すると、その依存関係として自動的に参照が追加されます。
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<...> Run(
    ...
    
    public static class Function1
    {
        [Function("Function1")]
        public static async Task<...> Run(
    ...
    
  • メソッドの引数でILoggerを引き渡している場合、FunctionContext経由で取得するよう変更します。
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<...> Run(..., ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");
            ...
    
    public static class Function1
    {
        [Function("Function1")]
        public static async Task<...> Run(..., FunctionContext context)
        {
            var log = context.GetLogger(nameof(Function1));
            log.LogInformation("C# HTTP trigger function processed a request.");
    

HTTPトリガー使用時の変更

  • メソッドの引数型HttpRequestをHttpRequestData、戻り値型IActionResultをHttpResponseDataに変更します。
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(...)] HttpRequest req, ...)
        {
            ...
            string responseMessage = "...";
            return new OkObjectResult(responseMessage);
        }
    }
    
    public static class Function1
    {
        [Function("Function1")]
        public static async Task<HttpResponseData> Run(
            [HttpTrigger(...)] HttpRequestData req, ...)
        {
            ...
            string responseMessage = "...";
            var response = req.CreateResponse(System.Net.HttpStatusCode.OK);
            response.Body.Write(Encoding.Default.GetBytes(responseMessage));
            return response;
        }
    }
    

ローカル実行用のホスト定義の変更

  • local.settings.jsonのFUNCTIONS_WORKER_RUNTIMEを、アウト・オブ・プロセス・モデルである”dotnet-isolated”に変更します。
    {
        "IsEncrypted": false,
        "Values": {
            "AzureWebJobsStorage": "UseDevelopmentStorage=true",
            "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
        }
    }
    

トラブルシューティング

ホストが開始していない

  • 事象
    ローカルで実行時に次のエラー・例外が発生して中断する。
    • Azure Functions Core Toolsのコンソールでは次のエラーが表示される。
      Microsoft.Azure.WebJobs.Script: Did not find functions with language [dotnet].
    • アプリのコンソールでは、Microsoft.Azure.WebJobs.Hostで次の例外が発生する。
      System.InvalidOperationException(“The host has not yet started.”)
  • 原因
    local.settings.jsonのFUNCTIONS_WORKER_RUNTIMEが”dotnet-isolated”以外になっていることが原因。(当該値に変更する必要がある。)
    {
        "IsEncrypted": false,
        "Values": {
            "AzureWebJobsStorage": "UseDevelopmentStorage=true",
            //"FUNCTIONS_WORKER_RUNTIME": "dotnet"
            "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
        }
    }
    

アセンブリをロードできない

gRPC公開ポイントの不正

  • 事象
    Azure Functionsアプリを起動すると、次の例外が発生して実行が中断される。
    Unhandled exception. System.InvalidOperationException: The gRPC channel URI ‘http://:0’ could not be parsed.
  • 原因
    • gRPCの公開ポイントは構成情報(IConfiguration)から取得します。何らかの理由で公開ポイントを取得できない場合、この事象が発生します。
    • 既定ではAzure Functionsホストビルダ実行時のConfigureFunctionsWorkerDefaults()で、構成情報が初期化されます。作成された構成情報に対して、意図しない操作を行い、構成情報のクリアや上書きをしている可能性があります。
    • 構成情報(IConfiguration)構成情報ビルダ(IConfigurationBuilder)に対する操作を追跡することで、原因個所を特定できると思います。
    • なお、次のように構成情報ビルダのソースをクリアすると同事象を再現できます。
      hostBuilder
          .ConfigureAppConfiguration(config => config.Sources.Clear());
      






-1. システムエンジニアリング, Azure, 基盤技術

関連記事

JIS X 0208, Shift_JIS, Windows-31Jの歴史と違い

文字コードの話は難しそうなイメージがあり、必要になったタイミングでその都度、最小限の知識を習得して対応してきました…が、効率が悪く踏み込んだ話になった時に困る場合もあるので、ここで真面目に …

保守運用

運用 と 保守 の 違い

若い頃は 運用 と 保守 の違いを調べても良くわからなかった… この辺を使い分けられる人をほとんど見たことない… ある事項が運用なのか保守なのかの話をすると認識が合わない&#8 …

SLF4Jの仕組みと使用方法

何気に使用しているSLF4Jの仕様やその仕組みを整理したいと思います。 前提 元ネタはSLF4J Manualサイトです。 2021年1月時点でSLF4Jは2.0系、1.8系、1.7系の3つがあります …

VBAでケバブ・スネーク・パスカル・キャメル変換

開発の現場では、Excelに定義したクラスやテーブル等の設計内容に基づいて、VBAで自動的にソースコードやSQL文等の成果物を生成したい場合があります。この処理を実装する場合、Excel上の項目名を、 …

ゾーン10進数の変換方法とC#のサンプル

なお、パック10進数はこちらで紹介しています。 ゾーン10進数 ゾーン10進数の変換方法 10進数の各桁を「ゾーン部(上位4ビット)+数値部(下位4ビット)」のバイトに変換して生成します。 生成したバ …