Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for multiple .jmx test plans executed at once #58

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Prerequisites to use this image:

Docker run template:
```
docker run -v <oath to jmx>:/plans -v <path to pem>:/keys -v <path to logs>:/logs \
docker run -v <path to jmx>:/plans -v <path to pem>:/keys -v <path to logs>:/logs \
--env AWS_ACCESS_KEY_ID=<key id> \
--env AWS_SECRET_ACCESS_KEY=<access key> \
--env AWS_DEFAULT_REGION=<region> \
Expand All @@ -45,6 +45,19 @@ docker run -v $PWD/plans:/plans -v $PWD/keys:/keys -v $PWD/logs:/logs \
--env MINION_COUNT=5 \
smithmicro/lucy /plans/demo.jmx
```
*Since 5.5:*
Run two .jmx test plans in parallel, 1st using 4 minions, 2nd and following using 2 minions:
```
docker run -v $PWD/plans:/plans -v $PWD/keys:/keys -v $PWD/logs:/logs \
--env AWS_ACCESS_KEY_ID=ABCDEFGHIJKLMNOPQRST \
--env AWS_SECRET_ACCESS_KEY=abcdefghijklmnopqrstuvwxyz0123456789ABCDEF \
--env AWS_DEFAULT_REGION=us-east-1 \
--env SECURITY_GROUP=sg-12345678 \
--env SUBNET_ID=subnet-12345678,subnet-87654321 \
--env KEY_NAME=jmeter-key \
--env MINION_COUNT=4,2 \
smithmicro/lucy /plans/TestOne.jmx,/plans/TestTwo.jmx,/plans/TestThree.jmx
```

