JMeter – Performing Distributed Load Testing with Docker

日期: 2023/11/06

JMeter 是一個強大的開源工具,用於對網絡應用進行性能和負載測試。在單個 JMeter 實例生成的負載不足的情況下,可以使用主從架構將負載分發給多個 JMeter 實例。文章內容將指導您如何使用 Docker 容器設置 JMeter 主從架構。

架構圖

先決條件

在開始之前,請確保您的系統上已安裝 Docker 和 Docker Compose。

建置容器

JMeter Base Dockerfile

在分散式測試中,確保所有環境都擁有相同版本的 Java、JMeter 和插件是至關重要的。主節點和從節點之間的唯一區別是所公開的埠和正在運行的進程。為了實現這個目標,我們創建了一個通用的 Dockerfile,我們將其稱為 jmbase 映像,其中包含主節點和從節點都通用的步驟。以下是構建基礎映像的步驟:

  1. 選擇 Java 11 版本: 使用 openjdk-11-jre-slim 精簡版,以減小映像的大小。

  2. 安裝實用工具: 我額外增加安裝了一些實用工具,如 wgetunziptelnet等。

  3. 安裝 JMeter: 下載並安裝了指定版本的 Apache JMeter(這裡使用的是版本 5.6)。

  4. 使用版本變數: 我創建了一個版本變數 JMETER_VERSION,以便未來維護更加方便。

  5. 添加插件文件夾: 將所有自定義插件添加到 JMeter 的 liblib/ext 文件夾中。

  6. 添加示例測試: 添加了一個示例測試的文件夾。

# Use Java 11 slim JRE
FROM openjdk:11.0.11-jre-slim
MAINTAINER CasterHsu

# JMeter version
ARG JMETER_VERSION=5.6

# Install few utilities
RUN apt-get clean && \
    apt-get update && \
    apt-get -qy install \
                wget \
                telnet \
                iputils-ping \
                unzip \
                vim

# Install JMeter
RUN   mkdir /jmeter \
      && cd /jmeter/ \
      && wget https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-$JMETER_VERSION.tgz \
      && tar -xzf apache-jmeter-$JMETER_VERSION.tgz \
      && rm apache-jmeter-$JMETER_VERSION.tgz

# ADD all the plugins
ADD plugins-lib /jmeter/apache-jmeter-$JMETER_VERSION/lib

## replace customer properties
ADD properties /jmeter/apache-jmeter-$JMETER_VERSION/bin

# ADD the sample test
ADD jmx jmx

# Set JMeter Home
ENV JMETER_HOME /jmeter/apache-jmeter-$JMETER_VERSION/

# Add JMeter to the Path
ENV PATH $JMETER_HOME/bin:$PATH

Build base jmeter image

docker build -t caster/jmeter-base:latest -f ./base.Dockerfile .

這樣,我們已經成功創建了通用的 jmbase Docker 映像,並且可以在主節點和從節點中使用它,確保它們都具有相同的環境設置,以順利進行 JMeter 測試。

JMeter Client/Master Dockerfile:

主節點的Docker文件應該繼承自基礎映像,並應該公開端口60000

# file name:jmmaster_.Dockerfile
# Use caster base image
FROM caster/jmeter
MAINTAINER CasterHsu

# Ports to be exposed from the container for JMeter Master
EXPOSE 60000

# keep container alive
CMD ["tail", "-f", "/dev/null"]

JMeter Server/Slave Dockerfile:

服務端的Docker文件應繼承自基礎映像,並應該公開端口1099和50000。jmeter-servert常駐運行

# file name:jmslave_.Dockerfile
# Use caster base image
FROM caster/jmeter

MAINTAINER CasterHsu

# Ports to be exposed from the container for JMeter Slaves/Server
EXPOSE 1099 50000

# Application to run on starting the container
ENTRYPOINT $JMETER_HOME/bin/jmeter-server

# keep container alive
CMD ["tail", "-f", "/dev/null"]

個別啟動 Master / Slava

docker build -t caster/jmmaster:latest -f ./master.Dockerfile .
docker build -t caster/jmslave:latest -f ./slave.Dockerfile .

JMeter 分散式測試

啟動 JMeter Server 後,您可以知道 Server 在容器內部的 IP 地址。以這個範例為例,Slave 的 IP 是 '192.168.80.4'。

獲取容器 IP方式 一

您可以在日誌(log)內容中查找 IP 地址,通常位於 WARN 訊息後。以下是示例內容:

WARN StatusConsoleListener The use of package scanning to locate plugins is deprecated and will be removed in a future release
WARN StatusConsoleListener The use of package scanning to locate plugins is deprecated and will be removed in a future release
WARN StatusConsoleListener The use of package scanning to locate plugins is deprecated and will be removed in a future release
WARN StatusConsoleListener The use of package scanning to locate plugins is deprecated and will be removed in a future release
Created remote object: UnicastServerRef2 [liveRef: [endpoint:[192.168.80.4:46521](local),objID:[-21c30768:18bacbd9455:-7fff, 76839754569490693]]]

查找容器的 IP 地址二

要使用主從架構,您需要知道從容器的 IP 地址。您可以使用以下命令查找 IP 地址:

docker inspect --format '{{ .NetworkSettings.Networks.jmeternet.IPAddress }}' jmeter-master
docker inspect --format '{{ .NetworkSettings.Networks.jmeternet.IPAddress }}' jmeter-slave01

如果無法使用上述命令找到 IP 地址,您可以以 JSON 格式查看容器的所有信息:

docker inspect [容器名稱或 ID]

執行 JMeter 測試

  • 單個 JMeter 執行: 使用以下命令運行單個 JMeter 測試:

    jmeter -n -t sample-test.jmx

    執行結果:

    Creating summariser <summary>
    Created the tree successfully using sample-test.jmx
    Starting standalone test @ 2023 Nov 8 02:39:16 UTC (1699411156838)
    Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
    Warning: Nashorn engine is planned to be removed from a future JDK release
    summary +    143 in 00:00:13 =   11.1/s Avg:    57 Min:    45 Max:   172 Err:     0 (0.00%) Active: 5 Started: 5 Finished: 0
    summary +    238 in 00:00:17 =   14.1/s Avg:    56 Min:    43 Max:   160 Err:     0 (0.00%) Active: 0 Started: 5 Finished: 5
    summary =    381 in 00:00:30 =   12.8/s Avg:    56 Min:    43 Max:   172 Err:     0 (0.00%)
    Tidying up ...    @ 2023 Nov 8 02:39:46 UTC (1699411186924)
    ... end of run
  • 分散模式執行: 若要在分散式模式下執行測試,您需要指定 Docker Slave 容器的內部 IP 地址,可以使用 -R 選項指定多個 IP 地址,以逗號分隔。

    jmeter -n -t sample-test.jmx -R 192.168.80.3,192.168.80.4

    執行結果:

    Creating summariser <summary>
    Created the tree successfully using sample-test.jmx
    Configuring remote engine: 192.168.80.3
    Configuring remote engine: 192.168.80.4
    Starting distributed test with remote engines: [192.168.80.3, 192.168.80.4] @ 2023 Nov 8 02:41:03 UTC (1699411263848)
    Warning: Nashorn engine is planned to be removed from a future JDK release
    Remote engines have been started:[192.168.80.3, 192.168.80.4]
    Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
    summary +      1 in 00:00:00 =    2.1/s Avg:    91 Min:    91 Max:    91 Err:     0 (0.00%) Active: 2 Started: 2 Finished: 0
    summary +    312 in 00:00:34 =    9.2/s Avg:   702 Min:    45 Max:  5547 Err:    10 (3.21%) Active: 0 Started: 10 Finished: 10
    summary =    313 in 00:00:34 =    9.1/s Avg:   700 Min:    45 Max:  5547 Err:    10 (3.19%)
    Tidying up remote @ 2023 Nov 8 02:41:38 UTC (1699411298753)
    ... end of run

有錯誤應該是被google 阻擋,太頻繁訪問...如果是自己的server應該是沒有這問題,至少我有在測試一次自己的server.

