AWS EC2+Docker+JMeter構建分佈式負載測試基礎架構

概述及範圍

本文介紹有關如何使用AWS EC2+Docker+JMeter創建分佈式負載測試基礎架構。完成所有步驟後,得到的基礎結構如下:

AWS EC2+Docker+JMeter構建分佈式負載測試基礎架構

Part 1中,我們將按照所需的步驟進行操作,以創建適合你需求的自定義JMeter Dockerfiles和映像。然後,在Part 2中,我們將在AWS EC2設置中使用這些元素。接下來開始第一步:

前提條件

為了能夠順利的逐步進行配置和操作,你需要上述每個系統(EC2,Docker和JMeter)的一些基本知識。此外,還需要一個活動的AWS賬戶才能執行所有步驟。

Part 1: Local setup—本地配置

Step 1: 從Dockerfile創建映像

dockerfile是開始使用docker所需的基本元素或“ cookbook”,因此我們將從此開始。我們需要建立2層:1、一是基礎層,該層創建運行JMeter實例所需的基本設置;2、二是邏輯層,它是一個JMeter實例,可以是主節點或從節點;

JMeter base映像的Dockerfile和entrypoint.sh腳本如下所示:Dockerfile:

<code># Use Java 11 JDK Oracle Linux
FROM openjdk:11-jdk-oracle
MAINTAINER Dragos
# Set the JMeter version you want to use
ARG JMETER_VERSION="5.1.1"
# Set JMeter related environment variables

