Docker构建Jmeter生成测试报告
环境准备
- CentOS 7
工具包
- jmeter5.4.3(截止2022.5.19) jmeter下载官网
- Ant1.10.12 ant下载官网
Docker构建jmeter运行生成测试报告
准备jmeter包,自己下载也可以通过命令下载
wget --no-check-certificate https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.4.3.tgz
创建并编辑jmeter的Dockerfile
基于jmeter版本5.4.3
[root@localhost jmeter]# vi Dockerfile
FROM java:8
MAINTAINER jmeter_test<binzichen@126.com
ENV MYPATH /jmeterdocker
WORKDIR $MYPATH
ENV JMETER_VERSION=5.4.3
ENV JMETER_HOME=${MYPATH}/apache-jmeter-${JMETER_VERSION}
ENV JMETER_PATH=${JMETER_HOME}/bin:${PATH}
ENV PATH=${JMETER_HOME}/bin:${PATH}
ADD apache-jmeter-${JMETER_VERSION}.tgz ${MYPATH}
构建镜像
[root@localhost jmeter]# docker build -f Dockerfile -t jmeter_test:0.1 .
Sending build context to Docker daemon 202.5MB
Step 1/9 : FROM java:8
8: Pulling from library/java
5040bd298390: Pull complete
fce5728aad85: Pull complete
76610ec20bf5: Pull complete
60170fec2151: Pull complete
e98f73de8f0d: Pull complete
11f7af24ed9c: Pull complete
49e2d6393f32: Pull complete
bb9cdec9c7f3: Pull complete
Digest: sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c0e9d
Status: Downloaded newer image for java:8
---> d23bdf5b1b1b
Step 2/9 : MAINTAINER jmeter_test<binzichen@126.com
---> Running in 24c7e4f442e1
Removing intermediate container 24c7e4f442e1
---> 9fc52580e7ac
Step 3/9 : ENV MYPATH /JmeterDocker
---> Running in 107165a88e9f
Removing intermediate container 107165a88e9f
---> 323ae31b925f
Step 4/9 : WORKDIR $MYPATH
---> Running in f1bf4cea7d33
Removing intermediate container f1bf4cea7d33
---> 9b0afa6658f2
Step 5/9 : ENV JMETER_VERSION=5.4.3
---> Running in a95aac00f101
Removing intermediate container a95aac00f101
---> 0d82ad043dba
Step 6/9 : ENV JMETER_HOME=${MYPATH}/apache-jmeter-${JMETER_VERSION}
---> Running in a26c19d0ad10
Removing intermediate container a26c19d0ad10
---> 4d910c196696
Step 7/9 : ENV JMETER_PATH=${JMETER_HOME}/bin:${PATH}
---> Running in 032804cc7c6f
Removing intermediate container 032804cc7c6f
---> 50f383e1b882
Step 8/9 : ENV PATH=${JMETER_HOME}/bin:${PATH}
---> Running in 856cf7dddaa1
Removing intermediate container 856cf7dddaa1
---> 6c287bdaeef3
Step 9/9 : ADD apache-jmeter-${JMETER_VERSION}.tgz ${MYPATH}
---> 6a3b2ca939fd
Successfully built 6a3b2ca939fd
Successfully tagged jmeter_test:0.1
查看镜像
[root@localhost jmeter]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
jmeter_test 0.1 6a3b2ca939fd 13 seconds ago 773MB
java 8 d23bdf5b1b1b 5 years ago 643MB
运行容器
# -v 挂载目录
# -d 后台运行
# --name 容器命名
docker run --name="jmeter_test" \
-v /tmp/jmeterspace/test/input/jmx:/jmeterdocker/test/input/jmx \
-v /tmp/jmeterspace/test/input/testdata:/jmeterdocker/test/input/testdata \
-v /tmp/jmeterspace/test/report/html:/jmeterdocker/test/report/html \
-v /tmp/jmeterspace/test/report/jtl:/jmeterdocker/test/report/jtl \
-v /tmp/jmeterspace/test/report/outputputdata:/jmeterdocker/test/report/outputdata \
-it -d jmeter_test:0.1
# 查看已启动容器 docker ps
[root@localhost jmeter]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
06878940ac66 jmeter_test:0.1 "/bin/bash" 4 seconds ago Up 3 seconds jmeter_test
#
测试运行生成报告
准备jmx脚本
脚本放在 /tmp/jmeterspace/test/input/jmx 目录下
# 脚本
[root@localhost jmx]# ls
for_docker.jmx
# 运行产生的数据(jtl文件)放在 /tmp/jmeterspace/test/report/jtl 目录下
# 运行产生的报告(HTML文件包)放在 /tmp/jmeterspace/test/report/html 目录下
# 通过docker运行jmeter生成jtl和html包
[root@localhost jmx]# docker exec -it jmeter_test jmeter -n -t /jmeterdocker/test/input/jmx/for_docker.jmx \
-l /jmeterdocker/test/report/jtl/html.jtl \
-e -o /jmeterdocker/test/report/html/result
Creating summariser <summary>
Created the tree successfully using /jmeterdocker/test/input/jmx/for_docker.jmx
Starting standalone test @ Thu May 19 02:20:36 UTC 2022 (1652926836208)
Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
summary = 4 in 00:00:05 = 0.7/s Avg: 1273 Min: 484 Max: 2813 Err: 2 (50.00%)
Tidying up ... @ Thu May 19 02:20:42 UTC 2022 (1652926842232)
... end of run
查看报告
# 进入 /tmp/jmeterspace/test/report/html 目录下
[root@localhost /]# cd /tmp/jmeterspace/test/report/html
[root@localhost html]# ls
result
[root@localhost html]# cd result/
[root@localhost result]# ls
content index.html sbadmin2-1.0.7 statistics.json
# 可以看到html包已生成

