しばやん雑記

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

Azure Functions v4 で廃止された Azure Functions Proxies の代替ソリューションを実装した

先月に .NET 6 と同時にリリースされた Azure Functions v4 では、ひっそりと Azure Functions Proxies が廃止されました。公式には API Management を使うように推奨されていますが、明らかに一部のユースケースしか見えておらず、廃止の理由を聞いても何も教えてくれませんでした。

Azure Functions Proxies を利用していた場合は、何とかしないと v4 への移行は行えないという状態です。

正直なところ Azure Functions Proxies は結構便利に使っていたので非常に困ります。だからと言って API Management や Application Gateway で代替できるかと言ったらそういうわけでもないので、失われた機能を補うためのライブラリを 2 つ作成しました。

Static Website として利用していた場合

今では Static Web Apps があるので Azure Functions 上でわざわざ静的コンテンツをホストする必要はないのですが、Static Web Apps が出る前までは Azure Storage の Static Website を有効にして、フロントに CDN やリバースプロキシを置いて HTTPS やカスタムドメインの対応をしていたと思います。

Azure Functions Proxies を利用すると Consumption Plan を使いつつカスタムドメインや認証を手軽に追加できるのと、フロントエンドとバックエンドの実装を 1 つの zip にまとめてアトミックにデプロイできるので結構便利に使っていました。

Azure Functions v4 ではこのあたりのユースケースが失われてしまったので、以前実装した Azure Functions の HttpTrigger をより簡単に書くためのライブラリに必要な機能を実装しました。

このライブラリを使うと Azure Functions Proxies よりも柔軟で、静的コンテンツのホスティングに特化した機能を実現できます。同時に HttpTrigger のルート選択処理がイマイチなのを修正します。

例えば Azure Storage の Static Website にホストされた静的コンテンツにルートからアクセスする場合は以下のような HttpTrigger な Function を追加します。

public class StaticWebsite : HttpFunctionBase
{
    public StaticWebsite(IHttpContextAccessor httpContextAccessor)
        : base(httpContextAccessor)
    {
    }

    [FunctionName(nameof(Serve))]
    public IActionResult Serve(
        [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "{*path}"})] HttpRequest req,
        ILogger log)
    {
        // Azure Storage の Static Website へのプロキシとして動作する
        return RemoteStaticApp("https://ststaticwebsiteproxy.z11.web.core.windows.net", fallbackExclude: $"^/_nuxt/.*");
    }
}

Azure Storage 側には create-nuxt-app で作成したサンプルサイトをコピーしてあります。404 時に Nuxt.js 側で処理を継続するための Fallback 処理も組み込んでいます。

この状態で Azure Functions を実行してルートにアクセスすると、以下のようにサイトが表示されます。

f:id:shiba-yan:20211201182553p:plain

あまり知られていないと思いますが、Azure Functions の HttpTrigger のルートは Function 名のアルファベット順で決定されるので、Catch All 定義がある場合には意図した動作にならないことが多いです。このライブラリではそのイマイチなルート決定処理を置き換えているので、ASP.NET Core と同様の処理になります。

Azure Functions Proxies ではローカルファイルを配信することは出来ませんでしたが、このライブラリでは外部ストレージだけではなく、Azure Functions と一緒にデプロイされたファイルの配信にも対応しています。

実際に Key Vault Acmebot では利用しているので興味があれば参考にしてください。

サンプルコードもリポジトリに含まれているので、こちらも一緒に参照してもらえればすぐに使い方は分かると思います。単一パッケージに含めてデプロイできるのは結構便利です。

軽量リバースプロキシとして利用していた場合

Azure Functions は Web Apps と同様に VNET Integration を使って、閉域環境や ExpressRoute 経由でのオンプレミス環境へのアクセスが可能で、更に App Service Authentication を使った認証も簡単に用意できるので、簡易ゲートウェイとして利用していたケースもあると思います。

このユースケースでは Function は全くデプロイせずに proxies.json だけ用意するだけで済むので、案外便利に使えていたのではないかと思います。そのようなユースケース向けには App Service Proxy という Site Extension を実装しました。

中身は .NET 6 と YARP で作られたシンプルなリバースプロキシですが、特徴としては Azure Functions Proxies で使われていた proxies.json との互換性があることです。シンプルに wwwroot 以下に proxies.json を置くと自動的に再読み込みされて利用可能になります。

{
    "$schema": "http://json.schemastore.org/proxies",
    "proxies": {
        "proxy1": {
            "matchCondition": {
                "methods": [ "GET" ],
                "route": "/{*path}"
            },
            "backendUri": "https://shibayan.jp/{path}"
        }
    }
}

現在はレスポンスを独自に返す系の処理には対応していません。実装できるか不明なので、今後もどうなるのかはわからないですが上手くいきそうなら検討します。

ファイルを配置した後に Web App にアクセスすると、定義通りに動作していることが分かります。

f:id:shiba-yan:20211130202858p:plain

この Web App には App Service Authentication を使った Azure AD 認証を組み込んでいるので、アクセスすると以下のように自動的に Azure AD のログイン画面にリダイレクトして、アクセスしているユーザーに権限がある場合のみ見られるようになっています。

f:id:shiba-yan:20211130203039p:plain

Application Gateway を使っても実現できますが、デプロイに時間がかかり認証や無料証明書は用意されていないですし、オートスケールも App Service のように素早くはありません。

もっとカジュアルに利用できる L7 のリバースプロキシが欲しい場面は結構あるので、そういった場面でピンポイントに利用できる Site Extension に出来たかと思います。