前言

现在各种业务都追求上云,通俗的讲,“XX即服务”,作为一名专职的性能测试调优人员的我,由于会点三脚猫的开发功夫,“性能测试即服务”这种开发大任就落到我头上了,先做一个能完成核心压测功能的基础版。

本着不再重复造轮子的思想,充分利用已有的优秀软件,性能测试要想上云的话,当然封装jmeter是不二选择,但是大佬的硬性要求是,所有要提供的交付物,必须是docker镜像。

jmeter貌似没有官方镜像啊。。有好多个人做的,也没啥详细介绍,算了,还是自己做吧。

最终,需要实现前端传入用户输入的压测相关配置,后端jmeter docker镜像+.netcore测试服务镜像,测试服务镜像调用jmeter镜像进行测试,然后分析生成的结果,生成报告。

本文只涉及后端,如果你是用的Jenkins镜像+jmeter镜像做的持续集成相关,可能也会遇见跟我一样的jmeter容器调用问题。

过程及一些坑

目前后端用.netcore开发的性能测试服务基本已经完成了,可以实现前端传入一个规定格式的json,后端解析,自动在某个路径下生成jmx脚本。

现在需要的就是,在我们的测试服务容器里,调用另一个容器里的jmeter,对当前路径下的脚本测试,生成结果。

创建一个jmeter docker镜像

首先下载linux版本的jmeter,推荐下载binary版的,拷贝完直接可用,source版部署麻烦不说,还可能有找不到jar的坑。

然后就是Dockerfile,由于容器只能读取容器内的文件,脚本肯定是在容器外的,所以需要把jmeter相关的脚本、结果、报告等路径挂载出来,这个根据自己的实际需求就行。

Dockerfile:

FROM java:8
ENV http_proxy ""
ENV https_proxy ""
RUN mkdir /jmeterspace
RUN mkdir -p /jmeterspace/test
RUN mkdir -p /jmeterspace/test/input/jmx
RUN mkdir -p /jmeterspace/test/input/testdata
RUN mkdir -p /jmeterspace/test/report/html
RUN mkdir -p /jmeterspace/test/report/jtl
RUN mkdir -p /jmeterspace/test/report/outputdata
RUN chmod -R 777 /jmeterspace
ENV JMETER_VERSION=5.1.1
ENV JMETER_HOME=/jmeterspace/apache-jmeter-${JMETER_VERSION}
ENV JMETER_PATH=${JMETER_HOME}/bin:${PATH}
ENV PATH=${JMETER_HOME}/bin:${PATH}
COPY apache-jmeter-5.1.1.tgz /jmeterspace
RUN cd /jmeterspace\
&& tar xvf apache-jmeter-5.1.1.tgz \
&& rm apache-jmeter-5.1.1.tgz

分的有点细,其实可能用不了这么多路径。

然后 docker build -t invokerr/jmeter . 生成镜像。接下来是运行了,把路径挂载出来:

docker run --name="jmeter" -v /jmeterspace/test/input/jmx:/jmeterspace/test/input/jmx \
-v /jmeterspace/test/input/testdata:/jmeterspace/test/input/testdata \
-v /jmeterspace/test/report/html:/jmeterspace/test/report/html \
-v /jmeterspace/test/report/jtl:/jmeterspace/test/report/jtl \
-v /jmeterspace/test/report/outputputdata:/jmeterspace/test/report/outputdata \
-v /etc/localtime:/etc/localtime \
-it -d invokerr/jmeter

运行起来后,docker exec -it jmeter /bin/bash 进入容器,jmeter -v 如果出来jmeter相关的信息,就表示ok了。当然如果需要运行脚本的话,目前是需要像这样进入容器后用指令运行的。

至此,jmeter镜像成功。

创建一个.net core docker镜像

这个网上有好多说明,而且官方也提供了docker支持。但是微软默认生成的Dockerfile是拷贝源码-编译-发布,感觉有点多余,而且我试过,这么做有时候会报nuget路径错误,所以还是发布出来自己搞吧。

