JVM Profiler: Uber Engineering Blog

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

Az Apache Sparkhoz hasonló számítástechnikai keretrendszereket széles körben alkalmazzák nagyméretű adatalkalmazások építésére. Az Uber számára az adatok a stratégiai döntéshozatal és a termékfejlesztés középpontjában állnak. Annak érdekében, hogy jobban ki tudjuk használni ezeket az adatokat, globális mérnöki irodáinkban hatalmas Spark telepítéseket kezelünk.

Míg a Spark elérhetőbbé teszi az adatechnológiát, a Spark-alkalmazásokhoz rendelt erőforrások megfelelő méretezése és az adatinfrastruktúránk működési hatékonyságának optimalizálása finomabb betekintést igényel ezekről a rendszerekről, nevezetesen az erőforrás-használati mintázatokról.

Azért, hogy ezeket a mintákat a felhasználói kód módosítása nélkül bányásszuk ki, létrehoztuk és nyílt forráskódúvá tettük a JVM Profiler-t, egy elosztott profilozót, amely teljesítmény- és erőforrás-használati metrikákat gyűjt, és további elemzésre szolgál. Bár a Spark számára készült, általános megvalósítása miatt bármilyen Java virtuális gép (JVM) alapú szolgáltatásra vagy alkalmazásra alkalmazható. Olvassa el, hogyan használja az Uber ezt az eszközt Spark-alkalmazásaink profilozására, valamint hogyan használhatja saját rendszereihez.

Profilozási kihívások

Naponta az Uber több tízezer, több ezer gépen futó alkalmazást támogat. Ahogy nőtt a technológiai stackünk, gyorsan rájöttünk, hogy a meglévő teljesítményprofilozási és optimalizálási megoldásunk nem képes kielégíteni az igényeinket. Különösen azt szerettük volna, ha képesek lennénk:

Metrikák korrelálása nagyszámú folyamaton keresztül alkalmazásszinten

Elosztott környezetben több Spark-alkalmazás fut ugyanazon a szerveren, és minden egyes Spark-alkalmazás nagyszámú folyamatot (pl. több ezer végrehajtót) futtat számos szerveren, ahogy az 1. ábrán látható:

1. ábra. Elosztott környezetben a Spark-alkalmazások több szerveren futnak.

A meglévő eszközeink csak szerverszintű metrikákat tudtak figyelni, és nem mérték az egyes alkalmazások metrikáit. Olyan megoldásra volt szükségünk, amely képes az egyes folyamatokra vonatkozó metrikákat összegyűjteni és az egyes alkalmazások folyamatai között korrelálni. Ráadásul nem tudtuk, hogy ezek a folyamatok mikor indulnak el, és mennyi ideig tartanak. Ahhoz, hogy ebben a környezetben metrikákat gyűjthessünk, a profilozót automatikusan el kell indítani minden egyes folyamattal együtt.

A metrikák metrikagyűjtése ne legyen tolakodó a tetszőleges felhasználói kód számára

A Spark és az Apache Hadoop könyvtárak jelenlegi implementációikban nem exportálnak teljesítménymetrikákat; azonban gyakran vannak olyan helyzetek, amikor a felhasználói vagy keretrendszer kódjának módosítása nélkül kell gyűjtenünk ezeket a metrikákat. Ha például magas késleltetést tapasztalunk egy Hadoop Distributed File System (HDFS) NameNode-on, szeretnénk ellenőrizni az egyes Spark-alkalmazások által megfigyelt késleltetést, hogy megbizonyosodjunk arról, hogy ezek a problémák nem replikálódtak. Mivel a NameNode klienskódok a Spark könyvtárunkba vannak beágyazva, nehézkes a forráskódját módosítani, hogy hozzáadjuk ezt a speciális metrikát. Ahhoz, hogy lépést tudjunk tartani az adatinfrastruktúránk folyamatos növekedésével, képesnek kell lennünk arra, hogy bármikor, kódmódosítások nélkül elvégezhessük bármely alkalmazás méréseit. Ráadásul egy nem tolakodó metrikagyűjtési folyamat megvalósítása lehetővé tenné számunkra, hogy betöltési idő alatt dinamikusan kódot injektáljunk a Java-módszerekbe.

A JVM Profiler bemutatása

Az említett két kihívás megoldására megépítettük és nyílt forráskódúvá tettük a JVM Profilerünket. Van néhány létező nyílt forráskódú eszköz, például az Etsy’s statsd-jvm-profiler, amely képes metrikákat gyűjteni az egyes alkalmazások szintjén, de ezek nem nyújtanak lehetőséget arra, hogy dinamikusan kódot injektáljanak a meglévő Java binárisokba a metrikák gyűjtéséhez. Ezen eszközök némelyikétől inspirálódva építettük fel a profilerünket még több képességgel, például tetszőleges Java metódus/argumentum profilozással.

Mit csinál a JVM Profiler?

