一、简介

Jenkins是一款开源的CI&CD软件,可以用于自动化执行各种任务,包括构建、测试和部署软件等;它支持各种运行方式,可通过系统包、Docker或独立的Java程序。

二、安装与运行

1、安装

  • 必须项

需要安装Java 8

  • 下载

下载适合操作系统的安装包运行安装。

安装后会自动运行Jenkins;也可以直接下载war包并运行。

2、运行

  • 打开终端进入到Jenkins安装目录(或war包所在目录)

  • 运行java -jar jenkins.war --httpPort=8080

  • 访问http://localhost:8080即可

首次访问时会提示(admin)初始密码路径:

C:\Users\acer123\.jenkins\secrets\initialAdminPassword

也可以在用户Configure - Password中修改密码。

3、问题及处理方法

  • 实例已离线

登录后向导(SetupWizard)中显示”该Jenkins实例似乎已离线。”:

此时需要访问http://localhost:8080/pluginManager/advanced,修改升级站点URL的值为:

http://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json

然后重启jenkins。

  • 插件安装

直接安装插件时报SunCertPathBuilderException错:

sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.provider.certpath.SunCertPathBuilder.build(Unknown Source)
	at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
	at java.security.cert.CertPathBuilder.build(Unknown Source)
Caused: sun.security.validator.ValidatorException: PKIX path building failed
	at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
	at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
	at sun.security.validator.Validator.validate(Unknown Source)
	at sun.security.ssl.X509TrustManagerImpl.validate(Unknown Source)
	at sun.security.ssl.X509TrustManagerImpl.checkTrusted(Unknown Source)
	at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
Caused: javax.net.ssl.SSLHandshakeException
	at sun.security.ssl.Alerts.getSSLException(Unknown Source)
	at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
	at sun.security.ssl.Handshaker.fatalSE(Unknown Source)

	......

Caused: java.io.IOException: Failed to load https://updates.jenkins.io/download/plugins/ant/1.11/ant.hpi to C:\Users\acer123\.jenkins\plugins\ant.jpi.tmp
	at hudson.model.UpdateCenter$UpdateCenterConfiguration.download(UpdateCenter.java:1287)
Caused: java.io.IOException: Failed to download from https://updates.jenkins.io/download/plugins/ant/1.11/ant.hpi
	at hudson.model.UpdateCenter$UpdateCenterConfiguration.download(UpdateCenter.java:1321)
	at hudson.model.UpdateCenter$DownloadJob._run(UpdateCenter.java:1869)
	at hudson.model.UpdateCenter$InstallationJob._run(UpdateCenter.java:2153)
	at hudson.model.UpdateCenter$DownloadJob.run(UpdateCenter.java:1843)
	at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
	at java.util.concurrent.FutureTask.run(Unknown Source)
	at hudson.remoting.AtmostOneThreadExecutor$Worker.run(AtmostOneThreadExecutor.java:118)
	at java.lang.Thread.run(Unknown Source)

处理方式:

手工访问上面报错中的下载地址,下载后在 插件管理 -- 高级 菜单的上传插件功能区上传安装即可。

  • 更有效的处理方式

Plugin Manager中设置升级站点的URL为:

http://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json

修改jenkins运行目录updates/default.json中的文件内容:

C:\Users\acer123\.jenkins\updates\default.json

https://updates.jenkins.io/download统一替换为http://mirrors.tuna.tsinghua.edu.cn/jenkins并重新启动即可。

三、流水线(Pipeline)语法

流水线是用户定义的一个持续交付(CD)流水线模型,它向Jenkins中添加了一组强大的工具, 可以将用例持续集成到CD流水线中。它定义了整个构建过程,包括构建、布署、测试和交付应用程序。

流水线最基本的部分是”步骤”(step),步骤的基本作用是告诉Jenkins要做什么,并作为声明性语法和脚本流水线(Pipeline)语法的基本构建块。

1、相关述语

  • 节点(node)

节点是Jenkins运行的机器,称为节点。在脚本流水线语法中使用节点块。

node{
}
  • 阶段(stage)

此块中包含流水线中的一系列步骤,例如:构建、测试和部署流程都集中在一个阶段。通常情况通过阶段块就可以看到Jenkins流水线的全过程。

stages {
	stage('Build') { 
		steps {
			//...
		}
	}
	stage('Test') { 
		steps {
			//...
		}
	}
}

嵌套、并行、串行:

pipeline {
    agent none
    stages {
        stage('Non-Sequential Stage') {
            agent {
                label 'for-non-sequential'
            }
            steps {
                echo "On Non-Sequential Stage"
            }
        }
        stage('Sequential') {
            agent {
                label 'for-sequential'
            }
            environment {
                FOR_SEQUENTIAL = "some-value"
            }
            stages {
                stage('In Sequential 1') {
                    steps {
                        echo "In Sequential 1"
                    }
                }
                stage('In Sequential 2') {
                    steps {
                        echo "In Sequential 2"
                    }
                }
                stage('Parallel In Sequential') {
                    parallel {
                        stage('In Parallel 1') {
                            steps {
                                echo "In Parallel 1"
                            }
                        }
                        stage('In Parallel 2') {
                            steps {
                                echo "In Parallel 2"
                            }
                        }
                    }
                }
            }
        }
    }
}
  • 工具(tools)

定义用来自动安装并设置到PATH上的内容,如果没有指定代理(agent none),则忽略该部分。

pipeline {
    agent any
    tools {
        maven 'apache-maven-3.0.1' 
    }
    stages {
        stage('Example') {
            steps {
                sh 'mvn --version'
            }
        }
    }
}
  • 步骤(step)

步骤是在指定时间执行的单个任务,例如执行shell命令:

...
steps { 
	sh 'make' 
}
...
  • 脚本(script)

script是一个定义在step中的脚本流水线(Scripted Pipeline):

pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'

                script {
                    def browsers = ['chrome', 'firefox']
                    for (int i = 0; i < browsers.size(); ++i) {
                        echo "Testing the ${browsers[i]} browser"
                    }
                }
            }
        }
    }
}
  • 流程控制(Flow Control)

    脚本流水线(Scripted Pipeline)从Jenkinsfile的顶部向下连续执行,也支持if/elestry/catch/finally流程控制语法:

    • if/else
      node {
          stage('Example') {
              if (env.BRANCH_NAME == 'master') {
                  echo 'I only execute on the master branch'
              } else {
                  echo 'I execute elsewhere'
              }
          }
      }
    
    • try/catch/finally
      node {
          stage('Example') {
              try {
                  sh 'exit 1'
              }
              catch (exc) {
                  echo 'Something failed, I should sound the klaxons!'
                  throw
              }
          }
      }
    
  • When

when语法允许Pipeline根据给定条件确定是否应执行该stage;when块中必须至少包含一个条件,如果包含多个条件,则所有子条件都必须返回true时才执行stage:

pipeline {
    agent any
    stages {
        stage('Example Build') {
            steps {
                echo 'Hello World'
            }
        }
        stage('Example Deploy') {
            when {
                branch 'production'
            }
            steps {
                echo 'Deploying'
            }
        }
    }
}

2、内置条件

  • branch

当正在构建的分支与给定的分支模式匹配时执行stage:

when { branch 'master' }
  • buildingTag

当构建生成一个标记时执行stage:

when { buildingTag() }
  • changelog

如果构建的SCM(Source Coce Management)更改日志(changelog)包含指定的正则表达式时执行stage:

when { changelog '.*^\\[DEPENDENCY\\] .+$' }
  • changeset

如果构建的SCM变更集(changeset)包含一个或多个与给定模式匹配的文件,则执行stage:

when { changeset "**/*.js" }
  • changeRequest

如果当前的构建是为了”变更请求”(例如GitHub上的Pull Request等)时执行stage:

when { changeRequest() }

when { changeRequest target: 'master' }

when { changeRequest authorEmail: "[\\w_-.]+@example.com", comparator: 'REGEXP' }

当没有传递参数时,stage将在每个更改请求上运行。

  • environment

当指定的环境变量被设置为给定的值时执行stage:

when { environment name: 'DEPLOY_TO', value: 'production' }
  • equals

当期望值等于实际值时执行stage:

when { equals expected: 2, actual: currentBuild.number }
  • expression

当指定的Groovy表达式计算结果为true时执行stage:

when { expression { return params.DEBUG_BUILD } }
  • tag

如果TAG_NAME变量与给定的模式匹配,则执行stage:

when { tag "release-*" }
  • not

当嵌套条件为false时执行stage,必须包含一个条件:

when { not { branch 'master' } }
  • allOf

当所有嵌套条件都为真时执行stage,必须至少包含一个条件:

when { allOf { branch 'master'; environment name: 'DEPLOY_TO', value: 'production' } }
  • anyOf

当至少有一个嵌套条件为真时执行stage,必须至少包含一个条件:

when { anyOf { branch 'master'; branch 'staging' } }
  • triggeredBy

当当前构建被给定的参数触发时,执行stage:

when { triggeredBy 'SCMTrigger' }
when { triggeredBy 'TimerTrigger' }
  • example
pipeline {
    agent any
    stages {
        stage('Example Build') {
            steps {
                echo 'Hello World'
            }
        }
        stage('Example Deploy') {
            when {
                expression { BRANCH_NAME ==~ /(production|staging)/ }
                anyOf {
                    environment name: 'DEPLOY_TO', value: 'production'
                    environment name: 'DEPLOY_TO', value: 'staging'
                }
            }
            steps {
                echo 'Deploying'
            }
        }
    }
}

3、指令(Directives)

  • environment

environment指令定义所有步骤的环境变量或特定于阶段的step:

pipeline {
    agent any
    environment { 
        CC = 'clang'
    }
    stages {
        stage('Example') {
            environment { 
                AN_ACCESS_KEY = credentials('my-predefined-secret-text') 
            }
            steps {
                sh 'printenv'
            }
        }
    }
}

其中,定义在最外层的environment块将应用于pipeline中的所有step;定义在stage中的environment块只将给定的环境变量应用到此stage中的步骤;在environment块中可以通过credentials()方法访问预定义的凭证。

  • options

options指令允许在pipeline里配置特定于流水线的选项:

pipeline {
    agent any
    options {
        timeout(time: 1, unit: 'HOURS') 
    }
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

上面为Pipeline运行设置一个超时周期,在此之后Jenkins会中止此Pipeline。

  • parameters

parameters指令提供了用户在触发Pipeline时应该提供的参数列表:

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}"
            }
        }
    }
}
  • triggers

triggers指令定义了重新触发管道的自动方式:

pipeline {
    agent any
    triggers {
        cron('H */4 * * 1-5')
    }
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

4、声明式流水线

所有有效的声明式Pipelines必须定义在pipeline块中,例如:

pipeline {
    /* insert Declarative Pipeline here */
}

Declarative Pipeline中的基有本语句和表达式遵循与Groovy相同的语法规则,但有以下例外:

  • 没有分号作为语句分隔符,每个语句必须在它自己的行上。

  • 块必须只包含章节(Sections)、指令(Directives)、步骤(Steps)或赋值语句。

  • 属性引用语句被视为无参方法调用,例如:input会被当作input()

Jenkinsfile (Declarative Pipeline):

pipeline { 
    agent any 
    stages {
        stage('Build') { 
            steps { 
                sh 'make' 
            }
        }
        stage('Test'){
            steps {
                sh 'make check'
                junit 'reports/**/*.xml' 
            }
        }
        stage('Deploy') {
            steps {
                sh 'make publish'
            }
        }
    }
}

其中:

  • agent any

表示在任何可用的代理上,执行流水线。

  • stage(‘Xxx’)

定义Xxx阶段。

  • steps{}

在steps中定义此阶段需要执行的步骤。

5、脚本流水线

脚本流水线在轻量级执行程序的帮助下在Jenkins主机上运行,它使用很少的资源将流水线(pipeline)转换为原子命令。

脚本流水线通过一个或多个node块执行核心工作:

Jenkinsfile (Scripted Pipeline):

node {  
    stage('Build') { 
        //...
    }
    stage('Test') { 
        //...
    }
    stage('Deploy') { 
        //...
    }
}

虽然stage块在脚本化流水线语法中是可选的,但使用stage块可以清楚的显示Jenkins UI中的每个任务子集。

四、样例

1、Hello World

  • 创建项目

点击New Item菜单,构建一个自由风格的软件项目。

  • 配置任务

在Build内容区,添加”执行Windows批处理命令”,输入命令:echo Hello后保存。

  • 构建项目

点击 立即构建,构建完可以查看构建历史。

  • 构建输出

进入某次构建历史后,点击Console Output可以查看控制台输出内容。

2、流水线

  • 创建项目

创建项目时选择 流水线 项目。

  • 配置脚本

Pipeline Script内容如下:

pipeline {
    agent any 
    stages {
        stage('Stage 1') {
            steps {
                echo 'Hello world!' 
            }
        }
    }
    post {
        always {
            echo 'This will always run'
        }
        success {
            echo 'This will run only if successful'
        }
        failure {
            echo 'This will run only if failed'
        }
        unstable {
            echo 'This will run only if the run was marked as unstable'
        }
        changed {
            echo 'This will run only if the state of the Pipeline has changed'
            echo 'For example, if the Pipeline was previously failing but is now successful'
        }
    }
}

  • 构建并查看输出

参考资料: