最近工作上需要打架一个Azure pipeline,借此机会把Azure pipeline学习了一下。主要参考的资料是微软官方文档。感觉学习的过程还是有些痛苦的,主要原因是之前对pipeLine没有太多概念。只是知道它可以自动对程序进行编译。官方文档知识点比较多,内容写的很详细。我只是挑了些我目前用的到的内容进行了学习。学习后的感触是,pipeLine功能确实很强,可以做很多事情,对于整个项目开发流程都能提供很多帮助。我目前用涉及到的主要就是编译程序,执行前后处理操作,下载编译好的程序文件等功能。其他关于测试,部署的内容还没有涉及。
一、yaml语言介绍
首先需要了解的是yaml语言。这个语言是目前官网上推荐的创建管道的描述语言。它的音标是/ˈjæməl/,尾音类似camel骆驼。这个语言是Json的一个超集。一个yaml文件中描述了一个数据对象。这个数据对象包含了一些键值对(可以把这个对象理解为一个map)。这个对象可能是嵌套结构的,也就是说,有些key对应的值可能也是对象。此外,在对象中还可以包含数组。
yaml语言的语法比较简单,其中需要注意的是,不同层级键值对需要显示进行缩进。以下是一个简单的yaml文档示例。
house:
family:
name: Doe
parents:
- John
- Jane
children:
- Paul
- Mark
- Simone
address:
number: 34
street: Main Street
city: Nowheretown
zipcode: 12345
yaml语言中,字符串首尾的单引号或双引号是可选的。但是如果字符串中包含一些特殊字符,则字符串首尾需要加上单引号或双引号。例如:
special_characters: "[ John ] & { Jane } - <Doe>"
对于多行字符串可以用”|“或”>>“符号表示,例如:
literal_block: |
This entire block of text will be the value of the 'literal_block' key,
with line breaks being preserved.
folded_style: >
This entire block of text will be the value of 'folded_style', but this
time, all newlines will be replaced with a single space.
这两个符号不同之处在于:”|“这个符号会把字符串中的换行符转换为‘\n’字符。而”>>“符号会把字符串中的换行符直接丢掉。
yaml语言中数组表示方法如下所示:
a_sequence:
- Item 1
- Item 2
- 0.5 # sequences can contain disparate types.
- Item 4
- key: value
another_key: another_value
- - This is a sequence
- inside another sequence
- - - Nested sequence indicators
- can be collapsed
其中‘-’符号用于显示说明缩进量,缩进量相同的项属于同一个数组,缩进量更大的项数据属于子数组。数组中的项数据类型可以不同。
以上就是yaml的一些基本知识,也是pipeLine中会用到的内容。更详细的内容可以参考以下链接:
Learn yaml in Y Minutes (learnxinyminutes.com)
二、pipeLine基本知识
说到pipeLine基础概念,需要展示如下图片:
一个pipeline由stage组成,stage由job组成,job由step组成。step有两种类型:一个是task,另一个是script。
stage是一个比较大的逻辑单元, 每个pipeline必须有一个stage。是否需要多个stage可以根据具体情况来确定,通常是考虑如下因素:
1、pipeline中是否有多个比较大块的逻辑单元,比如说存在创建,测试,部署这种功能分类。如果有可以考虑将他们划分为不同的stage
2、pipeline中是否需要验证许可(approval),如果需要验证,那么可以按照许可需要验证的job集合,将job划分为不同的stage。
3、如果有些job运行时间很长,那么可以将他们划为独立的stage
在pipeline中job是一个在代理中独立运行的单元(agent)。这里需要解释一下什么是代理。代理是服务器提供的一个运行环境(可以是虚拟机或是容器),在这个环境中预装了一些应用程序,比如编译器,操作系统,cmd等。pipeline运行的时候会为每一个job选择一个满足它要求的代理,并依次执行它的steps。代理在执行job的时候被创建,执行之后被销毁。因此每次代理运行的时候都是全新的。如果需要多个job并行运行,需要将每个job放入不同的代理中进行同时处理。
step是最小的执行单元,它分为task和script。其中script包含的就是命令行字符串。task则是各种预定义的任务。这些任务类型很广泛,其中包括各种编译任务,对文件的复制、删除任务,编译结果上传、下载任务,文件打包任务等。使用这些task,仅需要输入一些参数,就可以定制自己需要的操作。yaml编辑器,提供了很方便的ui界面,可以方便大家对task进行筛选以及指定参数。具体界面如下:
三、pipeline中的表达式
pipeline使用的是yaml语言,但是它对表达式及变量的语法有特殊要求。这里需要进行说明:
pipeline中表达式可以用于表示条件(条件后面会详细说明)或者给变量赋值。表达式可以指定在编译时求值或者在运行时求值。以下是指定方式:
1、(${{ <expression> }}
)这种形式表示表达式在编译时求值。这种表达式在开始编译时对这些表达式进行求值,并且将表达式直接替换为结果。因此,这种表达式的值在运行时不会改变。
2、($[ <expression> ]
)这种形式表示表达式在运行时求值,也就是在每个step执行中被求值
3、$(<expression>
)这种形式表示表达式在step执行前被求值,在step运行过程中,表达式的值不会改变。每个step开始前,step中对应的表达式会被替换为表达式的值。
四、变量
变量定义的方式如下:
variables:
- name: projectName
value: contoso
- name: fileName
value: output
pipeline中变量可以在多种位置进行定义。在不同位置定义的变量具有不同的作用域,具体如下:
1、在yaml文件最外层定义的变量具有全局作用域,所有job都可以访问这些变量
2、在stage中定义的变量只有属于这个stage的job可以访问
3、在job中定义的变量只有属于这个job的step能够访问
如果出现同名的变量,那么内层定义的变量会覆盖外层定义的变量
变量可以按照三种方式求值(对应于三种表达式求值方式):
1、模板表达式求值方式:${{ variables.var }},这种形式的变量求值发生在编译阶段。
2、宏语法求值方式:$(var),这种形式的变量求值发生在每个step开始之前。
3、运行时表达式求值方式:$[variables.var],这种方式的变量求值发生在step运行过程中。
变量引用方式:
1、索引形式:variables['MyVar']
2、解引用形式:variables.MyVar
变量修改方式:
1、可以在yaml文档中对已定义的变量进行赋值,如下图所示:
variables:
configuration: debug
platform: x64
2、还可以在命令行中修改变量的值,如下图所示:
steps:
# Create a variable
- bash: |
echo "##vso[task.setvariable variable=sauce]crushed tomatoes" # remember to use double quotes
# Use the variable
# "$(sauce)" is replaced by the contents of the `sauce` variable by Azure Pipelines
# before handing the body of the script to the shell.
- bash: |
echo my pipeline variable is $(sauce)
在这种方式中,用到了logging命令,它用于在step和代理之间传递信息。在这里,step调用了task.setvariable命令用于修改变量值或是创建变量。通过这种方式创建的变量可以被后续step进行访问。
pipeline中还有一些预定义的变量,这些变量在运行时,会被代理自动赋值。其中比较常用的是build.SourcesDirectory变量,他表示了库文件所在的根目录,其他预定义变量可参考如下链接:Predefined variables - Azure Pipelines | Microsoft Learn
在这里需要说明的是,pipeline中的变量只能存储字符串类型的值。
五、参数
可以在yaml文档中指定参数(parameters)。参数不同于变量,他可以存储各种类型的值。但是他仅能采用模板表达式方式进行求值(${{parameter}})。以下是一个参数定义示例:
parameters:
- name: image
displayName: Pool Image
type: string
default: ubuntu-latest
values:
- windows-latest
- ubuntu-latest
- macOS-latest
定义参数的时候必须指定name字段及type字段,其他字段是可选的。
以下是一个使用参数的例子:
parameters:
- name: image
displayName: Pool Image
type: string
default: ubuntu-latest
values:
- windows-latest
- ubuntu-latest
- macOS-latest
trigger: none
jobs:
- job: build
displayName: build
pool:
vmImage: ${{ parameters.image }}
steps:
- script: echo building $(Build.BuildNumber) with ${{ parameters.image }}
当在yaml文档中定义了参数之后,在运行管道的ui界面中可以对这些参数进行赋值。如下图所示:
在这个例子中,在运行管道的ui界面中可以选择要使用的代理池。之后pipeline会按照用户指定的选项运行pipeline。
五、condition
默认情况下,stage、job只有当它们没有依赖项或者它们依赖的项都执行成功它们才会被执行。默认情况下,当step所在的job没有其他step执行失败,且它前面的一个step执行成功后他才会被执行。通过condition可以修改这种默认行为,如下所示:
jobs:
- job: Foo
steps:
- script: echo Hello!
condition: always() # this step will always run, even if the pipeline is canceled
- job: Bar
dependsOn: Foo
condition: failed() # this job will only run if Foo fails
在这个例子中,采用了job状态函数表达式来指定运行条件。(在pipeline中包含了一些预定义函数。这些函数可以作为表达式进行求值。预定义函数可参考链接:Expressions - Azure Pipelines | Microsoft Learn)。
还可以在条件中根据变量值决定任务是否执行
variables:
isMain: $[eq(variables['Build.SourceBranch'], 'refs/heads/main')]
stages:
- stage: A
jobs:
- job: A1
steps:
- script: echo Hello Stage A!
- stage: B
condition: and(succeeded(), eq(variables.isMain, 'true'))
jobs:
- job: B1
steps:
- script: echo Hello Stage B!
- script: echo $(isMain)
六、代理
在这里再啰嗦的说一些有关代理的内容,之前提到过,代理是job运行的一个环境(可以是虚拟机或是容器),在这个环境中安装了job运行所需的编译器,操作系统,以及其他一些程序。微软为用户提供了多种代理。具体资料可以参考如下链接:Microsoft-hosted agents for Azure Pipelines - Azure Pipelines | Microsoft Learn
此外用户还可以自己创建代理,这方面的知识目前还没有太深入的学习。
用户可以在yaml文件中显示指定代理需要满足的各种需求。比如:
在这两个例子中,用户指定了满足要求的代理需要安装的特定程序以及所需要的代理版本编号。
所有代理是通过代理池进行管理的。编写yaml文件时,指定的是代理池,而不是具体的代理。运行时pipeline会从指定的代理池中选取满足要求的代理运行job。
六、触发器
可以通过触发器指定pipeline自动执行的条件。默认情况下对于所有分支,如果有新的提交那么pipeline就会被自动执行。可以通过如下方式,指定仅某些分支有新的提交才触发pipeline:
在这个例子中,仅master分支以及名字以release/开头的分支有新的提交时pipeline才被自动触发。
也可以通过以下方式,指定仅能通过手动方式进行触发
七、pipeline执行顺序
pipeline按照如下顺序运行:
1、对模板及模板表达式进行展开
2、计算每个stage的依赖关系,并选出第一个被运行的stage
3、对于每一个将要被运行的stage,需要执行如下操作
(1)收集jobs所需的所有资源,并验证所有job授权是否有效
(2)计算jobs的依赖关系,并选出第一个被运行的job
4、对于每一个将被运行的job,对他的condition进行求值,判断这个job是否被运行。
5、为符合运行条件的job请求一个代理
当一个job运行完毕,pipeline会查找是否还有满足运行条件的job,如果有则重复4、5步骤。同样的,如果一个stage执行完毕,会对下一个stage执行2-5步骤。
以上运行顺序也解释了为什么模板表达式的值不会在job运行时改变,因外在pipeline开始执行的之前,模板表达式就被求值,并且把文档中模板表达式出现的位置替换为所求得的值。因此在job运行的时候模板表达式已经不存在了。
七、实例
以上讲了关于pipeline的一些基础知识以及个人的一些理解。接下来想给大家展示一个应用pipeline的例子。
首先需要创建一个库 ,这个库中的程序比较简单,只是在命令行中输出hello world而已。这里仅仅为了演示pipeline所以程序复杂与否并不重要。
之后进到pipeline tab页中,点击new pipeline按钮
因为我的这个库是一个Azure Repos,所以选择第一个选项
在界面中选择pipeline要关联的库
在这个界面中我选择了最小pipeline选项
执行完以上操作后,pipeline会为我们生成一份yaml文档。在这份初始文档中,仅仅是描述了两个script,这些script只是在控制台中输出一些字符串而已。(这里需要提醒大家的是,需要点击保存,否则之前的操作就白做了)
以下是我自己写的一份yaml文档。其中使用了MSBuild任务、Publish Pipeline Artifacts任务以及Download Pipeline Artifacts任务。这个pipeline做的事情很简单,首先是对程序进行编译,之后在代理中运行编译好的exe程序,在控制台中输出hello world,其后是是将编译结果上传到pipeLine公共区域,最后将编译结果在下载回代理指定目录中,并在下载目录中执行程序。
trigger:
- main
pool:
vmImage: windows-latest
steps:
- task: VSBuild@1
inputs:
solution: 'testPipeLine\testPipeLine.sln'
platform: 'x64'
configuration: 'debug'
msbuildArchitecture: 'x64'
displayName: 编译程序
- script: |
cd $(Build.SourcesDirectory)\testPipeLine\x64\Debug
testPipeLine.exe
displayName: 在生成目录中运行程序
- publish: $(Build.SourcesDirectory)\testPipeLine\x64\Debug\
artifact: WebApp
displayName: 将生成的程序上传至公共区
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: 'WebApp'
targetPath: 'D:\学习\代码\testPipeLine\testPipeLine'
displayName: 将生成的程序下载到指定目录下
- script: |
cd D:\学习\代码\testPipeLine\testPipeLine
testPipeLine.exe
displayName: 在下载目录中运行程序
执行结果如下:
这个管道触发器指定的是当main分支有提交时被触发。
八、总结
以上就是我这段时间学习的一些总结,内容比较简单,没有设计太多关于部署的内容。对更复杂的pipeline yaml语言特性也没有涉及。但是他已经可以满足我目前工作中的需求。其中有不足和错误的地方还请各位大神指教。文章来源:https://www.toymoban.com/news/detail-443325.html
文章来源地址https://www.toymoban.com/news/detail-443325.html
到了这里,关于Azure Pipeline入门的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!