다음을 통해 공유


Durable Functions의 오케스트레이션 버전 관리(Azure Functions) - 공개 미리 보기

오케스트레이션 버전 관리는 Durable Functions에 필요한 결정적 실행 모델을 유지하면서 오케스트레이터 함수에 변경 내용을 배포하는 핵심 과제를 해결합니다. 이 기능이 없으면 오케스트레이터 논리나 작업 함수 서명에 대한 중대한 변경으로 인해 재생 중에 실행 중인 오케스트레이션 인스턴스가 실패하게 됩니다. 이는 신뢰할 수 있는 오케스트레이션 실행을 보장하는 결정론 요구 사항을 위반하기 때문입니다. 이 기본 제공 기능은 최소한의 구성으로 자동 버전 격리를 제공합니다. 백 엔드에 구애받지 않으므로 지속성 작업 스케줄러를 포함하여 지속성 함수의 스토리지 공급자를 활용하는 앱에서 사용할 수 있습니다.

Note

지속성 작업 스케줄러 사용자의 경우 지속성 함수 대신 지속성 작업 SDK를 사용하는 경우 Durable Task SDK 버전 관리 문서를 참조해야 합니다.

Terminology

이 문서에서는 다음과 같은 두 가지 관련 있지만 고유한 용어를 사용합니다.

  • 오케스트레이터 함수 (또는 단순히 "오케스트레이터"): 워크플로를 실행하는 방법에 대한 템플릿 또는 청사진인 워크플로 논리를 정의하는 함수 코드를 참조합니다.
  • 오케스트레이션 인스턴스 (또는 단순히 "오케스트레이션"): 자체 상태, 인스턴스 ID 및 입력을 사용하여 오케스트레이터 함수의 특정 실행 실행을 나타냅니다. 여러 오케스트레이션 인스턴스는 동일한 오케스트레이터 함수에서 동시에 실행할 수 있습니다.

오케스트레이터 함수 코드에 버전 인식 논리가 포함된 오케스트레이션 버전 관리에서는 이러한 차이점을 이해하는 것이 중요하지만 오케스트레이션 인스턴스는 생성될 때 특정 버전과 영구적으로 연결됩니다.

작동 방식

오케스트레이션 버전 관리 기능은 다음 핵심 원칙에 따라 작동합니다.

  • 버전 연결: 오케스트레이션 인스턴스가 만들어지면 영구적으로 연결된 버전을 가져옵니다.

  • 버전 인식 실행: Orchestrator 함수 코드는 현재 오케스트레이션 인스턴스와 연결된 버전 값을 검사하고 그에 따라 분기 실행을 검사할 수 있습니다.

  • 이전 버전과의 호환성: 최신 오케스트레이터 버전을 실행하는 작업자는 이전 오케스트레이터 버전에서 만든 오케스트레이션 인스턴스를 계속 실행할 수 있습니다.

  • 전달 보호: 런타임은 이전 오케스트레이터 버전을 실행하는 작업자가 최신 오케스트레이터 버전에서 시작된 오케스트레이션을 실행하지 못하도록 자동으로 차단합니다.

Important

오케스트레이션 버전 관리가 현재 공개 미리 보기로 제공됩니다.

필수 조건

오케스트레이션 버전 관리를 사용하기 전에 프로그래밍 언어에 필요한 패키지 버전이 있는지 확인합니다.

확장 번들과 함께 non-.NET 언어(JavaScript, Python, PowerShell 또는 Java)를 사용하는 경우 함수 앱은 확장 번들 버전 4.26.0 이상을 참조해야 합니다. extensionBundle 범위를 host.json에 구성하여 최소 버전을 4.26.0 이상이 되도록 하십시오. 예를 들어:

{
    "version": "2.0",
    "extensionBundle": {
        "id": "Microsoft.Azure.Functions.ExtensionBundle",
        "version": "[4.26.0, 5.0.0)"
    }
}

번들 버전 선택 및 업데이트에 대한 자세한 내용은 확장 번들 구성 설명서를 참조하세요.

