1. 概述

"Pipeline-as-code" 是一种允许参与 DevOps 的每个人创建并维护 Jenkins 管道的想法。实际上,应用 "pipeline-as-code" 原则有两种方式:脚本化和声明式管道。"Pipeline-as-code" 允许 Jenkins 将管道视为常规文件。参与 DevOps 的人员可以存储、共享,并使用这些文件与版本控制系统一起使用。

2. 声明式管道及其优势

声明式管道是 "pipeline-as-code" 原则的一个较新的方法。它们很容易编写和理解。结构可能看起来有点复杂,但总体上只包含几个基本部分。pipeline 块是整个管道声明的主要块。在本文档中,我们将仅考虑以下部分:agentstagessteps

  • pipeline - 包含整个管道
  • agent - 定义将处理此管道的机器
  • stages - 定义管道的阶段
  • steps - 特定阶段内的小操作

让我们看看我们的声明式管道结构会是什么样子:

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

声明式管道的另一个重要部分是指令。实际上,指令是一种方便的方法来包含额外的逻辑。它们可以帮助将工具纳入管道,并且还可以帮助设置触发器、环境变量和参数。有些指令可以提示用户输入额外信息。有一个易于使用的生成器,可以帮助创建这些指令。

在前面的例子中,“steps” 是一个包含将在阶段内执行的逻辑的指令。有一个专门的片段生成器,提供了一种创建步骤的方便方式。

声明式管道的力量主要来自指令。声明式管道可以通过使用“script”指令利用脚本化管道的力量。这个指令将内部的行作为脚本化管道执行。

3. 脚本化管道及其优势

脚本化管道是 "pipeline-as-code" 原则的第一个版本。它们是使用 Groovy 构建的 DSL,提供了出色的力量和灵活性。然而,这也需要一些基本的 Groovy 知识,这有时并不理想。

这类管道在结构上受到较少限制。此外,它们只有两个基本块:“node” 和 “stage”。一个“node”块指定执行特定管道的机器,而“stage”块用于分组代表单独操作的一系列步骤。缺乏额外规则和块使这些管道易于理解

node {
    stage('Hello world') {
        sh 'echo Hello World'
    }
}

将脚本化管道视为声明式管道,但只有阶段。在这种情况下,“node”块扮演了声明式管道中的“pipeline”块和“agent”指令的角色。

如前所述,脚本化管道的步骤可以通过相同的片段生成器生成。由于这种类型的管道不包含指令,步骤包含所有逻辑。对于非常简单的管道,这可以减少总体代码量。

然而,对于一些模板设置,它可能需要额外的代码,这可以通过指令解决。在这种管道中实现更复杂的逻辑通常在 Groovy 中进行。

4. 脚本化和声明式管道的比较

让我们检查一个三个步骤的管道,从 Git 拉取项目,然后测试、打包并部署它:

pipeline {
    agent any
    tools {
        maven 'maven' 
    }
    stages {
        stage('Test') {
            steps {
                git 'https://github.com/user/project.git'
                sh 'mvn test'
                archiveArtifacts artifacts: 'target/surefire-reports/**'
            }
        }
        stage('Build') {
            steps {
                sh 'mvn clean package -DskipTests' 
                archiveArtifacts artifacts: 'target/*.jar'
            }
        }
        stage('Deploy') {
            steps {
                sh 'echo Deploy'
            }
        }
    }
}

如您所见,所有逻辑都位于“steps”部分。因此,如果我们要将这个声明式管道转换为脚本化管道,这部分不会改变:

node {
    stage('Test') {
        git 'https://github.com/user/project.git'
        sh 'mvn test'
        archiveArtifacts artifacts: 'target/surefire-reports/**'
    }
    stage('Build') {
        sh 'mvn clean package -DskipTests' 
        archiveArtifacts artifacts: 'target/*.jar'
    }
    stage('Deploy') {
        sh 'echo Deploy'
    }
}

对于相同功能的脚本化管道看起来比其声明式对应物更密集。但是,我们应该确保服务器上的所有环境变量正确设置。同时,如果有多个 Maven 版本,我们需要直接在管道中更改它们。为此,我们可以使用具体路径或环境变量。

还有“withEnv”步骤在脚本化管道中很有用。另一方面,在声明式管道中,很容易在 Jenkins 配置中更改工具版本。

前面的例子表明,对于日常任务,这两种方法之间几乎没有区别。如果步骤可以满足管道的基本需求,则这两种方法几乎相同。声明式管道仍然被首选,因为它们可以简化一些常见的逻辑。

5. 结论

脚本化和声明式管道追求相同的目标,并在幕后使用相同的管道子系统。它们之间的主要差异在于灵活性和语法。它们只是解决相同问题的两种不同工具,因此我们完全可以并且应该互换使用它们。

声明式管道简洁的语法将确保快速和流畅地进入这一领域。同时,脚本化管道可能会为更有经验的用户提供更多力量。为了从两者中获得最佳效果,我们可以利用声明式管道与脚本指令。