CI/CD 工具链的分分合合

作案动机

一种对 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_stepExecute 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 Commandstart 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 Commandremove 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 来共享

相关

comments powered by Disqus