|
| 1 | +--- |
| 2 | +title: Zero downtime deployment for Durable Functions |
| 3 | +description: Learn how to enable your Durable Functions orchestration for zero downtime deployments. |
| 4 | +services: functions |
| 5 | +author: tsushi |
| 6 | +manager: gwallace |
| 7 | +ms.service: azure-functions |
| 8 | +ms.topic: conceptual |
| 9 | +ms.date: 10/10/2019 |
| 10 | +ms.author: azfuncdf |
| 11 | +#Customer intent: As a Durable Functions user, I want to deploy with zero downtime so that updates don't interrupt my Durable Functions orchestration execution. |
| 12 | +--- |
| 13 | + |
| 14 | +# Zero downtime deployment for Durable Functions |
| 15 | +The [reliable execution model](durable-functions-checkpointing-and-replay.md) of Durable Functions requires that orchestrations be deterministic, which creates an additional challenge to consider when deploying updates. When a deployment contains changes to activity function signatures or orchestrator logic, in-flight orchestration instances fail. This situation is especially a problem for instances of long-running orchestrations, which may represent hours or days of work. |
| 16 | + |
| 17 | +To prevent these failures from happening, you must either delay your deployment until all running orchestration instances have completed, or make sure that any running orchestration instances use the existing versions of your functions. For more information about versioning, see [Versioning in Durable Functions](durable-functions-versioning.md). |
| 18 | + |
| 19 | +The following chart compares the three main strategies to achieve a zero downtime deployment for Durable Functions: |
| 20 | + |
| 21 | +| Strategy | When to use | Pros | Cons | |
| 22 | +| -------- | ------------ | ---- | ---- | |
| 23 | +| **[Versioning](#versioning)** | Applications that don't experience frequent [breaking changes.](durable-functions-versioning.md) | Simple to implement. | Increased function app size in memory and number of functions.<br/>Code duplication. | |
| 24 | +| **[Status check with slot](#status-check-with-slot)** | A system that doesn't have long-running orchestrations lasting more than 24-hours or frequently overlapping orchestrations. | Simple code base.<br/>Doesn't require additional function app management. | Requires additional storage account or task hub management.<br/>Requires periods of time when no orchestrations are running. | |
| 25 | +| **[Application routing](#application-routing)** | A system that doesn't have periods of time when orchestrations aren't running, such as those with orchestrations lasting more than 24-hours or with frequently overlapping orchestrations. | Handles new versions of systems with continually running orchestrations that have breaking changes. | Requires an intelligent application router.<br/>Could max-out the number of function apps allowed by your subscription (default 100). | |
| 26 | + |
| 27 | +## Versioning |
| 28 | +Define new versions of your functions and leave the old versions in your function app. As you can see on the diagram, a function's version becomes part of its name. Because previous versions of functions are preserved, in-flight orchestration instances can continue to reference them. Meanwhile, requests for new orchestration instances call for the latest version, which your orchestration client function can reference from an app setting. |
| 29 | + |
| 30 | + |
| 31 | + |
| 32 | +In this strategy, every function must be copied and its references to other functions updated. You can make it easier by writing a script. Here is a [sample project](https://github.com/TsuyoshiUshio/DurableVersioning) with a migration script. |
| 33 | + |
| 34 | +>[!NOTE] |
| 35 | +>This strategy uses deployment slots to avoid downtime during deployment. For more detailed information about how to create and use new deployment slots refer to [Azure Functions deployment slots](../functions-deployment-slots.md). |
| 36 | + |
| 37 | +## Status check with slot |
| 38 | + |
| 39 | +While the current version of your function app is running in your production slot, deploy the new version of your function app to your staging slot. Before you swap your production and staging slots, check to see if there are running orchestration instances. After all orchestration instances are complete, you can do the swap. This strategy works when you have predictable periods when no orchestration instances are in-flight. This is the best approach when your orchestrations aren't long-running and when your orchestration executions don't frequently overlap. |
| 40 | + |
| 41 | +### Function app configuration |
| 42 | + |
| 43 | +You can use the following procedure to set up this scenario: |
| 44 | + |
| 45 | +1. [Add deployment slots](../functions-deployment-slots.md#add-a-slot) to your function app for staging and production. |
| 46 | + |
| 47 | +1. For each slot, set the [AzureWebJobsStorage application setting](../functions-app-settings.md#azurewebjobsstorage) to the connection string of a shared storage account. This will be used by the Azure Functions runtime. This account will be used by the Azure Functions runtime and manages the function's keys. |
| 48 | + |
| 49 | +1. For each slot, create a new app setting (ex. DurableManagementStorage) and set its value to the connection string of different storage accounts. These storage accounts will be used by the Durable Functions extension for [reliable execution](durable-functions-checkpointing-and-replay.md). Use a separate storage account for each slot. Don't mark this setting as a deployment slot setting. |
| 50 | + |
| 51 | +1. In your function app's [host.json file's durableTask section](durable-functions-bindings.md#hostjson-settings), specify azureStorageConnectionStringName as the name of the app setting you created in step 3. |
| 52 | + |
| 53 | +The diagram below shows illustrates the described configuration of deployment slots and storage accounts. In this potential pre-deployment scenario, version 2 of a function app is running in the production slot, while version 1 remains in the staging slot. |
| 54 | + |
| 55 | + |
| 56 | + |
| 57 | +### host.json examples |
| 58 | + |
| 59 | +The following JSON fragments are examples of the connection string setting in the host.json file. |
| 60 | + |
| 61 | +#### Functions 2.x |
| 62 | + |
| 63 | +```json |
| 64 | +{ |
| 65 | + "version": 2.0, |
| 66 | + "extensions": { |
| 67 | + "durableTask": { |
| 68 | + "azureStorageConnectionStringName": "DurableManagementStorage" |
| 69 | + } |
| 70 | + } |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +#### Functions 1.x |
| 75 | + |
| 76 | +```json |
| 77 | +{ |
| 78 | + "durableTask": { |
| 79 | + "azureStorageConnectionStringName": "DurableManagementStorage" |
| 80 | + } |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +### CI/CD pipeline configuration |
| 85 | + |
| 86 | +Configure your CI/CD pipeline to deploy only when your function app has no pending or running orchestration instances. When you're using Azure Pipelines, you can create a function that checks for these conditions, as in the following example: |
| 87 | + |
| 88 | +```csharp |
| 89 | +[FunctionName("StatusCheck")] |
| 90 | +public static async Task<IActionResult> StatusCheck( |
| 91 | + [HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestMessage req, |
| 92 | + [OrchestrationClient] DurableOrchestrationClient client, |
| 93 | + ILogger log) |
| 94 | +{ |
| 95 | + var runtimeStatus = new List<OrchestrationRuntimeStatus>(); |
| 96 | +
|
| 97 | + runtimeStatus.Add(OrchestrationRuntimeStatus.Pending); |
| 98 | + runtimeStatus.Add(OrchestrationRuntimeStatus.Running); |
| 99 | +
|
| 100 | + var status = await client.GetStatusAsync(new DateTime(2015,10,10), null, runtimeStatus); |
| 101 | + return (ActionResult) new OkObjectResult(new Status() {HasRunning = (status.Count != 0)}); |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +Next, configure the staging gate to wait until no orchestrations are running. For more information, see [Release deployment control using gates](/azure/devops/pipelines/release/approvals/gates?view=azure-devops) |
| 106 | + |
| 107 | + |
| 108 | + |
| 109 | +Azure Pipelines checks your function app for running orchestration instances before your deployment starts. |
| 110 | + |
| 111 | + |
| 112 | + |
| 113 | +Now the new version of your function app should be deployed to the staging slot. |
| 114 | + |
| 115 | + |
| 116 | + |
| 117 | +Finally, swap slots. |
| 118 | + |
| 119 | +Application settings that aren't marked as deployment slot settings are also swapped, so the version 2 app keeps its reference to storage account A. Because orchestration state is tracked in the storage account, any orchestrations running on the version 2 app continue to run in the new slot without interruption. |
| 120 | + |
| 121 | + |
| 122 | + |
| 123 | +To use the same storage account for both slots, you can change the names of your task hubs. In this case, you need to manage the state of your slots and your apps HubName settings. To learn more, see [Task hubs in Durable Functions](durable-functions-task-hubs.md). |
| 124 | + |
| 125 | +## Application routing |
| 126 | + |
| 127 | +This strategy is the most complex. However, it can be used for function apps that don't have time between running orchestrations. |
| 128 | + |
| 129 | +For this strategy, you must create an *application router* in front of your Durable Functions. This router can be implemented with Durable Functions, and has the following responsibilities: |
| 130 | + |
| 131 | +* Deploying the function app. |
| 132 | +* Managing the version of Durable Functions. |
| 133 | +* Routing orchestration requests to function apps. |
| 134 | + |
| 135 | +The first time an orchestration request is received, the router does the tasks: |
| 136 | + |
| 137 | +1. Creates a new function app in Azure. |
| 138 | +2. Deploys your function app's code to the new function app in Azure. |
| 139 | +3. Forwards the orchestration request to the new app. |
| 140 | + |
| 141 | +The router manages the state of which version of your app's code is deployed to which function app in Azure. |
| 142 | + |
| 143 | + |
| 144 | + |
| 145 | +The router directs deployment and orchestration requests to the appropriate function app based on the `version` sent with the request, ignoring patch version. |
| 146 | + |
| 147 | +When you deploy a new version of your app *without* a breaking change, you can increment the patch version. The router deploys to your existing function app, and sends requests for the old and new versions of the code are routed to same function app. |
| 148 | + |
| 149 | + |
| 150 | + |
| 151 | +When you deploy a new version of your app with a breaking change, you can increment the major or minor version. Then the application router creates a new function app in Azure, deploys to it, and routes requests for the new version of your app to it. In the diagram below, running orchestrations on the 1.0.1 version of the app keep running, but requests for the 1.1.0 version are routed to the new function app. |
| 152 | + |
| 153 | + |
| 154 | + |
| 155 | +The router monitors the status of orchestrations on the 1.0.1 version, and removes apps after all orchestrations have been finished. |
| 156 | + |
| 157 | +### Tracking store settings |
| 158 | + |
| 159 | +Each function app should use separate scheduling queues, possibly in separate storage accounts. However, if you want to query all orchestrations instances across all versions of your application, you can share instance and history tables across your function apps. You can share tables by configuring the `trackingStoreConnectionStringName` and `trackingStoreNamePrefix` in the [host.json settings](durable-functions-bindings.md#host-json) file so that they all use the same values. |
| 160 | + |
| 161 | +For more details, [Manage instances in Durable Functions in Azure](durable-functions-instance-management.md). |
| 162 | + |
| 163 | + |
| 164 | + |
| 165 | +## Next steps |
| 166 | + |
| 167 | +> [!div class="nextstepaction"] |
| 168 | +> [Versioning Durable Functions](durable-functions-versioning.md) |
| 169 | + |
0 commit comments