1. 简介

Jenkins 是一个广泛使用的用 Java 编写的自动化服务器。它能够将任务和作业执行组织成有序的批处理流程。我们可能通过 Jenkins 执行的一个常见任务是文件的移动与复制,这在以下场景中非常有用:

  • 软件包安装
  • 容器部署
  • 常规配置
  • 数据备份

在本教程中,我们将深入探讨 Jenkins,并展示如何编写用于在路径之间复制或移动文件的脚本。首先,我们介绍 Jenkins 的基本术语。接着,我们将演示一个典型的部署及其 Web 界面(UI)。然后,我们将学习如何使用命令行接口(CLI)。随后,我们将查看 Jenkins 流程输出的方式。接下来,我们将了解 Jenkins 的系统配置与安全机制。最后,我们通过一个简明示例来演示这些概念的实际应用。

本教程中的代码在 Debian 12(Bookworm)系统上测试通过,使用 GNU Bash 5.2.15。除非另有说明,它应该在大多数 POSIX 兼容环境中正常运行。

2. Jenkins 术语解析

在深入实践之前,先理解 Jenkins 的基本术语非常重要。

2.1. Job(任务)

在 Jenkins 中,Job 是执行的最上层单元,以前也被称为 Project

“Job” 这个词在其他自动化框架中可能表示一个通用的工作单元,但在 Jenkins 中,它特指一种任务组织方式。因此,在 Jenkins 的上下文中,“Job” 有时会被误用,需注意区分

Job 可以是多种类型,但最常见的是 Pipeline 类型。

2.2. Pipeline(流水线)

自 2016 年起,基于 Apache Groovy 的 Jenkins Pipeline 插件成为每个 Jenkins 安装的默认组件。这是创建流水线的基本方式,主要通过 Jenkinsfile Groovy 脚本来实现。

Pipeline 是一种常用于长期流程自动化的 Job 类型。它将多个任务按输入输出关系进行组织。

例如,一个基础的编译和部署流程可以表示为:

  • 编译代码
  • 测试可执行文件
  • 部署到生产环境

理解数据在流程中的流转方式,有助于我们实现对应的 Pipeline。

2.3. Step(步骤)、Task(任务)与 Stage(阶段)

每个 Pipeline 由多个 Step 组成。在 Jenkins 中,Step 是在某个 Stage 中执行的任务。Task 是具体的操作,如编译、运行、移动等;Stage 则表示这些操作的顺序和分组。

例如,不能在写代码之前编译,也不能在编译之前部署:

+---------+     +------+     +--------+
| Compile |---->| Test |---->| Deploy |
+---------+     +------+     +--------+

因此,任务顺序非常重要。此外,Folder(文件夹) 可以帮助我们更好地组织任务,类似于文件系统的结构。

2.4. Executor(执行器)、Worker(工作者)与 Agent(代理)

在 Jenkins 中,执行 Step 的实体被称为 Executor 或 Worker。Executor 负责任务执行,而 Jenkins 负责调度。

如果有多个 Executor 可用于某个 Step,意味着我们可以并行执行任务:

+---------+     +------+     +--------+
| Compile |---->| Test |---->| Deploy |
+---------+     +------+     +--------+
|               |
|CRunner1       |TRunner1
|CRunner2       |TRunner2
|...            |...
|CRunnerN       |TRunnerN

每个 Executor 可以由不同的 Node(节点)生成,即 Jenkins 部署中的机器。

2.5. Build(构建)

任何 Step 的执行都被称为一次 Build。Build 表示某个 Step 执行的过程和结果

例如,我们可能希望在某个任务失败时终止整个 Pipeline,或者只是通知后续 Step。Build 的状态值包括:

  • aborted:手动中断或超时
  • failed:不可恢复的错误
  • stable:稳定成功
  • successful:成功
  • unstable:非致命错误

Build 可以按照脚本计划运行,也可以通过触发器(如显式构建调用、外部调度如 cronwebhook 等)启动。