버전 Microsoft.Azure.Functions.Worker.Extensions.DurableTask 이상을 사용합니다.

기본 사용법

오케스트레이션 버전 관리에서 가장 흔한 사용 사례는 기존의 실행 중인 오케스트레이션 인스턴스를 원래 버전으로 계속 유지하면서 오케스트레이터 논리의 파괴적인 변경을 해야 하는 경우입니다. defaultVersion에서 host.json을 업데이트하고 오케스트레이터 코드를 수정하여 오케스트레이션 버전과 분기 실행을 적절히 확인하기만 하면 됩니다. 필요한 단계를 살펴보겠습니다.

Note

이 섹션에서 설명하는 동작은 가장 일반적인 상황을 대상으로 하며 기본 구성에서 제공하는 동작입니다. 그러나 필요한 경우 수정할 수 있습니다(자세한 내용은 고급 사용 참조 ).

1단계: defaultVersion 구성

오케스트레이션의 기본 버전을 구성하려면 Azure Functions 프로젝트의 defaultVersion 파일에서 host.json 설정을 추가하거나 업데이트해야 합니다.

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "<version>"
    }
  }
}

버전 문자열은 버전 관리 전략에 맞는 모든 형식을 따를 수 있습니다.

  • 다중 파트 버전 관리: "1.0.0", "2.1.0"
  • 단순 번호 매기기: "1", "2"
  • 날짜 기반: "2025-01-01"
  • 사용자 지정 형식: "v1.0-release"

설정 defaultVersion하면 모든 새 오케스트레이션 인스턴스가 해당 버전과 영구적으로 연결됩니다.

버전 비교 규칙

Strict 또는 CurrentOrOlder 전략을 선택하면(버전 일치 참조) 런타임은 다음 규칙을 사용하여 오케스트레이션 인스턴스의 버전을 작업자의 값과 defaultVersion 비교합니다.

  • 빈 버전 또는 null 버전은 같음으로 처리됩니다.
  • 빈 또는 null 버전은 정의된 버전보다 오래된 것으로 간주됩니다.
  • 두 버전을 모두 System.Version으로 구문 분석할 수 있으면 CompareTo 메서드가 사용됩니다.
  • 그렇지 않으면 대/소문자를 구분하지 않는 문자열 비교가 수행됩니다.

2단계: 오케스트레이터 함수 논리

오케스트레이터 함수에서 버전 인식 논리를 구현하려면 오케스트레이터에 전달된 컨텍스트 매개 변수를 사용하여 현재 오케스트레이션 인스턴스의 버전에 액세스하여 버전에 따라 오케스트레이터 논리를 분기할 수 있습니다.

Important

버전 인식 논리를 구현할 때 이전 버전에 대한 정확한 오케스트레이터 논리를 유지하는 것이 매우 중요 합니다. 기존 버전에 대한 작업 호출의 시퀀스, 순서 또는 서명을 변경하면 결정적 재생이 중단되고 진행 중인 오케스트레이션이 실패하거나 잘못된 결과를 생성할 수 있습니다. 배포된 후에는 이전 버전 코드 경로가 변경되지 않은 상태로 유지되어야 합니다.

[Function("MyOrchestrator")]
public static async Task<string> RunOrchestrator(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    if (context.Version == "1.0")
    {
        // Original logic for version 1.0
        ...
    }
    else if (context.Version == "2.0")
    {
        // New logic for version 2.0
        ...
    }
    ...
}

Note

context.Version 속성은 읽기 전용 이며 오케스트레이션 인스턴스를 만들 때 영구적으로 연결된 버전을 반영합니다. 오케스트레이션 실행 중에는 이 값을 수정할 수 없습니다. 다른 수단을 host.json통해 버전을 지정하려는 경우 오케스트레이션 클라이언트 API를 사용하여 오케스트레이션 인스턴스를 시작할 때 그렇게 할 수 있습니다( 특정 버전으로 새 오케스트레이션 및 하위 오케스트레이션 시작 참조).

Tip

