JVM Profiler: An Open Source Tool for Tracing Distributed JVM Applications at Scale | Uber Engineering Blog

JVM Profiler: An Open Source Tool for Tracing Distributed JVM Applications at Scale
954 Shares

Computing frameworks zoals Apache Spark zijn wijdverbreid geadopteerd om grootschalige data applicaties te bouwen. Voor Uber vormen gegevens de kern van strategische besluitvorming en productontwikkeling. Om ons te helpen deze gegevens beter te benutten, beheren we massale implementaties van Spark in onze wereldwijde engineeringkantoren.

Terwijl Spark datatechnologie toegankelijker maakt, vereist het afstemmen van de middelen die aan Spark-applicaties worden toegewezen en het optimaliseren van de operationele efficiëntie van onze data-infrastructuur meer verfijnde inzichten in deze systemen, namelijk hun brongebruikspatronen.

Om deze patronen te ontginnen zonder de gebruikerscode te wijzigen, hebben we JVM Profiler gebouwd en open source gemaakt, een gedistribueerde profiler om prestatie- en brongebruiksmetriek te verzamelen en deze voor verdere analyse in te dienen. Hoewel gebouwd voor Spark, maakt de generieke implementatie het toepasbaar op elke Java virtual machine (JVM) -gebaseerde service of applicatie. Lees verder om te leren hoe Uber deze tool gebruikt om onze Spark-applicaties te profileren, en ook hoe je het voor je eigen systemen kunt gebruiken.

Profilering uitdagingen

Op dagelijkse basis ondersteunt Uber tienduizenden applicaties die op duizenden machines draaien. Toen onze technische stack groeide, realiseerden we ons al snel dat onze bestaande oplossing voor prestatieprofilering en -optimalisatie niet aan onze behoeften zou kunnen voldoen. In het bijzonder wilden we de mogelijkheid hebben om:

Correlatie metrics over een groot aantal processen op applicatieniveau

In een gedistribueerde omgeving draaien meerdere Spark-applicaties op dezelfde server en heeft elke Spark-applicatie een groot aantal processen (bijv. duizenden executors) die over vele servers lopen, zoals geïllustreerd in figuur 1:

Figuur 1. In een gedistribueerde omgeving worden Spark-toepassingen op meerdere servers uitgevoerd.

Onze bestaande tools konden alleen metrics op serverniveau monitoren en konden geen metrics voor afzonderlijke toepassingen meten. We hadden een oplossing nodig die metrics voor elk proces kon verzamelen en deze kon correleren tussen processen voor elke applicatie. Bovendien weten we niet wanneer deze processen zullen starten en hoe lang ze zullen duren. Om in deze omgeving metrics te kunnen verzamelen, moet de profiler automatisch bij elk proces worden gestart.

Maak het verzamelen van metrics niet opdringerig voor willekeurige gebruikerscode

In hun huidige implementaties exporteren Spark en Apache Hadoop bibliotheken geen performance metrics; er zijn echter vaak situaties waarin we deze metrics moeten verzamelen zonder de gebruikers- of frameworkcode te veranderen. Als we bijvoorbeeld hoge latency op een Hadoop Distributed File System (HDFS) NameNode ervaren, willen we de latency van elke Spark-toepassing controleren om er zeker van te zijn dat deze problemen niet zijn gerepliceerd. Aangezien de NameNode-clientcodes in onze Spark-bibliotheek zijn ingebed, is het lastig om de broncode aan te passen om deze specifieke metric toe te voegen. Om de voortdurende groei van onze data-infrastructuur bij te houden, moeten we in staat zijn om de metingen van elke applicatie op elk gewenst moment uit te voeren, zonder codewijzigingen aan te brengen. Bovendien zou het implementeren van een meer niet-intrusief metriek verzamelproces ons in staat stellen om dynamisch code te injecteren in Java methoden tijdens laadtijd.

Inleiding JVM Profiler

Om deze twee uitdagingen aan te pakken, hebben we onze JVM Profiler gebouwd en open source beschikbaar gesteld. Er zijn enkele bestaande open source tools, zoals Etsy’s statsd-jvm-profiler, die metrieken kunnen verzamelen op individueel applicatieniveau, maar ze bieden niet de mogelijkheid om dynamisch code te injecteren in bestaande Java binary om metrieken te verzamelen. Geïnspireerd door sommige van deze tools, hebben we onze profiler gebouwd met nog meer mogelijkheden, zoals arbitraire Java methode/argument profiling.

Wat doet de JVM Profiler?

