# eda2-ci
**Repository Path**: libbylg/eda2-ci
## Basic Information
- **Project Name**: eda2-ci
- **Description**: EDA2相关项目的ci工具和配置
- **Primary Language**: Unknown
- **License**: MulanPSL-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-07-13
- **Last Updated**: 2026-08-06
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# CIKIT & 流水线工程
本文档将介绍 gitee 上 dizo 的工程系统的基本信息以及如何使用 cikit 工具进行 dizo 工程系统的搭建。
以期望,本文对 gitee 上 dizo 现有流水线的后续维护者有所帮助。
希望能够帮助他们能够不需要再花太多时间,即可快速完成 dizo 工程量系统的重建和维护。
## dizo 流水线现有工程系统概貌
#### 部署视图
下面描述的整个流水线所涉及的主要组件,以及组件之间的通信关系:
```
+------------------------------------+
| Cache-Storage |<----+
+------------------------------------+ |
| (HTTP)
+------------------------------------+ |
| Jenkins-Slave(Node) |<----+
| |
+-------------------+ (http) +----------------+ (http) | +--------------+ |
| Gitee(WebService) |<-------->| Jenkins-Master |<-------|---> [Agent]<---->| Docker | |
+-------------------+ (git) +----------------+ | | [TaskScript] | |
| +--------------+ |
| |<----+
+------------------------------------+ |
| (NAS)
+------------------------------------+ |
| Shared-Storage |<----+
+------------------------------------+
```
各组件的含义和主要功能介绍如下:
* `Gitee` 是开放的基于 git 的代码托管 Web 服务。
* `Jenkins-Master` 是我们需要部署的 Jenkins 服务器的主控端。
* `Jenkins-Slave` 是我们需要部署的 Jenkins 流水线的的任务执行端,后文有些地方也描述为 Node,这些都是 Jenkins 系统的设计术语。
* `Agent` 是在 Node 上(自动)部署和运行的代理进程。
* `Docker` 是 Node 上执行具体脚本时需要的容器服务。
* `TaskScript` 是在 Docker 容器内执行的脚本,这些脚本一般都是由 Jenkins-Master 通过 Agent 传递到 Docker 内执行的。
* `Shared-Storage` 是在多个 Node 之间共享的存储。主要用于实现 Node 共享数据。
* `Cache-Storage` 是二进制包缓存存储服务。主要用于为流水线提供一些预编译的组件的二进制包下载能力。
> 提示:
> 1. 从部署形态上看,本套流水线最少需要两台服务器才能全部打通。
> 2. `Cache-Storage` 是可选的。只要没有下载场景可以不配置。
#### 数据流向关系&凭证
下面是本套流水线的数据流向(访问)关系图,通过该图可以大致勾勒出整套系统之前是如何通信的。
```
+------------+ +--------------------------------+
| |---(git-1)--->| |
| |---(http-2)-->| Gitee:Build-Target-Source-Repo |
| |<--(http-3)---| |
| | +--------------------------------+
| |
| Jenkins |---(git-4)--->[ Gitee:Pipeline-Source-Repo ]
| (Pipeline) |
| |---(ssh-5)--->[ Node ]<---(CLI-7)-->[ Docker ]
| |
| |---(http-6)-->[ Cache-Storage ]
+------------+
```
上图中,通信连接中间通过 `(PROTOCOL-ID)` 的方式来描述通信连接。其中,PROTOCOL 表示的是通信的协议。ID 为通信连路的编号,全局不重复。
关于通信协议,有下面几种:
* `git`:即执行 git 命令时用到的协议。实际承载的七层协议可能更多是 htt,但也可能为 SSH。具体哪种主要取决于 git 仓库的 URL 地址的配置。
* `http`:即通过 http(s) 访问服务器,比如 gitee 和 Jenkins 本身都提供 WEB 接口,都需通过 http 协议访问。
* `ssh`:常用于服务器之间的登录访问。目前主要用到 Jenkins-Master 访问 Jenkins-slave 场景下。
* `CLI`:当前仅指,容器环境下,宿主机启动容器时需要传输的环境上下文相关信息。
每个通信连路都需要有相应的凭证才能完成数据的传输(访问)。
下面给出上图中具体的凭证的含义和使用的场景:
| 数据流向 | 所需凭证(名) | 含义 | 通信场景 |
|:---------:|--------------|------|----------|
| `(git-1)` | `JENKINS_CREDENTIAL_GITEE_ACCOUNT` | 使用 git 命令从 gitee 的构建目标代码仓下载代码时的凭证 | 流水线在做构建时,需要通过 git 命令从 gitee 服务器下载构建仓库的代码。此时,需要提供 git 客户端访问 gitee 上的 git 服务端的用户名和密码。 |
| `(http-2)`| `GITEE_API_TOKEN` | 将构建状态和结果投递到 gitee 服务器时,用到的 gitee 用户的私人 token | 在构建过程中或者构建结束时,流水线需要将构建状态和结果发送会 gitee 上的仓库的 MR 中,此时需要使用 gitee 的 HTTP 协议的 API 访问 gitee 服务器,此时需要 gitee 提供的私人 token。|
| `(http-3)`| 无 | gitee 通过 webhook 将仓库状态变更通知给流水线时所需的访问 Jenkins 的凭证 |当代码仓有变更时(比如有人在 MR 上发起评论、有新的 commit 上库等),代码仓需要通过 webhook 的方式,将变更通知到 Jenkins 服务器,以便触发流水线的相关功能(比如,收到 `/self-test` 评论时,需要自动流水线执行一系列构建任务)。 |
| `(git-4)` | `JENKINS_CREDENTIAL_PIPELINE_ACCESS_TOKEN` | 使用 git 命令从 gitee 下载自身代码时的凭证 |这个和 `(git-1)` 的场景类似,只是下载的仓库是流水线仓库自身的代码。但这里单独设置凭证的目的,是考虑到支持流水线的代码并不一定必须放到 gitee 上,也可能是其他的仓库,只要能访问就行。|
| `(ssh-5)` |`JENKINS_CREDENTIAL_ACCESS_NODE` | Jenkins 连接 Node 时用到的访问凭证 | 当 Jenkins-Master 通过 SSH 远程启动 Jenkins-slave(Node) 上的 Agent 时,需要先通过 SSH 链接到 Jenkins-slave 这台机器。此时,需要能登录 Jenkins-slave(Node) 机器的用户名和密码。|
| `(http-6)`|`JENKINS_CREDENTIAL_DATACENTER_ACCOUNT` | 流水线下载缓存服务器时,需要的缓存服务器的访问凭证| 流水线上的构建任务,可能需要从缓存存储服务远程下载预编译的二进制包,此时,需要用到缓存服务的用户名和密码 |
| `(CLI-7)` |无| 流水线构建任务的脚本涉及到的宿主机与容器之间的凭证 | 当流水线通过Docker执行命令时,需要启动容器,此时需要将当前宿主机的账户信息映到容器。这主要包括如下几个部分: `宿主机当前用户名`、`宿主机当前的UID`、`宿主机当前的GID`、`数据映射`、`环境变量`等。 |
## 流水线 IT 系统部署
流水线部署的流程大致是:配置(基础配置)、下载(软件包)、安装(流水线 IT 系统)、导入(流水线和钩子)。
其中,除了第一步必须手工来做外,其他后续步骤都能自动化,但配置数据的好坏会直接影响后续的步骤是否能顺利执行。
一般来说,我们需要三组配置:
1. 基本配置:主要放 Jenkins 服务器自身以及为所有外部组件以及流水线所共享的数据。
2. 保密配置:被基本配置所依赖的一些安全性相关的数据,这部分我们一般不会入库。
3. 流水线配置:描述所有流水线在 web 端的信息(自描述信息),其本身属于流水线的一部分,只是 Jenkins 设计上导致这部分要跟流水线脚本和配置分离。可以理解为流水线本身暴露给 Jenkins 的接口。
注意事项:
> 1. 基本配置和保密配置有强耦合关系,有这两个文件才能完整地部署 Jenkins 服务器,所以这两个文件总是需要位于同一个目录下。
> 2. 小型项目,流水线较简单、或者较少时,流水线配置可以与基本配置合并放在统一文件中。但是,一般我们建议拆分成多个文件与流水线文件放在一起。
假设基本配置文件名叫 `eda2.conf`,保密配置文件叫 `secrets.properties`(具体名字取决于 eda2.conf 中 `SECRETS_FILE` 配置项的值),流水线配置文件叫`pipelines.txt`,而且他们都在当前工作目录下。而流水线 IT 系统安装到当前目录下的子目录 install 下。
那么后续的下载、安装、导入的脚本将是如下:
```
# 将 ./eda2.conf 文件中定义的所有依赖包都下载到本地(./packages/ 目录下)
sh cikit download -conf eda2.conf
# 安装所有的软件(jdk、jenkins、jpm),并导入 Jenkins 的配置
sh cikit install ./install -conf eda2.conf
# 导入流水线定义
install/cikit pipeline:import pipelines.txt
# 导入仓库钩子
install/cikit webhook:setup -conf eda2.conf
```
这个脚本执行完毕后,目录结构大致如下:
```
${PWD}
+-- cikit # 安装脚本
+-- eda2.conf # 基本配置文件
+-- secrets.properties # 安全配置文件
+-- pipelines.txt # 流水线配置文件
+-- packages/ # cikit download 阶段生成的依赖包的存放目录
+-- openjdk-x.y.z_linux-aaaaa_bin.tar.gz # JDK 软件安装包
+-- jenkins.war # Jenkins WEB 服务器的软件包
+-- jenkins-plugin-manager-x.y.z.jar # 插件管理器工具(依赖 JDK 和 Jenkins 包)
+-- install/ # 目标安装目录
+-- cikit # 维护脚本(和外部的 cikit 内容一样,但是不同目录功能不同)
+-- jdk/ # JDK 的安装目录
+-- jenkins/ # Jenkins 的安装目录
+-- jpm/ # JPM 的安装目录
+-- setup.bash # 环境初始化脚本,一般由脚本自动调用
+-- .cikit-installed # 安装完成的占位文件
+-- .cikit-apitoken.txt # 安装过程中生成的 token 文件
```
特别提醒:
> 1. 当 IT 系统正式交付运维时,`secrets.properties` 和 `.cikit-apitoken.txt` 文件需要移走或者直接删除。
### step-1: 配置
以 eda2 为例,在当前工作目录新建配置文件 `eda2.conf` 文件。配置文件内容如下:
#### 基本配置:软件包(PACKAGE)
示例:
```
PACKAGE openjdk/25.0.2 :jdk:aarch64: openjdk-25.0.2_linux-aarch64_bin.tar.gz https://download.java.net/java/GA/jdk25.0.2/b1e0dfa218384cb9959bdcb897162d4e/10/GPL/openjdk-25.0.2_linux-aarch64_bin.tar.gz sha256:671208d205e70c9805da45a483f670d49dd64654990a7b7223ccffb2abb070dd
PACKAGE openjdk/25.0.2 :jdk:x86_64: openjdk-25.0.2_linux-x64_bin.tar.gz https://download.java.net/java/GA/jdk25.0.2/b1e0dfa218384cb9959bdcb897162d4e/10/GPL/openjdk-25.0.2_linux-x64_bin.tar.gz sha256:555ce0821e4fe175ea50d54518cd6fbece9663c1998de529bc6ce429534457df
PACKAGE jenkins/2.555.1 :jenkins:aarch64:x86_64: jenkins.war https://get.jenkins.io/war-stable/2.555.1/jenkins.war sha256:2608ba58c6a56c653f0abf5ac62771949a217437df299dff39083be37cb7bd03
```
PACKAGE 行配置软件部署依赖的三方软件相关的信息。
每行一个包,每个包由用空格分割的 5 个字段组成。
* 第一个字段:软件包的名称和版本。名称和版本之间使用斜杠分割。这两个字段主要起到包识别的作用。
* 第二个字段:软件包的标签。这里主要填写软件包的类型和对应安装的硬件环境。每个标签前后都需要确保有冒号。
- 软件包的类型:目前只支持 `jdk` 和 `jenkins`,分别表示是 java 和 jenkins 两个软件的包。
- 安装环境:需要保持和安装环境的 `arch` 工具查询到的结果一致。目前主要是有 `x86_64` 和 `aarch64` 两种。
* 第三个字段:软件包的名字。这个软件包下载下来之后,存储在本地磁盘的文件名。
* 第四个字段:软件包的下载地址。一般是一个 http 地址。这个地址支持直接通过 `wget` 工具下载。
* 第五个字段:软件包的校验码。校验码由校验码类型和哈希串,用冒号拼接而成。校验码类型当前只支持 sha256。
#### 基本配置:Jenkins 服务监听地址(LISTEN_URL)
示例:
```
LISTEN_URL http://192.168.0.11:9000/eda2
```
这个字段用于定义 jenkins 服务器的地址。以上述例子为例,主要由下面几个部分组成:
* 网络协议:`http`
* 服务器地址:`192.168.0.11`
* 监听端口:`9000`
* 服务前缀:`/eda2`
#### 基本配置:Jenkins 插件(JENKINS_PLUGIN)
示例:
```
JENKINS_PLUGIN hudson-wsclean-plugin:1.0.8
JENKINS_PLUGIN gitee:1292.v2559f2f3f2c0
```
cikit 工具使用的是 Jenkins Plugin Manager 工具来安装的(在 PACKAGE 中有对应的配置,随系统一起自动安装,后面简称 jpm)。
插件由插件名称和版本号用冒号拼接而成。插件的名称和版本需要与 Jenkins 官网上登记的插件的名称和版本号一致。
如果省略版本号部分,cikit 将使用 jpm 的保守策略,也就是自动选用与 jenkins 服务器版本兼容的保守版本。
#### 基本配置:Jenkins 初始化配置(INITIAL_CONF BEGIN ... INITIAL_CONF END)
示例:
```yaml
# Jenkins 基本配置
INITIAL_CONF BEGIN
jenkins:
# 一个提示信息
systemMessage: "Jenkins configured automatically by Jenkins Configuration as Code plugin\n\n"
# 管理员账户
securityRealm:
local:
users:
- id: "${JENKINS_USERNAME}"
password: "${JENKINS_PASSWORD}"
# 认证策略
authorizationStrategy:
loggedInUsersCanDoAnything:
allowAnonymousRead: false
# 监听地址
unclassified:
location:
url: "{{LISTEN_URL}}"
INITIAL_CONF END
```
Jenkins 初始化配置需要用 `INITIAL_CONF BEGIN` 和 `INITIAL_CONF END` 包裹其来。
其配置内容采用 YAML 格式。
Jenkins 初始化配置中可以配置的内容很多,详细的可以配置的内容可以参考 https://plugins.jenkins.io/configuration-as-code/ 。
其中,必须配置的最小的内容,为下面几部分(前面示例就是最小配置):
* 管理员账户
* 认证策略
* 监听地址
* 凭证(可选)
需要注意:
1. 监听地址这里由于与前面的 LISTEN_URL 配置是重复的,所以可以使用 `{{LISTEN_URL}}` 来直接引用 `LISTEN_URL` 配置项的值。
2. 初始化配置文件中,有好多配置项可能涉及密码,所有涉及密码的配置,都可以用 `${xxx}` 的占位符号代替。
而 `xxx` 部分的真实值,可以在后面介绍的专用的保密配置文件中指定。
3. 本仓库下有个 [example-jenkins.yml](example-jenkins.yml) 文件,这个文件中包含了几乎所有可以放到这个 yaml 文件中的内容,我们可以适当作为参考。然而,为了能精简数据维护难度,我们并不需要将每一个配置都放进来,尽可能使用 Jenkins 的缺省值也是合理的且必要的。
#### 基本配置:Jenkins 凭证(JENKINS_CREDENTIAL BEGIN ... JENKINS_CREDENTIAL END)
Jenkins 凭证,有两种配置方式:
* 第一种配置方式是:直接在 Jenkins 初始化配置中的 credentials 根节点下直接定义。
这种方式的配置可以直接参考 Jenkins 的手册。这种方式的优点是,可以利用保密配置文件,将密码相关的信息拆
分出去单独管理。但 Jenkins 的 CasC 插件并不成熟,与其他插件的配合不好,会导致类似 gitee 插件的凭证配
置上去后,状态异常。
* 第二种方式是:使用这里介绍的在 JENKINS_CREDENTIAL BEGIN ... JENKINS_CREDENTIAL END 部分放 XML 格式的凭证配置
这种方式没有 CasC 插件的缺点,但 XML 格式的数据看起来会比较难阅读。
第一种方式可以通过 Jenkins 的文档获得详细的介绍。这里介绍第二种方式。
下面给出示例:
```
JENKINS_CREDENTIAL BEGIN
GLOBAL
JENKINS_CREDENTIAL_ACCESS_NODE
Jenkins 通过该 token 登录节点服务器
zhangsan
P34.Ly%
false
JENKINS_CREDENTIAL END
```
Jenkins 在与周边服务器通信时,通常会用到访问凭证。我们可以在 Jenkins 基本配置中直接指定凭证,可以避免手工通过 WEB 页面配置。
每个凭证由独立的 `JENKINS_CREDENTIAL BEGIN` 和 `JENKINS_CREDENTIAL END` 包裹。其内容为 xml 格式。
Jenkins 支持多种不同格式的凭证。示例中给出的是比较常见的用户名+密码的风格的凭证。
下面给出不同风格凭证的各字段的说明:
##### 用户名+密码凭证
* 样例:
可参考前文的示例。
* 实现类(根节点):
`com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl`
* 插件:
` plugin="credentials"`
* 字段及含义:
| 字段 | 含义 |
|------------------|-----------------------|
| `scope` | [必需] 凭证允许被使用的范围,`GLOBAL` 表示全局可用。 |
| `id` | [必需] 凭证的唯一编号(也可以叫名字),在同一 scope 中需要唯一。 |
| `description` | [可选] 对凭证的描述,一般描述凭证是给谁用的、使用场景等。默认为空。 |
| `username` | [必需] 用户名 |
| `password` | [必需] 密码 |
| `usernameSecret` | [可选] 用户名是否也保密(页面上不显示)。默认值,为 `false` |
##### Gitee API token
* 样例:
```
GLOBAL
GITEE_API_TOKEN
gitee 插件需要的访问 gitee 服务的私人 token
a320fde67b0653eac05ee23dacc0349e
```
* 实现类(根节点):
`com.gitee.jenkins.connection.GiteeApiTokenImpl`
* 插件:
` plugin="gitee"`
* 字段及含义:
| 字段 | 含义 |
|------------------|-----------------------|
| `scope` | [必需] 凭证允许被使用的范围,`GLOBAL` 表示全局可用。 |
| `id` | [必需] 凭证的唯一编号(也可以叫名字),在同一 scope 中需要唯一。 |
| `description` | [可选] 对凭证的描述,一般描述凭证是给谁用的、使用场景等。默认为空。 |
| `apiToken` | [必需] token 串的文本。 |
##### 纯文本凭证
* 样例:
```
GLOBAL
GITHUB_API_TOKEN
访问 github 服务器的私人 token
a320fde67b0653eac05ee23dacc0349e
```
* 实现类(根节点):
`org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl`
* 插件:
` plugin="plain-credentials"`
* 字段及含义:
| 字段 | 含义 |
|------------------|-----------------------|
| `scope` | [必需] 凭证允许被使用的范围,`GLOBAL` 表示全局可用。 |
| `id` | [必需] 凭证的唯一编号(也可以叫名字),在同一 scope 中需要唯一。 |
| `description` | [可选] 对凭证的描述,一般描述凭证是给谁用的、使用场景等。默认为空。 |
| `secret` | [必需] token 串的文本。 |
#### 基本配置:Jenkins 节点(JENKINS_NODE BEGIN ... JENKINS_NODE END)
示例:
```
JENKINS_NODE BEGIN
NODE-11
/home/liugang
1
NORMAL
false
remoting
false
false
JENKINS_NODE END
JENKINS_NODE BEGIN
NODE-20
/home/liugang
1
NORMAL
192.168.0.20
22
JENKINS_CREDENTIAL_ACCESS_NODE
/home/liugang/install/jdk/bin/java
15
3
3
true
/home/liugang
2
KEY
VALUE
KEY2
VALUE2
JENKINS_NODE END
```
基本配置中,放在 `JENKINS_NODE BEGIN` 与 `JENKINS_NODE END` 部分的为节点配置。
各字段含义参考:
* 公共字段:
| 字段名 | 含义&约束 |
|---------------------|----------------------------------|
| `slave` | 根节点名。 |
| `name` | [必需] 节点名 |
| `description` | [可选] 对节点的描述信息。缺省为空 |
| `remoteFS` | [必需] 节点上启动 agent 工作的根目录 |
| `numExecutors` | [必需] 节点上最大可以并行指定多少个任务 |
| `mode` | [可选] 工作模式。当前只支持 `NORMAL` |
| `retentionStrategy` | [必需] - |
| `launcher` | [必需] Jenkins 将以哪种方式来连接/启动 slave |
| `launcher.class` | [必需] 启动方式的实现类,不同实现类,launcher 的子节点不同。 |
| `launcher.plugin` | [必需] 启动方式的实现类所属的插件名 |
| `nodeProperties` | [可选] 节点的自定义属性,这些自定义属性最终会以环境变量的形式传递给流水线的脚本。这部分比较复杂,但是配置样式固定,参照样例配置和即可。 |
| `label` | [可选] 标签列表。多个标签以空格分割 |
* `launcher.class` 为 `hudson.slaves.JNLPLauncher` 时,launcher 的子字段
| 字段名 | 含义&约束 |
|--------------------------|----------------------------------|
| `workDirSettings` | 工作目录设置 |
| `disabled` | 是否禁用 |
| `internalDir` | - |
| `failIfWorkDirIsMissing` | 当工作目录不存在时,是否以启动失败处理 |
| `webSocket` | 是否启用 websocket |
* `launcher.class` 为 `hudson.plugins.sshslaves.SSHLauncher` 时,launcher 的子字段
| 字段名 | 含义&约束 |
|--------------------------|----------------------------------|
| `host` | [必需] 节点主机名或者 IP 地址 |
| `port` | [必需] 节点的 SSH 服务暴露在哪个端口下 |
| `credentialsId` | [必需] 当 Jenkins master 通过 SSH 登录 slave 节点时,应该使用哪个凭证 |
| `javaPath` | [必需] java 程序的完整路径,必须指定到 java 进程 |
| `launchTimeoutSeconds` | [可选] 启动超时时间,单位秒,默认值 15 |
| `maxNumRetries` | [可选] 当启动失败时,最多重试多少次;默认值 `3` |
| `retryWaitTime` | [可选] 当重启失败需要重试时,延迟多少秒后才重试,默认值 `3` |
| `sshHostKeyVerificationStrategy` | [可选] SSH 服务器认证策略 |
| `sshHostKeyVerificationStrategy.class` | [可选] 默认值 `hudson.plugins.sshslaves.verifiers.KnownHostsFileKeyVerificationStrategy` |
| `tcpNoDelay` | [可选] 默认值 `true` |
| `workDir` | [必需] 工作目录 |
#### 基本配置:WEBHOOK 配置(WEBHOOK BEGIN ... WEBHOOK END)
示例:
```
WEBHOOK BEGIN
HOOK_TYPE gitee
HOOK_NAME demoapp:self-test
HOOK_REPO https://gitee.com/libbylg/eda2-demoapp.git
HOOK_EVENTS note
HOOK_URL http://58.34.109.34:18080/eda2/gitee-project/demoapp-self_test
HOOK_TOKEN bd318c3333acbcc15e44f543e7ee0f62
HOOK_ENABLED yes
WEBHOOK END
```
基本配置中,放在 `WEBHOOK BEGIN` 与 `WEBHOOK END` 部分的为 WEBHOOK 的配置。
WEBHOOK 的配置最终会被写入代码仓库。其主要字段含义如下:
| 字段 | 含义&约束 |
|---------------|----------------------------|
| HOOK_TYPE | [必需] 钩子的类型,当前仅支持 `gitee` 。 |
| HOOK_NAME | [必需] 钩子的名字,该名字需要确保对统一仓库来说是唯一的。 |
| HOOK_REPO | [必需] 钩子对应仓库的 git-http 地址。 |
| HOOK_EVENTS | [必需] 仓库中,发生哪些事件时,才触发本钩子。当前支持 `push`,`tag`,`issue`,`pr`,`note` 这几种。|
| HOOK_URL | [必需] 当钩子被激发时,所回调的 http 请求的 URL。 |
| HOOK_TOKEN | [必需] 当钩子被激发时,回调请求应携带的 token。该 token 通常由 jenkins 生成。|
| HOOK_ENABLED | [可选] 钩子是否启用。默认启用。 |
#### 流水线(定义)配置(PIPELINE BEGIN ... PIPELINE END)
流水线定义主要放流水线在 Jenkins 的 web 页面上的一些配置。主要包括,流水线的来源、Trigger、环境变量等信息。
示例:
```
PIPELINE BEGIN demoapp-self_test
demoapp-self_test
false
false
false
false
false
0
false
false
false
true
^/self-test$
NONE
false
true
true
true
true
All
bd318c3333acbcc15e44f543e7ee0f62
false
false
false
JENKINS_PARAM_CI_NAME
eda2-pipelines
流水线自身仓的仓库名
https://gitee.com/libbylg/eda2-pipelines.git
JENKINS_CREDENTIAL_PIPELINE_ACCESS_TOKEN
*/master
demoapp/pipeline_pr/pr_gate_self_test.groovy
true
PIPELINE END
```
流水线定义的相关配置可以放在基本配置文件中,也可以放在独立的文件中。
每条流水线的定义都被包裹在 `PIPELINE BEGIN` 至 `PIPELINE END` 的范围内。
其内容为 xml 格式。
流水线定义主要由 `定义` 和 `属性` 两部分组成。早期版本的 Jenkins 可能结构跟复杂,我们这里不考虑。
从总体来看,流水线定义的 xml 配置结构大概是下面这样:
```
xxxx
...
...
...
```
其中:
| 字段 | 含义&约束 |
|-------------------|-----------------------------|
| `flow-definition` | 流水线定义的根节点。 |
| `description` | 流水线的名字,这个就是在 web 页面上看到名字。 |
| `definition` | 流水线定义详情,这里面承载了流水的主要数据。 |
| `properties` | 流水线的属性列表。这里属性表主要包括 Trigger 和 Parameter。其中, Parameter 用于参数化流水线场景。 |
| `org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty` | 流水线的 Trigger 配置。 |
| `hudson.model.ParametersDefinitionProperty` | 流水线的 Parameter 配置。 |
| `definition.class` | 流水线定义的(Java)类型,采用不同方式,类型不同,当前我们用的是 `org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition`,表示采用 groovy 脚本来定义流水线的发方式 |
| `definition.plugin` | 流水线定义类所属的插件。`org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition` 属于插件 `workflow-cps` |
* `definition` 下的字段
这部分的主要的字段依赖于 `definition/scm.class` 的值。
作为最佳实践,我们期望流水线跟代码一样能够纳入代码仓库管理,有版本控制。
因此,这里我们只考虑 `definition/scm.class` 为 `hudson.plugins.git.GitSCM` 的场景。
| 字段 | 含义&约束 |
|-------------------------------|-----------------------------|
| `scm.class` | [必需] 如果采用将流水线代码托管到 git 代码仓模式时,必须为 `hudson.plugins.git.GitSCM` |
| `scm.plugin` | [必需] 如果采用将流水线代码托管到 git 代码仓模式时,必须为 `git` |
| `userRemoteConfigs` | [必需] 托管仓库的相关信息 |
| `hudson.plugins.git.UserRemoteConfig` | [必需] 同 `userRemoteConfigs` |
| `url` | [必需] 托管流水线自身代码的 git 仓库的地址 |
| `credentialsId` | [必需] 访问 `url` 执行仓库的凭证 |
| `branches` | [必需] 托管流水线自身代码所在的分支信息 |
| `hudson.plugins.git.BranchSpec` | [必需] 分支规格 |
| `name` | [必需] 分支名或者模式 |
| `scriptPath` | [必需] 流水线脚本(如 Jenkinsfile )在仓库中的路径 |
| `lightweight` | [可选] Jenkins 获取流水线自身代码时,是否采用轻量级 clone(只获取指定文件),默认值 `true` |
* `properties` 下的字段: 触发器(Trigger)
| 字段 | 含义&约束 |
|-------------------------------|-----------------------------|
| `flow-definition/org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty/` | Trigger 的根节点 |
| `triggers` | [必须] 触发器可以有多个,统一放到这个节点下 |
| `com.gitee.jenkins.trigger.GiteePushTrigger` | [可选] 指 gitee 插件提供的触发器,由 gitee 插件提供;如果是 github 或者 gitlab 等其他的触发器,这个类型会不同 |
| `triggerOnPush` | [可选] 当MR发生 push 事件时,触发流水线,默认值 `false` |
| `triggerOnCommitComment` | [可选] 当 MR 发生有 commit 变更时,触发流水线,默认值 `false` |
| `triggerOnOpenPullRequest` | [可选] 新建 MR 时,触发流水线,默认值 `false` |
| `triggerOnPipelineEvent` | [可选] gitee 自带流水线事件发生时,触发流水线,默认值 `false` |
| `triggerOnAcceptedPullRequest` | [可选] 当 MR 被合入时,触发流水线,默认值 `false` |
| `triggerOnUpdatePullRequest` | [可选] 当 MR 更新时(比如,改流水线标题或者内容),触发流水线,默认值 `false` |
| `triggerOnClosedPullRequest` | [可选] 当 MR 关闭时,触发流水线,默认值 `false` |
| `triggerOnApprovedPullRequest` | [可选] 当 MR 被同意合入时(还未被合入),触发流水线,默认值 `false` |
| `triggerOnTestedPullRequest` | [可选] 当 MR 的 webhook 测试被触发时,触发流水线,默认值 `false` |
| `triggerOnNoteRequest` | [可选] 当 MR 上有新增评论时,触发流水线,默认值 `false` |
| `noteRegex` | [可选] 当 `triggerOnNoteRequest` 未 `true` 时,还可以在本字段指定一个正则表达式,只有评论内容满足该正则表达式时,才触发流水线,默认值为空 |
| `secretToken` | [可选] 指定一个安全 token,该 token 同样需要设置到代码仓库的 webhook 中。主要起到 webhook 身份识别的作用。避免不相关的仓库的错误设置,触发了流水线。可同步参考下 `基本配置:WEBHOOK 配置(WEBHOOK BEGIN ... WEBHOOK END)` 章节的配置说明。 |
* `properties` 下的字段: 流水线参数(Parameter)
如下是单个参数的定义:
```
JENKINS_PARAM_CI_NAME
eda2-pipelines
流水线自身仓的仓库名
```
其中,这个定义的外层(比如,`hudson.model.StringParameterDefinition`)参数的数据的类型,期内基本是固定式。
而 Jenkins 当前支持的数据类型还有下面这些:
| 类型 | 解释 |
|------|------|
| `hudson.model.StringParameterDefinition` | 字符串类型,算是最常用的类型。在配置时会显示一个单行文本框 |
| `hudson.model.BooleanParameterDefinition` | 布尔(二值)类型,只支持 `true`、`false` 两个值 |
| `hudson.model.ChoiceParameterDefinition` | 多选一类型,在配置时会弹出一个下拉框 |
| `hudson.model.TextParameterDefinition` | 支持多行文本的类型 |
| `hudson.model.FileParameterDefinition` | 文件的类型,配置时允许指定一个文件路径 |
| `hudson.model.PasswordParameterDefinition` | 密码类型,配置时显示一个文本框,但是输入类型被掩码覆盖,常用于输入密码 |
其他字段定义:
| 字段 | 含义&约束 |
|--------|-----------------------------|
| `name` | [必需] 参数名字 |
| `defaultValue` | [必需] 参数的缺省值,在配置时默认会显示该值 |
| `description` | [必需] 参数的描述,在配置时会成为配置的提示标题 |
> 需要注意:
> 1. `PIPELINE BEGIN` 后面需要带上流水线的名字,这个名字会成为 jenkins 上能看到的流水线名称;
> 2. 节点的标签需要与流水线搭配,多个标签使用空格分隔;
> 3. 流水线定义配置的内容可以与基本配置合并到同一个文件中,也可以拆分出独立文件。但考虑到流水线定义配置与流水线自身脚本的强耦合关系,建议还是拆分到独立文件中去更合理。推荐每条流水线单独一份配置文件。
#### 保密配置
保密配置文件就是一个标准的 Java-Properties 文件格式,但是为了方便 shell 脚本读取数据,只支持 key=value 的语法形式。
示例如下:
```
# Jenkins CLI 等工具访问 Jenkins 时的用户名和密码
JENKINS_USERNAME=admin
JENKINS_PASSWORD=yyyy0123.com
# git 访问 Gitee 时的用户名和密码
GITEE_USERNAME=libbylg
GITEE_PASSWORD=yyyy0123.com
```
保密配置文件中并没有固定的内容,主要是将基本配置中的一些涉及保密内容的信息提取到独立的文件中管理。
以上述示例很显然格式足够简单,不需要过多赘述。
几个注意事项:
> 1. 保密配置文件需要通基本配置文件位于同一个目录下。
> 2. 保密配置中的 key 尽量不要出现一些奇怪的字符,避免 Jenkins 或者其他工具识别错误。推荐 key 符合正则表达式 `[a-zA-Z][a-zA-Z0-9_.]`。
> 3. 保密配置文件并非一致要放在磁盘固定位置。这个配置文件仅在安装和重启时才需要。
### step-2: 下载
示例如下:
```
./cikit download -conf eda2.conf
```
上述命令执行完毕后,所有的包都将安装到 `eda2.conf` 同目录下的 `packages/` 目录下。
如果 `-conf` 未在命令行上提供,脚本将尝试从 `${CI_CONFIG_FILE}` 环境变量获取配置文件名。
### step-3: 安装
示例:
```
./cikit install ${HOME}/install -conf eda2.conf
```
上述命令将自动将必要的软件安装到 `${HOME}/install` 目录下。
`install` 子命令的后的参数为安装路径,该字段必须指定。
和 `download` 子命令类似,如果未指定 `-conf` 参数,脚本将自动从 `${CI_CONFIG_FILE}` 环境变量获取配置文件名。
> 需要注意:如果修改了 eda2.conf 文件,需要重新执行 install 命令才会生效
### step-4: 导入:流水线
为简化后续的流水线创建和理解,这里需要引入 Jenkins 上流水线的基本概念:
```
流水线=流水线定义+流水线脚本
```
其中:
* 流水线定义:用于承载流水线在 Jenkins 服务器上 WEB 页面上的信息。
* 流水线脚本:用于承载流水线运行阶段的信息,比如,分哪些阶段,跑哪些任务等等。
流水线定义 和 流水线脚本 是互相配合的关系,二者缺一不可。
导入流水线的命令主要是将流水线定义导入到 Jenkins 服务器。这个过程等价于在页面上手工创建流水线。
下面以 demoapp-self_test 流水线为例来说明具体操作方式:
```
cd ${HOME}/install
./cikit ppl:import ./pipelines/pr_gate_self_test/pr_gate_self_test.txt
```
导入成功后,可以到 jenkins 的 web 页面上看到流水线详情。
### step-5: 导入:钩子(webhook)
示例:
```
cd ${HOME}/install
./cikit webhook:setup -conf ../eda2-ci/eda2.conf
```
安装成功后,可以到 gitee 上看到对应的钩子信息。
### step-6:某些不能自动化的操作
cikit 工具能够很大程度上简化整个流水线工程系统的部署。
但是,受限于 Jenkins 自身及其部分插件的缺陷,我们仍然不能百分百实现自动化。
现将当前已知不能自动化的操作列出来:
1. Gitee 的全局链接需要手工重新配置
Gitee 的全局连接会被流水线用于构建状态回传。
但是,Gitee 的全局连接依赖于 Gitee 的 API-TOKEN。
然而,由于 gitee 插件的 bug,无法使用 jcasc 的方式去配置 Gitee 的 API-TOKEN。
这导致我们采用 jcasc 这种自动化配置方式时,流水线工程系统安装之后,Jenkins 系统配置
中,关于 Gitee 的全局链接所关联的凭证会标红。但连接测试是可以正常工作的。
经过尝试这个问题的解决办法就是将 Gitee 的全局连接及其凭证重新手工配置一遍。
2. Agent 重启时,可能会出席无法重启的问题
这个问题的现象是,当 Jenkins Master 停止时间过程时,Jenkins Slave 上的 Agent 进程会自动退出。
或者因为某些问题 Agent 连不上。一般而言,Jenkins Master 在感知到 Agent 离线后,会尝试重新启动 Agent。此时,SSH Agent 插件需要重新下载 Agent 程序(也就是 remoting.jar 包)。
然而,只要 Agent 曾经运行过,Agent 所在的环境下就已经有一个 remoting.jar 包了。
由于 SSH Agent 插件的 bug,这个现有的 remoting.jar 包无法删除。
最终导致 SSH Agent 下载 remoting.jar 失败,从而表现为 Jenkins Master 重启 Node(Agent) 总是失败。
而解决这个问题的办法就是:由于 Agent 并不会经常需要重启,所以简单的办法就是手工删除 ${HOME}/remoting.jar 文件。
### step-7: 依赖的安装
#### 流水线构建环境结构
如下:
```
文件/目录结构 映射关系 主要作用
------------------------------ --------- --------------------------------------------------
${HOME}/ MAT # 普通用户目录
+-- demoapp.env -AT # demoapp 的构建环境初始化脚本
+-- devrepo/ -AT # 构建依赖(工具)安装位置
+-- pkgrepo/ -A- # 构建依赖(工具)软件包及安装脚本存放位置
+-- swprepo/ -A- # 构建依赖(工具)安装过程中的过程数据交换目录
+-- workspace/ -AT # 流水线任务工作的根目录
+-- pipeline-swap/ -AT # 跨流水线中间数据交换目录
+-- pipeline-publish/ -AT # 流水线构建产物归档目录
+-- install/ MA- # Agent 依赖的工具(主要是Java)的安装目录
+-- cikit --- # cikit 工具(脚本)
+-- jdk/ --- # Java 的安装目录
+-- jenkins/ --- # Jenkins 的安装目录
+-- jpm/ --- # Jenkins 的插件管理器
```
* `M` Jenkins Master 侧
* `A` Jenkins Agent 侧
* `T` Jenkins Task 侧(容器内)
* `-` 无意义,用作格式对齐
流水线环境主要是指构建任务的运行环境。流水线环境的设计不依赖于具体的部署环境,能提供最大的部署会环境适应能力。
总结来说,流水线环境可以有三种部署形态,可以根据实际项目情况选择:
* 形态1:整个流水线环境被直接做成容器镜像,后续维护者可以根据需要自行写 Dockerfile 容器化;
* 形态2:流水线环境可以先安装到 Jankins-slave(Node) 宿主机,然后在在容器启动时,再映射到容器内;
* 形态3:直接安装到 Jankins-slave(Node) 宿主机上;
另外,流水线环境是同时兼容 Jenkins-Master 和 Jenkins-Slave 环境的,所以如果只有单机,将 Jenkins-Master 所在的机器注册为一个 Node 也是可行。
#### 依赖定义
示例:
```
#Name/Version Slices Usages Reason
make/4.3 :dbx:wsl:dba: :-:b:q:d: #
binutils/2.44 :dbx:wsl:dba: :-:b:q:d: #Needed by GCC-BASIC
gmp/6.3.0 :dbx:wsl:dba: :-:b:q:d: #Needed by GCC-BASIC
isl/0.24 :dbx:wsl:dba: :-:b:q:d: #Needed by GCC-BASIC
mpfr/4.2.2 :dbx:wsl:dba: :-:b:q:d: #Needed by GCC-BASIC
mpc/1.2.1 :dbx:wsl:dba: :-:b:q:d: #Needed by GCC-BASIC
gcc/10.3.0 :dbx:wsl:dba: :-:b:q:d: #
```
这个是所有所有依赖的定义表,分为 4 列:
| 列名 | 含义 |
|----------------|-------|
| `Name/Version` | [必需] 软件名和版本号,软件名和版本号的组合需要在依赖包管理仓库中找到。 |
| `Slices` | [必需] 环境切片,每个切片用一个简短的名字表示,比如,用 `dev` 表示开发环境,用 `ci` 表示流水线环境,用 `arm` 表示 ARM 环境等。一个软件可以被安装到多个环境切片中,但每个切片的名字前后必须保证都有冒号(`:`)分隔符。需要注意,切片名最好不要只有一位字母避免与 Usages 产生冲突 |
| `Usages` | [必需] 软件用途,这里是几种标准的值:`b` 表示用于支撑软件编译构建打包、`q` 表示用于支撑质量检查类、`d` 表示开发阶段才会使用的软件 |
| `Reason` | [可选] 一个单行文本,用于注明软件引入的原因或者其他任何有价值的信息 |
#### 安装过程和伪自动化脚本
这部分需要采用半自动化处理,但总体而言自动化程度较高,只要确定好几个仓库,是完全可以脚本化的。
下面,以将所有在 `${HOME}/demoapp.conf` 中定义的依赖软件安装到当前目录 ${HOME} 为例(前面安装的 install 也在同一目录下),以伪自动化脚本的形式给出整个安装流程:
```
# 将依赖包管理仓库下载到 pkgrepo 目录下(pkgrepo 可考虑以 )
git clone pkgrepo
# 创建依赖软件/包的安装目录:
mkdir devrepo
# 设置三个关键环境变量:
export PKGREPO="${HOME}/pkgrepo"
export DEFREPO="${HOME}/devrepo"
export SWPREPO="${HOME}/swprepo"
# 准备 pkgrepo
cd ${HOME}/pkgrepo && ./pkgr prepare
# 初始化 devrepo
cd ${HOME}/devrepo && ./pkgr init "${DEFREPO}"
# 下载 demoapp.conf 中定义的 dbx 切片(根据你需要需要决定切片名)的所有依赖包到 pkgrepo 下
# 下载只需要成功执行一次即可。
cd ${HOME}/pkgrepo && ./pkgr download ${HOME}/demoapp.conf:dbx"
# 安装 demoapp.conf 中定义的 dbx 切片(根据你需要需要决定切片名)的所有依赖包到 devrepo 下
cd ${HOME}/pkgrepo && ./pkgr download ${HOME}/demoapp.conf:dbx"
# 生成环境初始化脚本(可以映射到容器,demoapp.env 名称根据需要命名,一般跟项目名匹配,后缀也推荐用例 .env)
rm -rf "${HOME}/demoapp.env"
echo 'export PKGREPO=${HOME}/pkgrepo' >> "${HOME}/demoapp.env"
echo 'export DEVREPO=${HOME}/devrepo' >> "${HOME}/demoapp.env"
echo 'export SWPREPO=${HOME}/swprepo' >> "${HOME}/demoapp.env"
echo 'source "${DEVREPO}/init.bash" "$DEVREPO/demoapp.conf@dbx"' >> "${HOME}/demoapp.env"
# 安装 pip 依赖(具体依赖主要看流水线和项目本身的需求)
source "${HOME}/demoapp.env"
pip3 install
```
## 附:用 cikit 对 Jenkins 服务器做例行维护
Jenkins 的维护指令主要有下面这些:
```
cikit status
cikit start [-conf CONFIG_FILE]
cikit stop [--force]
cikit kill
```
示例:
* 启动服务器(需要指定初始配置):
```
cd ${HOME}/install
./cikit start -conf ../eda2-ci/eda2.conf
```
Jenkins 采用 CasC 机制来管理配置 jenkins 自身缓存的配置停机后失效,所以启动时需要指定初始配置。
* 停止服务器:
```
cd ${HOME}/install
./cikit stop
```
停止服务命令发起后,如果还有流水线正在执行或者还有插件正在安装或者升级,会优先等待这些长时间运行的任务结束后才会停止。
如果不想等待,可以带 `--force` 参数。
* 杀停服务:
```
cd ${HOME}/install
./cikit kill
```
杀停服务(kill)与停止服务(stop)最大的区别是:停止服务会尽量考虑可靠性,避免数据毁坏。但是杀停服务不会考虑这些。
虽然大多数时候二者没有区别,但保不齐还会损坏数据。
* 查看服务器运行状态:
```
cd ${HOME}/install
./cikit status
```
服务器状态,主要分运行状态和链接状态。两个状态都正常,才能表明服务器工作正常。
下面是个服务器运行状态正常的示例:
```
[liugang@k8s-master install]$ sh cikit status
------------------------------------------------
STATUS: Running
liugang 38246 1 0 10:51 pts/2 00:00:34 /home/liugang/install/jdk/bin/java -Djenkins.install.runSetupWizard=false -jar /home/liugang/install/jenkins/jenkins.war --httpListenAddress=192.168.0.11 --httpPort=9000 --prefix=/eda2
------------------------------------------------
STATUS: Connection-OK
{"_class":"hudson.security.WhoAmI","anonymous":true,"authenticated":true,"authorities":["anonymous"],"name":"anonymous"}
```