오케스트레이션 버전 관리를 막 시작했고 defaultVersion을 지정하기 전에 만들어진 진행 중인 오케스트레이션이 이미 있는 경우 지금 defaultVersionhost.json 설정을 추가할 수 있습니다. 이전에 만든 모든 오케스트레이션에 대해 context.Versionnull(또는 해당 언어에 종속된 값)을 반환하므로, 레거시(null 버전) 및 새 버전 오케스트레이션을 적절하게 처리할 수 있도록 오케스트레이터 논리를 구조화할 수 있습니다. 다음은 레거시 사례를 확인할 언어 종속 값입니다.

  • C#: context.Version == null 또는 context.Version is null
  • JavaScript: context.df.version == null
  • 파이썬: context.version is None
  • PowerShell: $null -eq $Context.Version
  • Java: context.getVersion() == null 또한 "defaultVersion": null에서 host.json를 지정하는 것은 전혀 지정하지 않는 것과 같습니다.

Tip

상황에 따라 다른 수준에서 분기하는 것을 선호할 수 있습니다. 예제와 같이 이 변경이 필요한 위치를 정확하게 로컬로 변경할 수 있습니다. 또는 전체 오케스트레이터 구현 수준에서도 더 높은 수준에서 분기할 수 있습니다. 그러면 일부 코드 중복이 발생하지만 실행 흐름을 명확하게 유지할 수 있습니다. 시나리오 및 코딩 스타일에 가장 적합한 방법을 선택해야 합니다.

배포 후 수행되는 동작

새 버전 논리를 사용하여 업데이트된 오케스트레이터 함수를 배포한 후에 예상되는 것은 다음과 같습니다.

  • 작업자 공존: 새 오케스트레이터 함수 코드를 포함하는 작업자가 시작되고 이전 코드를 사용하는 일부 작업자는 여전히 활성 상태입니다.

  • 새 인스턴스에 대한 버전 할당: 새로운 작업자가 만든 모든 새 오케스트레이션 및 하위 오케스트레이션은 defaultVersion에서 버전을 할당받습니다.

  • 새 작업자 호환성: 이전 섹션의 2단계에서 수행된 변경 내용이 버전 인식 분기 논리를 통해 이전 버전과의 호환성을 보장하기 때문에 새 작업자는 새로 만든 오케스트레이션과 기존 오케스트레이션을 모두 처리할 수 있습니다.

  • 이전 작업자 제한 사항: 이전 작업자는 최신 버전과 호환되는 오케스트레이터 코드가 필요하지 않으므로 자체 defaultVersion버전에 지정된 버전과 host.json 버전의 오케스트레이션만 처리할 수 있습니다. 이 제한은 실행 오류 및 예기치 않은 동작을 방지합니다.

Note

오케스트레이션 버전 관리가 작업자 수명 주기에 영향을 주지 않습니다. Azure Functions 플랫폼은 호스팅 옵션에 따라 일반 규칙에 따라 작업자 프로비저닝 및 서비스 해제를 관리합니다.

예: 시퀀스의 작업 바꾸기

이 예제에서는 오케스트레이션 버전 관리를 사용하여 한 작업을 시퀀스 중간에 다른 작업으로 바꾸는 방법을 보여 있습니다.

버전 1.0

host.json 구성:

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "1.0"
    }
  }
}

오케스트레이터 함수:

[Function("ProcessOrderOrchestrator")]
public static async Task<string> ProcessOrder(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var orderId = context.GetInput<string>();
    
    await context.CallActivityAsync("ValidateOrder", orderId);
    await context.CallActivityAsync("ProcessPayment", orderId);
    await context.CallActivityAsync("ShipOrder", orderId);
    
    return "Order processed successfully";
}

할인 처리가 포함된 버전 2.0

host.json 구성:

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "2.0"
    }
  }
}

오케스트레이터 함수:

using DurableTask.Core.Settings;