ENV JMETER_HOME /opt/apache-jmeter-${JMETER_VERSION}
ENV JMETER_BIN ${JMETER_HOME}/bin
ENV JMETER_DOWNLOAD_URL https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-${JMETER_VERSION}.tgz
# Set default values for allocation of system resources (memory) which will be used by JMeter
ENV Xms 256m
ENV Xmx 512m
ENV MaxMetaspaceSize 1024m
# Change timezone to local time
ENV TZ="Europe/Bucharest"
RUN export TZ=$TZ
# Install jmeter
RUN yum -y install curl \\
&& mkdir -p /tmp/dependencies \\
&& curl -L --silent ${JMETER_DOWNLOAD_URL} > /tmp/dependencies/apache-jmeter-${JMETER_VERSION}.tgz \\
&& mkdir -p /opt \\
&& tar -xzf /tmp/dependencies/apache-jmeter-${JMETER_VERSION}.tgz -C /opt \\
&& rm -rf /tmp/dependencies
# Set JMeter home
ENV PATH $PATH:$JMETER_BIN
# copy our entrypoint
COPY entrypoint.sh /
RUN chmod +x ./entrypoint.sh
# Run command to allocate the default system resources to JMeter at 'docker run'
ENTRYPOINT ["/entrypoint.sh"/<code>

Entrypoint.sh:

<code>#!/bin/bash
# Run command to allocate the default or custom system resources (memory) to JMeter at 'docker run'
sed -i 's/\\("${HEAP:="\\)\\(.*\\)\\("}"\\)/\\1-Xms'${Xms}' -Xmx'${Xmx}' -XX:MaxMetaspaceSize='${MaxMetaspaceSize}'\\3/' ${JMETER_BIN}/jmeter
exec "$@"/<code>

在基礎層之上,可以創建一個Master層和一個Slave層。這些Dockerfile可以根據你的特定要求進行自定義。現在讓我們看一下我們的邏輯層:Master層的Dockerfile:

<code># Use my custom base image defined above
FROM dragoscampean/testrepo:jmetrubase
MAINTAINER Dragos
# Expose port for JMeter Master
EXPOSE 60000/<code>

Slave層的Dockerfile:

<code>
# Use my custom base image defined above
FROM dragoscampean/testrepo:jmetrubase
MAINTAINER Dragos
# Expose ports for JMeter Slave
EXPOSE 1099 50000
COPY entrypoint.sh /
RUN chmod +x ./entrypoint.sh
# Run command to allocate the default system resources to JMeter at 'docker run' and start jmeter-server with all required parameters
ENTRYPOINT ["/entrypoint.sh"]/<code>

Slave層的Entrypoint.sh:

<code>
sed -i 's/\\("${HEAP:="\\)\\(.*\\)\\("}"\\)/\\1-Xms'${Xms}' -Xmx'${Xmx}' -XX:MaxMetaspaceSize='${MaxMetaspaceSize}'\\3/' ${JMETER_BIN}/jmeter &&
$JMETER_HOME/bin/jmeter-server \\
-Dserver.rmi.localport=50000 \\
-Dserver_port=1099 \\
-Dserver.rmi.ssl.disable=true \\
-Djava.rmi.server.hostname=$HostIP
exec "$@"/<code>

我們不會詳細討論dockerfiles中的所有內容的含義,在網上有很多這樣的文檔。不過值得一提的是與Dockerfiles綁定在一起的entrypoint shell腳本。

docker entrypoints的作用是在運行時將數據初始化或者配置到容器中。在我們的例子中,我們需要它們來指定JMeter允許使用多少內存,並使用一些自定義配置來啟動JMeter服務器,這些配置是基礎設施工作所必需的。這將在“Step 2”部分中舉例說明。

現在,讓我們看一下創建Docker映像所需的命令。順便說一下,Docker圖像表示一組很好地集成在一起的層,是我們需要的環境的穩定快照。從這樣一個映像開始,我們可以生成N個容器,這正是我們在這個特定場景中所需要的,這取決於我們想要模擬的負載。

創建一個簡單的docker映像的命令:docker build /path/to/dockerfile

為docker映像創建一個標籤:docker tag imageId username/reponame:imageTag

同時創建docker映像和標籤:docker build -t username/reponame:imageTag /path/to/dockerfile

Step 2: 從一個映像創建一個容器

現在我們已經準備好映像,可以開始從中創建容器,在其中可以實際運行性能測試腳本。創建一個新的容器:sudo docker run -dit --name containername repository:tag or imageId /bin/bash

啟動/停止容器:docker start containerIddocker stop containerId

訪問正在運行的容器:docker exec -it containerId or containerName /bin/bash

到目前為止,如果你一直使用類似於Step 1中提供的Dockerfile,那麼您應該擁有一個完全可用的Java + JMeter容器。 你可以通過檢查工具版本來測試它,看看是否有任何錯誤,甚至可以嘗試運行你計劃在AWS中擴展的腳本(所有這些都應該在運行的容器中完成):Jmeter -vJava -versionJmeter -n -t -J numberOfThreads=1 /path/to/script.jmx -l /path/to/logfile.jtl

Step 3: 將映像Push/Pull到Dockerhub或任何私有的Docker倉庫(docker登錄CLI後)

測試創建的圖像是否符合要求的標準(容器內的所有內容),通常,最好將此圖像保存到存儲庫中。然後,你可以在後續隨時從那裡提取它,而不必每次都從Dockerfile構建它。

Push映像到dockerhub:docker push username/reponame:imageTag

從dockerhub中Pull已存在的映像(例如jdk映像):docker pull openjdk:version

到此為止,這意味著您已經為cloud setup準備好了一組功能強大的JMeter從屬映像和主映像。

Part 2: Cloud端基礎架構——Infrastructure

可以使用EC2免費層實例,最多750小時/月,持續1年,因此有很多時間進行試驗。注意:對於下面提供的示例,我使用了Ubuntu Server 18.04 LTS實例,因此提供的命令可能無法在其他Linux發行版上使用。

Step 4: 創建安全組——Security Group

使容器內的JMeter實例(master實例或slave實例)能夠通信,

自定義安全組已定義並將其附加到每個主機:

入站規則(Inbound rules):

AWS EC2+Docker+JMeter構建分佈式負載測試基礎架構

出站規則(Outbound rules):

AWS EC2+Docker+JMeter構建分佈式負載測試基礎架構

注意:確保將要成為負載測試基礎結構部分的所有實例分配給此安全組,否則它們可能無法相互通信。

Step 5: 創建一個IAM策略(可選)

假設您只需要一個由1個JMeter主節點和2個從節點組成的基礎架構。在這種情況下,訪問每個實例並對其進行配置(安裝docker +啟動容器)相對容易。

但是,如果需要處理的實例超過3個,會發生什麼情況呢?

手動逐個配置變得極其乏味,手動並不是一個好主意。這時,你將需要一個系統,能夠管理你正在使用的大量容器。一些著名的工具,如谷歌的Kubernetes,或者Rancher等工具。

由於當前使用的是AWS,因此這兩種解決方案似乎過於龐大了,因為亞馬遜針對這一點提供了一個開箱即用的解決方案:“Run Command”功能使我們可以同時在多個EC2實例上執行Shell腳本。因此,我們不必訪問每個實例,安裝docker並一次一個實例地啟動容器。

能夠通過“Run Command”功能在EC2實例上執行命令的唯一要求是,適當的IAM角色已與該實例相關聯。我將IAM策略命名為“ EC2Command”,併為每個新創建的實例選擇了該策略(但是稍後可以通過“attach/replace role”功能將該角色分配給該實例):

AWS EC2+Docker+JMeter構建分佈式負載測試基礎架構

AWS EC2+Docker+JMeter構建分佈式負載測試基礎架構

當您創建角色時,請確保將“AmazonEC2RoleforSSM”策略附加到您的角色上,這樣就可以了。

AWS EC2+Docker+JMeter構建分佈式負載測試基礎架構

現在您可以使用“Run command”功能對多個實例批量執行腳本。這將我們帶入流程的下一步。

Step 6: 在測試機器上安裝Docker

現在,你需要在EC2主機上安裝docker,以便可以啟動容器並將它們連接在一起以進行分佈式負載測試。

直接使用命令(直接在Ubuntu上的實例終端中執行):

<code>sudo apt-get install  curl  apt-transport-https ca-certificates software-properties-common \\
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add \\
&& sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \\
&& sudo apt-get update \\
&& sudo apt-get install -y docker-ce \\
&& sudo usermod -aG docker $USER \\
&& sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose \\
&& sudo chmod +x /usr/local/bin/docker-compose \\
&& sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose/<code>

通過“Run command”執行的Shell腳本:

<code>#!/bin/bash
sudo apt-get install curl apt-transport-https ca-certificates software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get install -y docker-ce
USER_DOCKER=$(getent passwd {1000..60000} | grep "/bin/bash" | awk -F: '{ print $1}')
sudo usermod -aG docker $USER_DOCKER/<code>

理想情況下,您將在多個EC2實例上運行第二個腳本,之後它們都將具有可用的Docker版本。下一步是配置主節點和從屬節點:

Step 7: 配置主節點——Master Node

在某些情況下,你甚至不需要多個從屬節點來分佈式運行測試,比如,當你有一臺功能強大的主機並且該計算機能夠生成目標的負載量時,對於這種特定情況,不需要Step 8和Step 9。對於這種情況,你甚至不想使用容器並直接在主機上安裝JMeter。

但是,假設你確實需要一個Master + Slaves系統,然後繼續啟動Master容器:直接使用命令(直接在Ubuntu上的實例終端中執行):

<code>HostIP=$(ip route show | awk '/default/ {print $9}') \\
&& docker pull dragoscampean/testrepo:jmetrumaster \\
&& docker run -dit --name master --network host -e HostIP=$HostIP -e Xms=256m -e Xmx=512m -e MaxMetaspaceSize=512m -v /opt/Sharedvolume:/opt/Sharedvolume dragoscampean/testrepo:jmetrumaster /bin/bash/<code>

通過“Run command”執行的Shell腳本:

<code>#!/bin/bash
HostIP=$(ip route show | awk '/default/ {print $9}')
docker pull dragoscampean/testrepo:jmetrumaster
docker run -dit --name master --network host -e HostIP=$HostIP -e Xms=256m -e Xmx=512m -e MaxMetaspaceSize=512m -v /opt/Sharedvolume:/opt/Sharedvolume dragoscampean/testrepo:jmetrumaster /bin/bash/<code>

腳本的第一行將機器的私有IP存儲在變量“HostIP”中。主的HostIP不用於任何目的,僅使用從屬節點的HostIP。我們將在Step 9看到具體要做什麼。現在,請記住,你可以快速訪問每個容器中主機的專用IP地址。

第二行很簡單,只是從適當的倉庫中獲取圖像。

最後一行創建我們將要使用的容器。此命令中有一些要點:1、'--network host '命令啟用主機連網,這意味著容器內的應用程序(JMeter),將在‘entrypoint.sh’腳本公開的端口上可用。如果沒有它,我就無法進行設置。問題是,即使腳本是在從節點上執行的,由於錯誤(

java.rmi.ConnectException: Connection refused to host:masterPrivateIP),主節點上也沒有聚集任何結果。注意,我在較老版本的JMeter(如3.x.x)中沒有遇到這個問題

2、‘- e Xms=256m -e Xmx=512m -e MaxMetaspaceSize=512m’ 是Xms和Xmx的參數化,MaxMetaspaceSize決定了允許使用JMeter的內存量。這是通過首先在容器內設置一些環境變量來完成的。然後,在“ entrypoint.sh”腳本中運行命令,將更改JMeter的“ / bin”文件夾中的“JMeter”文件。如果未指定這些值,則使用默認值。

要進一步瞭解這些變量代表什麼以及如何設置它們,請閱讀以下內容:Xmx計算如下:系統總內存-(OS使用的內存+ JVM使用的內存+在計算機上運行所需的任何其他腳本)如果您有一臺專用的測試機器,為避免在測試運行時重新分配Xms,請從一開始就設置Xms = Xmx

MaxMetaspaceSize跟蹤所有加載的類元數據和靜態內容(靜態方法,原始變量和對象引用)

例如:一臺專用機器上64 GB RAMXmx = 56GXms

= 56GMaxMetaspaceSize = 4096 MB這為操作系統和其他進程留下了將近4GB的空間,這綽綽有餘。

3、-v /opt/Sharedvolume:/opt/Sharedvolume userName/repoName:imageTag 該命令只是將主機上的文件夾映射到容器內的文件夾,你將在其中保存腳本文件和生成的日誌。我們將不做進一步詳細介紹,但是如果您想了解有關卷映射的更多信息,請參閱本文和迷你教程。

Step 8: 配置從節點——Slave Nodes

HostIP”變量僅在“entrypoint.sh”腳本中用於此處,以啟用從master服務器到slave服務器的遠程訪問(“-Djava.rmi。server.hostname = $ HostIP”)。直接使用命令(直接在Ubuntu上的實例終端中執行):

<code>HostIP=$(ip route show | awk '/default/ {print $9}') \\
&& docker pull dragoscampean/testrepo:jmetruslave \\
&& docker run -dit --name slave --network host -e HostIP=$HostIP -e HostIP=$HostIP -e Xms=256m -e Xmx=512m -e MaxMetaspaceSize=512m dragoscampean/testrepo:jmetruslave /bin/bash/<code>

通過“Run command”執行的Shell腳本:

<code>#!/bin/bash
HostIP=$(ip route show | awk '/default/ {print $9}')
docker pull dragoscampean/testrepo:jmetruslave
docker run -dit --name slave --network host -e HostIP=$HostIP -e Xms=256m -e Xmx=512m -e MaxMetaspaceSize=512m dragoscampean/testrepo:jmetruslave /bin/bash/<code>

Step 9: 分佈式模式下運行腳本

到此,準備就緒,可以開始運行測試了。這是我們需要在master主節點上運行以開始運行分佈式測試的命令:

<code>jmeter -n -t /path/to/scriptFile.jmx -Dserver.rmi.ssl.disable=true -R host1PrivateIP, host2PrivateIP,..., hostNPrivateIP -l /path/to/logfile.jtl/<code>

總結:

按照上面的操作步驟,可以實現AWS EC2+Jmeter+Docker的分佈式性能測試,可能會遇到一些問題,完全沒問題那是不可能的。比如:該文提到了一個EC2實例中有太多Websocket連接時可能遇到的問題。另一個例子是我的一位同事在對Apa​​che服務器進行負載測試時遇到的情況,他會在JMeter中遇到各種連接錯誤,我們最初認為這是來自被測試的服務器。解決這個問題的方法來自這篇簡短的文章。

我在一個項目中偶然發現的一個問題是,在嘗試從一臺計算機執行大約20000個線程時,進行了一些數據驅動的測試。如果在Linux / MacOS終端中鍵入“ ulimit -a”,則會看到名為“ open files”的行。問題在於該屬性在測試計算機上設置為1024。使用JMeter運行數據驅動的測試時,此工具將為每個啟動的線程打開.csv文件或描述符,一旦並行線程數超過1024,我將收到錯誤消息。解決方案:

是從'/etc/security/limits'文件中編輯'open files'的最大值,並設置為'unlimited'。


分享到:


相關文章: