【发布时间】:2021-01-19 15:07:19
【问题描述】:
我创建了一个简单的 Azure 函数,我想将其用作良好 DevOps 实践的练习。我准备了一个azure-pipelines.yml,它执行以下操作:
- 构建应用的代码
- 运行测试
- 将二进制文件作为工件发布
- 创建 Azure 资源(App Service Plan、Azure Function、Storage Account、App Insights)
- 将代码部署到 Azure 函数。
我听说了很多关于基础架构即代码的内容,我真的很想尝试一下,这就是第 4 点的原因。
这是我的azure-pipelines.yml:
trigger:
- master
variables:
azureServiceConnection: service-connection
appName: az-func-123456123
resourceGroup: rg-1223456123
location: North Europe
buildConfiguration: Release
pool:
vmImage: 'ubuntu-latest'
stages:
- stage: CI
jobs:
- job: Azure_Function
displayName: 'Azure Functions'
steps:
- checkout: self
- task: DotNetCoreCLI@2
displayName: Restore
inputs:
command: 'restore'
projects: '**/*.csproj'
- task: DotNetCoreCLI@2
displayName: Build
inputs:
command: build
projects: '**/*.csproj'
arguments: '--configuration $(buildConfiguration)'
- task: DotNetCoreCLI@2
displayName: Test
inputs:
command: test
projects: '**/*Tests/*.csproj'
arguments: '--configuration $(buildConfiguration)'
- task: DotNetCoreCLI@2
displayName: Zip Artifact
inputs:
command: publish
publishWebProjects: false
arguments: '--no-build --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'
zipAfterPublish: True
workingDirectory: src/az-function-with-deployment
- publish: $(Build.ArtifactStagingDirectory)
artifact: AzureFunction
- stage: Deployment
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
jobs:
- deployment: Deploy
environment: test-env
strategy:
runOnce:
deploy:
steps:
- checkout: self
- task: AzureResourceGroupDeployment@2
displayName: Deploy Azure resources
inputs:
deploymentScope: 'Resource Group'
ConnectedServiceName: '$(azureServiceConnection)'
action: 'Create Or Update Resource Group'
resourceGroupName: $(resourceGroup)
location: $(location)
templateLocation: 'Linked artifact'
csmFile: 'templates/function-app-deployment.json'
deploymentMode: 'Incremental'
- task: AzureFunctionApp@1
displayName: Deploy Azure Function
inputs:
azureSubscription: $(azureServiceConnection)
resourceGroupName: $(resourceGroup)
appType: functionAppLinux
appName: $(appName)
package: $(Pipeline.Workspace)/AzureFunction/*.zip
这是AzureResourceGroupDeployment@2 任务使用的我的templates/function-app-deployment.json:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"appName": {
"type": "string",
"defaultValue": "az-func-123456123",
"metadata": {
"description": "The name of the function app that you wish to create."
}
},
"storageAccountType": {
"type": "string",
"defaultValue": "Standard_LRS",
"allowedValues": [
"Standard_LRS",
"Standard_GRS",
"Standard_RAGRS"
],
"metadata": {
"description": "Storage Account type"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
},
"appInsightsLocation": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for Application Insights"
}
},
"runtime": {
"type": "string",
"defaultValue": "dotnet",
"allowedValues": [
"node",
"dotnet",
"java"
],
"metadata": {
"description": "The language worker runtime to load in the function app."
}
}
},
"variables": {
"functionAppName": "[parameters('appName')]",
"hostingPlanName": "[parameters('appName')]",
"applicationInsightsName": "[parameters('appName')]",
"storageAccountName": "[concat(uniquestring(resourceGroup().id), 'azfunctions')]",
"functionWorkerRuntime": "[parameters('runtime')]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "[variables('storageAccountName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('storageAccountType')]"
},
"kind": "Storage"
},
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2020-06-01",
"name": "[variables('hostingPlanName')]",
"location": "[parameters('location')]",
"kind": "linux",
"sku": {
"tier": "Dynamic",
"name": "Y1"
},
"properties": {
"name": "[variables('hostingPlanName')]",
"reserved": true
}
},
{
"type": "Microsoft.Web/sites",
"apiVersion": "2020-06-01",
"name": "[variables('functionAppName')]",
"location": "[parameters('location')]",
"kind": "functionapp",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
],
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
"siteConfig": {
"appSettings": [
{
"name": "AzureWebJobsStorage",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
},
{
"name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
},
{
"name": "WEBSITE_CONTENTSHARE",
"value": "[toLower(variables('functionAppName'))]"
},
{
"name": "FUNCTIONS_EXTENSION_VERSION",
"value": "~2"
},
{
"name": "APPINSIGHTS_INSTRUMENTATIONKEY",
"value": "[reference(resourceId('microsoft.insights/components', variables('applicationInsightsName')), '2020-02-02-preview').InstrumentationKey]"
},
{
"name": "FUNCTIONS_WORKER_RUNTIME",
"value": "[variables('functionWorkerRuntime')]"
}
],
"linuxFxVersion": "dotnet|3.1"
}
}
},
{
"type": "microsoft.insights/components",
"apiVersion": "2020-02-02-preview",
"name": "[variables('applicationInsightsName')]",
"location": "[parameters('appInsightsLocation')]",
"tags": {
"[concat('hidden-link:', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
},
"properties": {
"ApplicationId": "[variables('applicationInsightsName')]",
"Request_Source": "IbizaWebAppExtensionCreate"
}
}
]
}
部署成功,我创建了我的资源。但是,我注意到了一些问题:
- 每次部署都会重置我的应用程序配置!目前,在部署之后,我手动将所有配置值添加到我的 Azure Function 中,这非常不方便。我想我可以在管道本身中进行配置,但是,我不想在那里硬编码任何值!好像不太对。我还没有找到有关云中应用程序配置的任何资源/最佳实践。我错过了什么?
- 我的管道中的某些值是硬编码的(例如应用程序的名称)。目前我真的没有看到任何问题,但我想知道,事情应该是这样的吗?我可以改进什么吗?
总的来说,我很高兴看到我发布的关于 YAML 和 JSON 的任何 cmets。我相信还有很多地方需要改进。
【问题讨论】:
-
嗨@Loreno,我注意到
@KrzysztofMadej在他的回答中分享了一些很好的建议。请检查他的回答。有任何更新,请随时告诉我们。
标签: azure azure-devops azure-pipelines azure-resource-manager azure-pipelines-yaml