Durable Functions的零停机时间部署

Durable Functions可靠的执行模型要求业务流程具有确定性,这在部署更新时会产生挑战。 当部署包含中断性变更(如修改的活动函数签名或变更的协调程序逻辑)时,正在执行的业务流程实例将会失败。 这种情况尤其对长时间运行的编排是个问题,因为这可能涉及数小时或数天的工作。

注意

本文中的策略假定你使用的是Durable Functions的默认Azure 存储提供程序。 如果使用的是其他存储提供程序,则指南可能不适用。 编排版本控制策略是个例外,它适用于任何存储后端。 有关存储提供程序选项的详细信息,请参阅 Durable Functions 存储提供程序

下表比较了实现零停机部署的四种策略。 选择最适合工作负荷的策略:

策略 何时使用 Pros 缺点
编排版本控制 (建议) 具有 重大更改 的应用程序,这些更改需要同时运行的多个业务流程版本。 通过中断性变更启用零停机时间部署。
内置功能,几乎无需配置。
适用于任何存储后端。
需要仔细修改业务流程协调程序代码,以便实现版本兼容性。
基于名称的版本控制 具有不经常发生的重大变更的应用程序,其中优先考虑简单性。 易于实施。 函数应用的内存大小和函数数目增加。
代码重复
插槽状态检查 具有短生存期(少于24小时)和执行之间的可预测间隙的编排系统。 简单的基本代码。
不需要额外的函数应用管理。
需要额外的存储帐户或任务中心管理。
需要有一段时间没有任何协调运行。
应用程序路由 系统持续运行业务流程(超过 24 小时)或频繁重叠的执行,且没有空闲窗口。 处理持续运行且引入重大变更的编排的新系统版本。 需要智能应用程序路由器。
可以最多使用订阅允许的函数应用数(默认值为 100)。

业务流程版本控制

编排版本控制功能是建议在有破坏性更改的情况下进行零停机部署的策略。 它使不同版本的业务流程可以共存并同时执行,而不会发生冲突。

使用业务流程版本控制:

  • 每个编排实例在创建时都会永久与一个版本相关联。
  • 运行更新版本业务流程协调程序的辅助角色可以继续执行更旧版本的实例。
  • 运行更旧版本业务流程协调程序的辅助角色无法执行更新版本的实例。
  • 协调程序函数可以检查其版本并相应地执行分支操作。

此方法有助于滚动升级,其中运行不同版本的应用程序的工作节点可以在同一系统中安全地共存。 与本文中的其他策略不同,业务流程版本控制与 后端无关 ,适用于任何存储提供程序。

有关完整的实现步骤(包括如何配置版本控制、在业务流程协调程序代码中处理版本分支和管理滚动升级),请参阅 业务流程版本控制

其余策略是在编排版本控制不适用情况下的替代方案。

基于名称的版本控制

使用此策略,可以在同一函数应用中与旧版本一起创建新的函数版本。 每个函数的版本都成为其名称的一部分(例如,MyOrchestrator_v1MyOrchestrator_v2)。 由于保留了以前的版本,因此运行中的编排实例可以继续引用它们。 对新业务流程实例的请求调用最新版本,业务流程客户端函数可以从应用设置引用该版本。 下图说明了此方法。

基于名称的版本控制策略图的截图,显示函数版本如何在 Durable Functions 应用程序中共存。

在此策略中,必须复制每个函数,并且必须更新其对其他函数的引用。 可以通过编写脚本来简化此过程。 下面是使用迁移脚本的示例项目

注意

此策略使用部署槽来避免部署期间发生停机。 有关如何创建和使用新部署槽的详细信息,请参阅 Azure Functions 部署槽

插槽状态检查

当前版本的函数应用正在生产插槽中运行时,请将新版本的函数应用部署到备用插槽。 在交换生产槽和暂存槽之前,检查是否有任何正在运行的协调实例。 所有编排实例完成后,可以进行交换。 在没有编排实例运行的可预测时间段内,此策略适用。 如果业务流程不会长时间运行,并且业务流程执行不经常重叠,则这是最佳方法。

函数应用配置

请使用以下过程来设置此方案。

  1. 将部署插槽添加到函数应用,以用于预发布和生产。

  2. 对于每个槽,请将 AzureWebJobsStorage 应用程序设置指定为共享存储帐户的连接。 此存储帐户连接由 Azure Functions 运行时用来安全地存储函数的访问密钥。 要实现最高级别的安全性,建议使用与存储帐户的托管身份连接

  3. 对于每个插槽,请新建一个应用设置,例如 DurableManagementStorage。 将其值设置为不同存储帐户的连接字符串。 Durable Functions 扩展使用这些存储帐户来实现可靠执行。 对每个槽使用单独的存储帐户。 不要将此设置标记为部署插槽设置。 再次强调,基于托管标识的连接是最安全的。

  4. 在函数应用的 host.json 文件的 durableTask 节中,将 connectionStringName (Durable 2.x) 或 azureStorageConnectionStringName (Durable 1.x) 指定为在步骤 3 中创建的应用设置的名称。

下图显示了部署槽和存储帐户的所述配置。 在这种可能的预部署方案中,函数应用版本 2 在生产槽中运行,而版本 1 保留在预生产槽中。

