【DevOps云实践】使用Azure Pipeline部署Function App
@TOC
推荐超级课程:
介绍
有时需要创建像Azure Function App这样的特殊应用程序,以执行一些后台工作或作为无服务器架构的一部分。此应用程序需要部署(托管)在Azure App Service上 - 但作为Function App(因为Azure上的所有应用程序都作为App Service)。还有Azure DevOps服务,提供完整功能来开发应用程序,并通过CI/CD流程管理工作。
因此,基本流程如下:
- Azure Repos用于Git存储库。
- Azure Pipelines用于构建和部署。
- Azure云托管应用程序。
1 Azure DevOps
这是开发过程中的主要服务,将用于跟踪工作、代码、CI/CD、构建产物、测试。在这里假设已经拥有了带有代码的存储库(至少是基本的web应用程序)作为样本应用程序(Function App)。
为了实现我们的目标和工作,我们需要:
- Azure Repos - 带有代码的Git存储库。
- Azure Pipelines - 用于CI/CD过程的工具。
需要配置的内容:
- 用于Azure的服务连接 - 以便使用特殊凭据部署到Azure。
- Git存储库 - 用于存储代码。
2 微软Azure
要开始使用新的Azure Function App,我们需要在Azure门户上创建此资源。可以手动创建,也可以使用一些自动脚本。
通常,要创建新函数需要以下资源:
- 功能应用 - 用于托管函数的实际应用程序。
- 存储账户 - 用于存储函数运行的日志和锁。
- 应用程序监视 - 监视和日志记录。
- 应用程序服务计划 - 以能够在适当的机器上运行,汇总定价并选择可用级别。
新的函数应用和应用服务计划
在创建所有资源之后,我们可以继续下一步 - 创建和配置流水线。
3 Azure Pipelines
- 在Azure Repos中具有Git存储库。
- 创建用于部署的环境 - 准备环境定义的配置以部署应用程序,配置它们(如审批等)。
- 向库中添加新的(或更新现有的)变量组 - 以便在流水线中使用变量,并且可以在不更改代码的情况下进行编辑。
- 创建流水线 - 准备在存储库中的文件并选择它们用于新流水线。
- 配置安全性 - 访问库的权限,流水线权限,用于环境的服务连接以便能够使用它们。
这里将提供特定于函数的所需步骤和操作。这里唯一提供的是用于流水线的YAML文件的模板代码。
3.1 创建流水线
作为.NET应用程序的示例流水线,函数应用包含存储库根目录中的一些文件。所有文件都放在存储库根目录中的构建文件夹中,包括解决方案和其他文件夹和项目。
流水线中使用的模板的文件结构
提供以下所有文件的内容。大部分值和设置可以保持不变,有些需要简单更改。
文件 azure-pipelines.yml:
# 运行流水线的参数
parameters:
- name: environment
displayName: 环境
type: string
default: BuildOnly
values:
- BuildOnly
- Development
- name: projectName
displayName: 项目名称
type: string
default: DevTestPlayground
- name: skipLint
displayName: 跳过Lint
type: boolean
default: false
# 自动触发
trigger:
- dev
variables:
- group: Deploy-Environment-Shared
- ${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
- name: why
value: pr
- ${{ elseif eq(variables['Build.Reason'], 'Manual' ) }}:
- name: why
value: manual
- ${{ elseif eq(variables['Build.Reason'], 'IndividualCI' ) }}:
- name: why
value: indivci
- ${{ else }}:
- name: why
value: other
- ${{ if eq(parameters.Environment, 'Development') }}:
- name: envShortName
value: dev
- ${{ else }}:
- name: envShortName
value: none
# 为构建自定义名称
name: $(Date:yyyyMMdd)$(Rev:.r)_$(SourceBranchName)_$(why)_$(envShortName)
# 阶段 - 逐步执行的作业列表
stages:
# 构建和Lint(和其他内容)阶段
- template: pipelines/build.yml
parameters:
环境: ${{ parameters.Environment }}
项目名称: ${{ parameters.ProjectName }}
跳过Lint: ${{ parameters.SkipLint }}
# 将应用程序部署到单个特定环境
- ${{ if not(eq(parameters.Environment,'BuildOnly')) }}:
- template: pipelines/release.yml
parameters:
环境: ${{ parameters.Environment }}
项目名称: ${{ parameters.ProjectName }}
文件 pipelines/build.yml:
stages:
- 阶段: 构建
displayName: 构建${{ parameters.ProjectName }}
# 使用多个作业,以便Linter可以并行运行构建。
# 这样还可以让Linter在Linux上运行,而构建可以在Windows或Mac上运行。
jobs:
# 使用GitHub的Super-Linter对代码进行Lint
- job: lint
displayName: Lint代码库
condition: and(eq(${{ parameters.SkipLint }}, false), ne(variables['Build.Reason'], 'IndividualCI'))
pool:
vmImage: 'ubuntu-latest'
steps:
- script: docker pull github/super-linter:latest
displayName: 拉取GitHub Super-Linter镜像
- script: >-
docker run
-e RUN_LOCAL=true
-e VALIDATE_JSCPD=false
-e VALIDATE_MARKDOWN=false
-e VALIDATE_EDITORCONFIG=false
-v $(System.DefaultWorkingDirectory):/tmp/lint
github/super-linter
displayName: '运行GitHub Super-Linter'
continueOnError: true
# 执行主要构建动作
- job: build
displayName: 构建和测试,创建构建产物
pool:
vmImage: 'windows-latest'
variables:
构建配置: 'Release'
steps:
- template: build-dotnet.yml
parameters:
项目服务名称: ${{ parameters.ProjectName }}
恢复构建项目: '**/*.csproj'
测试项目: 'tests/**/*.csproj'
Dotnet版本: '$(DotnetVersion)'
SimpleFunctionApp: '**/DevTestPlayground.SimpleFunction.csproj'
文件 pipelines/build-dotnet.yml:
steps:
- checkout: 自身
clean: true
# 安装和缓存.NET SDK
- task: UseDotNet@2
displayName: '安装Dotnet Core cli'
inputs:
version: ${{ parameters.DotnetVersion }}
# 恢复NuGet软件包
- task: DotNetCoreCLI@2
displayName: 'Dotnet恢复'
inputs:
command: 'restore'
projects: |
${{ parameters.RestoreBuildProjects }}
${{ parameters.TestProjects }}
feedsToUse: 'config' # 在需要时使用它以拥有自定义feeds!
nugetConfigPath: NuGet.config # 在需要时使用它以拥有自定义feeds!
# 构建项目
- task: DotNetCoreCLI@2
displayName: 'Dotnet构建'
inputs:
command: 'build'
projects: |
${{ parameters.SimpleFunctionApp }}
arguments: --configuration $(BuildConfiguration) --no-restore
# 运行测试
# - task: DotNetCoreCLI@2
# condition: ne('${{ parameters.TestProjects }}', '')
# displayName: 'Dotnet测试'
# inputs:
# command: 'test'
# projects: ${{ parameters.TestProjects }}
# arguments: '--configuration $(BuildConfiguration) --no-restore --collect "Code coverage"'
# 发布web应用程序(以用于稍后部署)
- task: DotNetCoreCLI@2
displayName: 发布
inputs:
command: 'publish'
publishWebProjects: false
zipAfterPublish: true
projects: |
${{ parameters.SimpleFunctionApp }}
arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory) --no-restore'
# 在流水线完成后发布构建产物
- task: PublishPipelineArtifact@1
displayName: 发布流水线产物
condition: ne(variables['Build.Reason'], 'PullRequest')
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)'
artifactName: '${{ parameters.ProjectServiceName }}'
publishLocation: 'pipeline'
文件 pipelines/release.yml:
stages:
# 每个选择的环境的主要部署阶段
- 阶段: 部署
displayName: 部署${{ parameters.ProjectName }}
variables:
- group: Deploy-Environment-${{ parameters.Environment }}
jobs:
# 将实际功能应用部署到Azure - 1)Azure Functions
- 部署: 部署Function
displayName: 将Azure Function App部署到Azure
pool:
vmImage: 'windows-latest'
environment: ${{ parameters.Environment }}
strategy:
runOnce:
部署:
steps:
# 部署Azure函数(SimpleFunctionApp)
- template: deploy-function.yml
parameters:
环境: ${{ parameters.Environment }}
项目名称: ${{ parameters.ProjectName }}
功能程序包名称: 'DevTestPlayground.SimpleFunction'
App服务名称: '$(AppServiceNameSimpleFunction)'
# 运行单独的作业以在部署Web应用程序之前处理Function逻辑
- job: DeployAgentlessJob
displayName: 运行函数应用程序
dependsOn: DeployFunction
pool: 服务器
steps:
# 调用Azure Function。
- task: AzureFunction@1
displayName: '调用Azure Function以运行函数'
inputs:
function: '$(SimpleFunctionUrl)'
key: '$(SimpleFunctionKey)'
method: 'POST'
waitForCompletion: 'false'
body: '{ "name": "AzurePipeline" }'
文件 pipelines/deploy-function.yml:
steps:
- checkout: 自身
clean: true
# 下载流水线产物
- task: DownloadPipelineArtifact@2
inputs:
artifactName: '${{ parameters.ProjectName }}'
buildType: 'current'
targetPath: '$(Build.ArtifactStagingDirectory)${{ parameters.ProjectName }}'
# 部署Azure函数
- task: AzureFunctionApp@2
displayName: 'Azure函数应用程序部署'
inputs:
azureSubscription: '$(AzureServiceConnection)'
appType: 'functionApp'
appName: '${{ parameters.AppServiceName }}'
package: '$(Build.ArtifactStagingDirectory)${{ parameters.ProjectName }}${{ parameters.FunctionPackageName }}.zip'
一些注意事项:
- 在构建模板中,恢复步骤中使用了带有NuGet.config文件的自定义配置。因此,在需要仅使用标准nuger.org feed的情况下,不需要更改。但是,如果还在Azure DevOps中使用一些私有feed(Artifacts),那么需要一个位置来正确构建项目。
- 流水线可以用于PR构建和手动构建-添加了一些特殊逻辑来识别调用方式并执行一些步骤。对于PR,可以仅进行构建而不进行部署,并可用于确保所有推送的代码都有效(代码构建,测试通过)。
- 部署步骤使用特殊作业来调用函数应用程序。有时需要在流水线中的主应用程序准备就绪之前做一些部署后事项或运行一些过程(例如准备一些数据,数据库更新等)。此步骤需要运行为Agentless作业而不是Agent作业 - 这是与其他任务之间的主要区别!
在下面的图像中提供了用于函数应用的简单项目结构。这里只有一个用于函数的项目,没有其他东西。
项目文件结构(示例函数应用)
3.1.2 创建流水线
需要将项目的所有文件推送到存储库。之后,在创建流水线时选择Azure Repos → 选择仓库 → 存在的Azure管道YAML文件。
它将打开主文件并允许查看并保存。通常,名称将与存储库相同,因此如果需要,之后需要重命名(如果需要)- 在一个项目中可以有多个存储库,每个存储库可以包含一个或多个流水线。
为Azure Function项目创建的流水线
此流水线具有默认名称,并使用*/build/azure-pipelines.yml*文件作为入口点。
3.2 创建库
在流水线中使用了一些库变量。因此,为确保流水线正常工作,需要创建变量组(用于每个环境)并设置值。
有关变量组的一些说明:
- Deploy-Environment-Shared - 可用于所有环境的一些共享变量,如全局变量。
- Deploy-Environment-Development - 用于开发环境的变量。
已创建的变量组
共享变量组
用于开发环境的变量
对于所有变量,需要添加权限 - 流水线权限。
3.3 运行流水线
要运行流水线,需要进入选择的流水线并点击“运行流水线”按钮。
由于流水线使用参数,它将在用户界面中显示这些参数,以便用户能够选择并根据需要更改。可以添加更多值以执行一些附加步骤或决定在其中使用哪种流(例如,“SkipLint”将不运行线路作业)。
运行流水线选项
当流水线成功验证编译的YAML时,将显示阶段和作业。
运行作业后,我们可以看到该过程中所有步骤的列表,以及每个步骤的所有日志和状态。因此,可以简单地查看每个步骤的更多细节,甚至下载原始日志文件。
流水线运行详细信息
流水线步骤和活动
如果选择了任何环境,例如在此示例中,我们可以在构建结果的“环境”选项卡中看到环境的名称和状态。
部署至开发环境
所有部署至开发环境
3.4 流水线审阅
文件模板中提供的文件的可视依赖关系如下图所示 — 在这里我们可以看到这里有哪些阶段和作业。所有这些都在单独的文件中。
模板中的文件
以下图片提供了构建.NET应用程序的流水线的实际模板的详细视图。
所有这些步骤是通用且必要的,但在您自己的项目中,如果需要的话可以配置为添加更多步骤和内容。这主要是关于可以用于所有项目的最小设置。
3.5 检查函数应用
流水线使用特殊的作业步骤,在部署后运行函数。有时需要这样做以确保函数正在工作或执行一些部署后的操作。在示例中,使用了HttpTrigger来进行函数调用,这意味着函数可以通过HTTP请求调用并获取一些结果。
这些详细信息在Azure门户中提供。
函数应用及已部署函数的列表(可能有多个)
监控结果和运行详细信息(日志)
基于这些结果,我们可以决定是否一切正常工作,整个流水线是否正常工作和正确。
注意:并非所有函数都需要运行。因此,在这种情况下,只需删除文件*/build/pipelines/release.yml*中的作业即可。
结论
使用Azure流水线是将CI/CD应用于您的项目的绝佳方式,甚至非常简单。在本文中,我们看到了如何为Azure函数应用实现流水线(至少在本文中 — 只有一个函数),使用Azure DevOps(代码库)的存储库、配置(参数、变量)、模板的使用(带有共享逻辑的YAML文件)、环境、库。