2.6. Plugin(插件)

由于其开源性质,Jenkins 可通过插件扩展功能。尽管最初目标是 Java,但 Jenkins 现已支持多种语言,尤其适用于持续集成和部署。

此外,还有支持外部版本控制系统、安全与通知机制等多种插件。

3. Jenkins 部署

有多种方式部署 Jenkins

我们这里使用 Docker 容器部署:

$ docker run --name jenkins --publish 8080:8080 --detach jenkins/jenkins

✅ 运行命令后,我们可以在浏览器中访问 https://xost:8080(假设主机名为 xost)查看 Jenkins 的主界面:

Jenkins dashboard

默认安装会设置多个 Jenkins 相关的环境变量:

$ env | grep JENKINS
JENKINS_UC_EXPERIMENTAL=https://updates.jenkins.io/experimental
JENKINS_INCREMENTALS_REPO_MIRROR=https://repo.jenkins-ci.org/incrementals
JENKINS_SLAVE_AGENT_PORT=50000
JENKINS_VERSION=2.442
JENKINS_UC=https://updates.jenkins.io
JENKINS_HOME=/var/jenkins_home

其中 $JENKINS_HOME 是 Jenkins 的主目录:

$ echo $JENKINS_HOME
/var/jenkins_home

目录结构如下:

