Azure WebApp - 版本控制的配置/用户数据与应用程序分开部署

标签 azure azure-web-app-service azure-pipelines-release-pipeline kudu

我开发了一个 WebApp (API),它托管在 Azure 中并使用 VSTS 进行 CI/CD/版本控制。

我想让这个 API 的客户(所有者)能够更新 wwwroot 下的各种配置/数据文件,但是我希望这些文件受到版本控制(事实来源 - 一个单独的存储库API 源代码)。在存储库中创建/更新/删除这些文件之一应该会导致该文件在 WebApp 中上传/删除(在 wwwroot 下的文件夹中)。

修改(创建/删除)其中一个文件不应触发(Web应用程序)的完全重新部署

我怎样才能实现这个目标?到目前为止,我已经考虑过 GIT 工件的 VSTS 发布管道,但是我看不到一种低摩擦的方法来在 Azure Web 应用程序中进行更改(KUDU API 似乎有点复杂和粗暴)

**编辑:** 使用构建工件同步 WebApp 中的配置文件的示例 PowerShell 脚本(仅在必要时调用 PUT/DELETE)。

VSTS Configuration

# The idea behind this script is to synchronize the configuration files on the server with what's in the repo, only updating files where necessary

param (
    [string]$resourceGroupName = "XXX",
    [Parameter(Mandatory=$true)][string]$webAppName,
    [Parameter(Mandatory=$true)][string]$latestConfigFilesPath
)

function Get-AzureRmWebAppPublishingCredentials($resourceGroupName, $webAppName, $slotName = $null) {
    if ([string]::IsNullOrWhiteSpace($slotName)) {
        $resourceType = "Microsoft.Web/sites/config"
        $resourceName = "$webAppName/publishingcredentials"
    } else {
        $resourceType = "Microsoft.Web/sites/slots/config"
        $resourceName = "$webAppName/$slotName/publishingcredentials"
    }

    $publishingCredentials = Invoke-AzureRmResourceAction -ResourceGroupName $resourceGroupName -ResourceType $resourceType -ResourceName $resourceName -Action list -ApiVersion 2015-08-01 -Force

    return $publishingCredentials
}

