Computing frameworks som Apache Spark er blevet bredt anvendt til at bygge store dataapplikationer. For Uber er data centralt for strategisk beslutningstagning og produktudvikling. For at hjælpe os med at udnytte disse data bedre, administrerer vi massive implementeringer af Spark på tværs af vores globale tekniske kontorer.
Mens Spark gør datateknologi mere tilgængelig, kræver det en mere finkornet indsigt i disse systemer, nemlig deres ressourceforbrugsmønstre, at vi tilpasser de ressourcer, der er allokeret til Spark-applikationer, og optimerer den operationelle effektivitet af vores datainfrastruktur.
For at udvinde disse mønstre uden at ændre brugerkoden har vi bygget og open source JVM Profiler, en distribueret profiler til at indsamle præstations- og ressourceforbrugsmetrikker og servere dem til yderligere analyse. Selv om den er bygget til Spark, gør dens generiske implementering den anvendelig til enhver Java virtual machine (JVM)-baseret tjeneste eller applikation. Læs videre for at lære, hvordan Uber bruger dette værktøj til at profilere vores Spark-applikationer, og hvordan du kan bruge det til dine egne systemer.
- Profileringsudfordringer
- Korrelere målinger på tværs af et stort antal processer på applikationsniveau
- Gør indsamling af metrikker ikke forstyrrende for vilkårlig brugerkode
- Introduktion af JVM Profiler
- Hvad gør JVM-profileren?
- Typiske brugssituationer
- Implementeringsdetaljer og udvidelighed
- Sådan udvider du JVM Profiler til at sende metrikker via en brugerdefineret reporter
- Integration med Ubers datainfrastruktur
- Anvendelse af JVM Profiler
- Brug profilereren til at profilere Spark-applikationen
- Eksempler på metriske forespørgsler
- Resultater og næste skridt
Profileringsudfordringer
På daglig basis understøtter Uber titusindvis af applikationer, der kører på tusindvis af maskiner. Efterhånden som vores teknologiske stak voksede, indså vi hurtigt, at vores eksisterende løsning til profilering og optimering af ydeevne ikke ville kunne opfylde vores behov. Vi ønskede især at få mulighed for at:
Korrelere målinger på tværs af et stort antal processer på applikationsniveau
I et distribueret miljø kører flere Spark-applikationer på den samme server, og hver Spark-applikation har et stort antal processer (f.eks. tusindvis af eksekutorer), der kører på tværs af mange servere, som illustreret i figur 1:
Vores eksisterende værktøjer kunne kun overvåge metrikker på serverniveau og kunne ikke måle metrikker for individuelle applikationer. Vi havde brug for en løsning, der kunne indsamle metrikker for hver proces og korrelere dem på tværs af processer for hvert program. Desuden ved vi ikke, hvornår disse processer starter, og hvor lang tid de vil tage. For at kunne indsamle metrikker i dette miljø skal profileren startes automatisk med hver proces.
Gør indsamling af metrikker ikke forstyrrende for vilkårlig brugerkode
I deres nuværende implementeringer eksporterer Spark- og Apache Hadoop-bibliotekerne ikke præstationsmetrikker; men der er ofte situationer, hvor vi har brug for at indsamle disse metrikker uden at ændre bruger- eller rammekode. Hvis vi f.eks. oplever høj latenstid på et Hadoop Distributed File System (HDFS) NameNode, ønsker vi at kontrollere den observerede latenstid fra hvert enkelt Spark-program for at sikre, at disse problemer ikke er blevet replikeret. Da NameNode-klientkoderne er indlejret i vores Spark-bibliotek, er det besværligt at ændre kildekoden for at tilføje denne specifikke måleenhed. For at holde trit med den evige vækst i vores datainfrastruktur skal vi kunne foretage målinger af enhver applikation til enhver tid og uden at foretage kodeændringer. Desuden ville implementering af en mere ikke-intrusiv målemetriindsamlingsproces gøre det muligt for os at injicere kode dynamisk i Java-metoder under indlæsningstidspunktet.
Introduktion af JVM Profiler
For at løse disse to udfordringer byggede vi vores JVM Profiler og open source den. Der findes nogle eksisterende open source-værktøjer, som Etsys statsd-jvm-profiler, der kan indsamle metrikker på individuelt applikationsniveau, men de giver ikke mulighed for dynamisk at injicere kode i eksisterende Java-binær for at indsamle metrikker. Inspireret af nogle af disse værktøjer byggede vi vores profiler med endnu flere muligheder, såsom vilkårlig Java-metode/argumentprofilering.
Hvad gør JVM-profileren?
JVM-profileren består af tre nøglefunktioner, der gør det lettere at indsamle metrikker for ydeevne og ressourceforbrug og derefter servere disse metrikker (f.eks. Apache Kafka) til andre systemer til yderligere analyse:
- En java-agent: Ved at inkorporere en Java-agent i vores profiler kan brugerne indsamle forskellige metrikker (f.eks. CPU-/hukommelsesforbrug) og stack-traces for JVM-processer på en distribueret måde.
- Avancerede profileringsmuligheder: Vores JVM-profiler gør det muligt at spore vilkårlige Java-metoder og -argumenter i brugerkoden uden at foretage egentlige kodeændringer. Denne funktion kan bruges til at spore HDFS NameNode RPC-opkaldslatency for Spark-applikationer og identificere langsomme metodeopkald. Den kan også spore HDFS-filstierne, som hvert Spark-program læser eller skriver, for at identificere varme filer med henblik på yderligere optimering.
- Rapportering af dataanalyser: Hos Uber bruger vi profilen til at rapportere metrikker til Kafka-emner og Apache Hive-tabeller, hvilket gør dataanalyser hurtigere og nemmere.
Typiske brugssituationer
Vores JVM Profiler understøtter en række forskellige brugssituationer og gør det først og fremmest muligt at instrumentere vilkårlig Java-kode. Ved hjælp af en simpel konfigurationsændring kan JVM Profiler knyttes til hver eksekutor i en Spark-applikation og indsamle Java-metode-kørtidsmetrikker. Nedenfor berører vi nogle af disse anvendelsestilfælde:
- Højre størrelse af eksekutor: Vi bruger hukommelsesmetrikker fra JVM Profiler til at spore det faktiske hukommelsesforbrug for hver eksekutor, så vi kan indstille den rigtige værdi for Spark-argumentet “executor-memory”.
- Overvåg HDFS NameNode RPC-latenstid: Vi profilerer metoder på klassen org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB i en Spark-applikation og identificerer lange latenstider på NameNode-opkald. Vi overvåger mere end 50 tusind Spark-applikationer hver dag med flere milliarder af sådanne RPC-opkald.
- Overvågede driver droppede hændelser: Vi profilerer metoder som org.apache.spark.scheduler.LiveListenerBus.onDropEvent for at spore situationer, hvor Spark-driverens hændelseskø bliver for lang og dropper hændelser.
- Sporer datalinjen: Vi profilerer filstiargumenter på metoden org.apache.hadoop.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.getBlockLocations og org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.addBlock for at spore, hvilke filer der læses og skrives af Spark-applikationen.
Implementeringsdetaljer og udvidelighed
For at gøre implementeringen så problemfri som muligt har JVM Profiler et meget simpelt og udvideligt design. Folk kan nemt tilføje yderligere profilerimplementeringer for at indsamle flere metrikker og også implementere deres egne brugerdefinerede rapportører til at sende metrikker til forskellige systemer til dataanalyse.
JVM Profiler-koden indlæses i en Java-proces via et Java-agent-argument, når processen starter. Den består af tre hoveddele:
- Class File Transformer: instrumenterer Java-metodebytekode inde i processen for at profilere vilkårlig brugerkode og gemme målinger i en intern metrikbuffer.
- Metric Profilers
- CPU/Memory Profiler: indsamler CPU/Memory-forbrugsmetrikker via JMX og sender dem til rapporteringsenhederne.
- Method Duration Profiler: Læser metrikker for metodens varighed (latenstid) fra metrikbufferen og sender dem til indberetterne.
- Method Argument Profiler: læser metodeargumentværdier fra metrikbufferen og sender dem til indberetterne.
- Rapportere
- Konsolrapportør: Skriver metrikker i konsoludgangen.
- Kafka Reporter: Sender metrikker til Kafka-temaer.
Sådan udvider du JVM Profiler til at sende metrikker via en brugerdefineret reporter
Brugere kan implementere deres egne reportere og angive dem med -javaagent-indstillingen, som:
java
-javaagent:jvm-profiler-0.0.5.jar=reporter=com.uber.profiling.reporters.CustomReporter
Integration med Ubers datainfrastruktur
Vi integrerede vores JVM Profiler-metrikker med Ubers interne datainfrastruktur for at muliggøre:
- Dataanalyse på tværs af klynger: Metrikker føres først til Kafka og indlæses til HDFS, hvorefter brugerne foretager forespørgsler med Hive/Presto/Spark.
- Debugging af Spark-applikation i realtid: Vi bruger Flink til at aggregere data for en enkelt applikation i realtid og skrive til vores MySQL-database, hvorefter brugerne kan se målingerne via en webbaseret grænseflade.
Anvendelse af JVM Profiler
Nedenfor giver vi instruktioner for, hvordan du bruger vores JVM Profiler til at spore en simpel Java-applikation:
Først kloner vi projektet med git:
Derpå bygger vi projektet ved at køre følgende kommando:
Næst kalder vi bygge-resultatet JAR-filen (f.eks. target/jvm-profiler-0.0.5.jar) og kører applikationen inde i JVM Profiler ved hjælp af følgende kommando:
Kommandoen kører Java-eksemplet af applikationen og rapporterer dens ydeevne og ressourceforbrugsmetrikker til udgangskonsollen. For eksempel:
Profileren kan også sende metrikker til et Kafka-emne via en kommando som følgende:
Brug profilereren til at profilere Spark-applikationen
Nu skal vi gennemgå, hvordan du kører profilereren med Spark-applikationen.
Idet vi antager, at vi allerede har en HDFS-klynge, uploader vi JAR-filen JVM Profiler til vores HDFS:
Dernæst bruger vi kommandolinjen spark-submit til at starte Spark-applikationen med profileren:
Eksempler på metriske forespørgsler
I Uber sender vi vores metrikker til Kafka-temaer og programmerer baggrundsdata-pipelines til automatisk at indlæse dem til Hive-tabeller. Brugere kan opsætte lignende pipelines og bruge SQL til at forespørge på profilermetrikker. De kan også skrive deres egne rapportører til at sende metrikkerne til en SQL-database som MySQL og forespørge derfra. Nedenfor er et eksempel på et Hive-tabelleskema:
Nedenfor tilbyder vi et eksempel på et resultat, når man kører en tidligere SQL-forespørgsel, som viser hukommelses- og CPU-metrikker for hver proces for Spark-udførerne:
role | processUuid | maxHeapMemoryMB | avgProcessCpuLoad | |
executor | 6d3aa4ee-4233-4cc7-a628-657e1a87825d | 2805.25532525 | 7.61E-11 | |
udfører | 21f418d1-6d4f-440d-b28a-4eba1a3bb53d | 3993.969582 | 9.56E-11 | |
udfører | a5da6e8c-149b-4722-8fa8-74a16baabcf8 | 3154.484474 | 8.18E-11 | |
udfører | a1994e27-59bd-4cda-9de3-745ead954a27 | 2561.847374 | 8.847374 | 8.58E-11 |
Resultater og næste skridt
Vi anvendte JVM Profiler på en af Ubers største Spark-applikationer (som bruger over 1.000 eksekutorer), og i processen reducerede vi hukommelsesallokeringen for hver eksekutor med 2 GB, fra 7 GB til 5 GB. Alene for denne Spark-applikation sparede vi 2 TB hukommelse.
Vi anvendte også JVM Profiler på alle Hive on Spark-applikationer i Uber og fandt nogle muligheder for at forbedre effektiviteten af hukommelsesforbruget. Figur 3 nedenfor viser et af de resultater, vi fandt: Omkring 70 procent af vores applikationer brugte mindre end 80 procent af den tildelte hukommelse. Vores resultater viste, at vi kunne allokere mindre hukommelse til de fleste af disse applikationer og øge hukommelsesudnyttelsen med 20 procent.
Når vi fortsætter med at udvikle vores løsning, ser vi frem til yderligere hukommelsesreduktion på tværs af vores JVM’er.
JVM Profiler er et selvstændigt open source-projekt, og vi byder andre udviklere velkommen til at bruge dette værktøj og også bidrage (f.eks. indsende pull requests)!
Vores Big Data Engineering-team i Seattle, Palo Alto og San Francisco arbejder på værktøjer og systemer til at udvide hele Hadoop-økosystemet, herunder HDFS, Hive, Presto og Spark. Vi udvikler teknologier oven på denne familie af open source-software for at hjælpe os med at træffe bedre, datadrevne forretningsbeslutninger. Hvis dette lyder tiltalende for dig, så tjek vores jobmuligheder og overvej at blive en del af vores team!
Foto Credit Header:
Abonner på vores nyhedsbrev for at holde dig ajour med de seneste innovationer fra Uber Engineering.