## Architecture
This Docker image replaces the JMeter master/slave nomenclature with *Gru*, *Minion* and *Lucy*. *Gru* manages the *Minions* from within ECS, but *Lucy* orchestrates the entire process.
Expand Down Expand Up @@ -98,22 +111,22 @@ The following required and optional environment variables are supported:
|AWS_DEFAULT_REGION|Yes|None|AWS Region (e.g. `us-east-1`)|
|AWS_ACCESS_KEY_ID|Yes|None|AWS Access Key|
|AWS_SECRET_ACCESS_KEY|Yes|None|AWS Secret Key|
|INPUT_JMX|Yes|None|File path of JMeter Test file to run (.jmx). You can optionally specify this as the first command line option of `docker run`|
|INPUT_JMX|Yes|None|File path of JMeter Test file to run (.jmx). You can optionally specify this as the first command line option of `docker run`.<br>*Since 5.5:* Comma separated list of .jmx files|
|KEY_NAME|Yes|None|AWS Security Key Pair .pem file (do not specify the .pem extension)|
|SECURITY_GROUP|Yes|None|AWS Secuirty group that allows ports 22,1099,50000,51000/tcp and 4445/udp from all ports (e.g. sg-12345678)|
|SUBNET_ID|Yes|None|One or more Subnets (comma separated) that are assigned to your VPC|
|VPC_ID||VPC assigned to SUBNET_ID|We dautomatically erive this from your SUBNET_ID|
|VPC_ID||VPC assigned to SUBNET_ID|We automatically derive this from your SUBNET_ID|
|JMETER_VERSION||latest|smithmicro/jmeter Image tag. See Docker Hub for [available versions](https://hub.docker.com/r/smithmicro/jmeter/tags/).|
|INSTANCE_TYPE||t2.micro|To double your memory, pass `t2.small`|
|MEM_LIMIT||950m|If you are using t2.small, set MEM_LIMIT to `1995m`|
|MINION_COUNT||2||
|MINION_COUNT||2|*Since 5.5:* Comma separated list of minion counts for each .jmx specified in INPUT_JMX in its order. If MINION_COUNT list is shorter than INPUT_JMX, last value of MINION_COUNT will be used for remaining .jmx test plans from INPUT_JMX.|
|PEM_PATH||/keys|This must match your Volume map. See Volume section above.|
|CLUSTER_NAME||JMeter|Name that appears in your AWS Cluster UI|
|GRU_PRIVATE_IP||None|Set to `true` if you would like to run Lucy within AWS. See GitHub [Issue 8](https://github.com/smithmicro/jmeter-ecs/issues/8) for details.|
|GRU_PRIVATE_IP||None|Set to non-empty string if you would like to run Lucy within AWS. See GitHub [Issue 8](https://github.com/smithmicro/jmeter-ecs/issues/8) for details.|
|JMETER_FLAGS||None|Custom JMeter command line options. For example, passing `-X` will tell the Minion to exit at the end of the test|
|RETAIN_CLUSTER||None|Set to `true` if you want to re-use your cluster for future tests. Warning, you will incur AWS charges if you leave your cluster running.|
|RETAIN_CLUSTER||None|Set to non-empty string if you want to re-use your cluster for future tests. Warning, you will incur AWS charges if you leave your cluster running.|
|CUSTOM_PLUGIN_URL||None|The URL of a custom plugin you want to install in the Minions. File will be copied to $JMETER_HOME/lib/ext.||
|COPY_DIR||None|Set to `true` if you want to copy the directory in which the .jmx file is located to all Minions and Gru. The files will be located in all Docker containers in ` /plans`. Update your JMX file to reference external files at `/plans/...`|
|COPY_DIR||None|Set to non-empty string if you want to copy the directory in which the .jmx file is located to all Minions and Gru. The files will be located in all Docker containers in ` /plans`. Update your JMX file to reference external files at `/plans/...`. <br>*Since 5.5:* Directory from the first .jmx will be used.|

## Notes
All current JMeter Plugins are installed via the Plugins Manager.
Expand Down
48 changes: 48 additions & 0 deletions jmeter/5.2/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
FROM openjdk:8-alpine

LABEL maintainer="David Sperling <dsperling@smithmicro.com>"

ENV JMETER_VERSION apache-jmeter-5.2
ENV JMETER_HOME /opt/$JMETER_VERSION
ENV PATH $PATH:$JMETER_HOME/bin
ENV CMDRUNNER_VERSION 2.2
ENV PLUGINMGR_VERSION 1.3

# overridable environment variables
ENV RESULTS_LOG results.jtl
ENV JMETER_FLAGS=
ENV CUSTOM_PLUGIN_URL=

# Install the required tools for JMeter
RUN apk add --update --no-cache \
curl \
openssh-client

WORKDIR /opt

# install JMeter and the JMeter Plugins Manager
RUN curl -O https://archive.apache.org/dist/jmeter/binaries/$JMETER_VERSION.tgz \
&& tar -xvf $JMETER_VERSION.tgz \
&& rm $JMETER_VERSION.tgz \
&& rm -rf $JMETER_VERSION/docs $JMETER_VERSION/printable_docs \
&& cd $JMETER_HOME/lib \
&& curl -OL http://search.maven.org/remotecontent?filepath=kg/apc/cmdrunner/$CMDRUNNER_VERSION/cmdrunner-$CMDRUNNER_VERSION.jar \
&& cd $JMETER_HOME/lib/ext \
&& curl -OL4 http://search.maven.org/remotecontent?filepath=kg/apc/jmeter-plugins-manager/$PLUGINMGR_VERSION/jmeter-plugins-manager-$PLUGINMGR_VERSION.jar \
&& java -cp jmeter-plugins-manager-$PLUGINMGR_VERSION.jar org.jmeterplugins.repository.PluginManagerCMDInstaller

# install all available plugins except for those that are deprecated
RUN PluginsManagerCMD.sh install-all-except jpgc-hadoop,jpgc-oauth \
&& sleep 2 \
&& PluginsManagerCMD.sh status

# copy our entrypoint
COPY entrypoint.sh /opt/jmeter/

WORKDIR /logs

EXPOSE 1099 50000 51000 4445/udp

# default command in the entrypoint is 'minion'
ENTRYPOINT ["/opt/jmeter/entrypoint.sh"]
CMD ["minion"]
76 changes: 76 additions & 0 deletions jmeter/5.2/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/bin/sh
#
# Main entrypoint for our Docker image - runs Gru, Minions or other commands

# any .jmx file passed in the command line we act as 'Gru'
if [ ${1##*.} = 'jmx' ]; then

if [ "$MINION_HOSTS" = '' ]; then
echo "MINION_HOSTS must be specified - a command separated list of hostnames or IP addresses"
exit 1
fi
echo "Connecting to $MINION_HOSTS"

# AWS Public HOSTNAME API
echo "Detecting an AWS Environment"
PUBLIC_HOSTNAME=$(curl -s --max-time 5 http://169.254.169.254/latest/meta-data/public-hostname)

if [ "$PUBLIC_HOSTNAME" = '' ]; then
echo "Not running in AWS. Using Gru HOSTNAME $HOSTNAME"
else
HOSTNAME=$PUBLIC_HOSTNAME
echo "Using Gru AWS Public HOSTNAME $HOSTNAME"
fi
# empty the logs directory, or jmeter may fail
rm -rf /logs/report /logs/*.log /logs/*.jtl

# remove setting JAVA heap and use the RUN_IN_DOCKER variable
sed -i 's/-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m//' $JMETER_HOME/bin/jmeter
sed -i 's/# RUN_IN_DOCKER/RUN_IN_DOCKER/' $JMETER_HOME/bin/jmeter

# run jmeter in client (gru) mode
exec jmeter -n $JMETER_FLAGS \
-R $MINION_HOSTS \
-Dclient.rmi.localport=51000 \
-Dserver.rmi.ssl.disable=true \
-Djava.rmi.server.hostname=${PUBLIC_HOSTNAME} \
-l $RESULTS_LOG \
-t $1 \
-e -o /logs/report

fi

# act as a 'Minion'
if [ "$1" = 'minion' ]; then

# AWS Public HOSTNAME API
echo "Detecting an AWS Environment"
PUBLIC_HOSTNAME=$(curl -s --max-time 5 http://169.254.169.254/latest/meta-data/public-hostname)

if [ "$PUBLIC_HOSTNAME" = '' ]; then
echo "Not running in AWS. Using Minion HOSTNAME $HOSTNAME"
else
HOSTNAME=$PUBLIC_HOSTNAME
echo "Using Minion AWS Public HOSTNAME $HOSTNAME"
fi

# remove setting JAVA heap and use the RUN_IN_DOCKER variable
sed -i 's/-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m//' $JMETER_HOME/bin/jmeter
sed -i 's/# RUN_IN_DOCKER/RUN_IN_DOCKER/' $JMETER_HOME/bin/jmeter

# install custom plugin if requested
if [ "$CUSTOM_PLUGIN_URL" != '' ]; then
echo "Installing custom plugin $CUSTOM_PLUGIN_URL"
CUSTOM_PLUGIN_FILE="${CUSTOM_PLUGIN_URL##*/}"
curl -o $JMETER_HOME/lib/ext/$CUSTOM_PLUGIN_FILE $CUSTOM_PLUGIN_URL
fi

# run jmeter in server (minion) mode
exec jmeter-server -n $JMETER_FLAGS \
-Dserver.rmi.localport=50000 \
-Dserver.rmi.ssl.disable=true \
-Djava.rmi.server.hostname=${HOSTNAME}

fi

exec "$@"
22 changes: 11 additions & 11 deletions jmeter/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
FROM openjdk:8-alpine
FROM eclipse-temurin:18-alpine

LABEL maintainer="David Sperling <dsperling@smithmicro.com>"

ENV JMETER_VERSION apache-jmeter-5.2
ENV JMETER_HOME /opt/$JMETER_VERSION
ENV PATH $PATH:$JMETER_HOME/bin
ENV CMDRUNNER_VERSION 2.2
ENV PLUGINMGR_VERSION 1.3
ENV JMETER_VERSION=apache-jmeter-5.5
ENV JMETER_HOME=/opt/$JMETER_VERSION
ENV PATH=$PATH:$JMETER_HOME/bin
ENV CMDRUNNER_VERSION=2.3
ENV PLUGINMGR_VERSION=1.8

# overridable environment variables
ENV RESULTS_LOG results.jtl
ENV JMETER_FLAGS=
ENV CUSTOM_PLUGIN_URL=
ENV JMETER_FLAGS=""
ENV CUSTOM_PLUGIN_URL=""

# Install the required tools for JMeter
RUN apk add --update --no-cache \
Expand All @@ -31,8 +30,9 @@ RUN curl -O https://archive.apache.org/dist/jmeter/binaries/$JMETER_VERSION.tgz
&& curl -OL4 http://search.maven.org/remotecontent?filepath=kg/apc/jmeter-plugins-manager/$PLUGINMGR_VERSION/jmeter-plugins-manager-$PLUGINMGR_VERSION.jar \
&& java -cp jmeter-plugins-manager-$PLUGINMGR_VERSION.jar org.jmeterplugins.repository.PluginManagerCMDInstaller

# install all available plugins except for those that are deprecated
RUN PluginsManagerCMD.sh install-all-except jpgc-hadoop,jpgc-oauth \
# install all available plugins except for those that are deprecated, not compatible or under licence
RUN PluginsManagerCMD.sh install-all-except \
jpgc-oauth,schema-assertion,ulp-jmeter-autocorrelator-plugin,ulp-jmeter-gwt-plugin,ulp-jmeter-videostreaming-plugin,di-kafkameter,tilln-iso8583,jmeter.backendlistener.elasticsearch,jmeter-grpc-request,websocket-sampler \
&& sleep 2 \
&& PluginsManagerCMD.sh status

Expand Down
17 changes: 14 additions & 3 deletions jmeter/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,26 @@ services:
volumes:
- ../plans:/plans
- ./logs:/logs
command: "/plans/TestCert.jmx"
command: "/plans/TestOne.jmx,/plans/TestTwo.jmx"
links:
- minion1
environment:
- MINION_HOSTS=minion1
- minion2
- minion3
environment:
- MINION_HOSTS=minion1,minion2,minion3
- MINION_COUNT=1,2
- JMETER_FLAGS=-X
ports:
- "4445:4445/udp"
minion1:
image: smithmicro/jmeter:latest
environment:
- JMETER_FLAGS=-Djavax.net.ssl.keyStore=/plans/test.p12
minion2:
image: smithmicro/jmeter:latest
environment:
- JMETER_FLAGS=-Djavax.net.ssl.keyStore=/plans/test.p12
minion3:
image: smithmicro/jmeter:latest
environment:
- JMETER_FLAGS=-Djavax.net.ssl.keyStore=/plans/test.p12
60 changes: 45 additions & 15 deletions jmeter/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
# any .jmx file passed in the command line we act as 'Gru'
if [ ${1##*.} = 'jmx' ]; then

if [ "$MINION_COUNT" = '' ]; then
echo "MINION_COUNT must be specified - a command separated list of minion counts of the same length as list of .jmx files in 1st command line argument"
exit 1
fi
if [ "$MINION_HOSTS" = '' ]; then
echo "MINION_HOSTS must be specified - a command separated list of hostnames or IP addresses"
exit 1
Expand All @@ -24,19 +28,46 @@ if [ ${1##*.} = 'jmx' ]; then
# empty the logs directory, or jmeter may fail
rm -rf /logs/report /logs/*.log /logs/*.jtl

# remove setting JAVA heap and use the RUN_IN_DOCKER variable
# remove setting JAVA heap
sed -i 's/-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m//' $JMETER_HOME/bin/jmeter
sed -i 's/# RUN_IN_DOCKER/RUN_IN_DOCKER/' $JMETER_HOME/bin/jmeter

# run jmeter in client (gru) mode
exec jmeter -n $JMETER_FLAGS \
-R $MINION_HOSTS \
-Dclient.rmi.localport=51000 \
-Dserver.rmi.ssl.disable=true \
-Djava.rmi.server.hostname=${PUBLIC_HOSTNAME} \
-l $RESULTS_LOG \
-t $1 \
-e -o /logs/report

# run jmeter in client (gru) mode for all testplans
TEMP_INPUT_JMX=$1,
TEMP_MINION_COUNT=$MINION_COUNT,
TEMP_MINION_HOSTS=$MINION_HOSTS

while [ "$TEMP_INPUT_JMX" ]
do
INPUT_JMX_FOR_TESTPLAN=${TEMP_INPUT_JMX%%,*}
MINION_COUNT_FOR_TESTPLAN=${TEMP_MINION_COUNT%%,*}
MINION_HOSTS_FOR_TESTPLAN=$(echo $TEMP_MINION_HOSTS | cut -f1-$MINION_COUNT_FOR_TESTPLAN -d,)
PORT_FOR_TESTPLAN=$((51000 + ${#TEMP_INPUT_JMX}))
NAME_FOR_TESTPLAN=$(basename $INPUT_JMX_FOR_TESTPLAN .jmx)
echo MINION_COUNT_FOR_TESTPLAN $MINION_COUNT_FOR_TESTPLAN
echo TEMP_MINION_HOSTS $TEMP_MINION_HOSTS

jmeter -n $JMETER_FLAGS \
-R $MINION_HOSTS_FOR_TESTPLAN \
-Dclient.rmi.localport=$PORT_FOR_TESTPLAN \
-Dserver.rmi.ssl.disable=true \
-Djava.rmi.server.hostname=${PUBLIC_HOSTNAME} \
-l $NAME_FOR_TESTPLAN.jtl \
-j $NAME_FOR_TESTPLAN.log \
-t $INPUT_JMX_FOR_TESTPLAN \
-e -o /logs/$NAME_FOR_TESTPLAN &

TEMP_INPUT_JMX=${TEMP_INPUT_JMX#*,}
TEMP_MINION_COUNT=${TEMP_MINION_COUNT#*,}
TEMP_MINION_HOSTS=$(echo $TEMP_MINION_HOSTS | cut -f$(($MINION_COUNT_FOR_TESTPLAN + 1))- -d,)
done

# wait for jmeter processes to finish
while pgrep jmeter > /dev/null
do
sleep 20
done

exit

fi

Expand All @@ -54,10 +85,9 @@ if [ "$1" = 'minion' ]; then
echo "Using Minion AWS Public HOSTNAME $HOSTNAME"
fi

# remove setting JAVA heap and use the RUN_IN_DOCKER variable
# remove setting JAVA heap
sed -i 's/-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m//' $JMETER_HOME/bin/jmeter
sed -i 's/# RUN_IN_DOCKER/RUN_IN_DOCKER/' $JMETER_HOME/bin/jmeter


# install custom plugin if requested
if [ "$CUSTOM_PLUGIN_URL" != '' ]; then
echo "Installing custom plugin $CUSTOM_PLUGIN_URL"
Expand Down
Loading