作案动机
一种对 ci/cd 工具的轻量化和解耦的尝试?
Jenkins 的传统集群方式,是使用不同环境的服务器构成不同能力的 Jenkins 节点,由主节点根据任务
环节的需要,调度不同能力的子节点来完成构建或部署任务。
进入容器云时代,情况发生了变化,我们可以使用不同能力的 Jenkins 镜像,使用 Kubernetes 插件来
完成这种任务的拆分和调度,为此,我构建了一个包含所有我们平时用到的工具的 Jenkins 镜像,简化了
节点的扩展和选择过程。
然而随着学习和应用的深入,我意识到这种做法有几个问题:
-
DevOps 中隐含着发挥个人能力的愿景,工具链的所谓大而全,只不过是在画一个比较大的圈,使用这样的一套 Jenkins,还是要被其中所包含的仅有的工具中进行选择,对身陷其中的技术人员绝不能说是友好,也绝不是鼓励各展所长的态度。
-
现有的功能测试、接口测试、压力测试等工具,越来越专业化,往往会有各自的工作集群调度甚至是托管方案,例如 selenium grid、JMeter 集群等。
-
同样的测试工作,可能有多种工具都可以完成,例如一个 Restful 的接口测试,不管是 JMeter 还是Postman,或者 SoapUI 以及五花八门的自有工具,都可以完成这样的工作。
-
各种 DevOps 以及微服务管控和治理平台会有各自的工具链构成以及扩展方案。
-
让各种工具自成镜像,无疑对镜像尺寸和更新速度都会有更好的支持。
这样就让我产生一个新的思路:能不能让 Jenkins 回归到一个原始状态,只负责问题的定义、任务的分发和结果的归集呢?于是就有了这样的一点尝试。
这一尝试的思路是,Jenkins 镜像/容器只使用插件和一些 Shell 脚本,同外部的调度能力(例如
Docker 的容器或者 Kubernetes 的任务等)进行交互,利用网络和共享存储,来实现任务的分发和协调
以及最后的结果汇聚。
下面以 JMeter 为例,进行一个简单的压力测试,测试环境为了节省起见,使用的是 Docker 加本地目录
共享的方式,这种方式也可以很方便的扩展为 Kubernetes 的 Job + PV/PVC 方式。
镜像
-
Jenkins: dustise/jenkins
-
JMeter: hauptmedia/jmeter
注意此处随意的选择了一个 2.x 版本的 JMeter。
Jenkins 插件
-
docker_build_step
:用于执行 Docker 指令,类似功能的 Docker Common 插件,其参数不支持环境变量,因此淘汰。 -
Performance Plugin:一个用于搜集和展示多种测试结果报告的插件。
存储
这里我们需要为 Jenkins 和各种工具(这里是 JMeter)提供一个可以共享访问的文件交换区域:
-
单机 Docker 下,可以是共享的主机目录。
-
Kubernetes 环境可以使用共享的分布式存储卷。
例如我们创建名为/var/cicd/exchange/jmeter
的目录,进行文件交换。
输入
使用 JMeter GUI 录制 jmx 文件,为任务生成汇总日志(保存到/root/summary.log)。
将 jmx 上传到上面所说的目录之中,这样就保证了 Docker 和 Jenkins 都能通过 -v
来进行加载。
这里我们启动 Jenkins 的时候,使用 -v /var/cicd/exchange/jmeter:/exchange/jmeter
参数让 Jenkins 加载这一目录。
Jenkins 任务
准备工作
首先的环节是,为后面要开工的 JMeter 准备工作环境。
# 创建当前 Build 目录
mkdir -p /exchange/jmeter/$BUID_TAG
# 复制 jmx 到当前 Build 目录
cp /exchange/jmeter/jd.jmx /exchange/jmeter/$BUID_TAG
设置容器
准备好文件之后,我们需要添加下一个环节就是设置一个容器:
这里添加的环节是docker_build_step
的Execute Docker Command
环节。
-
Image name: hauptmedia/jmeter
-
Command:bin/jmeter -n -t /root/jd2.jmx -l /root/result.log -j /root/process.log
-
Bind Mounts:
/var/cicd/kube/$BUILD_TAG /root
注意绑定卷这里就使用了 $BUILD_TAG 变量
这样就完成了 Jenkins 和 JMeter 的文件共享:
Jenkins 的 /exchange/jmeter/$BUILD_TAG
,对应的是新创建容器的/root
目录。
启动容器
使用Execute Docker Command
的start container(s)
环节,Container ID(s)
填写变量
$DOCKER_CONTAINER_IDS
,代表启动我们刚才创建的容器。
等待任务
如果我们需要使用 Jenkins 进行结果的汇聚,那么这里就需要进行阻塞——等到 JMeter 执行完毕后,
才能进行下面的搜集结果、清理现场等操作。
RESULT="/exchange/jmeter/$BUILD_TAG/summary.log"
while [ ! -f $RESULT ]
do
sleep 30
done
while [[ `lsof | grep summary.log` ]]
do
sleep 10
done
上面的脚本利用 lsof 来检测 summary.log 的占用情况,一旦该文件关闭,说明压力测试已经结束。
清理容器
使用 Execute Docker Command
的 remove container(s)
环节,Container ID(s)
填写变量
$DOCKER_CONTAINER_IDS
,代表清理我们刚才创建的容器。
结果展示
Performance Plugin 能够识别 JMeter、SoapUI 以及 Parrot 生成的报告文件,这里我们只设置一
个选项,就是 Source data files (autodetects format):
,这里填写
/exchange/jmeter/$BUILD_TAG/summary.log
。
插件会把这一文件拷贝到 Workspace,进行解析和显示。
Kubernetes Job
上述过程可以很方便的改造成为 Kubernetes Job:
-
Docker 相关内容,改为使用 kubectl 进行的 job 文件的生成和操作。
-
阻塞过程可以查询任务节点的状态
-
文件存储可以使用 PVC 来共享
文章来源于互联网:CI/CD 工具链的分分合合