De JVM Profiler bestaat uit drie belangrijke functies die het eenvoudiger maken om performance en resource gebruik metrieken te verzamelen, en vervolgens deze metrieken (bijv. Apache Kafka) aan andere systemen te serveren voor verdere analyse:

  • Een java agent: Door een Java-agent in onze profiler op te nemen, kunnen gebruikers verschillende metrics (bijv. CPU/geheugengebruik) en stack traces voor JVM-processen op een gedistribueerde manier verzamelen.
  • Geavanceerde profileringsmogelijkheden: Onze JVM Profiler maakt het mogelijk om willekeurige Java-methoden en argumenten in de gebruikerscode te traceren zonder feitelijke codewijzigingen aan te brengen. Deze functie kan worden gebruikt om HDFS NameNode RPC call latency voor Spark applicaties te traceren en trage method calls te identificeren. Het kan ook de HDFS bestandspaden traceren die elke Spark applicatie leest of schrijft om “hot files” te identificeren voor verdere optimalisatie.
  • Rapportage voor gegevensanalyse: Bij Uber gebruiken we de profiler om metrics te rapporteren aan Kafka-onderwerpen en Apache Hive-tabellen, waardoor data-analyse sneller en eenvoudiger wordt.

Typische use-cases

Onze JVM Profiler ondersteunt een verscheidenheid aan use-cases, met name het mogelijk maken om willekeurige Java-code te instrumenteren. Met een eenvoudige configuratiewijziging kan de JVM Profiler aan elke executor in een Spark-applicatie worden gekoppeld en metrieken verzamelen over de runtime van Java-methoden. Hieronder bespreken we enkele van deze use cases:

  • De juiste grootte van de uitvoerder: We gebruiken geheugen metrics van de JVM Profiler om het werkelijke geheugen gebruik voor elke executor te volgen, zodat we de juiste waarde voor het Spark “executor-memory” argument kunnen instellen.
  • Monitor HDFS NameNode RPC latency: We profileren methoden op de klasse org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB in een Spark applicatie en identificeren lange latency op NameNode calls. We monitoren meer dan 50 duizend Spark-applicaties per dag met enkele miljarden van dergelijke RPC-aanroepen.
  • Monitor driver dropped events: We profileren methoden zoals org.apache.spark.scheduler.LiveListenerBus.onDropEvent om situaties te traceren waarin de Spark-driver event-wachtrij te lang wordt en events laat vallen.
  • Traceer data lineage: We profileren bestandspad argumenten op de methode org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.getBlockLocations en org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.addBlock om te traceren welke bestanden worden gelezen en geschreven door de Spark applicatie.

Implementatiedetails en uitbreidbaarheid

Om de implementatie zo naadloos mogelijk te laten verlopen, heeft JVM Profiler een zeer eenvoudig en uitbreidbaar ontwerp. Mensen kunnen eenvoudig extra profiler-implementaties toevoegen om meer metrics te verzamelen en ook hun eigen aangepaste reporters inzetten om metrics naar verschillende systemen te sturen voor data-analyse.

Figuur 2. Onze JVM Profiler bestaat uit verschillende profilers die specifieke metrics meten met betrekking tot JVM-gebruik en -prestaties.

De JVM Profiler-code wordt in een Java-proces geladen via een Java-agent-argument zodra het proces start. Het bestaat uit drie hoofdonderdelen:

  • Class File Transformer: Instrumenten Java method bytecode binnen het proces om willekeurige gebruikerscode te profileren en metriek op te slaan in een interne metriekbuffer.
  • Metric Profilers
    • CPU/Memory Profiler: verzamelt CPU/Memory gebruiksmetrieken via JMX en stuurt ze naar de rapporteurs.
    • Method Duration Profiler: leest method duration (latency) metrics uit de metrics buffer en stuurt ze naar de reporters.
    • Method Argument Profiler: leest de argument waarden van de methode uit de metrics buffer en stuurt ze naar de reporters.
  • Reporters
    • Console Reporter: schrijft metrieken in de console uitvoer.
    • Kafka Reporter: stuurt metrieken naar Kafka topics.

Hoe de JVM Profiler uit te breiden om metrieken te versturen via een aangepaste reporter

Users zouden hun eigen reporters kunnen implementeren en deze specificeren met -javaagent optie, zoals:

java

-javaagent:jvm-profiler-0.0.5.jar=reporter=com.uber.profiling.reporters.CustomReporter

Integratie met de gegevensinfrastructuur van Uber

Figuur 3. Onze JVM Profiler integreert met het gegevensinfrastructuursysteem van Uber.

We integreerden onze JVM Profiler-metriek met de interne gegevensinfrastructuur van Uber om:

  1. Analyse van clusterbrede gegevens mogelijk te maken: Metrics worden eerst gevoed aan Kafka en ingested naar HDFS, dan gebruikers query’s met Hive/Presto/Spark.
  2. Spark applicatie debugging in real time: We gebruiken Flink om data voor een enkele applicatie in real time te aggregeren en naar onze MySQL database te schrijven, waarna gebruikers de metrics via een web-based interface kunnen bekijken.

