一、声明式流水线Pipeline语法

声明式流水线必须包含在一个 Pipeline 块中,比如以下是一个 Pipeline 块的格式:

pipeline {
    /* insert Declarative Pipeline here */
}

在声明式流水线中有效的基本语句和表达式遵循与 Groovy 的语法同样的规则,但有以下例外:

  • 流水线顶层必须是一个 block,即 pipeline{};
  • 分隔符可以不需要分号,但是每条语句都必须在自己的行上;
  • 块只能由 Sections、Directives、Steps 或 assignment statements 组成;
  • 属性引用语句被当做是无参数的方法调用,比如 input 会被当做 input()。

二、Agent

Agent表示整个流水线或特定阶段中的步骤和命令执行的位置,该部分必须在pipeline块的顶层被定义,也可以在 stage中再次定义,但是stage级别是可选的。

1.Agent的配置函数

为了支持可能有的各种各样的流水线,agent支持一些不同类型的参数,这些参数应用在pipeline块的顶层,或stage指令内部。agent可选配置如下:

  • any:在任何可用的代理上执行流水线,配置语法:
pipeline {
    agent any
}
  • none:表示该Pipeline脚本没有全局的agent配置。当顶层的agent配置为none时, 每个stage部分都需要包含它自己的agent。配置语法:
pipeline {
    agent none
    stages {
        stage('Stage For Build'){
            agent any
        }
    }
}
  • label:选择某个具体的节点执行 Pipeline 命令,例如:agent { label 'my-defined-label' }。 配置语法:
pipeline {
    agent none

    stages {
        stage('Stage For Build') {
            agent { label 'my-slave-label' }
            steps {
                // Add build steps here
            }
        }
    }
}
  • node:和 label 配置类似,只不过是可以添加一些额外的配置,比如 customWorkspace;

  • dockerfile:使用从源码中包含的Dockerfile所构建的容器执行流水线或stage。为了使用该选项,Jenkinsfile必须从多个分支流水线中加载,或者从pipeline from SCM加载。如果配置的语法为agent {dockerfile true},那么将从源码的根目录下使用Dockerfile文件进行构建。如果Dockerfile文件在其他目录,则需要使用dir字段更改Dockerfile所在的目录,配置方法为agent { dockerfile { dir 'DockerfileDir'} }。如果构建镜像的Dockerfile名称不是Dockerfile,可以使用filename选项指定Dockerfile文件名。同时也可以使用additionalBuildArgs参数传递构建镜像的参数,比如agent{dockerfile{additionalBuildArgs '--build-arg version=1.0.2' } }。假如有一个项目需要使用Dockerfile类型的agent,并且Dockerfile在build目录,文件名为Dockerfile.build,构建时期望有一个version的参数。此时对应的agent写法如下:

agent {
    dockerfile {
        filename 'Dockerfile.build'
        dir 'build'
        label 'my-defined-label'
        additionalBuildArgs '--build-arg version=1.0.2'
    }
}
  • docker:相当于dockerfile,可以直接使用docker字段指定外部镜像即可,可以省去构建的时间。比如使用 maven 镜像进行打包,同时可以指定 args:
agent {
    docker {
        image 'maven:3-alpine'
        label 'my-defined-label'
        args '-v /tmp:/tmp'
    }
}
  • kubernetes:Jenkins也支持使用 Kubernetes 创建Slave,也就是常说的动态Slave。配置示例如下:
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
"""
    }
}

2.Agent常用选项

  • label:一个字符串,该标签用于运行流水线的位置。该选项对node、docker和dockerfile可用,node必须选择该选项
  • customWorkspace:一个字符串,用于自定义工作区运行流水线或stage。它可以是相对路径,也可以是绝对路径,该选项对node、docker和dockerfile可用。比如:
agent none

node {
    label 'my-defined-label'
    customWorkspace '/some/other/path'
}
  • reuseNode:一个布尔值,默认为false。如果是true,则在同一个工作目录执行流水线。这个选项对docker和dockerfile有用,并且只有使用在个别stage的agent上才会有效。通常情况下使用docker作为agent时会开启此参数:
agent none

docker {
    image 'maven:3-alpine'
    label 'my-defined-label'
    args '-v /tmp:/tmp'
    reuseNode true
}

3.Agent配置示例

示例 1:假设有一个 Java 项目,需要用 mvn 命令进行编译,此时可以使用 maven 的镜像作 为 agent。配置如下:

Jenkinsfile (Declarative Pipeline) // 可以不要此行
pipeline {
    agent { docker 'maven:3-alpine' }

    stages {
        stage('Example Build') {
            steps {
                sh 'mvn -B clean verify'
            }
        }
    }
}

示例 2:本示例在流水线顶层将 agent 定义为 none,那么此时 stage 部分就需要必须包含它 自己的 agent 部分。在 stage('Example Build')部分使用 maven:3-alpine 执行该阶段步骤,在 stage('Example Test')部分使用 openjdk:8-jre 执行该阶段步骤。此时 Pipeline 如下:

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent none

    stages {
        stage('Example Build') {
            agent { docker 'maven:3-alpine' }
            steps {
                echo 'Hello, Maven'
                sh 'mvn --version'
            }
        }

        stage('Example Test') {
            agent { docker 'openjdk:8-jre' }
            steps {
                echo 'Hello, JDK'
                sh 'java -version'
            }
        }
    }
}

示例 3:上述的示例也可以用基于 Kubernetes 的 agent 实现。比如定义具有三个容器的 Pod, 分别为 jnlp(负责和 Jenkins Master 通信)、build(负责执行构建命令)、kubectl(负责执行 Kubernetes 相关命令),在 steps 中可以通过 containers 字段,选择在某个容器执行命令:

pipeline {
    agent {
        kubernetes {
            cloud 'kubernetes-default'
            slaveConnectTimeout 1200
            yaml '''
apiVersion: v1
kind: Pod
spec:
  containers:
    - args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
      image: 'registry.cn-beijing.aliyuncs.com/citools/jnlp:alpine'
      name: jnlp
      imagePullPolicy: IfNotPresent
    - command:
        - "cat"
      image: "registry.cn-beijing.aliyuncs.com/citools/maven:3.5.3"
      imagePullPolicy: "IfNotPresent"
      name: "build"
      tty: true
    - command:
        - "cat"
      image: "registry.cn-beijing.aliyuncs.com/citools/kubectl:self-1.17"
      imagePullPolicy: "IfNotPresent"
      name: "kubectl"
      tty: true
'''
        }
    }

    stages {
        stage('Building') {
            steps {
                container(name: 'build') {
                    sh """
                        mvn clean install
                    """
                }
            }
        }

        stage('Deploy') {
            steps {
                container(name: 'kubectl') {
                    sh """
                        kubectl get node
                    """
                }
            }
        }
    }
}