Azure Functions的零停机部署与Terraform

huangapple go评论84阅读模式
英文:

Zero-downtime deployment of Azure Functions with Terraform

问题

我有一个Azure函数应用,其中必要的云基础架构由Terraform脚本化。与应用程序存档的关系通过azurerm_storage_blobapp_settings属性WEBSITE_RUN_FROM_PACKAGE来实现。

// ...

resource "azurerm_storage_blob" "archive" {
  name                   = "application.zip"
  storage_account_name   = var.storage_account_name
  storage_container_name = var.storage_container_name
  type                   = "Block"
  source                 = "dist/application.zip"
}

resource "azurerm_function_app" "app" {
  name                      = "function-app"
  resource_group_name       = var.resource_group_name
  location                  = var.location
  app_service_plan_id       = var.azurerm_app_service_plan_id
  storage_connection_string = var.storage_connection_string

  app_settings = {
    // ...
    WEBSITE_RUN_FROM_PACKAGE     = "https://${var.storage_account_name}.blob.core.windows.net/${var.storage_container_name}/${azurerm_storage_blob.archive.name}${var.storage_account_sas}"
  }
}

// ...

要部署Function App的新版本,我会标记azurerm_storage_blob并运行terraform apply。在执行该命令期间,Function App 不可用数秒钟。

如何使用Terraform实现零停机部署?


我找到了两种方法,但无法解决问题。

  1. Terraform的create_before_destroy应该不起作用,因为Terraform会更新azurerm_function_app
  2. Terraform的azurerm_app_service_slot不适用于Function Apps(#1307#4684
英文:

I have an Azure Function App where the necessary cloud infrastructure is scripted by Terraform. The relation to application archive is implemented via azurerm_storage_blob and the app_settings property WEBSITE_RUN_FROM_PACKAGE.

// ...

resource "azurerm_storage_blob" "archive" {
  name                   = "application.zip"
  storage_account_name   = var.storage_account_name
  storage_container_name = var.storage_container_name
  type                   = "Block"
  source                 = "dist/application.zip"
}

resource "azurerm_function_app" "app" {
  name                      = "function-app"
  resource_group_name       = var.resource_group_name
  location                  = var.location
  app_service_plan_id       = var.azurerm_app_service_plan_id
  storage_connection_string = var.storage_connection_string

  app_settings = {
    // ...
    WEBSITE_RUN_FROM_PACKAGE     = "https://${var.storage_account_name}.blob.core.windows.net/${var.storage_container_name}/${azurerm_storage_blob.archive.name}${var.storage_account_sas}"
  }
}

// ...

To deploy a new version of the Function App, I taint the azurerm_storage_blob and run terraform apply. During the execution of the command the Function App isn't avialable for sevaral seconds.

How can I implemented a zero-downtime deployment with Terraform?


I found two approaches but couldn't solve the problem.

  1. Terraform's create_before_destroy shouldn't be working because the azurerm_function_app is updated by Terraform.
  2. Terraform's azurerm_app_service_slot isn't ready for Function Apps (#1307, #4684)

答案1

得分: 3

TL;DR:azurerm_app_service_slot的问题已经通过PR#6435解决,并由TerraformMicrosoft进行了记录。这个答案尚未完全涵盖此问题。为了避免删除然后重新创建您的 blob,请在代码更改时更改您的 blob name 并将 HASH 添加到您的 app_settings 中:

resource "azurerm_storage_blob" "archive" {
  name                   = "application.${var.package_version}.zip"
  ...
}

resource "azurerm_function_app" "app" {
  ...
}

resource "azurerm_function_app_slot" "slotStaging" {
  name                       = "stag"
  function_app_name          = azurerm_function_app.app.name
  ...
  app_settings = {
    ...
    HASH = base64encode(filesha256("dist/application.zip"))
  }
}

resource "null_resource" "swapSlots" {
  triggers = {
    HASH = azurerm_function_app.app.app_settings.HASH
  }
  provisioner "local-exec" {
    command = "sleep 1m && az functionapp deployment slot swap -g ${azurerm_resource_group.resourceGroup.name} -n ${azurerm_function_app.functionApp.name} --slot ${azurerm_function_app_slot.slotStaging.name} --target-slot production"
  }
  depends_on = [azurerm_function_app.functionApp]
}

Explanation:
有一个首要问题正在影响您,详细信息请参见https://github.com/terraform-providers/terraform-provider-azurerm/issues/1990。基本上,Terraform 不会检测到您的 zip 内容的更改,需要一种机制来实现这一点。确保包版本名称包含在 blob 名称中,以确保在发生新更改时进行更新。可以进行任何名称更改,只是似乎更加优雅地依赖于语义版本控制。

只要您的代码更新到存储桶并且其 HASH 作为函数配置的一部分(自动计算)存在,那么Terraform 将知道更新函数以重新加载新代码。这将作为无停机配置更改进行,以使之前的代码版本失效,并且将组成并部署一个新容器到您的主机以处理下一个准备接收的请求。

WIP
默认槽位为production。将更新的代码推送到stag槽位,然后从stag切换到production

使用null_resource可以进行槽位转换,命令为az functionapp deployment slot swap -g MyResourceGroup -n MyUniqueApp --slot staging --target-slot production

英文:

TL;DR: the azurerm_app_service_slot issues are resolved by PR#6435 and documented by Terraform and Microsoft. This answer doesn't yet cover that completely. To avoid deleting and then recreating your blob, change your blob name when your code changes and add HASH to your app_settings:

resource "azurerm_storage_blob" "archive" {
  name                   = "application.${var.package_version}.zip"
  ...
}

resource "azurerm_function_app" "app" {
  ...
}

resource "azurerm_function_app_slot" "slotStaging" {
  name                       = "stag"
  function_app_name          = azurerm_function_app.app.name
  ...
  app_settings = {
    ...
    HASH = base64encode(filesha256("dist/application.zip"))
  }
}

resource "null_resource" "swapSlots" {
  triggers = {
    HASH = azurerm_function_app.app.app_settings.HASH
  }
  provisioner "local-exec" {
    command = "sleep 1m && az functionapp deployment slot swap -g ${azurerm_resource_group.resourceGroup.name} -n ${azurerm_function_app.functionApp.name} --slot ${azurerm_function_app_slot.slotStaging.name} --target-slot production"
  }
  depends_on = [azurerm_function_app.functionApp]
}

Explanation:
There is a first issue impacting you described at https://github.com/terraform-providers/terraform-provider-azurerm/issues/1990. Basically that Terraform wont detect changes to the content of your zip and needs a mechanism to do so. Making sure the package version name is in the blob name ensure it is updated when a new change happens. It can be any name change, it just seems more elegant to rely on semver.

So long as your code is updated to the bucket and the HASH of it is part of your function's config (as automatically calculated), then Terraform will know to update the function to reload the new code. This will happen as a no downtime configuration change such that the previous code version will be invalidated and a new container will be composed and deployed to your host to handle the next request it is ready to catch.

WIP
The default slot is production. Pushing your updated code into the stag slot followed by the swapping from stag to production

The slot is transitioned with az functionapp deployment slot swap -g MyResourceGroup -n MyUniqueApp --slot staging --target-slot production which can be run in a null_resource.

答案2

得分: 1

如果我理解您在这个问题中的意思正确的话,您想要在使用Terraform从存储帐户中的.zip文件部署Azure函数应用的新版本时避免停机时间。

如果在运行terraform apply时只有一个属性WEBSITE_RUN_FROM_PACKAGE会发生变化,我认为Azure函数应用将会在原地更新。作为一种解决方法,您可以使用ignore_changes来忽略首次terraform apply上的网站更新。在创建了有问题的azurerm_storage_blob后,运行下一个terraform apply以更新函数应用。

lifecycle {
  ignore_changes = [
    app_settings["WEBSITE_RUN_FROM_PACKAGE"]
  ]
}

另一种选项是,您可以创建一个带有新包的新名称的函数应用。在新函数应用配置之后,您可以销毁旧函数应用。为此,您可以使用create_before_destroy = true。这将生成一个新的函数应用URL。

您可以参考这本书的第385页,即使它讨论的是应用服务,它与函数应用具有相同的部署逻辑。

Azure Functions的零停机部署与Terraform

英文:

If I am understanding you correctly in this question, you want to avoid the downtime when you deploy a new version of the Azure function app from a .zip file in the storage account with Terraform.

If there is only one attribute WEBSITE_RUN_FROM_PACKAGE that will change in the code when run terraform apply, I think the Azure function app will be updated in-place. As a workaround, you could use ignore_changes to ignore the websites update first on the first terraform apply. Run the next terraform apply to update the function app after the tainted azurerm_storage_blob has been created.

  lifecycle {
    ignore_changes = [
      app_settings["WEBSITE_RUN_FROM_PACKAGE"]
    ]
  }

Another option, you may create a function app with a new name with a new package. After the new function app provision, you could destroy the old function app. You will use create_before_destroy = true for this. It will generate a new function app URL.

You could refer to this book on page 385, even it talks to the app service, which has the same deployment logic as the function app.
Azure Functions的零停机部署与Terraform

huangapple
  • 本文由 发表于 2020年1月3日 14:36:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/59574191.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定