$ ls -1d $JENKINS_HOME/*/
/var/jenkins_home/jobs/
/var/jenkins_home/logs/
/var/jenkins_home/nodes/
/var/jenkins_home/plugins/
/var/jenkins_home/secrets/
/var/jenkins_home/updates/
/var/jenkins_home/userContent/
/var/jenkins_home/users/
/var/jenkins_home/war/

其中 jobs 目录包含所有 Job 及其构建日志:

$ tree $JENKINS_HOME/jobs/
/var/jenkins_home/jobs/
└── baelpipe
    ├── builds
    │   ├── 3
    │   │   ├── build.xml
    │   │   ├── log
    │   │   └── log-index
    │   ├── legacyIds
    │   └── permalinks
    ├── config.xml
    └── nextBuildNumber

4. Jenkins 命令行接口(CLI)

除了 Web UI,Jenkins 还提供 CLI 工具 jenkins-cli,它是用 Java 编写的单个 JAR 文件。

获取方式如下:

  1. 进入 Manage Jenkins 页面
  2. 选择 Jenkins CLI
  3. 下载 jenkins-cli.jar

运行帮助命令:

$ java -jar jenkins-cli.jar -s http://<JENKINS_HOSTNAME_OR_IP>:8080/ help

⚠️ 注意使用 -auth 参数进行身份验证:

$ java -jar jenkins-cli.jar -s http://<JENKINS_HOSTNAME_OR_IP>:8080/ -auth <JENKINS_USERNAME>:<JENKINS_PASSWORD>

CLI 支持所有 Web UI 可执行的操作,如构建、复制 Job、查看控制台输出等。

5. Jenkins 日志查看

Jenkins 的 Web UI 是查看系统活动和 Job 状态的首选方式。但构建过程会产生日志,我们也可以通过其他方式查看。

5.1. 构建结果

在 Web UI 中查看构建失败界面:

Jenkins build failure

查看构建日志:

Jenkins build log

如需更详细信息,查看 Console Output

Jenkins Console Output

5.2. 日志文件位置

每个构建的输出日志位于 $JENKINS_HOME/jobs/<job-name>/builds/<build-number>/log

$ cat $JENKINS_HOME/jobs/baelpipe/builds/3/log
Started by user ha:////4DjQqKtQOr9I+VnRz88sm666VTh1ehs1zY29phH5PmXzAAAAlx+LCAAAAAAAAP9b85aBtbdexTGjNKU4P08vOT+vOD8nVc83PyU1x6OyILUoJzMv2y+/JJUBAhiZGBgqihh666SjKDWzXb3RdlLBUSYGJk8GtpzUvPSSDBmxtKinBIGIZ+sxLJE/ZzEvHT94JKizLx0a6BxUmjGOUNodHsLgAzeEgZu/c0x1CL9xJTczDwACG0V4sAAAAA=Administrator
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: 8: Expected a step @ line 8, column 17.
                   badcommand
                   ^

1 error
[...]
Finished: FAILURE

6. 系统与安全上下文

要执行任何操作,必须具备正确的上下文和权限。

6.1. Jenkins 运行用户

Jenkins 的运行用户可以在 System Information 页面查看。通常默认用户为 jenkins

6.2. 数据权限

查看目录权限:

$ ls -ld /var/source/
drwxr--r-- 2 root root 4096 Jan 31 10:01 /var/source/
$ ls -ld /var/destination/
drwxr--r-- 2 root root 4096 Jan 31 10:02 /var/destination/

⚠️ Jenkins 用户无法读写这些目录,需要修改权限:

$ chown --recursive jenkins:jenkins /var/source/ /var/destination/

6.3. Pipeline 脚本访问权限

Jenkins Pipeline 默认在沙箱中运行,限制文件系统访问。若要绕过限制,可取消勾选 Groovy Sandbox 选项,但需管理员审批。

Jenkins Groovy Sandbox

7. 文件移动与复制操作

我们以复制 /var/source/file/var/destination/ 为例,演示几种实现方式。

7.1. 使用 Shell 命令

Jenkins Pipeline 支持 sh()bat() 执行 Shell 命令:

pipeline {
  agent any

  stages {
    stage('shellcopy') {
      steps {
        script {
          try {
            sh(script: 'cp /var/source/file /var/destination/', returnStdout: true)
          } catch (Exception ex) {
            echo 'Exception: ' + ex.toString()
          }
        }
      }
    }
  }
}

✅ 该脚本使用 Jenkins 默认节点执行 cp 命令。

7.2. 使用原生 Groovy 脚本

Groovy 本身支持文件操作:

import java.nio.file.Files;
import java.nio.file.Paths;

pipeline {
  agent any

  stages {
    stage('groovycopy') {
      steps {
        script {
          File source = new File('/var/source/');
          File[] filesList = source.listFiles();

          for (File file : filesList) {
            if (file.isFile()) {
              echo file.getName()
              Files.copy(Paths.get(file.path), Paths.get('/var/destination/' + file.getName()));
            }
          }
        }
      }
    }
  }
}

⚠️ 注意导入 java.nio.file 包以支持文件复制。

7.3. 使用 File Operations 插件

Jenkins 提供 file-operations 插件简化文件操作。

✅ 可通过创建 Freestyle Project 或使用 Pipeline 脚本:

pipeline {
  agent any

  stages {
    stage('plugincopy') {
      steps {
        script {
          dir('/var/source/') {
            fileOperations([fileCopyOperation(excludes: '', flattenFiles: true, includes: 'file', targetLocation: '/var/destination/')])
          }
        }
      }
    }
  }
}

输出示例:

Started by user Administrator
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/plugincopy
[Pipeline] {
[Pipeline] stage
[Pipeline] { (plugincopy)
[Pipeline] script
[Pipeline] {
[Pipeline] dir
Running in /var/source
[Pipeline] {
[Pipeline] fileOperations
File Copy Operation:
/var/source/file
[Pipeline] }
[Pipeline] // dir
[Pipeline] }
[Pipeline] // script
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

8. 总结

在本文中,我们通过一个文件复制的基本场景,深入解析了 Jenkins 的使用方式。

✅ Jenkins 是一个多功能系统,不仅可以自动化如文件复制等基础任务,还可以将其整合进复杂的流水线中。掌握其术语、部署、权限管理及脚本编写,是实现自动化流程的关键。


原始标题:Jenkins Walkthrough and Scripting Filesystem Operations

« 上一篇: Concourse 简介