> For the complete documentation index, see [llms.txt](https://xu-min-chang.gitbook.io/caster-develop-note/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://xu-min-chang.gitbook.io/caster-develop-note/java/util-gong-ju/jmeter-performing-distributed-load-testing-with-docker.md).

# JMeter – Performing Distributed Load Testing with Docker

**日期: 2023/11/06**

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

### 架構圖

<figure><img src="/files/9oZHiQ16SBVKHDAR2Ll2" alt=""><figcaption></figcaption></figure>

### 先決條件

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

## 建置容器

### JMeter Base Dockerfile

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

1. **選擇 Java 11 版本：** 使用 `openjdk-11-jre-slim` 精簡版，以減小映像的大小。
2. **安裝實用工具：** 我額外增加安裝了一些實用工具，如 `wget`、`unzip` 和 `telnet`等。
3. **安裝 JMeter：** 下載並安裝了指定版本的 Apache JMeter（這裡使用的是版本 5.6）。
4. **使用版本變數：** 我創建了一個版本變數 `JMETER_VERSION`，以便未來維護更加方便。
5. **添加插件文件夾：** 將所有自定義插件添加到 JMeter 的 `lib` 和 `lib/ext` 文件夾中。
6. **添加示例測試：** 添加了一個示例測試的文件夾。

```docker
# 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

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

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

### JMeter Client/Master Dockerfile：&#x20;

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

```docker
# 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常駐運行

```docker
# 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

```bash
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 訊息後。以下是示例內容：

```log
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 地址：

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

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

```bash
docker inspect [容器名稱或 ID]
```

### 執行 JMeter 測試

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

  ```bash
  jmeter -n -t sample-test.jmx
  ```

  執行結果：

  ```log
  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 地址，以逗號分隔。

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

  執行結果：

  ```log
  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&#x20;

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

RMI SSL 問題也可以參考官方文件操作: [前往](https://jmeter.apache.org/usermanual/remote-test.html#setup_ssl)

這不就上扣了嗎? 急什麼：[GitHub](https://github.com/MinchangHsu/JMeterLoadTest)

### 參考來源

* [jmeter-distributed-load-testing-using-docker](https://www.testautomationguru.com/jmeter-distributed-load-testing-using-docker/)
* [JMeter Blockly JMX](https://jmeter-plugins.org/editor/)

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

日期:2023/11/09

```docker
# 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  檔案要修改

#### 首次連接

```bash
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` 文件中，下次再次連接時就不會再提示。

#### 刪除容器後再次連接報錯

```bash
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    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
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xu-min-chang.gitbook.io/caster-develop-note/java/util-gong-ju/jmeter-performing-distributed-load-testing-with-docker.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