A JVM Profiler három fő funkcióból áll, amelyek megkönnyítik a teljesítmény- és erőforrás-használati metrikák gyűjtését, majd ezeket a metrikákat (pl. Apache Kafka) további elemzés céljából más rendszereknek szolgáltatják:

  • Egy Java-ügynök: Egy Java-ügynök beépítésével a profilerünkbe a felhasználók különböző metrikákat (pl. CPU/memóriahasználat) és stack traces-t gyűjthetnek a JVM-folyamatokhoz elosztott módon.
  • Fejlett profilkészítő képességek: A JVM Profilerünk lehetővé teszi tetszőleges Java-módszerek és argumentumok nyomon követését a felhasználói kódban anélkül, hogy tényleges kódmódosításokat végeznénk. Ez a funkció használható a Spark alkalmazások HDFS NameNode RPC híváskésleltetésének nyomon követésére és a lassú metódushívások azonosítására. Emellett nyomon követheti az egyes Spark-alkalmazások által olvasott vagy írt HDFS-fájlok útvonalát is, hogy azonosítani lehessen a további optimalizáláshoz szükséges forró fájlokat.
  • Adatelemzési jelentések: Az Ubernél a profilozót a Kafka-témák és az Apache Hive táblák metrikáinak jelentésére használjuk, így az adatelemzés gyorsabbá és egyszerűbbé válik.

Típusos felhasználási esetek

A JVM Profilerünk számos felhasználási esetet támogat, leginkább tetszőleges Java-kód instrumentálását teszi lehetővé. Egy egyszerű konfigurációs módosítással a JVM Profiler egy Spark-alkalmazás minden egyes végrehajtójához csatlakozhat, és gyűjthet Java-módszerek futásidejű metrikáit. Az alábbiakban néhány ilyen felhasználási esetet érintünk:

  • A végrehajtó megfelelő méretezése: A JVM Profiler memóriametrikáit használjuk az egyes végrehajtók tényleges memóriahasználatának nyomon követésére, így a Spark “executor-memory” argumentumának megfelelő értékét állíthatjuk be.
  • A HDFS NameNode RPC késleltetésének figyelése: Profilozzuk az org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB osztály metódusait egy Spark alkalmazásban, és azonosítjuk a NameNode hívások hosszú késleltetését. Naponta több mint 50 ezer Spark-alkalmazást monitorozunk több milliárd ilyen RPC-hívással.
  • Az illesztőprogramból kiesett események figyelése: Olyan metódusokat profilozunk, mint az org.apache.spark.scheduler.LiveListenerBus.onDropEvent, hogy nyomon kövessük azokat a helyzeteket, amelyek során a Spark-illesztőprogram eseménysorozata túl hosszú lesz és eseményeket dob le.
  • Nyomon követjük az adatok vonalát: Az org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.getBlockLocations és az org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.addBlock metóduson fájlútvonal argumentumokat profilozunk, hogy nyomon követhessük, milyen fájlokat olvas és ír a Spark alkalmazás.

Implementációs részletek és bővíthetőség

Az implementáció minél zökkenőmentesebbé tétele érdekében a JVM Profiler nagyon egyszerű és bővíthető felépítésű. Az emberek könnyen hozzáadhatnak további profilozó implementációkat, hogy több metrikát gyűjtsenek, és saját egyéni riportereket is telepíthetnek a metrikák különböző rendszereknek való elküldéséhez az adatelemzéshez.

2. ábra. A JVM Profilerünk több különböző profilozóból áll, amelyek a JVM használatával és teljesítményével kapcsolatos specifikus metrikákat mérnek.

A JVM Profiler kódja egy Java-ügynök argumentumán keresztül töltődik be egy Java-folyamatba, amint a folyamat elindul. Három fő részből áll:

  • Class File Transformer: a folyamaton belül a Java-módszer bytecode-ját instrumentálja, hogy tetszőleges felhasználói kódot profilozzon, és a metrikákat egy belső metrikus pufferbe mentse.
  • Metric Profilers
    • CPU/Memory Profiler: JMX-en keresztül CPU/Memory használati metrikákat gyűjt és elküldi a riportereknek.
    • Method Duration Profiler: metrikákat olvas be a metrikák pufferéből a metrikák időtartamára (latency) vonatkozó metrikákból, és elküldi a riportereknek.
    • Method Argument Profiler: beolvassa a metrikai pufferből a metódus argumentumértékeit, és elküldi a riportereknek.
  • Riporterek
    • Konzol riporter: Metrikákat ír a konzol kimenetre.
    • Kafka Reporter: metrikákat küld a Kafka topikokba.

How to extend the JVM Profiler to send metrics via a custom reporter

A felhasználók implementálhatják saját riportereiket és megadhatják őket a -javaagent opcióval, például:

java

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

Integráció az Uber adatinfrastruktúrájával

