지속성 함수 문제 해결 가이드
Durable Functions는 일반 코드를 사용하여 서버리스 오케스트레이션을 빌드할 수 있는 Azure Functions 확장 프로그램입니다. Durable Functions에 대한 자세한 내용은 Durable Functions 개요를 참조하세요.
이 문서에서는 Durable Functions 앱의 일반적인 시나리오 문제를 해결하기 위한 가이드를 제공합니다.
참고 항목
Microsoft 지원 엔지니어가 애플리케이션 문제 진단을 도와드립니다. 이 가이드를 사용하여 문제를 진단할 수 없는 경우 Azure Portal 함수 앱 페이지의 지원 + 문제 해결 섹션에 있는 새 지원 요청 블레이드에 액세스하여 지원 티켓을 제출할 수 있습니다.
팁
문제를 디버깅하고 진단할 때, 앱에서 최신 Durable Functions 확장 버전을 사용하고 있는지 확인하는 작업부터 시작하는 것이 좋습니다. 대부분의 경우 최신 버전을 사용하면 다른 사용자가 이미 보고한 알려진 문제를 완화할 수 있습니다. 확장 버전을 업그레이드하는 방법에 대한 지침은 Durable Functions 확장 버전 업그레이드 문서를 참조하세요.
Azure Portal의 문제 진단 및 해결 탭은 애플리케이션과 관련해서 발생할 수 있는 문제를 모니터링하고 진단하는 데 유용한 리소스입니다. 또한 진단에 따라 문제에 대한 잠재적 해결 방법을 제공합니다. 자세한 내용은 Azure 함수 앱 진단을 참조하세요.
위의 리소스로 문제를 해결하지 못한 경우 다음 섹션에서는 특정 애플리케이션 증상에 대한 조언을 제공합니다.
오케스트레이션이 Pending
상태에서 멈춤
오케스트레이션을 시작하면 Durable 확장이 관리하는 내부 큐에 "시작" 메시지가 기록되고, 오케스트레이션의 상태가 "보류 중"으로 설정됩니다. 오케스트레이션 메시지가 선택되고 사용 가능한 앱 인스턴스에서 성공적으로 처리되면 상태가 "실행 중"(또는 "보류 중"이 아닌 다른 상태)으로 전환됩니다.
다음 단계에 따라 "보류 중" 상태에서 계속 멈춰 있는 오케스트레이션 인스턴스 문제를 해결합니다.
지속성 작업 프레임워크 추적을 확인하여 영향을 받은 오케스트레이션 인스턴스 ID에 대한 경고 또는 오류가 있는지 확인합니다. 샘플 쿼리는 추적 오류/경고 섹션에서 찾을 수 있습니다.
멈춘 오케스트레이터에 할당된 Azure Storage 제어 큐를 확인하여 "시작 메시지"가 여전히 있는지 확인합니다. 제어 큐에 대한 자세한 내용은 Azure Storage 공급자 제어 큐 설명서를 참조하세요.
앱의 플랫폼 구성 버전을 "64비트"로 변경합니다. 앱의 메모리가 부족하여 오케스트레이션이 시작되지 않는 경우가 있습니다. 64비트 프로세스로 전환하면 앱에서 더 많은 메모리를 할당할 수 있습니다. 이 내용은 App Service 기본, 표준, 프리미엄 및 탄력적 프리미엄 플랜에만 적용됩니다. 무료 또는 사용량 플랜은 64비트 프로세스를 지원하지 않습니다.
오케스트레이션이 긴 지연 후에 시작됨
일반적으로 오케스트레이션은 예약된 후 몇 초 이내에 시작됩니다. 그런데 오케스트레이션을 시작하는 데 더 오래 걸리는 경우가 있습니다. 다음 단계에 따라 오케스트레이션이 실행될 때까지 몇 초 이상 걸리는 문제를 해결합니다.
Azure Storage에서 오케스트레이션이 지연되는 이유에 대한 설명서를 참조하여 알려진 제한으로 인한 지연인지 확인하세요.
지속성 작업 프레임워크 추적을 확인하여 영향을 받은 오케스트레이션 인스턴스 ID과 관련된 경고 또는 오류가 있는지 확인합니다. 샘플 쿼리는 추적 오류/경고 섹션에서 찾을 수 있습니다.
오케스트레이션이 완료되지 않음/Running
상태에서 멈춤
오케스트레이션이 장시간 동안 "실행 중" 상태로 남아 있으면 일반적으로 완료하도록 예약된 장기 실행 작업을 기다리고 있다는 뜻입니다. 예를 들어 지속형 타이머 작업, 작업 태스크 또는 외부 이벤트 작업이 완료되기를 기다리는 것일 수 있습니다. 그러나 예약된 작업이 성공적으로 완료되었는데도 오케스트레이션이 여전히 진행되지 않으면 오케스트레이션이 다음 작업을 진행할 수 없는 문제가 있는 것일 수 있습니다. 이 상태의 오케스트레이션을 "멈춘 오케스트레이션"이라고 부르곤 합니다.
다음 단계에 따라 멈춘 오케스트레이션 문제를 해결합니다.
함수 앱을 다시 시작해 봅니다. 앱 또는 확장 코드의 일시적인 버그 또는 교착 상태로 인해 오케스트레이션이 중단되는 경우에는 이 단계가 문제 해결에 도움이 될 수 있습니다.
Azure Storage 계정 제어 큐를 확인하여 큐가 지속적으로 증가하는지 확인합니다. 이 Azure Storage 메시지 KQL 쿼리는 오케스트레이션 메시지를 큐에서 제거하는 것과 관련된 문제를 확인하는 데 도움이 될 수 있습니다. 문제가 단일 제어 큐에만 영향을 주는 경우 특정 앱 인스턴스에만 문제가 있는 것일 수 있습니다. 이 경우 스케일 업 또는 다운하여 비정상 VM 인스턴스를 이동하면 문제 해결에 도움이 될 수 있습니다.
Azure Storage 메시지 섹션의 Application Insights 쿼리를 사용하여 해당 큐 이름을 파티션 ID로 필터링하고 해당 제어 큐 파티션과 관련된 문제를 찾습니다.
Durable Functions 모범 사례 및 진단 도구의 지침을 확인합니다. 일부 문제는 알려진 Durable Functions 안티 패턴으로 인해 발생할 수 있습니다.
Durable Functions 버전 관리 설명서를 확인합니다. 일부 문제는 진행 중인 오케스트레이션 인스턴스의 호환성이 손상되는 변경으로 인해 발생할 수 있습니다.
오케스트레이션 실행 속도가 느림
과도한 데이터 처리, 내부 오류 및 부족한 컴퓨팅 리소스로 인해 오케스트레이션이 평소보다 느리게 실행될 수 있습니다. 다음 단계에 따라 예상보다 느리게 실행되는 오케스트레이션 문제를 해결합니다.
지속성 작업 프레임워크 추적을 확인하여 영향을 받은 오케스트레이션 인스턴스 ID에 대한 경고 또는 오류가 있는지 확인합니다. 샘플 쿼리는 추적 오류/경고 섹션에서 찾을 수 있습니다.
앱에서 .NET In Process 모델을 활용하는 경우 확장 세션을 사용하도록 설정하는 것이 좋습니다. 확장 세션은 기록 로드를 최소화할 수 있으므로 처리 속도가 느려질 수 있습니다.
성능 및 확장성 병목 상태를 확인합니다. 애플리케이션 성능은 여러 요인에 따라 달라집니다. 예를 들어 CPU 사용량이 많거나 메모리 사용량이 많으면 지연이 발생할 수 있습니다. 자세한 지침은 Durable Functions의 성능 및 확장성을 참조하세요.
샘플 쿼리
이 섹션에서는 Azure Functions 앱에 대해 구성된 Azure Application Insights 인스턴스에서 사용자 지정 KQL 쿼리를 작성하여 문제를 해결하는 방법을 보여줍니다.
Azure Storage 메시지
기본 Azure Storage 공급자를 사용하는 경우 모든 Durable Functions 동작은 Azure Storage 큐 메시지에 의해 구동되며 오케스트레이션과 관련된 모든 상태는 테이블 스토리지 및 Blob Storage에 저장됩니다. 지속성 작업 프레임워크 추적을 사용하도록 설정하면 모든 Azure Storage 상호 작용이 Application Insights에 기록되며, 이 데이터는 실행 및 성능 문제를 디버깅하는 데 매우 중요합니다.
Durable Functions 확장 v2.3.0부터 host.json 파일에서 로깅 구성을 업데이트하여 이러한 지속성 작업 프레임워크 로그를 Application Insights 인스턴스에 게시할 수 있습니다. 이 작업을 수행하는 방법에 대한 자세한 내용과 지침은 지속성 작업 프레임워크 로깅 문서를 참조하세요.
다음은 특정 오케스트레이션 인스턴스에 대한 엔드투엔드 Azure Storage 상호 작용을 검사하는 쿼리입니다. start
및 orchestrationInstanceID
를 편집하여 시간 범위와 인스턴스 ID로 필터링합니다.
let start = datetime(XXXX-XX-XXTXX:XX:XX); // edit this
let orchestrationInstanceID = "XXXXXXX"; //edit this
traces
| where timestamp > start and timestamp < start + 1h
| where customDimensions.Category == "DurableTask.AzureStorage"
| extend taskName = customDimensions["EventName"]
| extend eventType = customDimensions["prop__EventType"]
| extend extendedSession = customDimensions["prop__IsExtendedSession"]
| extend account = customDimensions["prop__Account"]
| extend details = customDimensions["prop__Details"]
| extend instanceId = customDimensions["prop__InstanceId"]
| extend messageId = customDimensions["prop__MessageId"]
| extend executionId = customDimensions["prop__ExecutionId"]
| extend age = customDimensions["prop__Age"]
| extend latencyMs = customDimensions["prop__LatencyMs"]
| extend dequeueCount = customDimensions["prop__DequeueCount"]
| extend partitionId = customDimensions["prop__PartitionId"]
| extend eventCount = customDimensions["prop__TotalEventCount"]
| extend taskHub = customDimensions["prop__TaskHub"]
| extend pid = customDimensions["ProcessId"]
| extend appName = cloud_RoleName
| extend newEvents = customDimensions["prop__NewEvents"]
| where instanceId == orchestrationInstanceID
| sort by timestamp asc
| project timestamp, appName, severityLevel, pid, taskName, eventType, message, details, messageId, partitionId, instanceId, executionId, age, latencyMs, dequeueCount, eventCount, newEvents, taskHub, account, extendedSession, sdkVersion
추적 오류/경고
다음은 지정된 오케스트레이션 인스턴스에 대한 오류 및 경고를 검색하는 쿼리입니다. orchestrationInstanceID
값을 입력해야 합니다.
let orchestrationInstanceID = "XXXXXX"; // edit this
let start = datetime(XXXX-XX-XXTXX:XX:XX);
traces
| where timestamp > start and timestamp < start + 1h
| extend instanceId = iif(isnull(customDimensions["prop__InstanceId"] ) , customDimensions["prop__instanceId"], customDimensions["prop__InstanceId"] )
| extend logLevel = customDimensions["LogLevel"]
| extend functionName = customDimensions["prop__functionName"]
| extend status = customDimensions["prop__status"]
| extend details = customDimensions["prop__Details"]
| extend reason = customDimensions["prop__reason"]
| where severityLevel > 1 // to see all logs of severity level "Information" or greater.
| where instanceId == orchestrationInstanceID
| sort by timestamp asc
제어 큐/파티션 ID 로그
다음은 instanceId의 제어 큐와 연결된 모든 활동을 검색하는 쿼리입니다. orchestrationInstanceID
에서 instanceID의 값을 입력하고 start
에서 쿼리의 시작 시간을 입력해야 합니다.
let orchestrationInstanceID = "XXXXXX"; // edit this
let start = datetime(XXXX-XX-XXTXX:XX:XX); // edit this
traces // determine control queue for this orchestrator
| where timestamp > start and timestamp < start + 1h
| extend instanceId = customDimensions["prop__TargetInstanceId"]
| extend partitionId = tostring(customDimensions["prop__PartitionId"])
| where partitionId contains "control"
| where instanceId == orchestrationInstanceID
| join kind = rightsemi(
traces
| where timestamp > start and timestamp < start + 1h
| where customDimensions.Category == "DurableTask.AzureStorage"
| extend taskName = customDimensions["EventName"]
| extend eventType = customDimensions["prop__EventType"]
| extend extendedSession = customDimensions["prop__IsExtendedSession"]
| extend account = customDimensions["prop__Account"]
| extend details = customDimensions["prop__Details"]
| extend instanceId = customDimensions["prop__InstanceId"]
| extend messageId = customDimensions["prop__MessageId"]
| extend executionId = customDimensions["prop__ExecutionId"]
| extend age = customDimensions["prop__Age"]
| extend latencyMs = customDimensions["prop__LatencyMs"]
| extend dequeueCount = customDimensions["prop__DequeueCount"]
| extend partitionId = tostring(customDimensions["prop__PartitionId"])
| extend eventCount = customDimensions["prop__TotalEventCount"]
| extend taskHub = customDimensions["prop__TaskHub"]
| extend pid = customDimensions["ProcessId"]
| extend appName = cloud_RoleName
| extend newEvents = customDimensions["prop__NewEvents"]
) on partitionId
| sort by timestamp asc
| project timestamp, appName, severityLevel, pid, taskName, eventType, message, details, messageId, partitionId, instanceId, executionId, age, latencyMs, dequeueCount, eventCount, newEvents, taskHub, account, extendedSession, sdkVersion
Application Insights 열 참조
다음은 위의 쿼리에서 예상하는 열 목록과 해당 설명입니다.
열 | 설명 |
---|---|
pid | 함수 앱 인스턴스의 프로세스 ID입니다. 오케스트레이션이 실행되는 동안 프로세스가 다시 시작되었는지 확인하는 데 유용합니다. |
taskName | 로그되는 이벤트의 이름입니다. |
eventType | 메시지 유형입니다. 일반적으로 오케스트레이터가 수행한 작업을 나타냅니다. 가능한 전체 값 목록과 해당 설명은 여기를 참조하세요. |
extendedSession | 확장 세션의 사용 여부를 나타내는 부울 값입니다. |
거래처 | 앱에서 사용하는 스토리지 계정입니다. |
details | 특정 이벤트에 대한 추가 정보입니다(제공되는 경우). |
instanceId | 지정된 오케스트레이션 또는 엔터티 인스턴스의 ID입니다. |
messageId | 지정된 큐 메시지의 고유한 Azure Storage ID입니다. 이 값은 주로 ReceivedMessage, ProcessingMessage 및 DeletingMessage 추적 이벤트에 나타납니다. 메시지 ID는 메시지를 보낸 후에 Azure Storage에서 생성하므로 SendingMessage 이벤트에는 이 값이 없습니다. |
executionId | 오케스트레이터 실행의 ID입니다. continue-as-new 가 호출될 때마다 바뀝니다. |
연령 | 메시지가 큐에 추가된 후 경과한 시간(밀리초)입니다. 이 값이 크면 성능 문제일 수 있습니다. 타이머의 기간에 따라 Age 값이 클 수 있는 TimerFired 메시지 유형은 예외입니다. |
latencyMs | 일부 스토리지 작업에 걸린 시간(밀리초)입니다. |
dequeueCount | 메시지가 큐에서 삭제된 횟수입니다. 정상적인 상황에서는 이 값이 항상 1입니다. 1보다 크면 문제가 있는 것일 수 있습니다. |
partitionId | 이 로그와 연결된 큐의 이름입니다. |
totalEventCount | 현재 작업과 관련된 기록 이벤트의 수입니다. |
taskHub | 작업 허브의 이름입니다. |
newEvents | 쉼표로 구분된 기록 이벤트 목록입니다. 스토리지의 기록 테이블에 기록됩니다. |