Azure Functionsの技術ブログ：顧客のフィードバックに応えるためのエンジニアリング

沢山読んでいただいている[世界一流エンジニアの思考法](https://www.amazon.co.jp/dp/4163917683/)にも登場する[@anirudhgarg](https://twitter.com/anirudhgarg)がPublicPreviewになった `Flex Consumption` のインターナルに関するブログを書いてくれましたので、日本語訳してより多くの人に読んでもらえるようにしました。ご質問があれば答えれる範囲でしたらコメントいただければお答えいたします。

* 原文: [Azure Functions at Build 2024 – addressing customer feedback with deep engineering](https://techcommunity.microsoft.com/t5/apps-on-azure-blog/azure-functions-at-build-2024-addressing-customer-feedback-with/ba-p/4148341)

# はじめに

`Azure Functions`は、何十万もの顧客が毎月数兆回の実行を行っているAzureの主要なサーバーレスサービスです。2016年初頭に初めてリリースされて以来、私たちは顧客からのフィードバックを通じて、どの部分がうまく機能し、どの部分に改善の余地があるのかを学んできました。

このすべてのフィードバックを考慮して、`Azure Functionsチーム`は初めての利用体験から非常に高いスケールでの運用に至るまで、スタック全体での体験を向上させるために努力してきました。同時に、顧客がAIアプリを構築するための機能を追加しました。今年のBuildカンファレンスでリリースしたすべての機能については、この[リンク]()をご覧ください。これまでのすべてを考慮すると、これは**Functionsの歴史の中で最も重要なリリース**の一つです。

このブログ投稿では、顧客のフィードバックと、それに応えるためにFunctionsや他のパートナーチームが行った技術的な裏側の作業を共有します。将来的には、これらのトピックのそれぞれについてさらに深く掘り下げていきますが、ここでは簡単な概要をお伝えします。

# Flex Consumption: ネットワークサポートを備えたアプリのバーストスケール
私たちは新しいSKUである[Flex Consumption](https://techcommunity.microsoft.com/t5/apps-on-azure-blog/announcing-public-preview-of-azure-functions-flex-consumption/ba-p/4139028)をリリースします。このSKUは、これまでの`Functions Consumptionプラン`に対する多くのフィードバックに対応しています。これには、より速いスケール、より多くのインスタンスサイズ、VNETサポート、より高いインスタンス制限などが含まれます。スタックの各部分を見直し、すべてのレベルで改善を行いました。多くの新機能が含まれています。

* ユーザーが制御するインスタンスごとの並列性
* これまでよりも速くスケールこれまでよりも多くのインスタンスにスケール（最大1000まで）
* VNET統合イベントソースをサポートするサーバーレスの「ゼロにスケール」SKU
* 常に割り当てられる ワーカーをサポート
* 複数のメモリサイズをサポート

# 特別に構築されたバックエンド「Legion」
`Flex Consumption`を可能にするために、内部で特別に構築された新しいバックエンドである[Legion](https://techcommunity.microsoft.com/t5/apps-on-azure-blog/project-legion/ba-p/4141871)を作成しました。

# Trigger Monitor - ネットワーク制限付きでゼロにスケールおよびスケールアウト

`Flex Consumption`は、Functionアプリへのアクセスを制限し、ネットワーク制限されたイベントソースでトリガーするためのネットワーク機能も導入します。これらのイベントソースがネットワーク制限されているため、イベントのレートを監視してスケールアウトまたはスケールインを決定するためのマルチテナントスケーリングコンポーネントである[スケールコントローラー](https://learn.microsoft.com/en-us/azure/azure-functions/event-driven-scaling?tabs=azure-cli#runtime-scaling)はアクセスできません。`Elastic Premiumプラン`では、1インスタンスにスケールダウンすることでこれを解決しました。そのインスタンスがネットワーク制限されたイベントソースにアクセスし、スケールコントローラーにスケールの決定を伝達します。しかし、`Flex Consumptionプラン`では、0インスタンスにスケールダウンしたいと考えました。

これを解決するために、`Trigger Monitor` と呼ばれる小さなスケーリングコンポーネントを導入しました。これは顧客のVNETに注入されます。このコンポーネントはネットワーク制限されたイベントソースにアクセスできます。スケールコントローラーはこのコンポーネントと通信してスケールの決定を行います。

# Httpベースのアプリの並列性に基づくスケーリング

FunctionアプリでHttpベースのワークロードをスケーリングする際、以前の実装では内部のヒューリスティックを使用してスケールアウトのタイミングを決定していました。このヒューリスティックは、[フロントエンドサーバー](https://learn.microsoft.com/en-us/archive/msdn-magazine/2017/february/azure-inside-the-azure-app-service-architecture#front-end)が現在顧客のワークロードを実行しているワーカーにpingを送り、その応答の遅延に基づいてスケールを決定するものでした。この実装では、ワーカーとその割り当てを追跡するためにSQL Azureを使用していました。

`Flex Consumption`では、このロジックを書き直し、スケーリングがユーザー設定の並列性に基づくようにしました。ユーザー設定の並列性は、顧客が言語やワークロードに基づいてインスタンスごとに設定する並列性を決定する柔軟性を提供します。例えば、Pythonの顧客はマルチスレッドについて考える必要がなく、並列性を1（Pythonアプリのデフォルトでもある）に設定できます。このアプローチはスケーリングの挙動を予測可能にし、顧客にコスト対性能のトレードオフを制御する能力を与えます。高い遅延の可能性を容認する場合、各ワーカーをより高い並列性で実行することでコストを節約できるかもしれません。

私たちの実装では、[Data Role](https://learn.microsoft.com/en-us/archive/msdn-magazine/2017/february/azure-inside-the-azure-app-service-architecture#data-role)によって管理される「リクエストスロット」を使用します。インスタンスを「リクエストスロット」に分割し、それらを異なるフロントエンドサーバーに割り当てます。例えば、インスタンスごとの並列性が16に設定されている場合、Data RoleがFunctionアプリにインスタンスを割り当てると、16のリクエストスロットがあります。それらを1つのフロントエンドにすべて与えることも、複数のフロントエンドに分散することもできます。これにより、フロントエンド間の調整が不要になり、フロントエンドは受け取ったリクエストスロットを好きなだけ使用できますが、リクエストスロットごとに1つの同時リクエストのみが許可されます。この実装では、Cosmos DBを使用して割り当てとワーカーを追跡します。

Legionをコンピュートプロバイダーとして使用することで、アプリごとに大規模なコンピュート割り当てが可能になり、迅速なスケールインと容量の回収が行えるため、顧客にこれまで以上に優れた体験を提供できます。

# 非Httpベースのアプリの並列性に基づくスケーリング
Httpアプリと同様に、非Httpベースのアプリも並列性に基づいてスケールできるようになりました。これを[ターゲットベースのスケーリング](https://learn.microsoft.com/en-us/azure/azure-functions/functions-target-based-scaling?tabs=v5%2Ccsharp)と呼んでいます。実装の観点から、さまざまな拡張機能がスケーリングロジックを拡張機能内に実装し、スケールコントローラーがこれらの拡張機能をホストするように移行しました。これにより、スケーリングロジックが一元化され、すべてのスケーリングが並列性に基づいて統一されます。

# 構成をコントロールプレーンに移動
顧客からのフィードバックに基づいて、さまざまな構成プロパティを`AppSettings`からコントロールプレーンに移行する方向に変更を加えています。パブリックプレビューでは、デプロイメント、スケーリング、言語の領域でこれを行っています。以下は、新しいコントロールプレーンプロパティを示す例です。GAまでに他のプロパティも移行する予定です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/bff249aa-ac8a-5457-fc0f-c7735b91e45b.png)

# Azure Load Testing統合
顧客からは、Functionアプリを最適なスループットで構成する方法について常に質問がありました。これまで、パフォーマンステストを実行するようにガイダンスを提供してきましたが、新たなオプションとして、[Azure Load Testingとのネイティブ統合を導入します](https://techcommunity.microsoft.com/t5/apps-on-azure-blog/optimize-azure-functions-for-performance-and-costs-using-azure/ba-p/4138467)。新しいパフォーマンス最適化ツールが利用可能となり、異なるメモリやHttp並列性の構成を指定してテストを作成・実行し、アプリの最適な構成を決定できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/883a2af2-a99a-6bf6-2845-ecf457a41c9c.png)


# Functions on Azure Container Apps: クラウドネイティブのマイクロサービスデプロイメント
Buildでは、[Azure Container Apps上で動作するFunctionsのGA](https://techcommunity.microsoft.com/t5/apps-on-azure-blog/general-availability-of-azure-functions-on-azure-container-apps/ba-p/4145577)も発表します。この新しいSKUにより、顧客はAzure Functionsのプログラミングモデルとイベント駆動のトリガーを使用して、他のマイクロサービスやWebアプリケーションと同じ環境でアプリを実行できます。これにより、共通のネットワークリソースと観測機能をすべてのアプリケーションで活用できます。さらに、Functionsの顧客は、Daprなどのフレームワークや、`Container Apps`環境でのみ利用可能なGPUなどのコンピュートオプションを活用できます。

このSKUを他のFunction SKU/プランと一貫性を保つ必要がありましたが、異なるプラットフォーム（Container Apps）で動作し、スケールします。

具体的には、

* このSKUの異なるスキーマニーズに対応できる新しいデータベースを作成し、クエリパフォーマンスを向上させました。制御プレーンの一部も再設計しました。
* ARM拡張ルーティングを使用してトラフィックを安全にホストし、内部VNET内で動作するアプリのFunction Host APIをARM経由で有効にしました。
* Azure Container Apps環境内にシンクトリガーサービスを構築し、Function Appを検出して顧客の関数コードからトリガー情報を読み取り、自動的に対応するKEDAスケーラールールを作成しました。これにより、KEDAスケーリングプラットフォームを知らなくても、`Azure Container Apps`上で`Function Apps`が自動的にスケーリングされるようになります。
* タイマートリガー関数のゼロスケールシナリオをサポートするためのカスタムKEDA外部スケーラーを開発しました。

# VSCode.Webサポート: ブラウザで関数を開発
Azure Functionsチームは開発者の生産性を重視しており、VSCodeとの統合とCore Toolsはファーストクラスサポートであり、このカテゴリの他の類似製品に対して大きな利点となっています。しかし、私たちは常にこの体験を向上させようと努めています。

開発者がローカル開発マシンを正しい前提条件で構成するのはしばしば難しいことです。このセットアップは、ローカルツールや言語の新しいバージョンに更新する必要もあります。一方で、`GitHub Codespaces`や同様の開発環境は、クラウドにホストされた効果的な開発環境を提供できることを示しています。

私たちはAzure Functions用のVSCode for the Webを使用した新しい開始体験を提供します。この体験により、開発者はブラウザから直接VS Code for the Webを使用して関数コードを記述、デバッグ、テスト、およびデプロイできます。このコンテナはすべての必要な依存関係を備えており、VS Codeが提供する豊富な機能（拡張機能を含む）をサポートします。この体験は、既にコードがデプロイされている関数アプリにも使用できます。

この機能を構築するために、[VS Code for the Web](https://code.visualstudio.com/docs/editor/vscode-web#:~:text=Visual%20Studio%20Code%20for%20the%20Web%20provides%20a%20free%2C%20zero,vscode.dev%20in%20your%20browser.)を起動する拡張機能を作成しました。これは、ユーザーのブラウザで動作する軽量のVS Codeです。このVS Codeクライアントは、Azure Functionsのバックエンドインフラストラクチャと通信して、[Dev Tunnel](https://code.visualstudio.com/docs/remote/tunnels)を使用して[VS Codeサーバー](https://code.visualstudio.com/docs/remote/vscode-server)への接続を確立します。VS CodeクライアントとサーバーがDev Tunnelを介して接続されると、ユーザーは望むように関数を編集できます。

# AIアプリを簡単に構築するためのOpen AI拡張機能
`Azure Functions`は、Webアプリ、データパイプライン、および他の関連するワークロードなど、さまざまな種類のアプリの開発を簡素化することを目指しています。AIアプリは明確な新しいドメインです。`Azure Functions`には豊富な拡張モデルがあり、統合に必要な多くの単純作業を抽象化し、すべての言語で利用できるようにしています。

[OpenAIを利用した拡張機能](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-openai?tabs=isolated-process&pivots=programming-language-csharp)をリリースします。これにより、以下のシナリオが数行のコードで実現できます。

* 検索強化生成（独自のデータを持ち込む）
* テキスト補完およびチャット補完
* アシスタント機能

重要なのは、開発者がFunctionsでサポートされている任意の言語でAIアプリを構築でき、数分以内に使用できるサービスでホストされることです。

以下のC#のコードスニペットをご覧ください。数行のコードで以下のように動作します。

このHTTPトリガー関数は、クエリプロンプトを入力として受け取り、セマンティックに類似したドキュメントチャンクをプロンプトに取り込み、結合されたプロンプトをOpenAIに送信します。結果は関数に返され、呼び出し元にチャット応答として返されます。

```csharp
public class SemanticSearchRequest
{
    [JsonPropertyName("Prompt")]
    public string? Prompt { get; set; }
}

[Function("PromptFile")]
public static IActionResult PromptFile(
    [HttpTrigger(AuthorizationLevel.Function, "post")] SemanticSearchRequest unused,
    [SemanticSearchInput("AISearchEndpoint", "openai-index", Query = "{Prompt}", ChatModel = "%CHAT_MODEL_DEPLOYMENT_NAME%", EmbeddingsModel = "%EMBEDDING_MODEL_DEPLOYMENT_NAME%")] SemanticSearchContext result)
{
    return new ContentResult { Content = result.Response, ContentType = "text/plain" };
}
```
拡張機能を構築する際の課題は、「グルーコード」を隠しつつ、開発者にビジネスユースケースのための柔軟性を提供することです。

さらに、次のような追加の課題に直面しました。

* チャット補完シナリオでの呼び出し間で状態を保存するために、Durable Functionsを含むさまざまな実装を試みましたが、最終的にはTableストレージを使用して会話中の状態を保存することにしました。
* どのエンベディングストアをサポートするかを決定する必要がありました。現在、Azure AI Search、Cosmos DB、およびAzure Data Explorerをサポートしています。
* 急速に進化する技術であるため、基盤となるOpen AIモデルとSDKを使用するための適切な戦略を見つける必要がありました。

# NodeとPythonでのストリーミングサポート
Buildで追加されたもう一つの長い間要望されていたサポートは、[Node（GA）](https://techcommunity.microsoft.com/t5/apps-on-azure-blog/azure-functions-support-for-http-streams-in-node-js-is-generally/ba-p/4140209)と[Python（プレビュー）](https://techcommunity.microsoft.com/t5/azure-compute-blog/azure-functions-support-for-http-streams-in-python-is-now-in/ba-p/4146697)でのストリーミングサポートです。

この機能により、顧客はHTTPリクエストをFunctionアプリにストリーミングし、Functionアプリからのレスポンスをストリーミングできます。以前は、HTTPリクエストで送信できるデータの量はSKUインスタンスのメモリサイズに制限されていました。HTTPストリーミングを使用すると、大量のデータをチャンク化して処理できます。特に今日では、この機能により、OpenAIレスポンスの大規模データストリーミングを処理し、動的コンテンツを提供する新しいシナリオが可能になります。

ストリーミングサポートを有効にする旅は興味深いものでした。最初は.NETのin-procと[isolated](https://learn.microsoft.com/en-us/azure/azure-functions/dotnet-isolated-process-guide?tabs=windows)の間でのパリティを目指しました。これを実現するために、新しいHttpパイプラインを実装し、HttpリクエストをFunctions Hostから分離ワーカーにプロキシしました。同じ技術を他のプロック外の言語でのストリーミングサポートを構築するためにも利用しました。

# OpenTelemetryサポート
Buildでは、FunctionsでのOpenTelemetryサポートをリリースします。これにより、顧客はOpenTelemetryのセマンティクスを使用して、Functions Hostおよび言語ワーカーからのテレメトリデータをエクスポートできます。以下は、この作業のために取ったいくつかの興味深い設計方向です：

* 顧客のコードはFunctionsホストを無視し、スムーズな体験のために各言語ワーカーでコンテキストを再作成します。
* テレメトリはApplicationInsightsおよび他のベンダーに対して同じです。顧客は何を使用しても同じテレメトリデータを得ることができます。LiveLogsはAIと連携していますが、全体的な体験は変わりません。
* 顧客の便宜を図るために、各言語ワーカーには余分なコードを削除するパッケージ/モジュールがあります。

# 今後の展望
Azure Functionsを長年にわたり使用してきたすべての顧客および開発者の皆様に感謝します。これらの新機能と能力を試し、フィードバックや提案をお寄せいただければ幸いです。

今後は以下に取り組んでいきます：

* 最も要求の厳しい顧客のためにFlex ConsumptionをGAにし、さらに改善を続けます。
* Open AI拡張機能をさらに強化し、Azure FunctionsをAIサービスを作成する最も簡単で迅速な方法にします。
* 開始体験を継続的に改善し、VSCode.Webの統合を他の言語にも拡大し、GAにします。
ストリーミングサポートをJavaなどの他の言語にも追加します。