首先发布工程,选择发布成linux64版本,基于框架,然后咱们就得到了基于.net core运行时环境的linux版本,在安装.net core sdk的linux上可直接运行。

接下来,基于.net core runtime镜像,制作我们自己的镜像,为了保证我们生成的脚本可以让jmeter容器读取到,这里挂载到了与jmeter镜像相同的路径:

FROM microsoft/dotnet:2.1-aspnetcore-runtime
RUN mkdir -p /jmeterspace
RUN mkdir -p /jmeterspace/test
RUN mkdir -p /jmeterspace/test/input/jmx
RUN mkdir -p /jmeterspace/test/input/testdata
RUN mkdir -p /jmeterspace/test/report/html
RUN mkdir -p /jmeterspace/test/report/jtl
RUN mkdir -p /jmeterspace/test/report/outputdata
RUN chmod -R 777 /jmeterspace
WORKDIR /app
COPY ApiPerftestService /app/
EXPOSE 5000
WORKDIR /app
ENTRYPOINT ["dotnet", "ApiPerftestService.dll"]

然后 docker build -t invokerr/apiperftestservice . 生成镜像。

运行:

docker run --name="perftest" -p 5000:5000 -v /jmeterspace:/jmeterspace\
-v /jmeterspace/test:/jmeterspace/test \
-v /jmeterspace/test/input/jmx:/jmeterspace/test/input/jmx \
-v /jmeterspace/test/input/testdata:/jmeterspace/test/input/testdata \
-v /jmeterspace/test/report/html:/jmeterspace/test/report/html \
-v /jmeterspace/test/report/jtl:/jmeterspace/test/report/jtl \
-v /jmeterspace/test/report/outputdata:/jmeterspace/test/report/outputdata \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/bin/docker:/usr/bin/docker \
-v /usr/lib64/libltdl.so.7:/usr/lib/x86_64-linux-gnu/libltdl.so.7 \
-v /etc/localtime:/etc/localtime \
-d invokerr/apiperftestservice

注意几行加粗的,下文会提及。目前这个容器已经成功运行了,并且可以完成在指定路径下生成脚本。

容器互相调用及一些问题

接下来,就是容器互相调用了,测试服务容器需要调用jmeter容器实现压测。

要想完成调用,最简单的办法,就是docker in docker的方式,即在某个容器里,可以访问宿主机的其他容器。

这就需要在启动容器的时候,把宿主机的docker及docker.sock文件挂载进容器,也就是

-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/bin/docker:/usr/bin/docker \

这两句,路径根据自己的实际情况,一般linux文件就是这俩路径。

坑1:找不到文件libltdl.so.7。

不知道为啥会有这种问题,简单粗暴的方式就是缺什么补什么,从宿主机挂载,-v /usr/lib64/libltdl.so.7:/usr/lib/x86_64-linux-gnu/libltdl.so.7

坑2:容器能访问了,怎么调用另一个容器

如果是能docker run方式调用还好,现在是需要进入容器,然后调用,如果用docker exec&&jmeter&&exit这种方式,连续发三个命令,你会发现这三个指令都是针对当前容器的,是没法用第一个指令进入jmeter容器,然后在jmeter的容器中执行第二个jmeter指令的。

既然这样,得用一个指令一步到位啊,进入容器后直接执行指令,docker是有相关语法的,在/bin/bash后直接跟命令:

docker exec -it jmeter /bin/bash jmeter -n -t {filePath.scriptPath}{Path.DirectorySeparatorChar}script.jmx -l {filePath.resultPath}{Path.DirectorySeparatorChar}result.jtl

坑3:找不到ApacheJmeter.jar

用上述命令执行的时候,报了个找不到ApacheJmeter.jar的问题,真是奇怪,在jmeter容器中执行压测命令是没问题的,不知道为啥会有这个问题,猜测可能是执行的时候,jmeter path相关的锅。