3. ábra. A JVM Profilerünk integrálódik az Uber adatinfrastruktúra rendszerébe.

A JVM Profilerünk metrikáit integráltuk az Uber belső adatinfrastruktúrájába, hogy lehetővé tegyük:

  1. Az egész klaszterre kiterjedő adatelemzést: A metrikákat először a Kafkába tápláljuk és a HDFS-be tápláljuk, majd a felhasználók lekérdezik a Hive/Presto/Spark segítségével.
  2. Valós idejű Spark alkalmazás hibakeresés: A Flinkkel valós időben aggregáljuk egyetlen alkalmazás adatait, és a MySQL adatbázisunkba írjuk, majd a felhasználók egy webes felületen keresztül megtekinthetik a metrikákat.

A JVM Profiler használata

Az alábbiakban utasításokat adunk arra vonatkozóan, hogyan használjuk a JVM Profilerünket egy egyszerű Java-alkalmazás nyomon követésére:

Először is, git klónozzuk a projektet:

A projektet a következő parancs futtatásával építjük:

Ezután meghívjuk a build result JAR fájlt (pl. target/jvm-profiler-0.0.5.jar) és a JVM Profilerben futtatjuk az alkalmazást a következő parancs segítségével:

A parancs futtatja a minta Java alkalmazást és a kimeneti konzolra jelenti a teljesítmény és erőforrás használat mérő adatait. Például:

A profiler a metrikákat egy Kafka-témára is elküldheti a következő parancs segítségével:

A profiler használata a Spark-alkalmazás profilozására

Most nézzük végig, hogyan futtassuk a profilt a Spark-alkalmazással.

Feltételezve, hogy már van HDFS fürtünk, feltöltjük a JVM Profiler JAR fájlt a HDFS-ünkre:

Ezután a spark-submit parancssorral elindítjuk a Spark alkalmazást a profilerrel:

Metrikus lekérdezési példák

Az Uberben a metrikáinkat Kafka témákba küldjük, és háttér adatvezetékeket programozunk, hogy automatikusan bekerüljenek a Hive táblákba. A felhasználók hasonló pipelineseket állíthatnak be, és SQL segítségével lekérdezhetik a profiler metrikákat. Írhatnak saját riportereket is, hogy a metrikákat egy SQL-adatbázisba, például a MySQL-be küldjék, és onnan kérdezzenek le. Az alábbiakban egy példa egy Hive tábla sémájára:

Az alábbiakban egy példaeredményt kínálunk egy korábbi SQL-lekérdezés futtatásakor, amely a Spark végrehajtók memória- és CPU-metrikáit mutatja az egyes folyamatokra vonatkozóan:

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

Eredmények és következő lépések

A JVM Profiler-t az Uber egyik legnagyobb Spark-alkalmazására (amely több mint 1000 végrehajtót használ) alkalmaztuk, és eközben 2 GB-tal csökkentettük az egyes végrehajtók memóriaelosztását, 7 GB-ról 5 GB-ra. Csak ennél a Spark-alkalmazásnál 2 TB memóriát takarítottunk meg.

A JVM Profiler-t az Uberen belüli összes Hive on Spark-alkalmazásra is alkalmaztuk, és találtunk néhány lehetőséget a memóriahasználat hatékonyságának javítására. Az alábbi 3. ábra mutatja az egyik eredményt, amelyet találtunk: alkalmazásaink mintegy 70 százaléka a kiosztott memória 80 százalékánál kevesebbet használt. Eredményeink azt mutatták, hogy ezen alkalmazások többsége számára kevesebb memóriát is kioszthatnánk, és 20 százalékkal növelhetnénk a memóriahasználatot.

3. ábra. A JVM Profilerünk azonosította, hogy az alkalmazások 70 százaléka a kiosztott memória kevesebb mint 80 százalékát használta.

Amint tovább bővítjük megoldásunkat, további memóriacsökkentést tervezünk a JVM-jeinkben.

A JVM Profiler egy önálló, nyílt forráskódú projekt, és szívesen látjuk, ha más fejlesztők is használják ezt az eszközt és hozzájárulnak (pl. pull requesteket nyújtanak be)!

A Seattle-ben, Palo Altóban és San Franciscóban működő Big Data Engineering csapatunk olyan eszközökön és rendszereken dolgozik, amelyek a teljes Hadoop ökoszisztémát bővítik, beleértve a HDFS-t, a Hive-ot, a Prestót és a Sparkot. A nyílt forráskódú szoftverek ezen családjára építünk technológiákat, amelyek segítségével jobb, adatvezérelt üzleti döntéseket hozhatunk. Ha ez vonzóan hangzik számodra, nézd meg állásajánlatainkat, és fontold meg, hogy csatlakozol a csapatunkhoz!

Fotóhitel fejléc:

Iratkozzon fel hírlevelünkre, hogy értesüljön az Uber Engineering legfrissebb innovációiról.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.