Monitorización de rendimiento de microservicios con Elastic APM

Cada vez es más necesaria la implantación de un sistema de APM para recopilar métricas de nuestros sistemas y poder así monitorizar su comportamiento. Los APM permiten realizar estos controles no solo a nivel de nuestras aplicaciones y las peticiones de los usuarios sino de toda la infraestructura involucrada en nuestros sistemas.

En el mercado hay dos soluciones consolidadas; AppDynamics y Dynatrace, pero en este post hemos querido realizar un análisis por encima sobre la reciente solución de la empresa Elastic llamada Elastic APM.

Para dicho análisis se ha creado el escenario base de 1 microservicio llamado BankService que en su lógica de negocio realiza dos invocaciones REST a otros 2 microservicios; RiskService y AccountService.

Los microservicios se han implementado con Spring Boot 2.1.2.RELEASE y se han ejecutado sobre una misma máquina con Windows. Para la prueba de carga, se ha usado SoapUI 5.x.

El código fuente y las configuraciones usados para este laboratorio se pueden encontrar en:

https://bitbucket.org/jrovirablog/elastic_apm_micros_spring2/src/master/

Se asume que el lector tiene conocimientos sobre los conceptos de OpenTracing y/o Dapper, para poder entender la información que puede explotarse con Elastic APM.

Elastic APM requiere de APM Server que será el encargado de recibir todas las métricas de los diferentes sistemas (en nuestro caso microservicios, pero podrían ser dockers, kubernetes, bases de datos....), los cuales requiere de un APM Agent que será arrancado en cada uno de los sistemas que quieran ser monitorizados.

APM Server tras recibir las métricas de los diferentes APM Agent, utiliza como sistema de persistencia Elastic Search.

La explotación de estas métricas se realiza a través de Kibana con una sección dedicada a APM y Dashboards específicos.

En la prueba también hemos usado Logstash para el envío de logs, los cuales también tendrán información sobre los Trace Id y Span Id, que será inyectada por APM Agent automáticamente en el MDC.

Las versiones exactas usadas en este laboratorio corresponden a:

