Профилировать использование памяти приложениями Python в Функции Azure

Во время разработки или после развертывания локального проекта приложения-функции Python в Azure рекомендуется анализировать потенциальные ограничения со стороны памяти в функциях. Такие ограничения могут снизить производительность функций и привести к ошибкам. В следующих инструкциях показано, как использовать пакет Python для профилировщика памяти, который предоставляет анализ потребления памяти по строкам функций при выполнении.

Примечание.

Профилирование памяти предназначено только для анализа объема памяти в средах разработки. Не применяйте профилировщик памяти в рабочих приложениях-функциях.

Предварительные требования

Прежде чем приступить к разработке приложения-функции Python, необходимо выполнить следующие требования:

Если у вас нет аккаунта Azure, создайте бесплатную учетную запись перед началом.

Процесс профилирования памяти

  1. В файл requirements.txt добавьте memory-profiler, чтобы пакет был включён в ваше развёртывание. Если вы разрабатываете на локальной машине, возможно, вам стоит активировать виртуальное окружение Python и разрешить зависимости пакетов с помощью pip install -r requirements.txt.

  2. В скрипте функции (например, __init__.py для модели программирования Python версии 1 и function_app.py для модели версии 2) добавьте следующие строки над 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)
    
  3. Примените следующий декоратор над любыми функциями, которым требуется профилирование памяти. Декоратор не работает напрямую с методом точки входа триггера main(). Необходимо создать вложенные функции и декорировать их. Кроме того, из-за известной проблемы в профилировщике памяти при профилировании асинхронной корутины возвращаемое значение корутины всегда равно None.

    @memory_profiler.profile(stream=profiler_logstream)
    
  4. Проверьте профилировщик памяти на локальном компьютере с помощью команды Функции Azure Core Tools func host start. При вызове функций они должны создать отчет об использовании памяти. Отчет содержит имя файла, строку кода, использование памяти, увеличение памяти и содержимое строки.

  5. Чтобы проверить журналы профилирования памяти в существующем экземпляре приложения-функции в Azure, можно запросить журналы профилирования памяти для последних вызовов с помощью запросов Kusto в Application Insights, logs.

    Снимок экрана: использование памяти запроса приложения Python в Application Insights.

    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
    

Пример

Ниже приведен пример выполнения профилирования памяти на асинхронном и синхронном триггере HTTP с именем HttpTriggerAsync и HttpTriggerSync соответственно. Мы создадим приложение-функцию Python, которое просто отправляет запросы GET на домашнюю страницу Майкрософт.

Создайте приложение-функцию Python

Приложение-функция Python должно соответствовать заданной в службе "Функции Azure" структуре папок. Чтобы сформировать шаблон проекта, рекомендуется использовать Функции Azure 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 определяет пакеты, используемые в нашем проекте. Помимо пакета SDK для службы "Функции Azure" и профилировщика памяти, мы представляем aiohttp для асинхронных HTTP-запросов и requests для синхронных HTTP-вызовов.

# requirements.txt

azure-functions
memory-profiler
aiohttp
requests

Создайте асинхронный триггер 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.

Замените код в асинхронном триггере 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 в локальной среде разработки

После внесения указанных выше изменений можно выполнить несколько дополнительных действий, чтобы инициализировать виртуальную среду Python для среды выполнения Функции Azure.

  1. Откройте Windows PowerShell или любую оболочку Linux по своему выбору.

  2. Создайте виртуальную среду Python с помощью py -m venv .venv в ОС Windows или python3 -m venv .venv в Linux.

  3. Активируйте виртуальную среду Python с помощью .venv\Scripts\Activate.ps1 в Windows PowerShell или source .venv/bin/activate в оболочке Linux.

  4. Восстановите зависимости Python с помощью pip install -r requirements.txt

  5. Запустите среду выполнения службы "Функции Azure" на локальном компьютере с помощью func host start Функции Azure Core Tools.

  6. Отправьте запрос GET в https://localhost:7071/api/HttpTriggerAsync или https://localhost:7071/api/HttpTriggerSync.

  7. Он должен отображать отчет профилирования памяти, аналогичный следующему разделу в Функции Azure 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" см. в статьях ниже.