[Function("ProcessOrderOrchestrator")]
public static async Task<string> ProcessOrder(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var orderId = context.GetInput<string>();

    await context.CallActivityAsync("ValidateOrder", orderId);

    if (VersioningSettings.CompareVersions(context.Version, "1.0") <= 0)
    {
        // Preserve original logic for existing instances
        await context.CallActivityAsync("ProcessPayment", orderId);
    }
    else // a higher version (including 2.0)
    {
        // New logic with discount processing (replaces payment processing)
        await context.CallActivityAsync("ApplyDiscount", orderId);
        await context.CallActivityAsync("ProcessPaymentWithDiscount", orderId);
    }
    
    await context.CallActivityAsync("ShipOrder", orderId);

    return "Order processed successfully";
}

고급 사용

보다 정교한 버전 관리 시나리오의 경우 런타임에서 버전 일치 및 불일치를 처리하는 방법을 제어하도록 다른 설정을 구성할 수 있습니다.

Tip

대부분의 시나리오에서 기본 구성을CurrentOrOlderReject사용하여 버전 전환 중에 오케스트레이션 상태를 유지하면서 안전한 롤링 배포를 사용하도록 설정합니다. 기본 동작을 충족할 수 없는 특정 요구 사항이 있는 경우에만 고급 구성을 계속 진행하는 것이 좋습니다.

버전 일치

설정은 versionMatchStrategy 오케스트레이터 함수를 로드할 때 런타임이 오케스트레이션 버전과 일치하는 방법을 결정합니다. 버전 호환성에 따라 작업자가 처리할 수 있는 오케스트레이션 인스턴스를 제어합니다.

Configuration

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "<version>",
      "versionMatchStrategy": "CurrentOrOlder"
    }
  }
}

사용 가능한 전략

  • None (권장되지 않음): 오케스트레이션 버전을 완전히 무시합니다. 받은 모든 작업은 버전에 관계없이 처리됩니다. 이 전략은 버전 검사를 효과적으로 사용하지 않도록 설정하고 모든 작업자가 오케스트레이션 인스턴스를 처리할 수 있도록 합니다.

  • Strict: 작업자 defaultVersionhost.json에 지정된 버전과 정확히 동일한 버전의 오케스트레이션에서만 작업을 처리합니다. 이 전략은 가장 높은 수준의 버전 격리를 제공하지만 분리된 오케스트레이션을 방지하기 위해 신중한 배포 조정이 필요합니다. 버전 불일치의 결과는 버전 불일치 처리 섹션에 설명되어 있습니다.

  • CurrentOrOlder (기본값): 작업자의 defaultVersion에 지정된 host.json 버전보다 작거나 같은 버전의 오케스트레이션 작업을 처리합니다. 이 전략을 사용하면 이전 버전과의 호환성이 가능하므로 최신 작업자가 이전 오케스트레이터 버전에서 시작한 오케스트레이션을 처리할 수 있는 동시에 이전 작업자가 최신 오케스트레이션을 처리할 수 없습니다. 버전 불일치의 결과는 버전 불일치 처리 섹션에 설명되어 있습니다.

버전 불일치 처리

설정은 versionFailureStrategy 오케스트레이션 인스턴스 버전이 현재 defaultVersion버전과 일치하지 않을 때 발생하는 동작을 결정합니다.

Configuration:

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "<version>",
      "versionFailureStrategy": "Reject"
    }
  }
}

사용 가능한 전략:

  • Reject (기본값): 오케스트레이션을 처리하지 마세요. 오케스트레이션 인스턴스는 현재 상태로 유지되며 호환되는 작업자를 사용할 수 있게 되면 나중에 다시 시도될 수 있습니다. 이 전략은 오케스트레이션 상태를 유지하는 가장 안전한 옵션입니다.

  • Fail: 오케스트레이션에 실패합니다. 이 전략은 버전 불일치가 심각한 배포 문제를 나타내는 시나리오에서 적절할 수 있는 오류 상태로 오케스트레이션 인스턴스를 즉시 종료합니다.

특정 버전으로 새 오케스트레이션 및 하위 오케스트레이션 시작

