Azure Functions에서 Python 앱 메모리 사용량 프로파일링

개발 중에 또는 Azure에 로컬 Python 함수 앱 프로젝트를 배포한 후에는 함수의 잠재적 메모리 병목 상태에 대해 분석하는 것이 좋습니다. 이러한 병목 상태는 함수의 성능을 저하하고 오류를 일으킬 수 있습니다. 다음 지침에서는 메모리 프로파일러 Python 패키지를 사용하는 방법을 보여줍니다. 이 패키지는 함수 실행 시 함수에 대한 라인별 메모리 사용량 분석을 제공합니다.

참고 항목

메모리 프로파일링은 개발 환경의 메모리 공간을 분석하는 용도로만 사용됩니다. 메모리 프로파일러를 프로덕션 함수 앱에 적용하지 마세요.

필수 조건

Python 함수 앱 개발을 시작하기 전에 다음 요구 사항을 충족해야 합니다.

Azure를 구독하고 있지 않다면 시작하기 전에 Azure 체험 계정을 만듭니다.

메모리 프로파일링 프로세스

  1. requirements.txt에서 memory-profiler를 추가하여 패키지가 배포와 함께 제공되도록 합니다. 로컬 머신에서 개발하는 경우 Python 가상 환경을 활성화하고 pip install -r requirements.txt에서 패키지를 확인할 수 있습니다.

  2. 함수 스크립트(예: 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)
    
  3. 메모리 프로파일링이 필요한 함수 위에 다음 데코레이터를 적용합니다. 데코레이터는 트리거 진입점 main() 메서드에서 직접 작동하지 않습니다. 하위 함수를 만들고 데코레이트해야 합니다. 또한 메모리 프로파일러의 알려진 문제로 인해 비동기 코루틴에 적용할 때 코루틴 반환 값은 항상 None입니다.

    @memory_profiler.profile(stream=profiler_logstream)
    
  4. Azure Functions Core Tools 명령 func host start를 사용하여 로컬 컴퓨터에서 메모리 프로파일러를 테스트합니다. 함수를 호출할 때 메모리 사용량 보고서를 생성해야 합니다. 보고서에는 파일 이름, 코드 줄, 메모리 사용량, 메모리 증분 및 그 안에 있는 줄 내용이 포함됩니다.

  5. Azure의 기존 함수 앱 인스턴스에서 메모리 프로파일링 로그를 확인하려면 Application Insights, Logs에서 Kusto 쿼리를 사용하여 최근 호출에 대한 메모리 프로파일링 로그를 쿼리할 수 있습니다.

    Screenshot showing the query memory usage of a Python app in 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
    

예시

다음은 각각 “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 및 메모리 프로파일러 외에 aiohttp 비동기 HTTP 요청 및 requests 동기 HTTP 호출에 대해 소개합니다.

# 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 가상 환경을 초기화하는 몇 가지 단계가 더 있습니다.

  1. Windows PowerShell 또는 Linux 셸 중 하나를 엽니다.

  2. py -m venv .venv(Windows용) 또는 python3 -m venv .venv(Linux용)로 Python 가상 환경을 만듭니다.

  3. .venv\Scripts\Activate.ps1(Windows PowerShell용) 또는 source .venv/bin/activate(Linux 셸용)로 Python 가상 환경을 활성화합니다.

  4. pip install -r requirements.txt를 통해 Python 종속성을 복원합니다.

  5. Azure Functions Core Tools func host start를 이용하여 Azure Functions 런타임을 로컬에서 시작합니다.

  6. https://localhost:7071/api/HttpTriggerAsync 또는 https://localhost:7071/api/HttpTriggerSync에 GET 요청을 보냅니다.

  7. 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()
    

다음 단계

Azure Functions Python 개발에 대한 자세한 내용은 다음 리소스를 참조하세요.