无法使用已安装存储的 Linux 部署 Azure App Service 的 ARM 模板

Can not deploy ARM template of Azure App Service with Linux that has mounted storage

我们正在尝试部署 Azure Linux 应用服务,它也已安装存储。这是我们的 ARM 模板

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "Environment": {
            "type": "String",
            "allowedValues": [
                "dev",
                "stg",
                "prd"
            ]
        },
        "Region": {
            "type": "string",
            "allowedValues": [
                "eu",
                "we"
            ]
        },
        "MagentoMediaFileShareName": {
            "type": "string"
        },
        "StorageAccountName": {
            "type": "string"
        },
        "StorageAccountAccessKey": {
            "type": "securestring"
        },
        "MagentoMediaMountPath": {
            "type": "string",
            "defaultValue": "/var/www/html/pub/external_media"
        }
    },
    "variables": {
        "Location": "[resourceGroup().location]",
        "ResourcePrefix": "[format('ariva-{0}-{1}-magento', parameters('Environment'), parameters('Region'))]",
        "WebSiteName": "[concat(variables('ResourcePrefix'), '-web')]",
        "ServicePlanId": "[format('/subscriptions/{0}/resourceGroups/ariva-{1}-{2}/providers/Microsoft.Web/serverfarms/ariva-{1}-{2}-asp', subscription().subscriptionId,  parameters('Environment'), parameters('Region'))]"
    },
    "resources": [
        {
            "type": "Microsoft.Web/sites",
            "apiVersion": "2020-12-01",
            "name": "[variables('WebSiteName')]",
            "location": "[variables('Location')]",
            "kind": "app,linux,container",
            "properties": {
                "enabled": true,
                "hostNameSslStates": [
                    {
                        "name": "[concat(variables('WebSiteName'), '.azurewebsites.net')]",
                        "sslState": "Disabled",
                        "hostType": "Standard"
                    },
                    {
                        "name": "[concat(variables('WebSiteName'), '.scm.azurewebsites.net')]",
                        "sslState": "Disabled",
                        "hostType": "Repository"
                    }
                ],
                "serverFarmId": "[variables('ServicePlanId')]",
                "reserved": true,
                "isXenon": false,
                "hyperV": false,
                "siteConfig": {
                    "numberOfWorkers": 1,
                    "linuxFxVersion": "DOCKER|mcr.microsoft.com/appsvc/staticsite:latest",
                    "acrUseManagedIdentityCreds": false,
                    "alwaysOn": false,
                    "http20Enabled": false,
                    "functionAppScaleLimit": 0,
                    "minimumElasticInstanceCount": 1
                },
                "scmSiteAlsoStopped": false,
                "clientAffinityEnabled": false,
                "clientCertEnabled": false,
                "clientCertMode": "Required",
                "hostNamesDisabled": false,
                "customDomainVerificationId": "1071794BD68C78EC0A4569F03C034F6E1B21BD4E6D35725D99523AC00AE12AA1",
                "containerSize": 0,
                "dailyMemoryTimeQuota": 0,
                "keyVaultReferenceIdentity": "SystemAssigned",
                "httpsOnly": false,
                "redundancyMode": "None",
                "storageAccountRequired": false
            }
        },
        {
            "type": "Microsoft.Web/sites/basicPublishingCredentialsPolicies",
            "apiVersion": "2020-12-01",
            "name": "[concat(variables('WebSiteName'), '/ftp')]",
            "location": "[variables('Location')]",
            "dependsOn": [
                "[resourceId('Microsoft.Web/sites', variables('WebSiteName'))]"
            ],
            "properties": {
                "allow": true
            }
        },
        {
            "type": "Microsoft.Web/sites/basicPublishingCredentialsPolicies",
            "apiVersion": "2020-12-01",
            "name": "[concat(variables('WebSiteName'), '/scm')]",
            "location": "[variables('Location')]",
            "dependsOn": [
                "[resourceId('Microsoft.Web/sites', variables('WebSiteName'))]"
            ],
            "properties": {
                "allow": true
            }
        },
        {
            "type": "Microsoft.Web/sites/config",
            "apiVersion": "2020-12-01",
            "name": "[concat(variables('WebSiteName'), '/web')]",
            "location": "[variables('Location')]",
            "dependsOn": [
                "[resourceId('Microsoft.Web/sites', variables('WebSiteName'))]"
            ],
            "properties": {
                "numberOfWorkers": 1,
                "defaultDocuments": [
                    "Default.htm",
                    "Default.html",
                    "Default.asp",
                    "index.htm",
                    "index.html",
                    "iisstart.htm",
                    "default.aspx",
                    "index.php",
                    "hostingstart.html"
                ],
                "netFrameworkVersion": "v4.0",
                "linuxFxVersion": "DOCKER|mcr.microsoft.com/appsvc/staticsite:latest",
                "requestTracingEnabled": false,
                "remoteDebuggingEnabled": false,
                "remoteDebuggingVersion": "VS2019",
                "httpLoggingEnabled": false,
                "acrUseManagedIdentityCreds": false,
                "logsDirectorySizeLimit": 35,
                "detailedErrorLoggingEnabled": false,
                "publishingUsername": "[concat('$', variables('WebSiteName'))]",
                "azureStorageAccounts": {
                    "magento-media": {
                        "type": "AzureFiles",
                        "accountName": "[parameters('StorageAccountName')]",
                        "shareName": "[parameters('MagentoMediaFileShareName')]",
                        "mountPath": "[parameters('MagentoMediaMountPath')]",
                        "accessKey": "[parameters('StorageAccountAccessKey')]"
                    }
                },
                "scmType": "None",
                "use32BitWorkerProcess": true,
                "webSocketsEnabled": true,
                "alwaysOn": true,
                "managedPipelineMode": "Integrated",
                "virtualApplications": [
                    {
                        "virtualPath": "/",
                        "physicalPath": "site\wwwroot",
                        "preloadEnabled": false
                    }
                ],
                "loadBalancing": "LeastRequests",
                "experiments": {
                    "rampUpRules": []
                },
                "autoHealEnabled": false,
                "vnetRouteAllEnabled": false,
                "vnetPrivatePortsCount": 0,
                "localMySqlEnabled": false,
                "ipSecurityRestrictions": [
                    {
                        "ipAddress": "Any",
                        "action": "Allow",
                        "priority": 1,
                        "name": "Allow all",
                        "description": "Allow all access"
                    }
                ],
                "scmIpSecurityRestrictions": [
                    {
                        "ipAddress": "Any",
                        "action": "Allow",
                        "priority": 1,
                        "name": "Allow all",
                        "description": "Allow all access"
                    }
                ],
                "scmIpSecurityRestrictionsUseMain": false,
                "http20Enabled": true,
                "minTlsVersion": "1.2",
                "scmMinTlsVersion": "1.0",
                "ftpsState": "AllAllowed",
                "preWarmedInstanceCount": 0,
                "functionAppScaleLimit": 0,
                //"healthCheckPath": "/health_check.php",
                "functionsRuntimeScaleMonitoringEnabled": false,
                "minimumElasticInstanceCount": 1
            }
        },
        {
            "type": "Microsoft.Web/sites/hostNameBindings",
            "apiVersion": "2020-12-01",
            "name": "[concat(variables('WebSiteName'), '/', variables('WebSiteName'), '.azurewebsites.net')]",
            "location": "[variables('Location')]",
            "dependsOn": [
                "[resourceId('Microsoft.Web/sites', variables('WebSiteName'))]"
            ],
            "properties": {
                "siteName": "[variables('WebSiteName')]",
                "hostNameType": "Verified"
            }
        }
    ]
}