기본적으로 모든 새 오케스트레이션 인스턴스는 defaultVersion 구성에 지정된 현재 host.json을 사용하여 만들어집니다. 그러나 현재 기본값과 다르더라도 특정 버전으로 오케스트레이션을 만들어야 하는 시나리오가 있을 수 있습니다.

특정 버전을 사용하는 경우:

  • 점진적 마이그레이션: 최신 버전을 배포한 후에도 이전 버전으로 오케스트레이션을 계속 만들고자 합니다.
  • 테스트 시나리오: 프로덕션에서 특정 버전 동작을 테스트해야 합니다.
  • 롤백 상황: 일시적으로 이전 버전으로 인스턴스를 만드는 것으로 되돌려야 합니다.
  • 버전별 워크플로: 비즈니스 프로세스에 따라 다른 오케스트레이션 버전이 필요합니다.

오케스트레이션 클라이언트 API를 사용하여 새 오케스트레이션 인스턴스를 만들 때 특정 버전 값을 제공하여 기본 버전을 재정의할 수 있습니다. 이렇게 하면 각 새 오케스트레이션 인스턴스에서 사용하는 버전을 세밀하게 제어할 수 있습니다.

[Function("HttpStart")]
public static async Task<HttpResponseData> HttpStart(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
    [DurableClient] DurableTaskClient client,
    FunctionContext executionContext)
{
    var options = new StartOrchestrationOptions
    {
        Version = "1.0"
    };
    
    string instanceId = await client.ScheduleNewOrchestrationInstanceAsync("ProcessOrderOrchestrator", orderId, options);

    // ...
}

오케스트레이터 함수 내에서 특정 버전으로 하위 오케스트레이션을 시작할 수도 있습니다.

[Function("MainOrchestrator")]
public static async Task<string> RunMainOrchestrator(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var subOptions = new SubOrchestratorOptions
    {
        Version = "1.0"
    };
    
    var result = await context.CallSubOrchestratorAsync<string>("ProcessPaymentOrchestrator", orderId, subOptions);
    
    // ...
}

레거시 코드 경로 제거

시간이 지남에 따라 오케스트레이터 함수에서 레거시 코드 경로를 제거하여 유지 관리를 간소화하고 기술적인 문제를 줄일 수 있습니다. 그러나 기존 오케스트레이션 인스턴스를 중단하지 않도록 코드를 신중하게 제거해야 합니다.

레거시 코드를 제거하는 것이 안전한 경우:

  • 이전 버전을 사용하는 모든 오케스트레이션 인스턴스가 완료되었습니다(성공, 실패 또는 종료됨).
  • 이전 버전으로 새 오케스트레이션 인스턴스가 만들어지지 않습니다.
  • 레거시 버전으로 실행 중인 인스턴스가 없는지 모니터링 또는 쿼리를 통해 확인했습니다.
  • 이전 버전이 마지막으로 배포된 이후 충분한 기간이 지났습니다(비즈니스 연속성 요구 사항 고려).

제거 모범 사례:

  • 실행 중인 인스턴스 모니터링: Durable Functions 관리 API를 사용하여 특정 버전을 사용하는 인스턴스를 쿼리합니다.
  • 보존 정책 설정: 각 버전에 대해 이전 버전과의 호환성을 유지할 기간을 정의합니다.
  • 증분 방식으로 제거: 동시에 여러 버전이 아닌 한 번에 하나의 버전을 제거하는 것이 좋습니다.
  • 문서 제거: 버전이 제거된 시기와 그 이유에 대한 명확한 레코드를 유지 관리합니다.

Warning

오케스트레이션 인스턴스가 여전히 해당 버전을 실행하는 동안 레거시 코드 경로를 제거하면 결정적인 재생 실패 또는 예기치 않은 동작이 발생할 수 있습니다. 코드를 제거하기 전에 항상 레거시 버전을 사용하는 인스턴스가 없는지 확인합니다.

모범 사례

