Buenas prácticas JMeter 

Apache JMeter es un software de código abierto diseñado y construido en Java que permite realizar baterías de pruebas sobre un sistema. Inicialmente nació para probar aplicaciones web, pero hoy está extendido para prácticamente cualquier tipo de aplicaciones. 

Es un proyecto de la Apache Software Fundation y, como tal, recomendamos tener como referencia su página y documentación oficial

JMeter en si mismo es una aplicación de escritorio multiplataforma con una interfaz gráfica que permite configurar suites de pruebas que se ejecutarán bajo demanda. Además, ofrece una herramienta CLI (Command Line Interface), la cual, como veremos, va a sernos muy útil en el ámbito de este artículo. 

Este artículo explica una casuística de uso real de JMeter para probar una API Rest servida por artefactos de Springboot corriendo en Pods de Openshift Kubernetes. 

No nos detendremos a explicar muy detalladamente las posibilidades que ofrece JMeter como tal, ya que son muy versátiles y extensas. Explicaremos someramente en qué consiste el caso de uso utilizado y cómo configurarlo. A continuación explicaremos una posible problemática que podría adulterar la precisión de los resultados de JMeter en entornos Cloud. Por último aportaremos una solución a dicha problemática. 

Versiones utilizadas

  • Infraestructura  
    1. Apache JMeter 5.3 
    2. Kubernetes 1.18.3 
    3. Openshift 4.5
  • Software (no son relevantes)  
    1. Java 8 
    2. Springboot 2.3.3 
    3. Spring 5.2.9

Caso de uso

Esta entrada se basa en la experiencia de un Proyecto real llevado a cabo durante el último trimestre de 2020. Se trata de la refactorización de un sistema de medios de pago Java legacy  (Java <= 7) monolítico a un sistema en Cloud con microservicios con Java (>=8) y Springboot. 

El núcleo del requisito consistía en estudiar los tiempos de respuesta del aplicativo desplegado con la nueva arquitectura en condiciones similares a Producción. Para realizar dicha simulación y poder obtener los tiempos de respuesta, utilizamos Apache JMeter 5.3 configurando un módulo de peticiones HTTP dentro de un grupo de hilos que define la concurrencia deseada. 

Configuración de las pruebas

Como se ha dicho anteriormente, no vamos extendernos en las muchas posibilidades que ofrece el cliente gráfico de JMeter como tal, aunque a continuación sí detallamos someramente cómo configuramos la test suite que necesitamos en nuestro caso de estudio. 

Para iniciarlo, ejecutar el comando jmeter  tras incluirlo en el PATH y sin ningún modificador. 

Interfaz en árbol

JMeter ofrece una intuitiva interfaz. En la parte izquierda se añaden en forma de árbol los módulos, configuraciones y reportes que necesitamos para nuestras pruebas. Al seleccionar un nodo, en la parte derecha se configuran los elementos y parámetros relativos al nodo seleccionado. 

Las pruebas se ejecutan leyendo los módulos de arriba a abajo, existiendo la posibilidad de crear bucles de ejecución concurrente, definición de contadores, variables y otros elementos de control, como veremos a continuación. 

Nodo raíz – Test Plan 

De este nodo cuelgan todas las configuraciones y módulos. Permite definir parametrización global y estrategias de ejecución de las pruebas. Obsérvese que los parámetros pueden llevar valores dinámicos aceptando código JavaScript (v. variable «DIA»). 

Para hacer referencia a las variables definidas en este módulo (u otros módulos de parametrización), se emplea la nomenclatura de ${nombre_variable}

Parámetros de usuario 

Este módulo permite la configuración de diferentes usuarios y parámetros asociados a ellos.

Thread Group

Se configura un nodo base Thread Group por cada grupo de concurrencia que se vaya a lanzar. Los módulos que estén jerárquicamente bajo este grupo se ejecutarán concurrentemente y de arriba a abajo. En nuestra prueba, sólo hay un grupo de hilos. Para definir la concurrencia y el comportamiento del grupo de hilos, se deben definir los siguientes parámetros: 

  • Número de hilos: simula los usuarios concurrentes que estarán llevando a cabo las pruebas definidas en el grupo.
  • Periodo de rampa de subida: define un número de segundos hasta alcanzar el rendimiento pleno de la prueba, generando un crecimiento aritmético desde 0 hasta N hilos en esos segundos.
    Por ejemplo, si el número de hilos definido es 50 y se define una rampa de subida de 300 segundos (5 minutos), JMeter iniciará los hilos a razón de 10 nuevos hilos por minuto durante esos 5 primeros minutos.
  • Loop count: número de veces que se ejecutarán las pruebas definidas en el grupo por cada hilo configurado.

Counter