如果我们执行这个确切的模板,我们将收到以下错误

{
    "status": "Failed",
    "error": {
        "code": "BadRequest",
        "message": "Required parameter AccessKey is missing.",
        "details": [
            {
                "message": "Required parameter AccessKey is missing."
            },
            {
                "code": "BadRequest"
            },
            {}
        ]
    }
}

如果我们通过门户手动安装存储并尝试更改配置或添加部署槽,我们会收到相同的错误。

只要我删除安装的存储配置,一切都会正常。

我们需要在 DevOps 管道中使用它,我们在部署前后手动卸载-重新安装存储是不可接受的。这将导致重大问题和停机时间。我们可以做些什么来克服这个问题?

更新 1

我尝试过手动创建一个完全独立的网络应用程序和存储帐户。例如,一旦我挂上存储挂载并尝试启用运行状况检查,我就遇到了同样的错误。我认为 Azure 中存在错误。

更新 2

这是部署依赖存储帐户的模板

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "Environment": {
            "type": "String",
            "allowedValues": [
                "dev",
                "stg",
                "prd"
            ]
        },
        "Region": {
            "type": "string",
            "allowedValues": [
                "eu",
                "we"
            ]
        }
    },
    "variables": {
        "Location": "[resourceGroup().location]",
        "StorageAccountName": "[format('ariva{0}{1}magentostorage', parameters('Environment'), parameters('Region'))]",
        "MagentoMediaFileShareName": "magento-media"
    },
    "resources": [
        {
            "type": "Microsoft.Storage/storageAccounts",
            "apiVersion": "2021-04-01",
            "name": "[variables('StorageAccountName')]",
            "location": "[variables('Location')]",
            "sku": {
                "name": "Standard_LRS",
                "tier": "Standard"
            },
            "kind": "StorageV2",
            "properties": {
                "allowCrossTenantReplication": true,
                "minimumTlsVersion": "TLS1_2",
                "allowBlobPublicAccess": true,
                "allowSharedKeyAccess": true,
                "networkAcls": {
                    "resourceAccessRules": [],
                    "bypass": "AzureServices",
                    "virtualNetworkRules": [],
                    "ipRules": [],
                    "defaultAction": "Allow"
                },
                "supportsHttpsTrafficOnly": true,
                "encryption": {
                    "services": {
                        "file": {
                            "keyType": "Account",
                            "enabled": true
                        },
                        "blob": {
                            "keyType": "Account",
                            "enabled": true
                        }
                    },
                    "keySource": "Microsoft.Storage"
                },
                "accessTier": "Hot"
            }
        },
        {
            "type": "Microsoft.Storage/storageAccounts/blobServices",
            "apiVersion": "2021-04-01",
            "name": "[concat(variables('StorageAccountName'), '/default')]",
            "dependsOn": [
                "[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]"
            ],
            "sku": {
                "name": "Standard_LRS",
                "tier": "Standard"
            },
            "properties": {
                "changeFeed": {
                    "enabled": false
                },
                "restorePolicy": {
                    "enabled": false
                },
                "containerDeleteRetentionPolicy": {
                    "enabled": true,
                    "days": 7
                },
                "cors": {
                    "corsRules": []
                },
                "deleteRetentionPolicy": {
                    "enabled": true,
                    "days": 7
                },
                "isVersioningEnabled": false
            }
        },
        {
            "type": "Microsoft.Storage/storageAccounts/fileServices",
            "apiVersion": "2021-04-01",
            "name": "[concat(variables('StorageAccountName'), '/default')]",
            "dependsOn": [
                "[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]"
            ],
            "sku": {
                "name": "Standard_LRS",
                "tier": "Standard"
            },
            "properties": {
                "shareDeleteRetentionPolicy": {
                    "enabled": true,
                    "days": 7
                }
            }
        },
        {
            "type": "Microsoft.Storage/storageAccounts/queueServices",
            "apiVersion": "2021-04-01",
            "name": "[concat(variables('StorageAccountName'), '/default')]",
            "dependsOn": [
                "[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]"
            ],
            "properties": {
                "cors": {
                    "corsRules": []
                }
            }
        },
        {
            "type": "Microsoft.Storage/storageAccounts/tableServices",
            "apiVersion": "2021-04-01",
            "name": "[concat(variables('StorageAccountName'), '/default')]",
            "dependsOn": [
                "[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]"
            ],
            "properties": {
                "cors": {
                    "corsRules": []
                }
            }
        },
        {
            "type": "Microsoft.Storage/storageAccounts/fileServices/shares",
            "apiVersion": "2021-04-01",
            "name": "[concat(variables('StorageAccountName'), '/default/', variables('MagentoMediaFileShareName'))]",
            "dependsOn": [
                "[resourceId('Microsoft.Storage/storageAccounts/fileServices', variables('StorageAccountName'), 'default')]",
                "[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]"
            ],
            "properties": {
                "accessTier": "TransactionOptimized",
                "shareQuota": 5120,
                "enabledProtocols": "SMB"
            }
        }
    ],
    "outputs": {
        "MagentoMediaFileShareName": {
            "type": "string",
            "value": "[variables('MagentoMediaFileShareName')]"
        },
        "StorageAccountName": {
            "type": "string",
            "value": "[variables('StorageAccountName')]"
        },
        "StorageAccountAccessKey": {
            "type": "securestring",
            "value": "[listKeys(variables('StorageAccountName'), '2019-04-01').keys[0].value]"
        }
    }
}

更新 3 - 临时解决方法

我们找到了临时解决方法。我们没有通过 ARM 模板部署文件挂载,而是从 ARM 模板中删除了该位,之后,我们在 DevOps 管道中使用以下构建步骤通过 azure CLI

挂载存储
 - task: AzureCLI@2
   displayName: 'Attach media volume'
   inputs:
     azureSubscription: '${{ parameters.azureSubscription }}'
     scriptType: pscore
     scriptLocation: inlineScript
     inlineScript: |
       az webapp config storage-account add `
         --resource-group ${{ parameters.resourceGroup }} `
         --name "$(WebSiteName)" `
         --access-key "$(StorageAccountAccessKey)" `
         --custom-id magento-media `
         --storage-type AzureFiles `
         --share-name "$(MagentoMediaFileShareName)" `
         --account-name "$(StorageAccountName)" `
         --mount-path "/var/www/html/pub/external_media"

这当然不能解决问题。

  1. 这样做,ARM 模板实际上会删除挂载,然后由脚本部署,导致停机
  2. 挂载到位后,我们仍然无法通过门户更改任何 WebApp 属性,我们将收到缺少 AccessKey 的错误

azureStorageAccounts 不是 Microsoft.Web sites/config 2020-12-01 however it is under Microsoft.Web sites 2020-12-01 SiteConfig 属性 的一部分。这可能是 accessKey 未找到、未上传的原因。我会把 azureStorageAccounts 移到 siteConfig