Apache JMeter是一个纯Java开发的用于负载测试或者性能测试的开源软件。这篇文章介绍一下如何使用Alpine基础镜像将JMeter的压测能力进行容器化,并结合具体的示例来演示此镜像从构建到使用的整体过程。
以Alpine为基础构建JMeter镜像
Alpine镜像的特点就是小,在Alpine镜像之上安装jre,并下载JMeter并解压,设定环境变量就完成了JMeter的镜像。
Dockerfile
- 示例的Dockerfile如下所示
[root@liumiaocn jmeter]# ls
Dockerfile
[root@liumiaocn jmeter]# cat Dockerfile
FROM alpine:3.10.2
ARG VERSION_JMETER="5.1.1"
ENV FILENAME_JMETER apache-jmeter-${VERSION_JMETER}
ENV HOME_JMETER /usr/local/${FILENAME_JMETER}
ENV DOWNLOAD_URL_JMETER https://archive.apache.org/dist/jmeter/binaries/${FILENAME_JMETER}.tgz
ENV DOWNLOAD_DIR_LOCAL /tmp/download
RUN apk update \
&& apk upgrade \
&& apk add --update openjdk8-jre curl unzip bash \
&& mkdir -p /tmp/download \
&& curl -L --silent ${DOWNLOAD_URL_JMETER} > ${DOWNLOAD_DIR_LOCAL}/${FILENAME_JMETER}.tgz \
&& mkdir -p /opt ${DOWNLOAD_DIR_LOCAL} \
&& tar -xzf ${DOWNLOAD_DIR_LOCAL}/${FILENAME_JMETER}.tgz -C /usr/local \
&& rm -rf /var/cache/apk/* \
&& rm -rf ${DOWNLOAD_DIR_LOCAL}
ENV PATH $PATH:${HOME_JMETER}/bin
WORKDIR ${HOME_JMETER}/bin
[root@liumiaocn jmeter]#
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
注:此处为了减小size使用的JRE,但是由于JMeter一部分功能是在keytool基础上的,所以实际使用的时候还是根据情况换成jdk,或者选择相关的apk包进行安装达到要求。
构建JMeter镜像
- 使用docker build构建JMeter的镜像,输入日志如下所示
[root@liumiaocn jmeter]# docker build -t jmeter:5.1.1 .
Sending build context to Docker daemon 2.56 kB
Step 1/9 : FROM alpine:3.10.2
---> 961769676411
Step 2/9 : ARG VERSION_JMETER="5.1.1"
---> Using cache
---> f4f342eab6ba
Step 3/9 : ENV FILENAME_JMETER apache-jmeter-${VERSION_JMETER}
---> Using cache
---> f8ca701c741b
Step 4/9 : ENV HOME_JMETER /usr/local/${FILENAME_JMETER}
---> Using cache
---> 056cc7e91e6f
Step 5/9 : ENV DOWNLOAD_URL_JMETER https://archive.apache.org/dist/jmeter/binaries/${FILENAME_JMETER}.tgz
---> Using cache
---> 49e87f232d91
Step 6/9 : ENV DOWNLOAD_DIR_LOCAL /tmp/download
---> Using cache
---> 58cb1fc2f1f3
Step 7/9 : RUN apk update && apk upgrade && apk add --update openjdk8-jre curl unzip bash && mkdir -p /tmp/download && curl -L --silent ${DOWNLOAD_URL_JMETER} -o ${DOWNLOAD_DIR_LOCAL}/${FILENAME_JMETER}.tgz && mkdir -p /opt ${DOWNLOAD_DIR_LOCAL} && tar -xzf ${DOWNLOAD_DIR_LOCAL}/${FILENAME_JMETER}.tgz -C /usr/local && rm -rf /var/cache/apk/* && rm -rf ${DOWNLOAD_DIR_LOCAL}
---> Running in 908fac559368
fetch http://dl-cdn.alpinelinux.org/alpine/v3.10/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.10/community/x86_64/APKINDEX.tar.gz
v3.10.2-80-g68e4e4a13a [http://dl-cdn.alpinelinux.org/alpine/v3.10/main]
v3.10.2-83-g64319a6606 [http://dl-cdn.alpinelinux.org/alpine/v3.10/community]
OK: 10336 distinct packages available
(1/2) Upgrading libcrypto1.1 (1.1.1c-r0 -> 1.1.1d-r0)
(2/2) Upgrading libssl1.1 (1.1.1c-r0 -> 1.1.1d-r0)
OK: 6 MiB in 14 packages
(1/48) Installing ncurses-terminfo-base (6.1_p20190518-r0)
...省略
(48/48) Installing unzip (6.0-r4)
Executing busybox-1.30.1-r2.trigger
Executing ca-certificates-20190108-r0.trigger
Executing java-common-0.2-r0.trigger
OK: 95 MiB in 62 packages
---> 655fe0283c8d
Removing intermediate container 908fac559368
Step 8/9 : ENV PATH $PATH:${HOME_JMETER}/bin
---> Running in 9c731c259c57
---> e32e05b622a9
Removing intermediate container 9c731c259c57
Step 9/9 : WORKDIR ${HOME_JMETER}/bin
---> 45f070d75ce2
Removing intermediate container 958b82eaf6e0
Successfully built 45f070d75ce2
[root@liumiaocn jmeter]#
[root@liumiaocn jmeter]# docker images |grep jmeter |grep 5.1.1
jmeter 5.1.1 45f070d75ce2 16 seconds ago 190 MB
[root@liumiaocn jmeter]#
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
测试准备
压力测试应用准备
在本地机器的8088端口使用Docker启动一个Nginx应用(使用其他方式也可),示例如下所示:
liumiaocn:~ liumiao$ docker images |grep nginx |grep latest
nginx latest e445ab08b2be 2 months ago 126MB
liumiaocn:~ liumiao$ docker run -p 8088:80 -d --name=nginx-test nginx:latest
a80fb1a4fc20627891a6bd7394fd79ae9aefb7dc8cf72c12967bc2673a815308
liumiaocn:~ liumiao$
- 1
- 2
- 3
- 4
- 5
使用curl命令或者直接使用浏览器确认nginx已正常运行
liumiaocn:~ liumiao$ curl http://localhost:8088/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
liumiaocn:~ liumiao$
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
准备jmx文件
jmx文件信息如下所示
liumiaocn:data liumiao$ ls
jmeter-nongui-test.jmx
liumiaocn:data liumiao$ cat jmeter-nongui-test.jmx
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.1.1 r1855137">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="测试计划" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="线程组" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="循环控制器" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">10</stringProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">100</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP请求" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">192.168.31.242</stringProp>
<stringProp name="HTTPSampler.port">8088</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>
liumiaocn:data liumiao$
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
注:关于此jmx的生成步骤和说明请参看
- https://liumiaocn.blog.csdn.net/article/details/101904948
注意事项:前面示例中关于本地Web应用的压测,hostname设定的为localhost,但是由于jmeter在容器中运行,此localhost不同于彼localhost,所以此处改为了IP(192.168.31.242),请实际验证时修改为自己的IP。
执行测试
启动容器&确认版本
liumiaocn:tmp liumiao$ docker run -it -v $(pwd)/data:/data jmeter:5.1.1 sh
/usr/local/apache-jmeter-5.1.1/bin # cd /data
/data # ls
jmeter-nongui-test.jmx
/data # which jmeter
/usr/local/apache-jmeter-5.1.1/bin/jmeter
/data # jmeter -v
_ ____ _ ____ _ _ _____ _ __ __ _____ _____ _____ ____
/ \ | _ \ / \ / ___| | | | ____| | | \/ | ____|_ _| ____| _ \
/ _ \ | |_) / _ \| | | |_| | _| _ | | |\/| | _| | | | _| | |_) |
/ ___ \| __/ ___ \ |___| _ | |___ | |_| | | | | |___ | | | |___| _ <
/_/ \_\_| /_/ \_\____|_| |_|_____| \___/|_| |_|_____| |_| |_____|_| \_\ 5.1.1 r1855137
Copyright (c) 1999-2019 The Apache Software Foundation
/data #
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
执行测试并生成结果
/data # jmeter -n -t jmeter-nongui-test.jmx -l result.jtl -e -o jmeter-nongui-rpt
Creating summariser <summary>
Created the tree successfully using jmeter-nongui-test.jmx
Starting the test @ Wed Oct 02 07:39:06 GMT 2019 (1570001946517)
Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
summary = 1000 in 00:00:02 = 538.5/s Avg: 55 Min: 2 Max: 393 Err: 0 (0.00%)
Tidying up ... @ Wed Oct 02 07:39:09 GMT 2019 (1570001949228)
... end of run
/data #
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9