如何使用Jenkins Pipeline

 

快速启动示例

以下是一些简单的复制和粘贴的一个简单的流水线与各种语言的例子。

Java

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent { docker 'maven:3.3.3' }
    stages {
        stage('build') {
            steps {
                sh 'mvn --version'
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
/* Requires the Docker Pipeline plugin */
node('docker') {
    checkout scm
    stage('Build') {
        docker.image('maven:3.3.3').inside {
            sh 'mvn --version'
        }
    }
}

Node.js / JavaScript

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent { docker 'node:6.3' }
    stages {
        stage('build') {
            steps {
                sh 'npm --version'
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
/* Requires the Docker Pipeline plugin */
node('docker') {
    checkout scm
    stage('Build') {
        docker.image('node:6.3').inside {
            sh 'npm --version'
        }
    }
}

Ruby

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent { docker 'ruby' }
    stages {
        stage('build') {
            steps {
                sh 'ruby --version'
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
/* Requires the Docker Pipeline plugin */
node('docker') {
    checkout scm
    stage('Build') {
        docker.image('ruby').inside {
            sh 'ruby --version'
        }
    }
}

Python

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent { docker 'python:3.5.1' }
    stages {
        stage('build') {
            steps {
                sh 'python --version'
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
/* Requires the Docker Pipeline plugin */
node('docker') {
    checkout scm
    stage('Build') {
        docker.image('python:3.5.1').inside {
            sh 'python --version'
        }
    }
}

PHP

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent { docker 'php' }
    stages {
        stage('build') {
            steps {
                sh 'php --version'
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
/* Requires the Docker Pipeline plugin */
node('docker') {
    checkout scm
    stage('Build') {
        docker.image('php').inside {
            sh 'php --version'
        }
    }
}

Jenkins 运行多个步骤

Pipeline由多个步骤组成,允许您构建,测试和部署应用程序。Jenkins Pipeline允许您以简单的方式组合多个步骤,可以帮助您建模任何类型的自动化过程。

想像一个“一步一步”,就像执行单一动作的单一命令一样。当一个步骤成功时,它移动到下一步。当步骤无法正确执行时,Pipeline将失败。

当Pipeline中的所有步骤成功完成后,Pipeline被认为已成功执行。

Linux,BSD和Mac OS

在Linux,BSD和Mac OS(类Unix)系统上,该sh步骤用于在Pipeline中执行shell命令。

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                sh 'echo "Hello World"'
                sh '''
                    echo "Multiline shell steps works too"
                    ls -lah
                '''
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
node {
    stage('Build') {
        sh 'echo "Hello World"'
        sh '''
            echo "Multiline shell steps works too"
            ls -lah
        '''
    }
}

Windows

基于Windows的系统应该使用 bat 执行批处理命令的步骤。

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any
    stages {
        stage('Deploy') {
            steps {
                retry(3) {
                    sh './flakey-deploy.sh'
                }

                timeout(time: 3, unit: 'MINUTES') {
                    sh './health-check.sh'
                }
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
node {
    stage('Build') {
        bat 'set'
    }
}

超时,重试等等

有一些强大的步骤,“包装”其他步骤,可以轻松地解决问题,如重试( retry )步骤,直到成功或退出,如果步骤太长( timeout )。

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any
    stages {
        stage('Deploy') {
            steps {
                retry(3) {
                    sh './flakey-deploy.sh'
                }

                timeout(time: 3, unit: 'MINUTES') {
                    sh './health-check.sh'
                }
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
node {
    stage('Deploy') {
        retry(3) {
            sh './flakey-deploy.sh'
        }

        timeout(time: 3, unit: 'MINUTES') {
            sh './health-check.sh'
        }
    }
}

“部署”阶段重试flakey-deploy.sh脚本3次,然后等待health-check.sh脚本执行最多3分钟。如果健康检查脚本在3分钟内未完成,则Pipeline将在“部署”阶段被标记为失败。

“包装”的步骤,例如timeout和retry可以包含其它步骤,包括timeout或retry。

我们可以组合这些步骤。例如,如果我们想重新部署我们的部署5次,但是在失败的阶段之前,总是不想花3分钟以上:

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any
    stages {
        stage('Deploy') {
            steps {
                timeout(time: 3, unit: 'MINUTES') {
                    retry(5) {
                        sh './flakey-deploy.sh'
                    }
                }
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
node {
    stage('Deploy') {
        timeout(time: 3, unit: 'MINUTES') {
            retry(5) {
                sh './flakey-deploy.sh'
            }
        }
    }
}

完成

当Pipeline完成执行后,您可能需要运行清理步骤或根据Pipeline的结果执行某些操作。可以在本post节中执行这些操作。

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any
    stages {
        stage('Test') {
            steps {
                sh 'echo "Fail!"; exit 1'
            }
        }
    }
    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'
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
node {
    try {
        stage('Test') {
            sh 'echo "Fail!"; exit 1'
        }
        echo 'This will run only if successful'
    } catch (e) {
        echo 'This will run only if failed'

        // Since we're catching the exception in order to report on it,
        // we need to re-throw it, to ensure that the build is marked as failed
        throw e
    } finally {
        def currentResult = currentBuild.result ?: 'SUCCESS'
        if (currentResult == 'UNSTABLE') {
            echo 'This will run only if the run was marked as unstable'
        }

        def previousResult = currentBuild.previousBuild?.result
        if (previousResult != null && previousResult != currentResult) {
            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'
        }

        echo 'This will always run'
    }
}

Jenkins 定义执行环境

您可能已经注意到agent每个示例中的指令。该 agent指令告诉Jenkins在哪里以及如何执行Pipeline或其子集。如您所料,agent所有Pipeline都是必需的。

在引擎盖下,有几件事情agent会发生:

  • 该块中包含的所有步骤均排队等待Jenkins执行。一旦执行者可用,步骤就会开始执行。
  • 将分配一个工作区,其中将包含从源代码管理检出的文件以及Pipeline的任何其他工作文件。

有几种方法可以定义代理在Pipeline中使用,对于本次巡视,我们将仅关注使用短暂的Docker container。

Pipeline设计用于容易地使用Docker图像和容器在里面运行。这允许Pipeline定义所需的环境和工具,而无需手动配置各种系统工具和对代理的依赖。这种方法允许您几乎可以使用可以打包在Docker容器中的任何工具 。

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent {
        docker { image 'node:7-alpine' }
    }
    stages {
        stage('Test') {
            steps {
                sh 'node --version'
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
node {
    /* Requires the Docker Pipeline plugin to be installed */
    docker.image('node:7-alpine').inside {
        stage('Test') {
            sh 'node --version'
        }
    }
}

当Pipeline执行时,Jenkins将自动启动指定的容器并执行其中定义的步骤:

[Pipeline] stage
[Pipeline] { (Test)
[Pipeline] sh
[guided-tour] Running shell script
+ node --version
v7.4.0
[Pipeline] }
[Pipeline] // stage
[Pipeline] }

混合和匹配不同容器或其他代理时,在执行Pipeline时可以有很大的灵活性,有关更多配置选项,请继续使用“使用环境变量”。

Jenkins 使用环境变量

Jenkins 环境变量可以全局设置,如下面的示例或每个阶段。您可能会期望,每个阶段设置环境变量意味着它们将仅适用于定义它们的阶段。

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any

    environment {
        DISABLE_AUTH = 'true'
        DB_ENGINE    = 'sqlite'
    }

    stages {
        stage('Build') {
            steps {
                sh 'printenv'
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
node {
    withEnv(['DISABLE_AUTH=true',
             'DB_ENGINE=sqlite']) {
        stage('Build') {
            sh 'printenv'
        }
    }
}

这种从内部定义环境变量的方法Jenkinsfile 对于指示脚本(例如a)可以非常有用Makefile,以不同的方式配置构建或测试,以在Jenkins内部运行它们。

环境变量的另一个常见用途是在构建或测试脚本中设置或覆盖“虚拟”凭据。因为(显而易见)将凭据直接放入一个不好的主意Jenkinsfile,Jenkins Pipeline允许用户快速而安全地访问预定义的凭据,而Jenkinsfile无需知道其价值。

环境证书

如果您的Jenkins环境配置了凭据,例如构建秘密或API令​​牌,那么可以将它们轻松插入环境变量中,以便在Pipeline中使用。下面的代码片段用于“秘密文本”类型的凭据。

environment {
    AWS_ACCESS_KEY_ID     = credentials('AWS_ACCESS_KEY_ID')
    AWS_SECRET_ACCESS_KEY = credentials('AWS_SECRET_ACCESS_KEY')
}

正如第一个例子,这些变量将在全局或每个阶段可用,具体取决于environment指令 在哪里Jenkinsfile。

第二种最常见的凭证类型是“用户名和密码”,仍然可以在environment指令中使用,但会导致稍微不同的变量被设置。

environment {
   SAUCE_ACCESS = credentials('sauce-lab-dev')
}

这将实际设置3个环境变量:

SAUCE_ACCESS 含 <username>:<password>
SAUCE_ACCESS_USR 包含用户名
SAUCE_ACCESS_PSW 包含密码
credentials仅适用于声明性Pipeline。对于使用脚本Pipeline的用户,请参阅该withCredentials步骤的文档。

到目前为止,我们关心的是创建一个配置并执行我们期望的方式的Pipeline。在接下来的两个部分中,我们将介绍连续交付的另一个重要方面:表面反馈和信息。

Jenkins 记录测试结果和工件

虽然测试是良好的连续交付流程的关键部分,但大多数人不想筛选数千行控制台输出,以查找有关测试失败的信息。为了使这更容易,Jenkins可以记录和汇总测试结果,只要您的测试运行器可以输出测试结果文件。Jenkins通常捆绑在一起junit,但是如果您的测试运行器无法输出JUnit风格的XML报告,则还会有其他插件,可以处理任何广泛使用的测试报告格式。

要收集我们的测试结果和工件,我们将使用该post部分。

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any
    stages {
        stage('Test') {
            steps {
                sh './gradlew check'
            }
        }
    }
    post {
        always {
            junit 'build/reports/**/*.xml'
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
node {
    try{
        stage('Test') {
            sh './gradlew check'
        }
    finally {
        junit 'build/reports/**/*.xml'
    }
}

这将永远抓住测试结果,让Jenkins跟踪他们,计算趋势并对其进行报告。测试失败的Pipeline将被标记为“不稳定”,在Web UI中用黄色表示。这与“FAILED”状态不同,用红色表示。

当有测试失败时,从Jenkins抓住建造的文物进行本地分析和调查往往是有用的。Jenkins内置的支持存储“工件”,在Pipeline执行过程中生成的文件,实际上是这样。

这很容易通过archive步骤和文件集合表达式完成,如下面的示例所示:

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                sh './gradlew build'
            }
        }
        stage('Test') {
            steps {
                sh './gradlew check'
            }
        }
    }

    post {
        always {
            archive 'build/libs/**/*.jar'
            junit 'build/reports/**/*.xml'
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
node {
    try{
        stage('Test') {
            sh './gradlew check'
        }
    finally {
        archiveArtifacts artifacts: 'build/libs/**/*.jar', fingerprint: true
        junit 'build/reports/**/*.xml'
    }
}

记录Jenkins中的测试和工件对于快速轻松地向团队的各个成员发布信息非常有用。接下来,我们将讨论如何告诉团队成员什么在我们的Pipeline已经发生的事情。

Jenkins 清理和通知

由于post Pipeline的部分保证在Pipeline执行结束时运行,因此我们可以添加一些通知或其他步骤来执行定稿,通知或其他Pipeline末端任务。

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any
    stages {
        stage('No-op') {
            steps {
                sh 'ls'
            }
        }
    }
    post {
        always {
            echo 'One way or another, I have finished'
            deleteDir() /* clean up our workspace */
        }
        success {
            echo 'I succeeeded!'
        }
        unstable {
            echo 'I am unstable :/'
        }
        failure {
            echo 'I failed :('
        }
        changed {
            echo 'Things were different before...'
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
node {
    try {
        stage('No-op') {
            sh 'ls'
        }
    }
}
catch (exc) {
    echo 'I failed'
}
finally {
    if (currentBuild.result == 'UNSTABLE') {
        echo 'I am unstable :/'
    } else {
        echo 'One way or another, I have finished'
    }
}

有很多方法可以发送通知,下面是一些演示如何将有关Pipeline的通知发送到电子邮件,Hipchat房间或Slack频道的片段。

Email

post {
    failure {
        mail to: 'team@example.com',
             subject: "Failed Pipeline: ${currentBuild.fullDisplayName}",
             body: "Something is wrong with ${env.BUILD_URL}"
    }
}

Hipchat

post {
    failure {
        hipchatSend message: "Attention @here ${env.JOB_NAME} #${env.BUILD_NUMBER} has failed.",
                    color: 'RED'
    }
}

Slack

post {
    success {
        slackSend channel: '#ops-room',
                  color: 'good',
                  message: "The pipeline ${currentBuild.fullDisplayName} completed successfully."
    }
}

现在,当事情出现故障,不稳定或甚至成功时,我们可以通过令人兴奋的部分完成我们的持续交付流程:shipping!好了现在让我们来看看下一节:Jenkins部署

Jenkins 部署

最基本的连续交付流程将至少具有三个阶段,这些阶段应在以下内容中定义Jenkinsfile:构建,测试和部署。

对于本节,我们将主要关注部署阶段,但应该注意的是,稳定的构建和测试阶段是任何部署活动的重要前身。

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                echo 'Building'
            }
        }
        stage('Test') {
            steps {
                echo 'Testing'
            }
        }
        stage('Deploy') {
            steps {
                echo 'Deploying'
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
node {
    stage('Build') {
        echo 'Building'
    }
    stage('Test') {
        echo 'Testing'
    }
    stage('Deploy') {
        echo 'Deploying'
    }
}

阶段作为部署环境

一个常见的模式是扩展级别以捕获额外的部署环境,如“分段”或“生产”,如下面的代码段所示。

stage('Deploy - Staging') {
    steps {
        sh './deploy staging'
        sh './run-smoke-tests'
    }
}
stage('Deploy - Production') {
    steps {
        sh './deploy production'
    }
}

在这个例子中,我们假设我们的./run-smoke-tests脚本运行的任何“烟雾测试” 都足以将释放资格或验证到生产环境。自动将代码自动部署到生产的这种Pipeline可以被认为是“持续部署”的实现。虽然这是一个崇高的理想,但对许多人来说,连续部署可能不实际的原因很好,但仍然可以享受持续交付的好处。 Jenkins Pipeline很容易支撑两者。

要求人力投入进行

通常在阶段之间,特别是环境阶段之间,您可能需要人为的输入才能继续。例如,判断应用程序是否处于“促进”到生产环境的状态。这可以通过input步骤完成。在下面的示例中,“真实检查”阶段实际上阻止输入,如果没有人确认进度,则不会继续进行。

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any
    stages {
        /* "Build" and "Test" stages omitted */

        stage('Deploy - Staging') {
            steps {
                sh './deploy staging'
                sh './run-smoke-tests'
            }
        }

        stage('Sanity check') {
            steps {
                input "Does the staging environment look ok?"
            }
        }

        stage('Deploy - Production') {
            steps {
                sh './deploy production'
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

Jenkinsfile (Scripted Pipeline)
node {
    /* "Build" and "Test" stages omitted */

    stage('Deploy - Staging') {
        sh './deploy staging'
        sh './run-smoke-tests'
    }

    stage('Sanity check') {
        input "Does the staging environment look ok?"
    }

    stage('Deploy - Production') {
        sh './deploy production'
    }
}

结论

这个导读旨在向您介绍使用Jenkins和Jenkins Pipeline的基础知识。因为它是非常可扩展的,Jenkins可以进行修改和配置,以处理几乎任何方面的自动化。要了解更多关于Jenkins可以做什么的信息,请查看用户手册或 Jenkins博客,了解最新的活动,教程和更新。

相关文章