英文:
Zero-downtime deployment of Azure Functions with Terraform
问题
我有一个Azure函数应用,其中必要的云基础架构由Terraform脚本化。与应用程序存档的关系通过azurerm_storage_blob
和app_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实现零停机部署?
我找到了两种方法,但无法解决问题。
- Terraform的
create_before_destroy
应该不起作用,因为Terraform会更新azurerm_function_app
。 - 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
得分: 3
TL;DR:azurerm_app_service_slot
的问题已经通过PR#6435解决,并由Terraform和Microsoft进行了记录。这个答案尚未完全涵盖此问题。为了避免删除然后重新创建您的 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页,即使它讨论的是应用服务,它与函数应用具有相同的部署逻辑。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论