进阶Docker构建Jmeter+Ant生成详细报告
构建jmeter+ant镜像
准备jmeter包和ant包,放在/home/jmeter 目录下
[root@localhost jmeter]# ls
apache-ant-1.10.12-bin.tar.gz apache-jmeter-5.4.3.tgz
创建并编辑jmeter+ant的Dockerfile
FROM java:8
MAINTAINER jmeter_test<binzichen@126.com
ENV MYPATH /jmeterdocker
WORKDIR $MYPATH
ENV JMETER_VERSION=5.4.3
ENV ANT_VERSION=1.10.12
ENV JMETER_HOME=${MYPATH}/apache-jmeter-${JMETER_VERSION}
ENV JMETER_PATH=${JMETER_HOME}/bin:${PATH}
ENV PATH=${JMETER_HOME}/bin:${PATH}
ENV ANT_HOME=${MYPATH}/apache-ant-${ANT_VERSION}
ENV PATH=${PATH}:${ANT_HOME}/bin
ADD apache-jmeter-${JMETER_VERSION}.tgz ${MYPATH}
ADD apache-ant-${ANT_VERSION}-bin.tar.gz ${MYPATH}
构建镜像 ant_jmeter
[root@localhost jmeter]# docker build -f Dockerfile -t ant_jmeter:0.1 .
Sending build context to Docker daemon 303.5MB
Step 1/13 : FROM java:8
8: Pulling from library/java
5040bd298390: Pull complete
fce5728aad85: Pull complete
76610ec20bf5: Pull complete
60170fec2151: Pull complete
e98f73de8f0d: Pull complete
11f7af24ed9c: Pull complete
49e2d6393f32: Pull complete
bb9cdec9c7f3: Pull complete
Digest: sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c0e9d
Status: Downloaded newer image for java:8
---> d23bdf5b1b1b
Step 2/13 : MAINTAINER jmeter_test<binzichen@126.com
---> Running in 93942d689465
Removing intermediate container 93942d689465
---> 4e5e374bf766
Step 3/13 : ENV MYPATH /jmeterdocker
---> Running in b28d2300105e
Removing intermediate container b28d2300105e
---> 0b5658636749
Step 4/13 : WORKDIR $MYPATH
---> Running in 167b7ef3d9fb
Removing intermediate container 167b7ef3d9fb
---> 1eaad545d4a4
Step 5/13 : ENV JMETER_VERSION=5.4.3
---> Running in 826a59f5469a
Removing intermediate container 826a59f5469a
---> 2ec8f0c390f2
Step 6/13 : ENV ANT_VERSION=1.10.12
---> Running in a939b11068f5
Removing intermediate container a939b11068f5
---> 1c08f52f2d57
Step 7/13 : ENV JMETER_HOME=${MYPATH}/apache-jmeter-${JMETER_VERSION}
---> Running in 7303622586e6
Removing intermediate container 7303622586e6
---> e41327572670
Step 8/13 : ENV JMETER_PATH=${JMETER_HOME}/bin:${PATH}
---> Running in 84b41fab2213
Removing intermediate container 84b41fab2213
---> 6160a2e2c322
Step 9/13 : ENV PATH=${JMETER_HOME}/bin:${PATH}
---> Running in 194a8eb21e25
Removing intermediate container 194a8eb21e25
---> 090538bf4369
Step 10/13 : ENV ANT_HOME=${MYPATH}/apache-ant-${ANT_VERSION}
---> Running in 5baeca3c6212
Removing intermediate container 5baeca3c6212
---> c7e14f8ee993
Step 11/13 : ENV PATH=${PATH}:${ANT_HOME}/bin
---> Running in bb7c8d5b6433
Removing intermediate container bb7c8d5b6433
---> 51a3e6c75aca
Step 12/13 : ADD apache-jmeter-${JMETER_VERSION}.tgz ${MYPATH}
---> 6144c52e759a
Step 13/13 : ADD apache-ant-${ANT_VERSION}-bin.tar.gz ${MYPATH}
---> fa4362da80f9
Successfully built fa4362da80f9
Successfully tagged ant_jmeter:0.1
查看镜像
[root@localhost jmeter]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ant_jmeter 0.1 fa4362da80f9 8 seconds ago 815MB
java 8 d23bdf5b1b1b 5 years ago 643MB
运行容器 ant_jmeter
docker run --name="ant_jmeter" \
-v /tmp/jmeterspace/test/input/jmx:/jmeterdocker/test/input/jmx \
-v /tmp/jmeterspace/test/input/build:/jmeterdocker/test/input/build \
-v /tmp/jmeterspace/test/report/html:/jmeterdocker/test/report/html \
-v /tmp/jmeterspace/test/report/jtl:/jmeterdocker/test/report/jtl \
-v /tmp/jmeterspace/test/report/outputputdata:/jmeterdocker/test/report/outputdata \
-it -d ant_jmeter:0.1
# 查看已启动容器 docker ps
[root@localhost jmeter]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
74295fe8b829 ant_jmeter:0.1 "/bin/bash" 16 minutes ago Up 16 minutes ant_jmeter
验证jmeter、ant已生效
# jmeter -v 查看jmeter能正常运行
[root@localhost jmeter]# docker exec -it ant_jmeter jmeter -v
_ ____ _ ____ _ _ _____ _ __ __ _____ _____ _____ ____
/ \ | _ \ / \ / ___| | | | ____| | | \/ | ____|_ _| ____| _ \
/ _ \ | |_) / _ \| | | |_| | _| _ | | |\/| | _| | | | _| | |_) |
/ ___ \| __/ ___ \ |___| _ | |___ | |_| | | | | |___ | | | |___| _ <
/_/ \_\_| /_/ \_\____|_| |_|_____| \___/|_| |_|_____| |_| |_____|_| \_\ 5.4.3
Copyright (c) 1999-2021 The Apache Software Foundation
# ant -version 查看ant能正常运行
[root@localhost jmeter]# docker exec -it ant_jmeter ant -version
Apache Ant(TM) version 1.10.12 compiled on October 13 2021
镜像内部配置更新
将jmeter extras目录下的ant-jmeter-1.1.1.jar文件拷贝到ant安装目录的lib目录中
[root@localhost jmeter]# docker exec -it ant_jmeter cp /jmeterdocker/apache-jmeter-5.4.3/extras/ant-jmeter-1.1.1.jar /jmeterdocker/apache-ant-1.10.12/lib/
修改jmeter bin目录下jmeter.properties配置
jmeter.save.saveservice.data_type=true
jmeter.save.saveservice.label=true
jmeter.save.saveservice.response_code=true
# response_data is not currently supported for CSV output
jmeter.save.saveservice.response_data=true
# Save ResponseData for failed samples
jmeter.save.saveservice.response_data.on_error=true
jmeter.save.saveservice.response_message=true
jmeter.save.saveservice.successful=true
jmeter.save.saveservice.thread_name=true
jmeter.save.saveservice.time=true
jmeter.save.saveservice.subresults=true
jmeter.save.saveservice.assertions=true
jmeter.save.saveservice.latency=true
# Only available with HttpClient4
jmeter.save.saveservice.connect_time=true
jmeter.save.saveservice.samplerData=true
jmeter.save.saveservice.responseHeaders=true
jmeter.save.saveservice.requestHeaders=true
jmeter.save.saveservice.encoding=true
jmeter.save.saveservice.bytes=true
# Only available with HttpClient4
jmeter.save.saveservice.sent_bytes=true
jmeter.save.saveservice.url=true
jmeter.save.saveservice.filename=true
jmeter.save.saveservice.hostname=true
jmeter.save.saveservice.thread_counts=true
jmeter.save.saveservice.sample_count=true
jmeter.save.saveservice.idle_time=true

