Guía práctica: Application Performance Monitoring con Elastic APM
Tan importante como desarrollar una app que nos acerque a nuestros clientes es velar porque esta funcione correctamente y sin errores. Por eso es fundamental contar con herramientas que nos permitan identificar problemas de rendimiento o que afecten a la experiencia del usuario, realizando diagnósticos que nos permitan realizar las correcciones oportunas.
Esto es lo que nos permite un APM: un tipo de herramienta muy potente que se encarga de monitorizar cada aplicación, comprobando que todo funcione según lo previsto.
De esto, y en concreto, de Elastic APM, te hablamos a continuación.
¿Qué es Application Performance Monitoring (APM)?
APM, por sus siglas en inglés Application Performance Monitoring, se refiere a un servicio de monitorización de rendimiento de aplicaciones. Básicamente se trata de un servicio que recibe datos de transacciones y otros trabajos realizados en nuestra aplicación, permitiendo obtener métricas tales como medias de latencias de nuestros endpoints, de nuestras consultas a base de datos, media de respuestas HTTP, etc.
En resumen, permiten trazar y gestionar el rendimiento de nuestra aplicación, permitiendo realizar acciones correctivas tempranas gracias a la observación en tiempo real de lo que está sucediendo en nuestra aplicación.
¿Qué es Elastic APM?
El servicio Elastic APM entra dentro de la gama de productos de Elastic llamado Observability. Esta gama de productos unifica logs, métricas y trazas de APM en un mismo panel de Kibana.
Entre otras muchas cosas, Elastic APM nos permite monitorizar cosas como:
• Media de latencias
• Rendimiento de la app (transacciones por segundo)
• Transacciones (con su latencia media, tps, ratio de error e impacto)
• Ratios de error
• Lista de errores
• Traza completa de cada transacción (traza de cada request)
Y no solo esto: también permite generar alertas y reglas, de forma que podamos enterarnos antes ante un comportamiento anómalo de la aplicación.
¿Qué se necesita para usar Elastic APM?
Para poder monitorizar nuestra aplicación en Elastic APM necesitamos los siguientes servicios:
• Servicio Elastic APM
• Servicio Elasticsearch
• Servicio Kibana
También se necesita el Agente del servicio que se necesita monitorizar.
Puesta en marcha del APM
Se podría tener acceso a los 3 servicios necesarios directamente hospedados en Elastic Cloud, pero en este caso nos vamos a centrar en hospedar los servicios nosotros mismos. Es más, usaremos Docker para hacer un PoC (proof of concept).
version: "3.1" services: apm-server: image: docker.elastic.co/apm/apm-server:7.14.1 depends_on: elasticsearch: condition: service_healthy kibana: condition: service_healthy cap_add: ["CHOWN", "DAC_OVERRIDE", "SETGID", "SETUID"] cap_drop: ["ALL"] ports: - 8200:8200 networks: - elastic command: > apm-server -e -E apm-server.rum.enabled=true -E setup.kibana.host=kibana:5601 -E setup.template.settings.index.number_of_replicas=0 -E apm-server.kibana.enabled=true -E apm-server.kibana.host=kibana:5601 -E output.elasticsearch.hosts=["elasticsearch:9200"] healthcheck: interval: 10s retries: 12 test: curl --write-out 'HTTP %{http_code}' --fail --silent --output /dev/null http://localhost:8200/ elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.14.1 environment: - bootstrap.memory_lock=true - cluster.name=docker-cluster - cluster.routing.allocation.disk.threshold_enabled=false - discovery.type=single-node - ES_JAVA_OPTS=-XX:UseAVX=2 -Xms1g -Xmx1g ulimits: memlock: hard: -1 soft: -1 volumes: - esdata:/usr/share/elasticsearch/data ports: - 9200:9200 networks: - elastic healthcheck: interval: 20s retries: 10 test: curl -s http://localhost:9200/_cluster/health | grep -vq '"status":"red"' kibana: image: docker.elastic.co/kibana/kibana:7.14.1 depends_on: elasticsearch: condition: service_healthy environment: ELASTICSEARCH_URL: http://elasticsearch:9200 ELASTICSEARCH_HOSTS: http://elasticsearch:9200 ports: - 5601:5601 networks: - elastic healthcheck: interval: 10s retries: 20 test: curl --write-out 'HTTP %{http_code}' --fail --silent --output /dev/null http://localhost:5601/api/status volumes: esdata: driver: local networks: elastic: driver: bridge
¿Donde encontramos los Agentes?
Lo siguiente sería configurar el agente de PHP que envía los distintos eventos que queremos monitorizar. Elastic APM cuenta con su propio agente PHP (en forma de extensión de PHP), pero es complejo y demasiado automatizado (más info de este agente: Elastic APM Agent).
Existe otra alternativa que es mucho más funcional que este agente: ZoiloMora APM PHP Agent, y que es usado por los chicos de Pccomponentes. No es más que una implementación del API de eventos que provee APM. Este agente necesita de otros módulos para monitorizar todos los componentes de nuestra aplicación.
Hasta el momento se han currado estos:
Custom reports
Transaction Wrappers
- PcComponentes/apm-symfony-console
- PcComponentes/apm-symfony-http-kernel
- PcComponentes/apm-symfony-messenger
HTTP Clients
Databases
Others
Prácticamente todos son compatibles con PHP8 y la última versión de Symfony (hasta la fecha, Symfony 5.3).
¿Cómo configurar APM sobre una aplicación Symfony 5.3?
En nuestro .env.local:
... APM_SERVER_URL=http://192.168.3.44:8200 # APM SERVER URL APM_SERVICE_NAME="Name APM Service" # APM SERVICE NAME
Es tan fácil como setear los siguientes servicios:
# services.yaml services: # APM Tracer config apm.configuration: class: \ZoiloMora\ElasticAPM\Configuration\CoreConfiguration arguments: - { appName: '%env(resolve:APM_SERVICE_NAME)%', stacktraceLimit: 50 } apm.reporter: class: \ZoiloMora\ElasticAPM\Reporter\ApmServerCurlReporter arguments: - '%env(resolve:APM_SERVER_URL)%' apm.poolFactory: class: \ZoiloMora\ElasticAPM\Pool\Memory\MemoryPoolFactory calls: - method: create apm.tracer: class: \ZoiloMora\ElasticAPM\ElasticApmTracer arguments: $coreConfiguration: '@apm.configuration' $reporter: '@apm.reporter' $poolFactory: '@apm.poolFactory'
A partir de este momento ya tendremos disponible la instancia del tracer en nuestra aplicación.
Configuración del APM Tracer para Symfony Messenger
# services.yaml services: # Messenger APM app.bus.middleware.apm: class: PcComponentes\ElasticAPM\Symfony\Component\Messenger\ApmMiddleware arguments: $elasticApmTracer: '@apm.tracer' # Instancia APM Tracer $nameExtractor: '@app.bus.middleware.apm.name_extractor' app.bus.middleware.apm.name_extractor: class: Nidum\Shared\Infrastructure\Messenger\APMTextNameExtractor
Configuración del APM Tracer para HTTPKernel
# services.yaml services: # HttpKernel APM PcComponentes\ElasticAPM\Symfony\Component\HttpKernel\EventSubscriber: class: PcComponentes\ElasticAPM\Symfony\Component\HttpKernel\EventSubscriber autoconfigure: true arguments: $router: '@router' $elasticApmTracer: '@apm.tracer' # Instancia APM Tracer
Configuración del APM Tracer para comandos de consola
# services.yaml services: # Console APM PcComponentes\ElasticAPM\Symfony\Component\Console\EventSubscriber: class: PcComponentes\ElasticAPM\Symfony\Component\Console\EventSubscriber autoconfigure: true arguments: $elasticApmTracer: '@apm.tracer' # Instancia APM Tracer
Configuración del APM Tracer para Doctrine DBAL
# services.yaml services: # Inyecta el SQLLogger de APM en el loggerChain por defecto (manteniendo los dos actuales) doctrine.dbal.logger.chain.default: class: Doctrine\DBAL\Logging\LoggerChain arguments: - { sqlLogger1: '@doctrine.dbal.logger.profiling.default', sqlLogger2: '@doctrine.dbal.logger', sqlLogger3: '@apm.dbal' } apm.dbal: class: PcComponentes\ElasticAPM\Doctrine\DBAL\Logging\SQLLogger arguments: $elasticApmTracer: '@apm.tracer' # Instancia APM Tracer $instance: 'test' $engine: 'mysql'
Configuración del APM Tracer para Symfony HTTP Client
# services.yaml services: amp.http_client: class: PcComponentes\RuleStorm\Infrastructure\TraceableApmHttpClient arguments: $client: '@http_client' $elasticApmTracer: '@apm.tracer' # Instancia APM Tracer
Conclusiones
Si todo se ha configurado correctamente, tendríamos nuevos datos en la sección Observability >> APM >> Services de nuestro kibana (http://localhost:5601/).
Y con esto podríamos tracear una petición al completo:
Disponemos de absolutamente todos los datos que dio origen la petición:
Incluso, tracear una excepción (error 500) ocurrida en nuestra aplicación:
Mediciones Extras: Filebeat & Metricbeat
Y por si no fuese suficiente con monitorizar el APM de nuestra app a través de Kibana, podemos usar otros dos servicios de Elastic, que dotarán a nuestro panel de Kibana de muchísima más información para analizar y prevenir errores:
- Metricbeat: este servicio se usa para poder medir el comportamiento y estado de la infraestructura que soporta nuestros aplicativos.
- Filebeat: este servicio de Elastic se usa para registrar y gestionar los logs que llega a generar nuestro aplicativo: apache, mysql…
Filebeat
Permite enviar los eventos tipo log para visualizarlos en Kibana (más info en https://www.elastic.co/es/beats/filebeat).
Instalación y configuración
Tan fácil como:
curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.14.1-amd64.deb dpkg -i filebeat-7.14.1-amd64.deb filebeat modules enable apache service filebeat start
Recordad editar el archivo /etc/filebeat/filebeat.yml y poner los parámetros host de elastic y kibana.
Metricbeat
Mide el comportamiento y el estado de la infraestructura que soporta nuestra aplicación (más info en https://www.elastic.co/es/beats/metricbeat).
Instalación y configuración
Tan fácil como:
curl -L -O https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-7.14.1-amd64.deb && \ dpkg -i metricbeat-7.14.1-amd64.deb && \ metricbeat modules enable apache && \ service metricbeat start
Recordad editar el archivo /etc/metricbeat/metricbeat.yml y los parámetros host de Elastic y Kibana.