Gebruik van de JVM Profiler

Hieronder geven we instructies voor het gebruik van onze JVM Profiler om een eenvoudige Java-toepassing te traceren:

Eerst klonen we het project git:

Hierna bouwen we het project door het volgende commando uit te voeren:

Volgende roepen we het JAR-bestand met het bouwresultaat op (bijv. target/jvm-profiler-0.0.5.jar) en voeren de applicatie in de JVM Profiler uit met het volgende commando:

Het commando voert de Java-voorbeeldapplicatie uit en rapporteert de prestaties en het gebruik van bronnen aan de uitvoerconsole. Bijvoorbeeld:

De profiler kan ook statistieken naar een Kafka topic sturen via een commando als het volgende:

Gebruik de profiler om de Spark applicatie te profileren

Nu, laten we eens doornemen hoe de profiler met de Spark applicatie te draaien.

Aannemend dat we al een HDFS-cluster hebben, uploaden we het JVM Profiler JAR-bestand naar onze HDFS:

Dan gebruiken we de spark-submit opdrachtregel om de Spark-applicatie met de profiler te starten:

Metrische query-voorbeelden

Bij Uber sturen we onze metrieken naar Kafka-onderwerpen en programmeren we achtergronddatapijplijnen om ze automatisch in Hive-tabellen op te nemen. Gebruikers kunnen vergelijkbare pijplijnen opzetten en SQL gebruiken om de profiler metrics te queryen. Ze kunnen ook hun eigen reporters schrijven om de metrics naar een SQL database zoals MySQL te sturen en van daaruit queries uit te voeren. Hieronder staat een voorbeeld van een Hive-tabel-schema:

Hieronder staat een voorbeeldresultaat van het uitvoeren van een eerdere SQL-query, die de geheugen- en CPU-metrics voor elk proces voor de Spark-executors laat zien:

role processUuid maxHeapMemoryMB avgProcessCpuLoad
executor 6d3aa4ee-4233-4cc7-a628-657e1a87825d 2805.255325 7.61E-11
uitvoerder 21f418d1-6d4f-440d-b28a-4eba1a3bb53d 3993.969582 9.56E-11
uitvoerder a5da6e8c-149b-4722-8fa8-74a16baabcf8 3154.484474 8.18E-11
uitvoerder a1994e27-59bd-4cda-9de3-745ead954a27 2561.847374 8.58E-11

Resultaten en volgende stappen

We hebben de JVM Profiler toegepast op een van de grootste Spark-toepassingen van Uber (die meer dan 1000 executors gebruikt), en in het proces hebben we de geheugentoewijzing voor elke executor met 2 GB teruggebracht, van 7 GB naar 5 GB. Alleen al voor deze Spark-applicatie hebben we 2 TB geheugen bespaard.

We hebben de JVM Profiler ook toegepast op alle Hive on Spark-applicaties binnen Uber, en hebben enkele mogelijkheden gevonden om het geheugengebruik efficiënter te maken. Figuur 3, hieronder, toont een resultaat dat we vonden: ongeveer 70 procent van onze applicaties gebruikte minder dan 80 procent van het toegewezen geheugen. Onze bevindingen gaven aan dat we voor de meeste van deze applicaties minder geheugen konden toewijzen en het geheugengebruik met 20 procent konden verhogen.

Figuur 3. Onze JVM Profiler stelde vast dat 70 procent van de toepassingen minder dan 80 procent van het toegewezen geheugen gebruikte.

Terwijl we onze oplossing blijven uitbreiden, kijken we uit naar nog meer geheugenvermindering in onze JVM’s.

JVM Profiler is een op zichzelf staand open-sourceproject en we verwelkomen andere ontwikkelaars om deze tool te gebruiken en ook bij te dragen (bijv. pull-requests indienen)

Onze Big Data Engineering-teams in Seattle, Palo Alto en San Francisco werken aan tools en systemen om het hele Hadoop-ecosysteem uit te breiden, met inbegrip van HDFS, Hive, Presto en Spark. We bouwen technologieën bovenop deze familie van open-sourcesoftware om ons te helpen betere, datagestuurde zakelijke beslissingen te nemen. Als dit je aanspreekt, bekijk dan onze vacatures en overweeg om je bij ons team aan te sluiten!

Photo Credit Header: Seahorse, Roatan, Honduras door Conor Myhrvold.

Schrijf je in voor onze nieuwsbrief om op de hoogte te blijven van de laatste innovaties van Uber Engineering.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.