버전 관리

  • 다중 파트 버전 관리 사용: 다음과 같은 major.minor.patch일관된 버전 관리 체계를 채택합니다.
  • 문서의 중요한 변경 사항: 새로운 버전이 필요한 변경 사항을 명확하게 문서화합니다.
  • 버전 수명 주기 계획: 레거시 코드 경로를 제거할 시기를 정의합니다.

코드 조직

  • 별도의 버전 논리: 다른 버전에 대해 명확한 분기 또는 별도의 메서드를 사용합니다.
  • 결정성 유지: 배포된 후 기존 버전 논리를 수정하지 않습니다. 변경이 절대적으로 필요한 경우(예: 중요한 버그 수정) 결정적 동작을 유지하고 작업 시퀀스를 변경하지 않도록 하거나 이전 오케스트레이션을 처리할 때 최신 오케스트레이터 버전이 실패할 것으로 예상합니다.
  • 철저히 테스트: 특히 전환 중에 모든 버전 경로를 테스트합니다.

모니터링 및 관찰 가능성

  • 로그 버전 정보: 더 쉽게 디버깅할 수 있는 버전을 로깅에 포함합니다.
  • 버전 배포 모니터링: 현재 실행 중인 버전을 추적합니다.
  • 경고 설정: 버전 관련 오류를 모니터링합니다.

Troubleshooting

일반적인 문제

  • 문제: 버전 2.0을 배포한 후 버전 1.0으로 만든 오케스트레이션 인스턴스가 실패합니다.

    • 해결 방법: 오케스트레이터의 버전 1.0 코드 경로가 정확히 동일하게 유지되는지 확인합니다. 실행 순서를 변경하면 결정적 재생이 중단됩니다.
  • 문제: 이전 오케스트레이터 버전을 실행하는 작업자는 새 오케스트레이션을 실행할 수 없습니다.

    • 해결 방법: 예상되는 동작입니다. 런타임은 의도적으로 이전 작업자가 안전을 유지하기 위해 최신 버전으로 오케스트레이션을 실행할 수 없도록 합니다. 모든 작업자가 최신 오케스트레이터 버전으로 업데이트되고 해당 defaultVersion 설정 host.json 이 그에 따라 업데이트되는지 확인합니다. 필요한 경우 고급 구성 옵션을 사용하여 이 동작을 수정할 수 있습니다(자세한 내용은 고급 사용 참조 ).
  • 문제: 버전 정보는 오케스트레이터에서 사용할 수 없습니다(context.Version 또는 context.getVersion() 설정에 defaultVersion 관계없이 null).

    • 해결 방법: 필수 구성 요소 섹션을 확인하여 환경이 오케스트레이션 버전 관리의 모든 요구 사항을 충족하는지 확인합니다.
  • 문제: 최신 버전의 오케스트레이션이 매우 느리게 진행되거나 완전히 중단됨

    • 해결 방법: 문제의 근본 원인은 다음과 같습니다.
      1. 최신 작업자 부족: 최신 오케스트레이션을 처리하기 위해 동일하거나 더 높은 버전을 defaultVersion 포함하는 충분한 수의 작업자가 배포되고 활성화되어 있는지 확인합니다.
      2. 이전 작업자의 오케스트레이션 라우팅 간섭: 이전 작업자는 오케스트레이션 라우팅 메커니즘을 방해하여 새 작업자가 처리를 위해 오케스트레이션을 선택하기가 더 어려워질 수 있습니다. 이는 특정 스토리지 공급자(Azure Storage 또는 MSSQL)를 사용할 때 특히 두드러질 수 있습니다. 일반적으로 Azure Functions 플랫폼은 배포 직후 오래된 작업자가 삭제되도록 하므로 지연은 일반적으로 중요하지 않습니다. 그러나 이전 작업자의 수명 주기를 제어할 수 있는 구성을 사용하는 경우 이전 작업자가 결국 종료되는지 확인합니다. 또는 이 문제에 덜 취약한 향상된 라우팅 메커니즘을 제공하므로 지속성 작업 스케줄러를 사용하는 것이 좋습니다.

다음 단계