這個示例的 jmeter.properties 文件已將 RMI SSL 連線關閉,如果您以後需要使用它,請自行調整屬性。現在,您已經具備了執行 JMeter 測試的基本知識,您可以在單台或分散式環境中開始執行測試。通過這些步驟,您可以使用 Docker 容器設置 JMeter 主從架構,從而更輕鬆地執行負載測試。

Slave 啟動錯誤回報RMI SSL

Error : java.io.FileNotFoundException: rmi_keystore.jks (No such file or directory)

RMI SSL 問題也可以參考官方文件操作: 前往

這不就上扣了嗎? 急什麼:GitHub

參考來源

後續補充 - master 容器增加 ssh 設定

日期:2023/11/09

# Use caster base image
FROM caster/jmeter-base:latest
MAINTAINER CasterHsu

# 更新 apt 套件管理系統的存儲庫,然後安裝 openssh-server 套件
RUN apt-get update && apt-get install -y openssh-server

# 將自定義啟動腳本覆制到容器內
COPY start.sh /start.sh

# 創建 SSH 伺服器需要的目錄
RUN mkdir /var/run/sshd

# 設定 root 用戶的密碼為 "aa123456"
RUN echo 'root:aa123456' | chpasswd

# 修改 SSH 伺服器的設定以允許 root 用戶透過密碼進行登入
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

# 修正 SSH 登入的問題,否則在登入後使用者會被踢出
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd

# 將 PATH 環境變數添加到 /etc/profile 中
RUN echo "export PATH=$PATH" >> /etc/profile

# Ports to be exposed from the container for JMeter Master
EXPOSE 60000 22

# keep container alive
CMD ["/start.sh"]

增加 SSH 設定實在不再守備範圍,花了一點時間才弄出來,而且還有點粗暴....

遇到的問題有:

  1. sshd_config 設定取代

  2. ssh server 啟動失敗,主因容器尚未完成,就下指令結果導致,改用 start.sh 啟動

  3. 連線成功後,user環境變數沒有吃到

  4. ssh 連線指紋變更導致.know_hosts 檔案要修改

首次連接

The authenticity of host '[172.20.160.120]:2222 ([172.20.160.120]:2222)' can't be established.
ED25519 key fingerprint is SHA256:ez34LpGqQO5RGB+jzl0/3+l/W+UIshHcXTgALiZASC0.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? y

Please type 'yes', 'no' or the fingerprint: yes
Warning: Permanently added '[172.20.160.120]:2222' (ED25519) to the list of known hosts.
root@172.20.160.120's password:
Linux a8ad45f5bdd7 5.10.16.3-microsoft-standard-WSL2 #1 SMP Fri Apr 2 22:23:49 UTC 2021 x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

這是 SSH 的一個安全性機制。當你第一次連接到一個 SSH 伺服器時,SSH 客戶端會提示你確認伺服器的身份。

該提示顯示了伺服器的公鑰指紋(在這個例子中是 ED25519 金鑰的 SHA256 指紋)。公鑰指紋是伺服器的一種唯一識別,通常用於確保你正在連接到正確的伺服器,而不是遭到中間人攻擊。

你可以選擇以下其中一種選項:

  • yes: 確認並繼續連接,將伺服器的公鑰指紋保存在你的本地 known_hosts 文件中。

  • no: 中斷連接,不保存伺服器的公鑰指紋。

  • [fingerprint]: 如果你事先知道伺服器的指紋,可以直接輸入該指紋確認連接。

通常,第一次連接時,你應該檢查伺服器的指紋,確保它是正確的伺服器。如果你確信伺服器是安全的,可以選擇 yes 繼續連接。之後,該伺服器的指紋將被保存在 ~/.ssh/known_hosts 文件中,下次再次連接時就不會再提示。

刪除容器後再次連接報錯

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:ez34LpGqQO5RGB+jzl0/3+l/W+UIshHcXTgALiZASC0.
Please contact your system administrator.
Add correct host key in C:\\Users\\xxx/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in C:\\Users\\xxx/.ssh/known_hosts:45
Host key for [172.20.160.120]:2222 has changed and you have requested strict checking.
Host key verification failed.

