• 性能测试即服务-docker部署jmeter及.netcore应用


    前言

      现在各种业务都追求上云,通俗的讲,“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;
      }
    }

  • 相关阅读:
    NodeJS系列~第一个小例子,实现了request.querystring功能
    JS~js里实现队列与堆栈
    最新CSS兼容方案
    js传真实地址 C:fakepath
    IE11的变化 navigator.userAgent中不再包含“MSIE”关键字
    看看该死的jquery.form.js的用法,不是个东西
    bootstrap模态框怎么传递参数
    方法的直接调用,反射调用
    IIS 7.5 配置伪静态
    TrippleDESCSPEncrypt 加密解密试试看
  • 原文地址:https://www.cnblogs.com/Invokerr/p/11171836.html
Copyright © 2020-2023  润新知