将jmeter.results.shanhe.me.xsl 放入jmeter的extras目录下
# 创建并编辑jmeter.results.shanhe.me.xsl文件 vi jmeter.results.shanhe.me.xsl
[root@localhost jmeter]# vi jmeter.results.shanhe.me.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="no" encoding="UTF-8" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" doctype-system="http://www.w3.org/TR/html4/loose.dtd"/>
<!-- Defined parameters (overrideable) -->
<xsl:param name="titleReport" select="'My Porsche APP API Test Results'"/>
<xsl:param name="dateReport" select="'date not defined'"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/testResults">
<html lang="en">
<head>
<meta name="Author" content="shanhe.me"/>
<title><xsl:value-of select="$titleReport" /></title>
<style type="text/css"><![CDATA[
* { margin: 0; padding: 0 }
html, body { width: 100%; height: 100%; background: #b4b4b4; font-size: 12px }
table { border: none; border-collapse: collapse; table-layout: fixed }
td { vertical-align: baseline; font-size: 12px }
#top-panel { position: absolute; left: 0; top: 0; bottom: 0; display: block }
#top-panel h1 { margin: 0px 0px 2px; font: 138% verdana,arial,helvetica}
#top-panel table tr td, table tr th { font-size: 118%; }
#top-panel table.details tr th{ color: #ffffff; font-weight: bold; text-align:center; background:#2674a6; white-space: nowrap; }
#top-panel table.details tr td{ background:#eeeee0; white-space: nowrap; }
#left-panel { position: absolute; left: 0; top: 81px; bottom: 0; width: 300px; overflow: auto; background: #dee4ea }
#left-panel li.navigation { font-weight: bold; cursor: default; color: #9da8b2; line-height: 18px; background-position: 12px 5px; background-repeat: no-repeat; padding: 0 0 0 25px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAICAYAAAArzdW1AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBQqGbO7BEcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAKRJREFUGNN1zM0KgkAYheF3RvtXSsGyWhRNaILS7bdt11W0KgJvoPwZp0UlBPUtz3nOJw7Hk7necv5dOA2Qaazo2vZP0LEt9olCVtqQROufKNmuqBuBNAYW4QzXGX6B0bDPcjGnMQYJ8Cg12U59oSzaUJQa4IUAXMclDHwAAn/MxPMw765FZd2QRgopBWmsKCrdfhXnS/4ZYElBXdyxewN008Y8AephLAkqz613AAAAAElFTkSuQmCC) }
#left-panel li.success { color: #565b60 }
#left-panel li.failure { color: red }
#left-panel li { list-style: none; color: black; cursor: pointer }
#left-panel li.selected { background-repeat: repeat-x; color: white; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAUCAYAAABMDlehAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBQxLTs5O2gAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAEdJREFUCNc1y7ERgEAMA0GNUhIyGqM2uqKgtyWZhE9v53A/7/A6D7BkMDNgy2AroB2wHTCZv5UMOgFLG1bvd7XBckBlwCXjA5wMOF5iOX/MAAAAAElFTkSuQmCC) }
#left-panel div { line-height: 20px; background-position: 25px 3px; background-repeat: no-repeat; padding: 0 0 0 45px }
#left-panel div.success { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAOCAYAAADwikbvAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBULEEc6wzcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAiNJREFUKM99kktIVGEYhp/jzJl08lI6logp2Y2EFkbtaqlFROsWrlq4ioJWQRs37VoUVItWkYEVRGSBlhleCpywDEWxTEuxcURTZ6YzxzP/5WshCOHUt36f93kXnyMi5Lsnb4clI4s4fhkXzp5w8mWcfHBvfEpUxVdCUUU6lUPNHuD86cYtBQX5GhPrM7hRg7GaSDRg2vuUd90WuOPVsOyqy6FFo2yOQHlU1S9z9dZT+S/8I7GCLlkAN4eyAf56mnT6Fy1HLnGuuYa++MS/4e74qMRqfXLaJ9BpfnsrLC0m2BYuoqwUbj/+274JD43OEqmexwvW8NUKXnaZtVSS1pNtAAyOvyC6v48HnUNb4Z7PH8UtTlIQWA5tb2RhYY7kz3l2FleytJYg/qWb8t2KZ/0PN+1hgI6uEUr2jpHKpGlquExVaS0VbjUZL7WxaqIXK6ADQ0n9GNfv9XCttWnD/O57t0TKFklnF3g5fJ/seoaa2D4O1x0F4PlgO9oIftbgFgYMfLgjACGqj0vlsddoUnj+Kt/mxunq72RP+UGqYjWMTA7R+b6dUCSEGEF5hoJQip6BaFs4HJtCyRrKs6wHCovDip/kys0WWpovMpOYBCtoT2N9B5uzWG0Zid8gnFrVFEQDtBaUrxEgXBimaEeER2/uIiK4roPOaMRYjBKsFly3fOO3G06dETGCWIsYjckprMphtEKMAQtgsMYi1mJMQHJ6xvkDKQoyphCzkl0AAAAASUVORK5CYII=) }
#left-panel div.failure { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAOCAYAAADwikbvAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBUJOEC5CU8AAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAeVJREFUKM+NkDtok2EUhp8vl9ZLo/EyKI6KFgqCKC4OClrBWUQEcRRx1cGpk3WyInWrgoMZKkW8thYaEYQ0i7WC2ngrNDTERHJvkv/L/3//dxwc7F8jeOAsh/c973OOEhG61aPnaen7maXYt4MLZ4+pbppQt+F06jNH3QWOb8pxUs+SmJzjv83hxY8SVy3wNdtVneiHqe54IhLoB4/TUkyMyOrKj5yXoVtPZK02kLyYK7OnlqFWzgcCGtUC/YUJ3n5a/jd28tU7ORTN0myUA6Jms8bpWIa798elqzn1fokjThrpVBC3ETzNbYAuca59j/Hp+b/N869Tsk8tgVMCXQk+RlfQuk1/tMLMwzsSMCcm5zjhvoR2AdpF0GuwO4aqttS05ZSbZHhsBoAIwI83Cdkd/460XDAOG02d24MxvlR8dsUUh3f2UHaEtgdbWCHz4oZwcVCp66PP5FLhKjEc8DXaCMsNy8DYn/SnZ+L0hhWOb/F8yLs9fDtwk8j+VpqwrlC34PrgGEu2bhlYhZ1b8dncq3AMeBaUr/k6NUyk4ChKzu+N2hc6Bqody+WDG8g2fLatD7F3axjPgmvAtYJvIbouhhIRrl0ZktnkBGIt1gqeMXQ8D2MMiCIUCqFEsFhEQMSykCuqX0MzLAUJTzRsAAAAAElFTkSuQmCC) }
#left-panel div.detail { display: none }
#right-panel { position: absolute; right: 0; top: 81px; bottom: 0; left: 301px; overflow: auto; background: white }
#right-panel .group { font-size: 12px; font-weight: bold; line-height: 16px; padding: 0 0 0 18px; counter-reset: assertion; background-repeat: repeat-x; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAQCAYAAADXnxW3AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBUkDq8pxjkAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAADdJREFUCNdVxrERwDAMAzGK0v47eS6Z927SpMFBAAbkvSvnRk5+7K5cVfLMyN39bWakJAjA5xw9R94jN3tVhVEAAAAASUVORK5CYII=) }
#right-panel .zebra { background-repeat: repeat; padding: 0 0 0 18px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAmCAYAAAAFvPEHAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBYWFlNztEcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAABdJREFUCNdjYKAtePv5338mBgYGBpoQAGy1BAJlb/y6AAAAAElFTkSuQmCC) }
#right-panel .data { line-height: 19px; white-space: nowrap }
#right-panel pre.data { white-space: pre }
#right-panel tbody.failure { color: red }
#right-panel td.key { min-width: 108px }
#right-panel td.delimiter { min-width: 18px }
#right-panel td.assertion:before { counter-increment: assertion; content: counter(assertion) ". " }
#right-panel td.assertion { color: black }
#right-panel .trail { border-top: 1px solid #b4b4b4 }
]]></style>
<script type="text/javascript"><![CDATA[
var onclick_li = (function() {
var last_selected = null;
return function(li) {
if( last_selected == li )
return;
if( last_selected )
last_selected.className = "";
last_selected = li;
last_selected.className = "selected";
document.getElementById("right-panel").innerHTML = last_selected.firstChild.nextSibling.innerHTML;
return false;
};
})();
var patch_timestamp = function() {
var spans = document.getElementsByTagName("span");
var len = spans.length;
for( var i = 0; i < len; ++i ) {
var span = spans[i];
if( "patch_timestamp" == span.className )
span.innerHTML = new Date( parseInt( span.innerHTML ) );
}
};
var patch_navigation_class = (function() {
var set_class = function(el, flag) {
if(el) {
el.className += flag ? " success" : " failure";
}
};
var traverse = function(el, group_el, flag) {
while(1) {
if(el) {
if(el.className == 'navigation') {
set_class(group_el, flag);
group_el = el;
flag = true;
} else {
var o = el.firstChild;
o = o ? o.className : null;
flag = flag ? (o == 'success') : false;
}
el = el.nextSibling;
} else {
set_class(group_el, flag);
break;
}
}
};
return function() {
var o = document.getElementById("result-list");
o = o ? o.firstChild : null;
if(o)
traverse(o, null, true);
};
})();
window.onload = function() {
patch_timestamp();
patch_navigation_class();
var o = document.getElementById("result-list");
o = o ? o.firstChild : null;
o = o ? o.nextSibling : null;
if(o)
onclick_li(o);
};
]]></script>
</head>
<body>
<div id="top-panel">
<xsl:call-template name="pageHeader" />
<xsl:call-template name="summary" />
<hr size="1" width="100%" align="center" />
</div>
<div id="left-panel">
<ol id="result-list">
<xsl:for-each select="*">
<!-- group with the previous sibling -->
<xsl:if test="position() = 1 or @tn != preceding-sibling::*[1]/@tn">
<li class="navigation">Thread: <xsl:value-of select="@tn"/></li>
</xsl:if>
<li onclick="return onclick_li(this);">
<div>
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="@s = 'true'">success</xsl:when>
<xsl:otherwise>failure</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:value-of select="@lb"/>
</div><div class="detail">
<div class="group">Sampler</div>
<div class="zebra">
<table>
<tr><td class="data key">Thread Name</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@tn"/></td></tr>
<tr><td class="data key">Timestamp</td><td class="data delimiter">:</td><td class="data"><span class="patch_timestamp"><xsl:value-of select="@ts"/></span></td></tr>
<tr><td class="data key">Time</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@t"/> ms</td></tr>
<tr><td class="data key">Latency</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@lt"/> ms</td></tr>
<tr><td class="data key">Bytes</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@by"/></td></tr>
<tr><td class="data key">Sample Count</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@sc"/></td></tr>
<tr><td class="data key">Error Count</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@ec"/></td></tr>
<tr><td class="data key">Response Code</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@rc"/></td></tr>
<tr><td class="data key">Response Message</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@rm"/></td></tr>
</table>
</div>
<div class="trail"></div>
<xsl:if test="count(assertionResult) > 0">
<div class="group">Assertion</div>
<div class="zebra">
<table>
<xsl:for-each select="assertionResult">
<tbody>
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="failure = 'true'">failure</xsl:when>
<xsl:when test="error = 'true'">failure</xsl:when>
</xsl:choose>
</xsl:attribute>
<tr><td class="data assertion" colspan="3"><xsl:value-of select="name"/></td></tr>
<tr><td class="data key">Failure</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="failure"/></td></tr>
<tr><td class="data key">Error</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="error"/></td></tr>
<tr><td class="data key">Failure Message</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="failureMessage"/></td></tr>
</tbody>
</xsl:for-each>
</table>
</div>
<div class="trail"></div>
</xsl:if>
<div class="group">Request</div>
<div class="zebra">
<table>
<tr><td class="data key">Method/Url</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="method"/><xsl:text> </xsl:text><xsl:value-of select="java.net.URL"/></pre></td></tr>
<tr><td class="data key">Query String</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="queryString"/></pre></td></tr>
<tr><td class="data key">Cookies</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="cookies"/></pre></td></tr>
<tr><td class="data key">Request Headers</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="requestHeader"/></pre></td></tr>
</table>
</div>
<div class="trail"></div>
<div class="group">Response</div>
<div class="zebra">
<table>
<tr><td class="data key">Response Headers</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="responseHeader"/></pre></td></tr>
<tr><td class="data key">Response Data</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="responseData"/></pre></td></tr>
<tr><td class="data key">Response File</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="responseFile"/></pre></td></tr>
</table>
</div>
<div class="trail"></div>
</div>
</li>
</xsl:for-each>
</ol>
</div>
<div id="right-panel"></div>
</body>
</html>
</xsl:template>
<xsl:template name="pageHeader">
<h1><xsl:value-of select="$titleReport" /></h1>
<table width="100%">
<tr>
<td align="left">Date report: <xsl:value-of select="$dateReport" /></td>
<td align="right">Designed for use with <a href="http://jmeter.apache.org/">JMeter</a> and <a href="http://ant.apache.org">Ant</a>.</td>
</tr>
</table>
<hr size="1" />
</xsl:template>
<xsl:template name="summary">
<table align="center" class="details" border="0" cellpadding="5" cellspacing="2" width="100%">
<tr valign="top">
<th>用例总数</th>
<th>失败数</th>
<th>通过率</th>
<th>平均消耗时间</th>
<th>最小响应时间</th>
<th>最大响应时间</th>
</tr>
<tr valign="top">
<xsl:variable name="allCount" select="count(/testResults/*)" />
<xsl:variable name="allFailureCount" select="count(/testResults/*[attribute::s='false'])" />
<xsl:variable name="allSuccessCount" select="count(/testResults/*[attribute::s='true'])" />
<xsl:variable name="allSuccessPercent" select="$allSuccessCount div $allCount" />
<xsl:variable name="allTotalTime" select="sum(/testResults/*/@t)" />
<xsl:variable name="allAverageTime" select="$allTotalTime div $allCount" />
<xsl:variable name="allMinTime">
<xsl:call-template name="min">
<xsl:with-param name="nodes" select="/testResults/*/@t" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="allMaxTime">
<xsl:call-template name="max">
<xsl:with-param name="nodes" select="/testResults/*/@t" />
</xsl:call-template>
</xsl:variable>
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="$allFailureCount > 0">Failure</xsl:when>
</xsl:choose>
</xsl:attribute>
<td align="center">
<xsl:value-of select="$allCount" />
</td>
<td align="center">
<xsl:value-of select="$allFailureCount" />
</td>
<td align="center">
<xsl:call-template name="display-percent">
<xsl:with-param name="value" select="$allSuccessPercent" />
</xsl:call-template>
</td>
<td align="center">
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$allAverageTime" />
</xsl:call-template>
</td>
<td align="center">
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$allMinTime" />
</xsl:call-template>
</td>
<td align="center">
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$allMaxTime" />
</xsl:call-template>
</td>
</tr>
</table>
</xsl:template>
<xsl:template name="min">
<xsl:param name="nodes" select="/.." />
<xsl:choose>
<xsl:when test="not($nodes)">NaN</xsl:when>
<xsl:otherwise>
<xsl:for-each select="$nodes">
<xsl:sort data-type="number" />
<xsl:if test="position() = 1">
<xsl:value-of select="number(.)" />
</xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="max">
<xsl:param name="nodes" select="/.." />
<xsl:choose>
<xsl:when test="not($nodes)">NaN</xsl:when>
<xsl:otherwise>
<xsl:for-each select="$nodes">
<xsl:sort data-type="number" order="descending" />
<xsl:if test="position() = 1">
<xsl:value-of select="number(.)" />
</xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="display-percent">
<xsl:param name="value" />
<xsl:value-of select="format-number($value,'0.00%')" />
</xsl:template>
<xsl:template name="display-time">
<xsl:param name="value" />
<xsl:value-of select="format-number($value,'0 ms')" />
</xsl:template>
</xsl:stylesheet>
# 把jmeter.results.shanhe.me.xsl文件从linux复制到jmeter的extras目录下
[root@localhost jmeter]# docker cp jmeter.results.shanhe.me.xsl ant_jmeter:/jmeterdocker/apache-jmeter-5.4.3/extras
# 通过容器查看 jmeter.results.shanhe.me.xsl 是否复制过去
[root@localhost jmeter]# docker exec -it ant_jmeter ls /jmeterdocker/apache-jmeter-5.4.3/extras
GrafanaJMeterTemplate.json execcode.bsh proxycert.sh
Test.jmx expand.png remote.bsh
addons.txt jmeter-results-detail-report_21.xsl schematic.cmd
addons.xml jmeter-results-report_21.xsl schematic.sh
ant-jmeter-1.1.1.jar jmeter.results.shanhe.me.xsl schematic.xml
build.xml printvars.bsh schematic.xsl
collapse.png proxycert.cmd startup.bsh
# 可以看到jmeter.results.shanhe.me.xsl 已存在
创建并编辑build.xml文件放入/jmeterdocker/test/input/build 目录下
# 进入/jmeterdocker/test/input/build目录
cd /jmeterdocker/test/input/build
# vim build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="ant-jmeter-test" default="run" basedir=".">
<tstamp>
<format property="time" pattern="yyyyMMddhhmm" />
</tstamp>
<!-- 需要改成自己本地的 Jmeter 目录-->
<property name="jmeter.home" value="/jmeterdocker/apache-jmeter-5.4.3" />
<!-- jmeter生成jtl格式的结果报告的路径-->
<property name="jmeter.result.jtl.dir" value="/jmeterdocker/test/report/jtl" />
<!-- jmeter生成html格式的结果报告的路径-->
<property name="jmeter.result.html.dir" value="/jmeterdocker/test/report/html" />
<!-- jmeter运行jmx脚本的路径-->
<property name="jmeter.run.jmx.dir" value="/jmeterdocker/test/input/jmx" />
<!-- jmeter生成html格式的结果报告的前缀-->
<property name="ReportName" value="report" />
<property name="jmeter.result.jtlName" value="${jmeter.result.jtl.dir}/html.jtl" />
<property name="jmeter.result.htmlName" value="${jmeter.result.html.dir}/${ReportName}.html" />
<target name="run">
<antcall target="test" />
<antcall target="report" />
</target>
<target name="test">
<taskdef name="jmeter" classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask" />
<jmeter jmeterhome="${jmeter.home}" resultlog="${jmeter.result.jtlName}">
<!-- 声明要运行的脚本。"*.jmx"指包含此目录下的所有jmeter脚本-->
<testplans dir="${jmeter.run.jmx.dir}" includes="*.jmx" />
<property name="jmeter.save.saveservice.output_format" value="xml"/>
</jmeter>
</target>
<path id="xslt.classpath">
<fileset dir="${jmeter.home}/lib" includes="xalan*.jar"/>
<fileset dir="${jmeter.home}/lib" includes="serializer*.jar"/>
</path>
<target name="report">
<tstamp><format property="report.datestamp" pattern="yyyy/MM/dd HH:mm"/></tstamp>
<xslt
classpathref="xslt.classpath"
force="true"
in="${jmeter.result.jtlName}"
out="${jmeter.result.htmlName}"
style="${jmeter.home}/extras/jmeter.results.shanhe.me.xsl" >
<param name="dateReport" expression="${report.datestamp}"/>
</xslt>
<!-- 因为上面生成报告的时候,不会将相关的图片也一起拷贝至目标目录,所以,需要手动拷贝 -->
<copy todir="${jmeter.result.html.dir}">
<fileset dir="${jmeter.home}/extras">
<include name="collapse.png" />
<include name="expand.png" />
</fileset>
</copy>
</target>
</project>
测试docker构建jmeter+ant运行jmx脚本生成html报告
[root@localhost jmeter]# docker exec -it ant_jmeter ant -buildfile /jmeterdocker/test/input/build/build.xml
Buildfile: /jmeterdocker/test/input/build/build.xml
run:
test:
[jmeter] Executing test plan: /jmeterdocker/test/input/jmx/for_docker.jmx ==> /jmeterdocker/test/report/jtl/html.jtl
[jmeter] Creating summariser <summary>
[jmeter] Created the tree successfully using /jmeterdocker/test/input/jmx/for_docker.jmx
[jmeter] Starting standalone test @ Thu May 19 11:50:19 UTC 2022 (1652961019519)
[jmeter] Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
[jmeter] summary = 4 in 00:00:03 = 1.4/s Avg: 643 Min: 281 Max: 1600 Err: 2 (50.00%)
[jmeter] Tidying up ... @ Thu May 19 11:50:22 UTC 2022 (1652961022836)
[jmeter] ... end of run
report:
[xslt] Processing /jmeterdocker/test/report/jtl/html.jtl to /jmeterdocker/test/report/html/report.html
[xslt] Loading stylesheet /jmeterdocker/apache-jmeter-5.4.3/extras/jmeter.results.shanhe.me.xsl
BUILD SUCCESSFUL
Total time: 5 seconds
查看生成的html报告
# 进入 /tmp/jmeterspace/test/report/html/目录
[root@localhost jmeter]# cd /tmp/jmeterspace/test/report/html/
[root@localhost html]# ls
collapse.png expand.png report.html

提交新镜像docker commit
由于最初构建镜像部分环境配置、文件缺失不够完善,为了保持当前容器的状态,就可以通过commit来提交,获得一个镜像。
[root@localhost report]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
74295fe8b829 ant_jmeter:0.1 "/bin/bash" About an hour ago Up About an hour ant_jmeter
# commit成一个新镜像
[root@localhost report]# docker commit -a="Ant_Jmeter" -m="add lib/exc/*.jar and new properies new build.xml" 74295fe8b829 ant_jmeter:1.0
sha256:14dd543cae3c3afae38d0e8d8603fdea88b74e7a6d4a10a737cff7fd8fdaf77e
[root@localhost report]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ant_jmeter 1.0 14dd543cae3c 4 seconds ago 984MB
ant_jmeter 0.1 fa4362da80f9 About an hour ago 815MB
java 8 d23bdf5b1b1b 5 years ago 643MB

浙公网安备 33010602011771号