Permite generar un valor diferente para cada una de las pruebas que van a ejecutarse. Esto es muy útil para diferenciar cada test y permitir así una trazabilidad específica. Cuando un hilo termina una ejecución, y si no ha alcanzado el número de pruebas definido en Loop Count, volverá a empezar el bucle del Thread Group pasando por este contador para incrementarlo en uno, como se ve en la siguiente imagen. 

HTTP Request

Este módulo representa el módulo más importante de nuestras pruebas, ya que define la petición HTTP a nuestro sistema de microservicios de Springboot alojado en OpenShift. 

Como se puede ver en la imagen, este módulo, junto a su hijo «Gestor de Cabecera HTTP» tiene todo lo necesario para construir una petición HTTP. 

Delay

El último elemento incluido en el bucle de concurrencia es un retraso en milisegundos antes de pasar a la siguiente iteración. 

Reports

JMeter tiene un amplio conjunto de módulos para generar informes. En nuestro proyecto no los hemos utilizado, ya que las mediciones las hemos hecho a través de la escritura de logs utilizando el stack EFG. Pero mostramos la imagen de lo que sería un módulo de salida de informe agregado. 

Ejecución de la prueba

Desde la interfaz gráfica

Desde el propio cliente se muestran los botones de play, que tienen modificadores para pausarla, retomarla, reiniciar, etc. Es intuitivo y no merece una explicación detallada. 

Es importante reseñar es que la propia herramienta advierte mediante un mensaje al inicio de que no está recomendado el uso de la versión gráfica para pruebas de carga, orientando su uso únicamente para creación de test y pruebas iniciales para comprobar la adecuación de los mismos. 

Desde línea de comandos (CLI)

JMeter ofrece la posibilidad de trabajar a través de su herramienta CLI, lo cual es necesario cuando se trata de realizar pruebas de carga o rendimiento. Además, la versión de comandos nos da la posibilidad de acceder al shell de un contenedor en Cloud que tenga el la herramienta instalada y trabajar aprovechando el networking que la plataforma Cloud nos ofrece. De esta forma evitamos que posibles latencias de red adulteren los resultados que la propia herramienta mide y ofrece. 

En el siguiente punto de este artículo describimos cómo configurarlo y ejecutar pruebas en Cloud utilizando OpenShift. Pero antes nos vamos a centrar en el propio comando y en los ficheros relacionados con JMeter. 

Archivos relacionados: 

  • JMX: es el xml donde JMeter almacena la configuración de un test. Es decir, la herramienta gráfica, al configurar el test y guardarlo, lo graba en este formato. Lógicamente, permite también cargar un test en la herramienta utilizando este fichero. 
  • JTL: almacena mediante una estrategia CSV el resultado acumulado de las pruebas. El contenido de este fichero puede exportarse mediante JMeter a un formato legible en forma de Web estática. 
  • Report Folder: JMeter también permite generar directamente la carpeta con el contenido HTML estático (el mencionado en el punto anterior), sin tener que generarlo a través de un JTL previo. 

Un ejemplo de comando sería:

jmeter -n -t test_suite.jmx -l resultado.jtl -o dashboard_directory
  • -n: indica que ejecute la versión no gráfica.
  • -t: indica el fuente JMX que va a ejecutarse.
  • -l: indica el JTL de salida.
  • -o: indica el nombre del directorio donde queremos que se almacene el HTML estático que compondrá la web con el resultado de las pruebas.

En este apartado de la documentación oficial de Apache JMeter se describen todos los modificadores del comando. 

Configuración en Cloud

Una arquitectura Cloud nos da la oportunidad de ejecutar JMeter en el mismo entorno de red en el que están alojados los microservicios a los que se quiere atacar. En nuestro caso, en el que usamos Openshift, es muy conveniente utilizar los nombres de servicio a la hora de especificar el host de la API. Esto es posible mediante la configuración de un Pod con un contenedor que monte lo mínimo para poder ejecutar JMeter. Concretamente: 

  • Un sistema operativo. En nuestro caso la elección es alpine (latest version). 
  • Instalación mediante apk de openjdk8_jre.
  • Descarga mediante curl y descompresión e instalación vía tar de JMeter 5.3. 

Dockerfile

Este es el Dockerfile utilizado para la creación del contenedor: 

FROM alpine:latest 


ARG JMETER_VERSION="5.3" 
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 
 
ARG TZ="Europe/Amsterdam" 
RUN    apk update \ 