Durable Functions零停机部署中的部署槽和存储帐户配置截图(在槽交换前)。

host.json 示例

以下 JSON 片段显示了 host.json 文件中的连接字符串设置。

{
  "version": 2.0,
  "extensions": {
    "durableTask": {
      "hubName": "MyTaskHub",
      "storageProvider": {
        "connectionStringName": "DurableManagementStorage"
      }
    }
  }
}

注意

对于旧版 Functions 1.x 应用,请在 azureStorageConnectionStringName 节中直接使用 durableTask 属性,而不是使用 storageProvider.connectionStringName

CI/CD 管道配置

将 CI/CD 管道配置为仅当函数应用不存在任何挂起或正在运行的协调实例时进行部署。 使用Azure Pipelines时,可以创建一个用于检查这些条件的函数,如以下 C# 示例所示。 同样的模式适用于其他语言 — 查询具有PendingRunning状态的业务流程实例,并返回是否存在。

[FunctionName("StatusCheck")]
public static async Task<IActionResult> StatusCheck(
    [HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestMessage req,
    [DurableClient] IDurableOrchestrationClient client,
    ILogger log)
{
    var runtimeStatus = new List<OrchestrationRuntimeStatus>();

    runtimeStatus.Add(OrchestrationRuntimeStatus.Pending);
    runtimeStatus.Add(OrchestrationRuntimeStatus.Running);

    var result = await client.ListInstancesAsync(new OrchestrationStatusQueryCondition() { RuntimeStatus = runtimeStatus }, CancellationToken.None);
    return (ActionResult)new OkObjectResult(new { HasRunning = result.DurableOrchestrationState.Any() });
}

接下来,将过渡门限配置为等到没有任何业务流程运行为止。 有关详细信息,请参阅使用入口的发布部署控制

Azure Pipelines 部署网关配置的截图,用于零停机部署的 Durable Functions。

在部署开始之前,Azure Pipelines 将检查您的函数应用中是否有正在运行的编排实例。

正在运行的 Azure Pipelines 部署门禁检查的编排实例

现在您的函数应用的新版本应已部署到暂存槽位。

零停机部署期间,新的 Durable Functions 应用版本已部署到暂存槽,截图如下。

最后交换插槽。

未标记为部署槽设置的应用程序设置也会交换,因此,版本 2 应用将保留它对存储帐户 A 的引用。由于业务流程状态是在存储帐户中跟踪的,因此,在版本 2 应用中运行的任何业务流程将继续在新槽中运行,而不会中断。

槽交换完成后的截图,Durable Functions 应用设置已移动到生产环境。

若要对两个槽使用同一个存储帐户,可以更改任务中心的名称。 在这种情况下,您需要管理槽位的状态以及应用的 HubName 设置。 若要了解详细信息,请参阅 Durable Functions 中的任务中心

应用路由

此策略是最复杂的,但对于那些持续运行且从未有空闲窗口进行槽交换的编排系统,这是唯一的选择。

对于此策略,请在Durable Functions前面创建应用路由器,例如,使用 HTTP 触发器的 Azure 函数或基于版本标头路由的 API 管理实例。 路由器负责:

  • 部署函数应用。
  • 管理应用的哪个版本处于活动状态。
  • 根据版本将编排请求路由到正确的函数应用。

首次收到业务流程请求时,该路由器执行以下任务:

  1. 在 Azure 中创建新的函数应用。
  2. 将函数应用的代码部署到 Azure 中的新函数应用。
  3. 将编排请求转发到新应用。

该路由器负责管理您的应用代码的哪个版本被部署到Azure中的哪个函数应用上。

用于 Durable Functions 零停机部署的初次应用路由和部署流程的截图。

该路由器根据连同请求一起发送的版本,将部署和业务流程请求定向到相应的函数应用。 它会忽略修补程序版本。

当您部署一个没有重大更改的新应用版本时,可以递增修补程序版本号。 该路由器将部署到现有的函数应用,并将对旧版和新版代码的请求发送到同一个函数应用。

当持久函数部署中没有中断性变更时的应用程序路由截图。

部署具有中断性变更的新应用版本时,可以递增主要版本或次要版本。 然后,应用程序路由器将在 Azure 中创建新的函数应用,部署到该应用,并将对新版应用的请求路由到该应用。 在下图中,在 1.0.1 版应用中运行的业务流程将保持运行,但对 1.1.0 版本的请求将路由到新的函数应用。

Durable Functions 部署中存在中断性变更的应用程序路由的截图。

该调度器监视 1.0.1 版本的编排状态,并在所有编排完成后删除应用程序。

跟踪店铺设置

每个函数应用应使用单独的计划队列(这些队列可能位于不同的存储帐户中)。 若要跨应用程序的所有版本查询所有业务流程实例,可以在函数应用之间共享实例和历史记录表。 可以通过在trackingStoreConnectionStringName文件中配置trackingStoreNamePrefix设置来共享表,使它们全部使用相同的值。

有关详细信息,请参阅在 Azure 中管理 Durable Functions 中的实例

显示不同版本的 Durable Functions 应用程序共享跟踪存储设置的屏幕截图。

后续步骤