這個警告表示你先前保存在你的 known_hosts 文件中的遠端主機金鑰已經發生了變化。這可能是正常的,例如當你重新安裝或更換遠端伺服器時,伺服器的金鑰會改變。

然而,這也可能是一個安全性問題的跡象,例如中間人攻擊。當你連接到一個伺服器時,SSH 會檢查伺服器的金鑰是否與先前保存的金鑰相符。如果不匹配,SSH 會發出警告,因為這可能是攻擊的跡象。

在這種情況下,你可以考慮以下步驟:

  1. 確認伺服器身份:確保伺服器確實是你預期的伺服器。你可以通過其他渠道(例如直接連絡伺服器管理員)來確認伺服器的指紋。

  2. 更新 known_hosts 文件:如果你確信伺服器的指紋已經更改並且是合法的,可以手動更新 known_hosts 文件。找到 C:\\Users\\caster.hsu/.ssh/known_hosts 文件,找到包含遠端伺服器指紋的行,將其刪除,然後再次嘗試連接。

    警告:請謹慎操作,確保你知道伺服器的指紋確實已經更改。

  3. 確保安全連接:如果你對伺服器的身份有任何疑慮,最好通過其他渠道驗證伺服器的指紋,以確保安全連接。

這種情況通常發生在伺服器重新安裝、更換金鑰或者其他可能改變伺服器身份的情況下。確保你確實知道伺服器的身份,然後決定是否要更新 known_hosts 文件。

個人筆記 - 存紀錄使用....

jmeter docker
    https://github.com/guitarrapc/docker-jmeter-gui
    https://stackoverflow.com/questions/61324195/how-to-use-jmeter-with-docker-in-a-virtual-machine
    https://stackoverflow.com/questions/67419731/use-jmeter-desktop-application-as-web-app
    https://kaichu.io/posts/build-jmeter-docker-with-plugins/
    https://vepo.medium.com/dockerized-jmeter-84228733e306 ---> 還沒看懂.... 
    https://www.testautomationguru.com/jmeter-distributed-load-testing-using-docker/

excute jmx file in docker
    https://hub.docker.com/r/egaillardon/jmeter
    https://hub.docker.com/r/justb4/jmeter/

jmeter 主從架構
    2023/11/06 啟動指令.... 類
        docker-compose run -dit --name jmeter-master jmmaster /bin/bash
        docker-compose run -dit --name jmeter-slave01 jmslave /bin/bash

        看你要起幾台slave 只是後面觸發時要先找到 slave 的 ipaddress
            docker-compose run -dit --name jmeter-slave[XX] jmslave /bin/bash

    確認container ipAddress
        docker inspect --format '{{ .NetworkSettings.Networks.jmeternet.IPAddress }}' jmeter-master
        docker inspect --format '{{ .NetworkSettings.Networks.jmeternet.IPAddress }}' jmeter-slave01
    真的都找不到 就接下 docker inspect [container name or id] 看所有資訊 json 格式

    單個 jmeter 執行:
        jmeter -n -t LocalTest.jmx  
    主從一起執行: 
        jmeter -n -t LocalTest.jmx -R172.25.0.3
            -R 要填入 docker slave 的內部Ip
    ssh command line:
        ssh root@172.20.160.120 -p 2222

    docker 檔案指令傳入 or 導出
        導出:
            docker cp [containerId]:[containerPath] [localSourcePath]
            Ex. docker cp f0812086941b:/sample-test/LoaclTest.jmx LoaclTest.jmx
        傳入:
            docker cp [localSourcePath] [containerId]:[containerPath]
            Ex. docker cp ./LocalTest.jmx f0812086941b:/sample-test/LocalTest.jmx

        build base & master & slave:
            docker build -t caster/jmeter-base:latest -f ./base.Dockerfile .
            docker build -t caster/jmmaster:latest -f ./master.Dockerfile .
            docker build -t caster/jmslave:latest -f ./slave.Dockerfile .

        docker-compose build & run :
            docker-compose up -d jmmaster
            docker-compose up -d jmslave

Last updated