function Get-KuduApiAuthorisationHeaderValue($resourceGroupName, $webAppName, $slotName = $null) {
    $publishingCredentials = Get-AzureRmWebAppPublishingCredentials $resourceGroupName $webAppName $slotName
    return ("Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $publishingCredentials.Properties.PublishingUserName, $publishingCredentials.Properties.PublishingPassword))))
}

function Get-KuduInode($kuduHref) {
    return Invoke-RestMethod -Uri $kuduHref `
                             -Headers @{"Authorization"=$kuduApiAuthorisationToken;"If-Match"="*"} `
                             -Method GET `
                             -ContentType "application/json"
}

function Get-AllFilesUnderKuduHref($kuduHref) {
    $result = @()
    $inodes = (Get-KuduInode $kuduHref)
    Foreach ($inode in $inodes) {
        if ($inode.mime -eq "inode/directory") {
            $result += (Get-AllFilesUnderKuduHref $inode.href)
        } else {
            $result += $inode.href
        }
    }

    return $result
}

function Get-LocalPathForUri([System.Uri]$uri) {
    $latestConfigFilesUri = [System.Uri]$latestConfigFilesPath
    $localFileUri = [System.Uri]::new($latestConfigFilesUri, $uri)
    return $localFileUri.LocalPath
}

function Get-RemoteUri([System.Uri]$uri) {
    return  [System.Uri]::new($configurationHref, $uri)
}

function Files-Identical($uri) {
    $localFilePath = Get-LocalPathForUri $uri
    $localFileHash = Get-FileHash $localFilePath -Algorithm MD5

    # Download the remote file so that we can calculate the hash. It doesn't matter that it doesn't get cleaned up, this is running on a temporary build server anyway.
    $temporaryFilePath = "downloded_kudu_file"
    $remoteFileUri = [System.Uri]::new($configurationHref, $uri)
    Invoke-RestMethod -Uri $remoteFileUri `
                      -Headers @{"Authorization"=$kuduApiAuthorisationToken;"If-Match"="*"} `
                      -Method GET `
                      -OutFile $temporaryFilePath `
                      -ContentType "multipart/form-data"

    $remoteFileHash = Get-FileHash $temporaryFilePath -Algorithm MD5

    return $remoteFileHash.Hash -eq $localFileHash.Hash
}

function CalculateRelativePath([System.Uri]$needle, [System.Uri]$haystack) {
    return $haystack.MakeRelativeUri($needle).ToString();
}

function Put-File([System.Uri]$uri) {
    Write-Host "Uploading file $uri"
    $localFilePath = Get-LocalPathForUri $uri
    $remoteFileUri = Get-RemoteUri $uri
    Invoke-RestMethod -Uri $remoteFileUri `
                      -Headers @{"Authorization"=$kuduApiAuthorisationToken;"If-Match"="*"} `
                      -Method PUT `
                      -InFile $localFilePath `
                      -ContentType "multipart/form-data"
}

function Delete-File([System.Uri]$uri) {
    Write-Host "Deleting file $uri"
    $remoteFileUri = Get-RemoteUri $uri
    Invoke-RestMethod -Uri $remoteFileUri `
                      -Headers @{"Authorization"=$kuduApiAuthorisationToken;"If-Match"="*"} `
                      -Method DELETE `
}

# Script begins here

$configurationHref = [System.Uri]"https://$webAppName.scm.azurewebsites.net/api/vfs/site/wwwroot/Configuration/"
$kuduApiAuthorisationToken = Get-KuduApiAuthorisationHeaderValue -resourceGroupName $resourceGroupName -webAppName $webAppName
$filenamesOnServer = Get-AllFilesUnderKuduHref $configurationHref $kuduApiAuthorisationToken | % { $configurationHref.MakeRelativeUri($_).OriginalString }

Write-Host "Files currently on server" $filenamesOnServer

$filesCurrentlyInRepo = Get-ChildItem -Path $latestConfigFilesPath -Recurse -File
$filenamesInRepo = $filesCurrentlyInRepo | Select-Object -ExpandProperty FullName | % { CalculateRelativePath $_ $latestConfigFilesPath}

Write-Host "Files currently in repo" $filenamesInRepo

$intersection = $filenamesOnServer | ?{$filenamesInRepo -contains $_}
Write-Host "Intersection: " $intersection

$onlyOnServer = $filenamesOnServer | ?{-Not($filenamesInRepo -contains $_)}
$onlyInRepo = $filenamesInRepo | ?{-Not($filenamesOnServer -contains $_)}
Write-Host "Only on server" $onlyOnServer
Write-Host "Only in repo" $onlyInRepo
Write-Host

Foreach ($uri in $onlyInRepo) {
    Put-File $uri
}

Foreach ($uri in $onlyOnServer) {
    Delete-File $uri
}

Foreach ($uri in $intersection) {
    if (-Not (Files-Identical $uri)) {
        Write-Host "Configuration file $uri needs updating"
        Put-File $uri
    } else {
        Write-Host "Configuration file $uri is identical, skipping"
    }
}

Write-Host "Sync complete"

最佳答案

使用 Azure 应用服务部署任务,您可以将文件上传到应用服务,但无法删除文件(取消选中使用 Web 部署发布选项并在包或文件夹输入框中指定文件夹路径)。

所以,更好的方法是使用 Kudu API在构建/发布期间删除/上传文件。

有关于使用 Kudu API 的线程和博客:

How to access Kudu in Azure using power shell script

Interacting with Azure Web Apps Virtual File System using PowerShell and the Kudu API

关于Azure WebApp - 版本控制的配置/用户数据与应用程序分开部署,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49218627/

相关文章:

reactjs - React SPA 在开发机器上工作,但只有一级路由在 Azure 上工作

iis - 将parameters.xml 或setParams.xml 替换为Azure DevOps Release Pipeline 变量

Azure Build Pipeline 无法发布项目

android - 如何测试 Amazon S3 或 Microsoft Azure?

azure - 队列触发的 Azure Webjobs 的时间限制

azure - 应用服务计划规模单位升级是否需要等待预热?

docker - 使用部署为Azure Webapp的单个Docker镜像从Python连接到Blob存储

xml - 如何访问 log4net 附加程序文件值并在 azure 管道中替换

Azure DevOps 发布管道 Azure 订阅字段已禁用

javascript - 在azure函数NodeJs Http触发器中获取请求 header