Azure Functions で Python アプリのメモリ使用量をプロファイルする
開発中、またはローカルの Python 関数アプリ プロジェクトを Azure にデプロイした後に、関数内で発生する可能性のあるメモリのボトルネックを分析することをお勧めします。 このようなボトルネックによって関数のパフォーマンスが低下し、エラーを招く可能性があります。 次の手順では、memory-profiler Python パッケージを使用する方法について説明します。これにより、関数の実行時のメモリ使用量分析が 1 行ずつ行われます。
注意
メモリ プロファイルは、開発環境でのメモリ占有領域の分析のみを目的としています。 運用環境の関数アプリにはメモリ プロファイラーを適用しないでください。
前提条件
Python 関数アプリの開発を開始する前に、次の要件を満たしておく必要があります。
Python 3.7 以降。 Azure Functions でサポートされている Python バージョンの完全な一覧を確認するには、Python 開発者ガイドを参照してください。
Azure Functions Core Tools バージョン 4.x 以降。
func --version
を使用して、お使いのバージョンを確認します。 更新の詳細については、「GitHub での Azure Functions Core Tools」を参照してください。サポートされているプラットフォームのいずれかにインストールされた Visual Studio Code。
有効な Azure サブスクリプション
Azure サブスクリプションをお持ちでない場合は、開始する前に Azure 無料アカウントを作成してください。
メモリ プロファイルのプロセス
requirements.txt に
memory-profiler
を追加して、パッケージがデプロイにバンドルされるようにします。 ローカル コンピューター上で開発している場合は、Python 仮想環境をアクティブ化し、pip install -r requirements.txt
によってパッケージを解決することができます。関数スクリプト (たとえば、Python v1 プログラミング モデルの場合は __init__.py、v2 モデルの場合は function_app.py) で、次の行を
main()
関数の上に追加します。 これらの行により、ルート ロガーによって子ロガー名が報告され、メモリ プロファイル ログがプレフィックスmemory_profiler_logs
によって識別できるようになります。import logging import memory_profiler root_logger = logging.getLogger() root_logger.handlers[0].setFormatter(logging.Formatter("%(name)s: %(message)s")) profiler_logstream = memory_profiler.LogFile('memory_profiler_logs', True)
メモリ プロファイルを必要とするすべての関数よりも上に、次のデコレータを適用します。 デコレーターは、トリガー エントリポイントの
main()
メソッドに直接機能しません。 サブ関数を作成してサブ関数を修飾する必要があります。 また、メモリプロファイラーの既知の問題により、非同期コルーチンに適用する場合、コルーチンの戻り値は常にNone
になります。@memory_profiler.profile(stream=profiler_logstream)
Azure Functions Core Tools コマンド
func host start
を使用して、ローカル コンピューター上のメモリ プロファイラーをテストします。 関数を呼び出すと、メモリ使用量レポートが生成されます。 レポートには、ファイル名、コード行、メモリの使用量、メモリの増分、および行の内容が含まれます。Azure の既存の関数アプリ インスタンスのメモリ プロファイル ログを確認するには、Application Insights のログで Kusto クエリを使用して、最近の呼び出しのメモリ プロファイル ログをクエリできます。
traces | where timestamp > ago(1d) | where message startswith_cs "memory_profiler_logs:" | parse message with "memory_profiler_logs: " LineNumber " " TotalMem_MiB " " IncreMem_MiB " " Occurrences " " Contents | union ( traces | where timestamp > ago(1d) | where message startswith_cs "memory_profiler_logs: Filename: " | parse message with "memory_profiler_logs: Filename: " FileName | project timestamp, FileName, itemId ) | project timestamp, LineNumber=iff(FileName != "", FileName, LineNumber), TotalMem_MiB, IncreMem_MiB, Occurrences, Contents, RequestId=itemId | order by timestamp asc
例
次に示すのは、それぞれ "HttpTriggerAsync" と "HttpTriggerSync" という名前の非同期および同期の HTTP トリガーでメモリ プロファイルを実行する例です。 作成するのは、単に Microsoft のホーム ページに GET 要求を送信する Python 関数アプリです。
Python 関数アプリを作成する
Python 関数アプリは Azure Functions で指定されたフォルダー構造に従っている必要があります。 プロジェクトをスキャフォールディングするには、次のコマンドを実行して Azure Functions Core Tools を使用することをお勧めします。
func init PythonMemoryProfilingDemo --python
cd PythonMemoryProfilingDemo
func new -l python -t HttpTrigger -n HttpTriggerAsync -a anonymous
func new -l python -t HttpTrigger -n HttpTriggerSync -a anonymous
ファイルの内容を更新する
requirements.txt では、プロジェクトで使用されるパッケージを定義します。 Azure Functions SDK と memory-profiler に加えて、非同期 HTTP 要求用の aiohttp
と同期 HTTP 呼び出し用の requests
を挿入します。
# requirements.txt
azure-functions
memory-profiler
aiohttp
requests
非同期 HTTP トリガーを作成します。
非同期 HTTP トリガー HttpTriggerAsync/__init__.py のコードを、次のコードに置き換えます。このコードでは、メモリ プロファイラー、ルート ロガー形式、ロガー ストリーミング バインディングを構成します。
# HttpTriggerAsync/__init__.py
import azure.functions as func
import aiohttp
import logging
import memory_profiler
# Update root logger's format to include the logger name. Ensure logs generated
# from memory profiler can be filtered by "memory_profiler_logs" prefix.
root_logger = logging.getLogger()
root_logger.handlers[0].setFormatter(logging.Formatter("%(name)s: %(message)s"))
profiler_logstream = memory_profiler.LogFile('memory_profiler_logs', True)
async def main(req: func.HttpRequest) -> func.HttpResponse:
await get_microsoft_page_async('https://microsoft.com')
return func.HttpResponse(
f"Microsoft page loaded.",
status_code=200
)
@memory_profiler.profile(stream=profiler_logstream)
async def get_microsoft_page_async(url: str):
async with aiohttp.ClientSession() as client:
async with client.get(url) as response:
await response.text()
# @memory_profiler.profile does not support return for coroutines.
# All returns become None in the parent functions.
# GitHub Issue: https://github.com/pythonprofilers/memory_profiler/issues/289
同期 HTTP トリガーを作成します。
非同期 HTTP トリガー HttpTriggerSync/__init__.py のコードを次のコードに置き換えます。
# HttpTriggerSync/__init__.py
import azure.functions as func
import requests
import logging
import memory_profiler
# Update root logger's format to include the logger name. Ensure logs generated
# from memory profiler can be filtered by "memory_profiler_logs" prefix.
root_logger = logging.getLogger()
root_logger.handlers[0].setFormatter(logging.Formatter("%(name)s: %(message)s"))
profiler_logstream = memory_profiler.LogFile('memory_profiler_logs', True)
def main(req: func.HttpRequest) -> func.HttpResponse:
content = profile_get_request('https://microsoft.com')
return func.HttpResponse(
f"Microsoft page response size: {len(content)}",
status_code=200
)
@memory_profiler.profile(stream=profiler_logstream)
def profile_get_request(url: str):
response = requests.get(url)
return response.content
ローカル開発環境で Python 関数アプリをプロファイルする
上記の変更を行ったら、さらにいくつかの手順を行って、Azure Functions ランタイムの Python 仮想環境を初期化します。
好みに応じて、Windows PowerShell または任意の Linux シェルを開きます。
Windows では
py -m venv .venv
、Linux ではpython3 -m venv .venv
を使用して、Python 仮想環境を作成します。Windows PowerShell では
.venv\Scripts\Activate.ps1
、Linux シェルではsource .venv/bin/activate
を使用して、Python 仮想環境をアクティブ化します。pip install -r requirements.txt
を使用して Python の依存関係を復元します。Azure Functions Core Tools
func host start
を使用して、Azure Functions ランタイムをローカルで起動します。GET 要求を
https://localhost:7071/api/HttpTriggerAsync
またはhttps://localhost:7071/api/HttpTriggerSync
に送信します。Azure Functions Core Tools で、次のセクションのようなメモリ プロファイル レポートが表示されます。
Filename: <ProjectRoot>\HttpTriggerAsync\__init__.py Line # Mem usage Increment Occurrences Line Contents ============================================================ 19 45.1 MiB 45.1 MiB 1 @memory_profiler.profile 20 async def get_microsoft_page_async(url: str): 21 45.1 MiB 0.0 MiB 1 async with aiohttp.ClientSession() as client: 22 46.6 MiB 1.5 MiB 10 async with client.get(url) as response: 23 47.6 MiB 1.0 MiB 4 await response.text()
次のステップ
Python による Azure Functions 開発の詳細については、次のリソースを参照してください。