* Elastic Search 6.6.0
* Kibana 6.6.0
* Logstash 6.6.0
* APM Server 6.6.1
* APM Agent 1.4.0 (https://mvnrepository.com/artifact/co.elastic.apm/elastic-apm-agent/1.4.0)

Arrancamos Elastic Search y Kibana sin ninguna configuración específica.

Para el arranque de Logstash, hemos usado la siguiente configuración.


input {

  tcp {

    port => 5044

 codec => json

  }

}

output {

  elasticsearch {

    hosts => ["http://localhost:9200"]

    index => "logs-%{+YYYY.MM.dd}"

  }

}


El arranque de APM Server se realiza con la siguiente configuración.


apm-server:

  host: "localhost:8200"

  # Instrumentation support for the server's HTTP endpoints and event publisher.

  instrumentation:

    # Set to true to enable instrumentation of the APM server itself.

    enabled: true

    # Environment in which the APM Server is running on (eg: staging, production, etc.)

    environment: "dev"



#-------------------------- Elasticsearch output ------------------------------

output.elasticsearch:

  hosts: ["localhost:9200"]

  indices:

    - index: "apm-%{[beat.version]}-sourcemap"

      when.contains:

        processor.event: "sourcemap"



    - index: "apm-%{[beat.version]}-error-%{+yyyy.MM.dd}"

      when.contains:

        processor.event: "error"



    - index: "apm-%{[beat.version]}-transaction-%{+yyyy.MM.dd}"

      when.contains:

        processor.event: "transaction"



    - index: "apm-%{[beat.version]}-span-%{+yyyy.MM.dd}"

      when.contains:

        processor.event: "span"



    - index: "apm-%{[beat.version]}-metric-%{+yyyy.MM.dd}"

      when.contains:

        processor.event: "metric"



    - index: "apm-%{[beat.version]}-onboarding-%{+yyyy.MM.dd}"

      when.contains:

        processor.event: "onboarding"


y usamos el comando 

apm-server.exe run --path [bit bucket project]\elastic_apm_micros_spring2\configuration\apm

Estamos indicando el directorio donde se encuentra el fichero de configuración de este laboratorio y que podéis descargar de BitBucket.

Una vez arrancada la infraestructura básica, deberemos iniciar los microservicios, los cuales tendrán los siguientes parámetros y argumentos de arranque referentes a la configuración del APM Agent.


-javaagent:${workspace_loc:elastic_apm_micros_spring2}/configuration/apm/elastic-apm-agent-1.4.1.jar
-Delastic.apm.service_name=bankservice
-Delastic.apm.service_version=1.0.0
-Delastic.apm.environment=dev
-Delastic.apm.sanitize_field_names=pass*,pwd*
-Delastic.apm.capture_headers=true
-Delastic.apm.enable_log_correlation=true
-Delastic.apm.application_packages=org.jrovira.blog.apm
-Delastic.apm.server_urls=http://localhost:8200

El significado de cada campo lo indicamos a continuación:

* -javaagent: indica la ruta donde hemos descargado nuestro APM Agent.
* elastic.apm.service_name: indicamos el nombre de nuestro servicio que será el que visualizaremos en Kibana.
* elastic.apm.service_version: indicamos la versión de nuestro servicio que será la que visualizaremos en Kibana.
* elastic.apm.environment: usado para poder segregar las métricas por entornos. En este caso usamos una referencia a un valor que indica que es un entorno de laboratorio.
* elastic.apm.sanitize_field_name: Indica que los valores de las peticiones que reciban información bajo el patrón indicado, deberán ser ofuscadas cuando se visualizen en Kibana. Esta medida es importante para cubrir requisitos de seguridad.
* elastic.apm.capture_headers: Indica si queremos capturar las HTTP Headers de las peticiones recibidas por los microservicios.
* elastic.apm.enable_log_correlation: Si true, APM Agent inyectará en el MDC de nuestro sistema de logs los valores del traceId y spanId. Opción interesante si queremos realizar trazabilidad de nuestros logs y correlacionarlo con Elastic APM.
* elastic.apm.application_packages: Packages base sobre los que analizará si hay creación de Span o cualquier interacción con Elastic APM API (API programática para poder ampliar las funcionalidades del APM Agent).
* elastic.apm.server_urls: URL del APM Server.

Los parámetros de configuración de cada microservicio, se pueden encontrar en los ficheros *.launch de cada microservicio.

La configuración de los microservicios para el envío de logs sobre TCP a Logstash se cubrió en otro post:



Una vez arrancados los microservicios y lanzada una batería de pruebas, procedemos a ver que información podemos ver en Kibana.

La sección de APM de Kibana nos ofrece por cada transacción, entendiendo esta como el conjunto de llamadas a un endpoint concreto, estadísticas generales.


Luego podemos ver para cada petición de la transacción seleccionada, los diferentes span por los que pasa la petición, así como una diferenciación de los span por cada uno de los microservicios por los que ha pasado, tal como podéis ver a continuación.


Se puede ver los 3 colores por los que se diferencia cada microservicio. A nivel general se muestra el timeline pero podemos ver el detalle de la request y response, así como mas datos a lo largo de las diferentes pestañas que nos ofrece APM.

También si pulsamos sobre un span en concreto, podemos ver el detalle de ese span en concreto.


Observamos como hay algunos headers que sus valores vienen ofuscados ([REDACTED]) tal como indicamos en la propiedad del APM Agent "-Delastic.apm.sanitize_field_names=pass*,pwd*".

La información que aporta en todos sus apartados es bastante detallada y permite realizar un análisis bastante preciso a nivel general o específico de una parte de una transacción.

En caso que no tuviéramos bastante con la información aportada, APM Server almacena todas las métricas en los índices apm-*, los cuales a través de la sección "Discover" de Kibana podemos analizarla en profundidad y realizar la explotación que queramos, incluso con Visualizations y Dashboards propios.

Anteriormente, habíamos comentado que podíamos habilitar la correlación de logs, haciendo que el APM Agent insertará en el MDC de nuestro sistema de logging, los datos de la transacción y el span, a través del parámetro "-Delastic.apm.enable_log_correlation=true". A continuación, podemos observar como en los logs que generan nuestros microservicios, podemos encontrar esta información.


APM Agent crea los span que considera necesarios, pero también podemos añadir nuevos spans dentro de nuestro código gracias a la API que ofrece Elastic APM. Para ello, deberemos añadir la siguiente dependencia en el pom de nuestros microservicios.


<dependency>

 <groupId>co.elastic.apm</groupId>

 <artifactId>apm-agent-api</artifactId>

 <version>1.4.0</version>

</dependency>

y a nivel de código fuente, podremos programáticamente crear spans nuevos así como añadirles tags que permiten en Kibana poderlos referenciar y segregarlos con categorías propias. Si se quisiera también se puede añadir información extra a la propia transacción.


 private static final Logger LOGGER = LoggerFactory.getLogger(AccountProcessController.class);

 

 private static final String TRANSACTION_TAG_NAME = "transaction_tag_example";

 private static final String TRANSACTION_TAG_VALUE = "transaction_tag_example_value";

 private static final String CUSTOM_SPAN_TYPE_VALUE = "type_test";

 private static final String CUSTOM_SPAN_SUBTYPE_VALUE = "subtype_test";

 private static final String CUSTOM_SPAN_ACTION_VALUE = "action_test";

 private static final String CUSTOM_SPAN_NAME = "span_name_test";

 private static final String CUSTOM_SPAN_TAG_NAME = "tag_test";

 private static final String CUSTOM_SPAN_TAG_VALUE = "tag_test_value";

 private static final long SLEEP_TIME = 30;



 private void otherActions() {

  ElasticApm.currentTransaction().addTag(TRANSACTION_TAG_NAME, TRANSACTION_TAG_VALUE);  

  Span span = ElasticApm.currentSpan().startSpan(CUSTOM_SPAN_TYPE_VALUE, CUSTOM_SPAN_SUBTYPE_VALUE, CUSTOM_SPAN_ACTION_VALUE);

  span.setName(CUSTOM_SPAN_NAME);

  span.addTag(CUSTOM_SPAN_TAG_NAME, CUSTOM_SPAN_TAG_VALUE);

  try {

   if(LOGGER.isInfoEnabled()) {

    LOGGER.info("Paramos {} ms el proceso.", SLEEP_TIME);

   }

   Thread.sleep(SLEEP_TIME);

  }

  catch (InterruptedException e) {

   e.printStackTrace();

  }

  finally {

   span.end();

  }

 }



El span que hemos creado así como la información global de la transacción se puede ver en las siguientes capturas de Kibana.





Tras realizar las primeras pruebas de concepto con Elastic APM, se puede observar que es un producto con muchas posibilidades aunque desgraciadamente todavía le falta algo de madurez y eso lo vemos a la que profundizamos mínimamente en el producto, encontrando las siguientes carencias.

* No hay soporte para nuevas tendencias en microservicios como webflux, GRPC..... y no está claro el roadmap evolutivo.
* Bugs básicos por resolver. En la elaboración de este post se encontró y reporto un bug (https://github.com/elastic/apm-agent-java/issues/499) de cosas bastante básicas. El tiempo de resolución y aportación de un SNAPSHOT fue de 24h, por lo que el sistema se encuentra activo y se resuelven las cosas con celeridad.
* El detalle de customizaciones en temas de trazabalidad tanto por defecto como a través de la API es inferior a otros productos como Zipkin.
* No hay APM Agents para productos conocidos actualmente como HAProxy, Nginx... que permitan realizar la trazabilidad a lo largo de elementos infraestructurales.
* No hay compatibilidad 100% con OpenTracing, aunque se está trabajando para que así sea.

Para tener más información sobre Elastic APM, se recomienda consultar su documentación oficial, la cual es extensa y bien documentada.

 https://www.elastic.co/guide/en/apm/get-started/current/overview.html

A la que el producto vaya teniendo más madurez, se vislumbra que será un competidor a tener en cuenta en el ámbito de los sistemas APM, pero no creo que eso pueda ser antes de 1 año. De momento, es un producto sobre el que ir haciendo un seguimiento para ir viendo los avances y ver en que momento puede ser un candidato para usar de forma productiva.

No hay comentarios