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 可以按照脚本计划运行,也可以通过触发器(如显式构建调用、外部调度如 cron、webhook 等)启动。
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 相关的环境变量:
$ 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 文件。
获取方式如下:
- 进入 Manage Jenkins 页面
- 选择 Jenkins CLI
- 下载
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 中查看构建失败界面:
查看构建日志:
如需更详细信息,查看 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 选项,但需管理员审批。
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 是一个多功能系统,不仅可以自动化如文件复制等基础任务,还可以将其整合进复杂的流水线中。掌握其术语、部署、权限管理及脚本编写,是实现自动化流程的关键。