灵机一动,我直接写死执行文件不就行了,于是命令就成了这样:

docker exec -it jmeter /bin/bash /jmeterspace/apache-jmeter-5.1.1/bin/jmeter -n -t {filePath.scriptPath}{Path.DirectorySeparatorChar}script.jmx -l {filePath.resultPath}{Path.DirectorySeparatorChar}result.jtl

直接把jmeter执行路径写上,果然解决了问题。

坑4:the input device is not a TTY

为了方便,以上的执行我都是在linux环境下直接用dotnet启动的测试服务,然后测试服务里调用的jmeter命令,并没有将测试服务打包到容器里调用,我认为应该是一样的。

结果打脸了,同样的应用程序,在linux下用dotnet启动后直接调用jmeter指令没问题,但是打包成镜像,在容器里调用,就报了the input device is not a TTY。

搜了下,说是-it 的问题,去掉 -it ,把指令改成:

docker exec jmeter /bin/bash /jmeterspace/apache-jmeter-5.1.1/bin/jmeter -n -t {filePath.scriptPath}{Path.DirectorySeparatorChar}script.jmx -l {filePath.resultPath}{Path.DirectorySeparatorChar}result.jtl

果然解决了问题。

最后,附上.netcore 中调用jmeter容器的代码吧,执行压测并读取控制台log:

public static string RunJmeterInDocker(FilePath filePath)
{
  string result = $"result path:{filePath.resultPath},log:";
  //创建一个ProcessStartInfo对象 使用系统shell 指定命令和参数 设置标准输出
  var psi = new ProcessStartInfo("docker", $"exec jmeter /bin/bash /jmeterspace/apache-jmeter-5.1.1/bin/jmeter -n -t {filePath.scriptPath}      {Path.DirectorySeparatorChar}script.jmx -l {filePath.resultPath}{Path.DirectorySeparatorChar}result.jtl") { RedirectStandardOutput = true };
  //启动
  try
  {
    var proc = Process.Start(psi);
    if (proc == null)
    {
      return "Process start failed!";
    }
    else
    {
    //开始读取
      using (var sr = proc.StandardOutput)
      {
        while (!proc.HasExited)
        {
          result += sr.ReadLine();
        }  
      }
    return result;
    }
  }
  catch (Exception ee)
  {
    return ee.Message;
  }
}

最新文章

  1. (七) 一起学 Unix 环境高级编程(APUE) 之 进程关系 和 守护进程
  2. 原生与jqueryDOM
  3. Delphi中文本文件的操作
  4. 绘图工具graphviz学习使用
  5. oracle_导入、导出数据
  6. JDK的并发容器
  7. git常用命令记录
  8. MAC开发环境安装
  9. 一个非常标准的连接Mysql数据库的示例代码
  10. js正则表达语法
  11. 史上最最靠谱,又双叒叒简单的基于MSXML的XML解析指南-C++
  12. NGUI之实现连连看小游戏
  13. qW3xT.2,解决挖矿病毒。
  14. LR运行负载测试场景-笔记
  15. sqlAlchemy语法增删改查
  16. 1418 抱歉 ACM 欧拉公式
  17. Pycharm 在Windows下出现闪退问题(即是在运行一段时间后,自己就退出崩掉了)的解决方法
  18. Unity和Android混合开发
  19. hdu 1856 求集合里元素的个数 输出最大的个数是多少
  20. Java 9 模块化(Modularity)

热门文章

  1. WPF ListBox的内容属性Items
  2. WPF之VirtualizingStackPanel.IsVirtualizing="False"
  3. datacontract helper
  4. Win8Metro(C#)数字图像处理--2.30直方图均衡化
  5. Win8Metro(C#)数字图像处理--2.17图像木刻效果
  6. storm和kafka的wordCount
  7. 海康SDK编程指南
  8. Qt4.7.4下单独编译QtWebkit
  9. C++的 RTTI 观念和用途(非常详细)
  10. 【canvas】基础练习三 图片