1. Jenkins 是什么
Jenkins 是一款由 Java 编写的开源的持续集成工具。
注:目前一些自动化编译,自动化测试都允许在 Jenkins 上运行。
2. 流水线
2.1 流水线是什么
Jenkins 2.0 的到来, Pipeline(流水线)进入了视野, jenkins 2.0 的核心特性,也是最适合持续交付的 feature。
注:简单的来说,就是把 Jenkins 1.0 版本中,Project 中的相关配置信息,如 SVN/Git 的配置,Parameter 的配置等都变成 Code,即 Pipeline as Code。
Pipeline(流水线)是用户定义的 CD 流水线模型。流水线的代码定义了整个的构建过程,它通常包括构建,测试和交付应用程序的阶段。
Jenkins 允许你将配置文件和执行步骤以代码的形式保持,这样就可以做到像管理源码一样管理 Jenkins 任务。
注:Jenkins Pipeline (流水线)是一套插件,将持续交付的实现和实施集成到 Jenkins 中。其提供了一套可扩展的工具,用于将「简单到复杂」的交付流程实现为「持续交付即代码」。
Jenkins Pipeline (流水线) 的定义通常被写入到一个文本文件——Jenkinsfile 中,该文件可以被放入项目的源代码控制库中。
2.2 流水线的分类
Jenkins 流水线分为脚本式的和声明式的
- 脚本式流水线比较灵活,可以嵌入一些 Groovy 语言编程,功能比较强大,但是对于新手而言上手较难。
- pipeline 块是 声明式流水线的基础。
- 声明式流水线更具有结构化,减少对 Groovy 语言的依赖,对新用户比较友好。
- node 块是 脚本化流水线的基础
2.2 为什么使用流水线
本质上,Jenkins 是一个自动化引擎,它支持许多自动模式。流水线向 Jenkins 中添加了一组强大的工具,支持用例简单的持续集成到全面的 CD 流水线。用户可以利用很多流水线的特性:
- 代码:Pipeline 以代码的形式实现,通常被检入源代码控制,使团队能够编辑、审查和迭代其 CD 流程
- 可持续性:Jenkins 重启或者中断后都不会影响 Pipeline Job
- 停顿:Pipeline 可以选择停止并等待人工输入或批准,然后在继续 Pipeline 运行
- 多功能:Pipeline 支持现实世界的复杂 CD 要求,包括 fork/join 子进程,循环和并行执行工作的能力
- 可扩展:Pipeline 插件支持其 DSL 的自定义扩展以及其他插件集成的多个选项
简而言之,就是使用 jenkins 的流水线比研发自己手动执行
- 编译
- 制作镜像
- 推送镜像到 hub
的流程有很多优势。
3. 声明式流水线——jenkinsfile 的语法
3.1 声明式流水线基础语法
- pipeline:声明其内容为一个声明式的 Pipeline 脚本
- agent:执行节点(job 运行的 slave 或者 master 节点)
- stages:阶段集合,包括所有的阶段(例如:打包、部署等各个阶段)
- stage:阶段,被 stages 包括,一个 stages 可以有多个 stage
- steps: 步骤,为每个阶段的最小执行单位,被 stage 包括
- post:执行构建后的操作,根据构建结果来执行对应的操作
3.2 典型的声明式流水线例子
在声明式流水线语法中,pipeline 块定义了整个流水线中完成的所有工作。
Jenkinsfile (Declarative Pipeline)
pipeline {
agent any /*1*/
stages {
stage('Build') { /*2*/
steps {
// /*3*/
}
}
stage('Test') { /*4*/
steps {
// /*5*/
}
}
stage('Deploy') { /*6*/
steps {
// /*7*/
}
}
}
}
上述定义的 1-7 的解释如下:
- 1 -> 在任何可以用的代理上,执行流水线或它的任何阶段。
- 2 -> 定义「Build」阶段
- 3 -> 定义与 「Build」阶段相关的步骤
- 4 -> 定义 「Test」阶段
- 5 -> 定义与 「Test」阶段相关的步骤
- 6 -> 定义 「Deploy」阶段
- 7 -> 定义与 「Deploy」阶段相关的步骤
注:部分 jenkins 的集群不支持 agent any 的写法。
3.3 流水线基础语法详解
所有有效的声明式流水线必须包含在一个 pipeline块中,比如:
pipeline {
/* insert Declarative Pipeline here */
}
在声明式流水线中有效的基本语句和表达遵循与 Groovy 的语法同样的规则。有以下例外:
- 流水线顶层必须是一个 block,特别地:
pipeline{} - 没有分号作为语句分隔符,每条语句必须在自己的行上
- 块只能由 Sections、Directives、Steps 和赋值语句组成
- 属性引用语句被视为无参数方法调用,因此,例如,输入被视为 input()
注:当前存在一个未解决的问题,该问题限制了 pipeline {} 内代码块的大小,但此限制不适用与脚本化 pipeline
3.3.1 agent (require)
agent 声明整个 pipeline 脚本或某个 stage 脚本在 jenkins 中的运行环境。
- 写在 pipeline 最外层则是声明整个 pipeline 的运行环境
- 写在 stage 里则是 stage 的运行环境
agent 的类型参数
-
any:在任意可用代理上运行 pipeline
-
none:当在全局声明 agent 为 none 时,那么每一个 stage 都需声明自己的 agent
-
label:按在 jenkins 环境中设定的 label 标签名声明 agent,如 label 所代表的节点。
agent { label 'my-node1' }注:label 的 value 可以使用逻辑符号组合
-
node:node 声明运行节点,与 label 功能类似,但是 node 可以附加选项参数,如 label、customWorkspace 等
agent { node { label 'my-node1' } }注:以上的 node 写法与 label 的写法相同
-
docker: 用于基于 docker 的 pipeline 流水线,在预配置的节点或指定 label 的节点上创建 docker 容器。除此之外,还可以接收 docker run 的参数,以及 docker registry 等参数。
agent { docker { image 'maven:3.8.1-adoptopenjdk-11' label 'my-defined-label' args '-v /tmp:/tmp' registryUrl 'https://myregistry.com/' registryCredentialsId 'myPredefinedCredentialsInJenkins' } } -
dockerfile:从一个 Dockerfile 创建 docker 容器来运行 pipeline 流水线。默认会从构建的根目录寻找 Dockerfile。
-
如果项目的 Dockerfile 存放在子目录,则需要通过 dir 参数声明。
-
如果 Dockerfile 名字是其他自定义名则需要通过 filename 参数说明。
注:还可以通过 additionBuildArgs 来设置 docker build 参数。使用 registryUrl 来设置 docker 仓库,使用 registryCredentialsId 从 jenkins 获取 docker 仓库的账号密码。
agent { // Equivalent to "docker build -f Dockerfile.build --build-arg version=1.0.2 ./build/ dockerfile { filename 'Dockerfile.build' dir 'build' label 'my-defined-label' additionalBuildArgs '--build-arg version=1.0.2' args '-v /tmp:/tmp' registryUrl 'https://myregistry.com/' registryCredentialsId 'myPredefinedCredentialsInJenkins' } } -
-
kubernetes:在 Kubernetes 集群中的一个 Pod 内执行 pipeline 流水线,pod 模板将定义在 kubernetes {} 模块中。如果需要在 Pod 中安装 Kaniko 则可在 yaml 参数中声明。
注:使用 Kaniko 是无需 docker demo 即可构建 docker 镜像的方式
agent { kubernetes { label podlabel yaml """ kind: Pod metadata: name: jenkins-agent spec: containers: - name: kaniko image: gcr.io/kaniko-project/executor:debug imagePullPolicy: Always command: - /busybox/cat tty: true volumeMounts: - name: aws-secret mountPath: /root/.aws/ - name: docker-registry-config mountPath: /kaniko/.docker restartPolicy: Never volumes: - name: aws-secret secret: secretName: aws-secret - name: docker-registry-config configMap: name: docker-registry-config """ }
3.3.2 stages (require)
pipeline 阶段集声明部分,至少包含一个或多个 stage
3.3.3 stage (require && alest one)
构建阶段模块。定义构建阶段要做的事情,每个 pipeline 流水线至少包含一个 stage。一个 stage 里有且只有一个 steps。
pipeline {
agent any
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
}
3.3.4 steps (require)
Steps 包含一个或多个可执行指令。
3.3.5 environment
environment 命令用于声明一系列的键值对作为整个 pipeline 或某个 stage 指令执行的环境变量,取决于它定义在 pipeline 的最外层或 stage 里。该指令还支持通过 credentials() 获取预设的账户密码。
其中支持的 credential 类型包括:
- Secret Text:该环境变量的值将会被设置为 Secret Text 的内容。
- Secret File:该环境变量的值将会被设置为临时创建文件路径。
- Username and Password:该环境变量的值将会被设置为 username:password ,并且还会自动创建两个环境变量:MYVARNAME_USR 和 MYVARNAME_PSW。
- SSH with Private Key:该环境变量的值将会被设置为临时创建的 ssh key 文件路径,并且还会自动创建两个环境变量:MYVARNAME_USR 和 MYVARNAME_PSW。
pipeline {
agent any
stages {
stage('Example Username/Password') {
environment {
SERVICE_CREDS = credentials('my-predefined-username-password')
}
steps {
sh 'echo "Service user is $SERVICE_CREDS_USR"'
sh 'echo "Service password is $SERVICE_CREDS_PSW"'
sh 'curl -u $SERVICE_CREDS https://myservice.example.com'
}
}
stage('Example SSH Username with private key') {
environment {
SSH_CREDS = credentials('my-predefined-ssh-creds')
}
steps {
sh 'echo "SSH private key is located at $SSH_CREDS"'
sh 'echo "SSH user is $SSH_CREDS_USR"'
sh 'echo "SSH passphrase is $SSH_CREDS_PSW"'
}
}
}
}
3.3.6 parameters
parameters 声明当构建触发时用户要输入的所有参数,steps 指令将通过 params 对象获取这些参数。
参数类型包括:
- string
- text
- boolean
- choice
- password
举个例子🌰:
pipeline {
agent any
parameters {
string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
text(name: 'BIOGRAPHY', defaultValue: '', description: 'Enter some information about the person')
booleanParam(name: 'TOGGLE', defaultValue: true, description: 'Toggle this value')
choice(name: 'CHOICE', choices: ['One', 'Two', 'Three'], description: 'Pick something')
password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'Enter a password')
}
stages {
stage('Example') {
steps {
echo "Hello ${params.PERSON}"
echo "Biography: ${params.BIOGRAPHY}"
echo "Toggle: ${params.TOGGLE}"
echo "Choice: ${params.CHOICE}"
echo "Password: ${params.PASSWORD}"
}
}
}
}
3.3.7 post
定义附加 steps 在整个 pipeline 流水线或某个 stage 执行完之后运行。post 可以定义在 pipeline 的最外层或某个 stage 里,如果定义在最外层则表示这个 pipeline 执行完之后运行,如果在 stage 里则表示该 stage 执行完后运行。
- always:不管 pipeline 或 stage 的执行结果状态,总会执行的 steps
- changed:只有在 pipeline 或 stage 的执行结果状态与前一次执行相比发生改变时执行
- fixed:当前 pipeline 或 stage 执行成功且它的前一次执行结果是 failure 或 unstable 时执行
- regression:当前 pipeline 或 stage 执行结果是 failure,unstable 或 aborted 且它的前一次执行成功时执行。
- aborted:当前 pipeline 或 stage 执行结果是 aborted 时执行
- failure:当前 pipeline 或 stage 执行结果是失败时执行
- success:当前 pipeline 或 stage 执行结果是成功时执行
- unstable:当前 pipeline 或 stage 执行结果是 unstable 时执行(ps (⊙_⊙) 什么是 unstable?)
- unsuccessful:当前 pipeline 或 stage 执行结果不是成功时执行
- cleanup:在其他所有的 post 场景脚本都处理完之后执行,不管当前 pipeline 或 stage 执行结果是什么
继续举个例子🌰:
pipeline {
agent any
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
post {
always {
echo 'I will always say Hello again!'
}
}
}
3.3.8 tools
声明需要自动按照构建所需的工具,并设置到 PATH 环境变量里。当 agent none 时,tools 声明会自动忽略。
支持安装的工具有:jdk、maven、gradle
例子:
pipeline {
agent any
tools {
maven 'apache-maven-3.0.1'
}
stages {
stage('Example') {
steps {
sh 'mvn --version'
}
}
}
}
3.3.9 值得学习的关键字大集合
-
input:stage 参数输入指令。在进入 agent 模块和 when 条件判断之前,stage 将会被暂停并弹出输入对话框,直到用户输入可用的参数后 stage 才会继续执行。所有输入的参数在余下的 stage 指令当中都可以通过环境变量获取。
-
pipeline:流水线选项参数,pipeline 流水线提供了一系列的选项参数。其中也有些由插件提供,例如 timestamps。全局 options 声明在最外面,而写在 stage 里的则只在该 stage 有效且 stage options 只支持:skipDefaultCheckout,timeout,retry和timestamps。
-
when:命令通过设定的条件判断决定 pipeline 流水线的当前stage是否执行, when 命令至少包含一个条件判断。如果when包含多个条件判断,那么当且仅当所有条件判断都为true时,当前 stage才 会被执行。
注:正常情况下 when 判断时在 agent,input,options 命令之后才执行的,但是可以通过 beforeAgent,beforeInput,beforeOptions 参数来设置提前执行。
4. 碎碎念
今天平安夜,希望大家都能平平安安的:
- 最好的状态是一点点向自己喜欢的东西靠近,保持热爱奔赴山海,忠于自己热爱生活,知足上进。
- 要在最快乐的年纪活的精彩且迷人。
- 务必请你,一而再,再而三,三而不竭,千次万次,毫不犹豫地,救自己于这世间水火。
最近真的是持续性萎靡不振,不想努力,那就允许自己放松一阵子吧。