&& apk upgrade \ 
&& apk add ca-certificates \ 
&& update-ca-certificates \ 
&& apk add --update openjdk8-jre tzdata curl unzip bash \ 
&& apk add --no-cache nss \ 
&& rm -rf /var/cache/apk/* \ 
&& 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 global PATH such that "jmeter" command is found 
ENV PATH $PATH:$JMETER_BIN 

# Entrypoint has same signature as "jmeter" command 
COPY entrypoint.sh / 
 
WORKDIR	${JMETER_HOME} 
 
ENTRYPOINT ["/entrypoint.sh"]

Configuración del Deployment

El yaml utilizado para configurar el Deployment en Kubernetes es el siguiente: 

apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: jmeter 
  namespace: onpay-dev 
  labels: 
    app: jmeter 
spec: 
  replicas: 1 
  selector: 
    matchLabels: 
      app: jmeter 
  template: 
    metadata: 
      creationTimestamp: null 
      labels: 
        app: jmeter 
    spec: 
      restartPolicy: Always 
      imagePullSecrets: 
        - name: solutionsregistry 
      schedulerName: default- 
      terminationGracePeriodSeconds: 30 
      securityContext: {} 
      containers: 
        - name: jmeter 
          image: 'solucionesregistry.azurecr.io/jmeter:dev' 
          command: 
            - /bin/sleep 
          resources: 
            limits: 
              cpu: '2' 
              memory: 1200Mi 
            requests: 
              cpu: 600m 
              memory: 1Gi 
          volumeMounts: 
            - name: jmeter-aux-files 
              mountPath: /opt/apache-jmeter-5.3/aux 
          terminationMessagePath: /dev/termination-log 
          terminationMessagePolicy: File 
          imagePullPolicy: Always 
      hostAliases: 
        - ip: 10.3.32.11 
          hostnames: 
            - >- 
              onpay-gateway-rest-preauthorization-onpay-dev.apps.soluciones-cluster-dev.b2bconnect.es 
      volumes: 
        - name: jmeter-aux-files 
          persistentVolumeClaim: 
            claimName: jmeter-pvc 
      dnsPolicy: ClusterFirst 
  strategy: 
    type: RollingUpdate 
    rollingUpdate: 
      maxUnavailable: 25% 
      maxSurge: 25% 
  revisionHistoryLimit: 10 
  progressDeadlineSeconds: 600

Obsérvese que utilizamos una imagen en el Registry llamada jmeter:

image: 'solucionesregistry.azurecr.io/jmeter:dev'

Podemos observar también que hay que asociarlo a un PVC para que las escrituras en fichero persistan: 

volumeMounts: 

  - name: jmeter-aux-files 

  mountPath: /opt/apache-jmeter-5.3/aux 

Funcionamiento del contenedor

Como hemos dicho, se trata de un contenedor que no tiene un servicio activo, como pudiese ser un Springboot a la escucha de peticiones. Es un simple sistema operativo con JMeter instalado que, tras su arranque, sencillamente no necesitamos que haga nada. Sólo «existir» para poder conectarnos a él por línea de comandos. 

Por este motivo, el «ENTRYPOINT» especificado en el Dockerfile es irrelevante, siendo el parámetro command del yaml el que cobra importancia

command: 

  - /bin/sleep 

Lo que le estamos especificando es que haga un sleep infinito para que, sencillamente, no haga nada. Pero, al mismo tiempo, la ejecución del contenedor queda viva y disponible para conectarnos al remote shell (rsh) vía el CLI de Openshift (OC). 

Es decir, una vez el Pod de Kubernetes ha arrancado, ya podemos ejecutar lo siguiente para entrar en su shell: 

oc rsh <nombre_pod> 

Ejecución de una prueba

Como ya se explicó anteriormente, el comando a ejecutar es jmeter -n  (no gui), pasándole -t test_suite.jmx y -l resultado.jtl 

Dado que estamos en Cloud, no es útil añadirle el -o folder  para generar el dashboard con los resultados, ya que el el mismo quedaría almacenado en el filesystem contenedor y su resultado no sería directamente accesible. 

En cambio, el mecanismo utilizado para ejecutar en remoto y recoger los resultados en local es el siguiente: 

  1. Copiar el jmx fuente: utilizar el comando oc cp (copy) para tenerlo disponible en el contenedor. 
    oc cp test_suite.jmx nombre_pod:/opt/apache-jmeter-5.3/test_suite.xml 
  1. Ejecutar el comando jMeter y esperar al final de la ejecución: 
    jmeter -n -t test_suite.jmx -l resultado.jtl 
  1. Copiar el jtl del resultado desde el contenedor a local: 
    oc nombre_pod:/opt/apache-jmeter-5.3/resultado.jtl resultado.jtl 
  1. A partir de la copia local de resultado.jtl, mediante JMetergenerar la carpeta de HTML estático con el Dashboard de los resultados: 
    jmeter -g resultado.jtl -o directorio_salida 

En el directorio de salida se genera un conjunto de archivos y directorios de HTML estático con esta estructura: 

El dashboard generado permite visualizar de forma gráfica medias, percentiles, transacciones por segundo, respuestas en error y otras estadísticas de mucha utilidad. 

Conclusión 

En este artículo hemos repasado la utilidad del Software Apache JMeter para configurar y ejecutar pruebas de rendimiento. Además, hemos explicado la estrategia utilizada para cargar el JMeter en un Pod de Openshift, ejecutar desde la nube y extraer el dashboard de resultados con el propio software de JMeter. 

Autor

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *