Azure Functionsから接続済みのAzure Filesへアクセスする

2021.10.10

いわさです。

Azure上の定期処理で外部からのダウンロードを行う際に、Azure FunctionsとAzure WebJobsのどちらでファイルダウンロードをするのが良いかを検討していました。
どのFaaSでもリソース使用時の制限などが存在していると思いますが、Azure Functionsの場合は以下に記載があります。

タイムアウトなどについてはApp Serviceプランを使ってホストすることで柔軟に対応出来ることは認識していましたが、ストレージサイズについてはApp Serviceの上限があります。

そして、従量課金プランの場合のストレージサイズについては以下のような記載がありました。

ストレージの上限は、同じ App Service プランのすべてのアプリにまたがる一時ストレージ内の合計コンテンツ サイズです。 従量課金プランでは、Azure Files を一時ストレージに使用します。

Azure Functions のスケールとホスティング | Microsoft Docs

この部分を実はよくわかってなかったので今回確認してみました。

関数アプリ作成

通常のHTTPトリガーの関数を作成します。
Functionsでは、作成時にストレージアカウントを指定する必要があります。(ARMテンプレートの場合、ストレージアカウントを使わないオプションもあるようです)

ストレージアカウントのファイル共有を見てみると、ソースコードなどが格納されています。

Azure Functionsのコンソールでファイルシステムを確認してみると、App Serivceと同じ形式でホームディレクトリが確認出来ます。
ディレクトリを作成してみましょう。

              _    _____   _ ___ ___
             /_\  |_  / | | | _ \ __| 
       _ ___/ _ \__/ /| |_| |   / _|___ _ _ 
     (___  /_/ \_\/___|\___/|_|_\___| _____) 
        (_______ _ _)         _ ______ _)_ _ 
               (______________ _ )   (___ _ _) 

一般コマンド ('mkdir'、ディレクトリを変更する 'cd' など) を実行して、Web アプリ環境を管理します。これはサンドボックス環境であるため、管理者特権を必要とするコマンドは機能しません。

C:\home\site\wwwroot>ls
HttpTrigger1
HttpTrigger2
host.json

C:\home\site\wwwroot>mkdir hoge

ディレクトリが作成出来ました。
Functionsをホストしているマシンに接続されているようなので、権限の問題がなければ関数からもアクセス出来そうです。
やってみます。

ファイルをダウンロードして保存してみる

上記ディレクトリ情報は静的に埋め込むしかないのでしょうか。
以下のドキュメントに実行中の関数のコンテキストを取得する方法が案内されています。

関数へExecutionContext型の引数を追加するだけで取得できそうです。
以下のようなコードをデプロイしました。

序盤のログは、関数アプリから見た各パスを確認したかったので入れています。

#r "Newtonsoft.Json"

using System.Net;
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;

public static async Task<IActionResult> Run(HttpRequest req, ILogger log, ExecutionContext context)
{
    log.LogInformation("TempPath: " + Path.GetTempPath());
    log.LogInformation("CurrentDirectory: " + Directory.GetCurrentDirectory());
    log.LogInformation("Context.FunctionDirectory: " + context.FunctionDirectory);
    log.LogInformation("Context.FunctionAppDirectory: " + context.FunctionAppDirectory);
    using(var wc = new WebClient())
    {
        wc.DownloadFileAsync(
            new Uri("https://hoge.tak1wa.com/hoge.txt"), 
            context.FunctionAppDirectory + "\\hoge.txt");
    }
    return new OkObjectResult("hoge");
}
2021-10-09T21:56:54.213 [Information] Executing 'Functions.HttpTrigger1' (Reason='This function was programmatically called via the host APIs.', Id=4a12417d-6a6e-4aeb-bae4-582a6ff9b102)
2021-10-09T21:56:54.214 [Information] TempPath: C:\local\Temp\
2021-10-09T21:56:54.214 [Information] CurrentDirectory: C:\Program Files (x86)\SiteExtensions\Functions\3.2.0\32bit
2021-10-09T21:56:54.214 [Information] Context.FunctionDirectory: C:\home\site\wwwroot\HttpTrigger1
2021-10-09T21:56:54.214 [Information] Context.FunctionAppDirectory: C:\home\site\wwwroot
2021-10-09T21:56:54.231 [Information] Executed 'Functions.HttpTrigger1' (Succeeded, Id=4a12417d-6a6e-4aeb-bae4-582a6ff9b102, Duration=18ms)

期待どおり、パスは取得出来ていますね。
保存時のエラーは発生していません。

共有ファイルはどうなっているでしょうか。

保存されていますね。
これを使うと永続ストレージとして扱うことが出来そうです。
また、同一環境でホストされている環境間でのファイルの共有も出来そうですね。

さいごに

共有ファイルのパス取得およびアクセスが出来ることを確認しました。

ただし、Azure Functionsのストレージに関するベストプラクティスについてはいくつかこの点の言及があります。

Azure Functions のストレージに関する考慮事項 | Microsoft Docs

共有のストレージ アカウント お使いの Function App で使用されているものと同じストレージ アカウントは、アプリケーション データを格納するためにも使用できます。 ただし、運用環境では、この手法が常に適切であるとは限りません。

上記にはパフォーマンスに関する言及もあります。
永続ストレージとして使って良さそうかは、利用ケースごとに上記のベストプラクティスの観点からも検討したほうが良さそうです。