Papers we love: Dapper – het debuggen van gedistribueerde systemen

 
01 juni 2017

Voor deze keer wil eens een heel ander type paper bekijken. Anders als gebruikelijk staat er nu een systeem centraal dat direct is ontstaan uit de industrie. Het is een paper van een team bij Google en heet Dapper, a Large-Scale Distributed Systems Tracing Infrastructure. Opvallend aan de context van deze paper is dat hij “from the trenches” is, echt ontstaan omdat er een praktisch probleem was waar een oplossing voor benodigd was.

De context van deze paper is de vraag “wat gebeurt er in hemelsnaam met ‘dit’ request binnen onze infrastructuur?”. Dit is, zoals je kunt indenken, niet eenvoudig als jouw bedrijf (een van de) meest gebruikte diensten op het internet aanbiedt. Doordat de Google infrastructuur uit een groot aantal systemen bestaat zal een enkel request wat naar een frontend service wordt gestuurd vermoedelijk ‘uitwaaieren’ over een groot aantal onderliggende systemen. Om inzicht te krijgen in de gedragingen van een enkele request, inclusief de onderliggende requests naar subsystemen, heb je dan toch een uitdaging.

Puur kijkend naar het volume van het verkeer wordt duidelijk dat er sprake is van een behoorlijke informatie overload mocht ieder request in detail logging gaan genereren. Zie hier maar eens gebruik van te maken gedurende het debuggen. Vanaf het begin kun je er dus eigenlijk van uit gaan dat niet alle requests en bijbehorende data opgeslagen zal kunnen worden. Tegelijkertijd wil je wel dat wanneer je een bericht opslaat om te loggen, dat ook alle onderliggende requests mee worden genomen. Het niet kunnen correleren met requests van-, en naar subsystemen heeft ook weinig zin, de totale dynamiek van het systeem is hiermee niet meer inzichtelijk.

Onder andere om deze reden is er dan ook gekozen om gebruik te maken van (adaptive) sampling. Hierbij wordt er bij een top level request (dus de root node in de callgraph) een unieke 64 bit int gegenereerd. Deze wordt vervolgens zowel door het request zelf, als alle onderliggende RPC-calls, gebruikt als request id. Dit ID stelt het systeem dus in staat om de verschillende subcalls met elkaar te correleren. Het sampling gedeelte wordt eenvoudig gerealiseerd door deze unieke id te hashen naar een waarde z in 0 < z < 1. Door het systeem te configureren met een sampling coefficient c kan eenvoudig en per node bepaald worden of een bepaald request getraced moet worden of niet. Hierbij hoeft er alleen gekeken te worden of z < c.

Op het moment dat er dus een request wordt gelogd weten we dus dat dit bij alle onderliggende systemen ook zal gebeuren. Dit betekent dat je deze traces kunt modelleren als een boom, waarbij elke node een subsysteem voor stelt. Iedere edge is dan vervolgens een request naar zo’n systeem toe. Dapper noemt deze nodes spans en identificeert iedere edge als een causal relationshiptussen een child span en zijn parent. Hierbij is het oorspronkelijke beginpunt natuurlijk uniek in de zin dat deze geen parent heeft.

De spans bevatten standaard een aantal velden. Naast request id is er o.a. ook nog een span id (om meerder spans te identificeren), een start en stop timestamp en een human readable naam. Daarnaast is er ook nog de mogelijkheid om custom metadata toe te voegen. Een engineer die informatie wil loggen in Dapper kan hier eigen key/value pairs aan toevoegen, welke dan in de span kunnen worden opgenomen. Een typisch voorbeeld zou bijvoorbeeld de zoekquery van een google search request kunnen zijn. Omdat de metadata zo free-format is kan er door applicatie engineers zelf bepaald worden wat er allemaal opgeslagen wordt. Om een overdaad aan logging te voorkomen is het aantal bytes aan ruimte dat de metadata maximaal mag beslaan beperkt.

Qua architectuur is de het systeem zowel elegant als eenvoudig. Iedere applicatie kan gebruik maken van een library die een aantal methodes of annotaties beschikbaar stelt om te tracen. Dit wordt vervolgens async naar een log file op de disk geschreven. Vervolgens draait er op ieder systeem een Dapper daemon, welke de berichten naar Google’s BigTable systeem doorstuurt. Iedere trace is gegroepeerd per row, geïdentificeerd via het request id. Elke span krijgt vervolgens een column in deze row met de betreffende informatie.

Om Dapper compleet te maken is er een aantal API’s beschikbaar voor engineers, alsmede een web UI voor incidentele verkenning van traces. Hierin kan een individuele trace worden gepinpoint, maar kan er ook naar geaggregeerde informatie worden gekeken. Een voorbeeld hiervan is bijvoorbeeld een grafiek met alle totale doorlooptijden van de gehele traces.

Ondanks dat Dapper traces dus sampled blijkt dat geen issue te zijn om scenarios te identificeren. Er wordt zelfs aangehaald dat een samplerate van 1 / 1024 gewoonlijk genoeg is om problemen te signaleren. Mocht er sprake zijn van een systeem met een lage throughput dan kan er altijd gekozen worden om hier deze frequentie voor te verlagen en zo meer traces te registreren. De kans dat je dan genoeg informatie hebt om een probleem te identificeren neemt dan natuurlijk toe.

Doordat de requests nu over alle systemen heen gecorreleerd kunnen worden is er nu sprake van application transparency. Je kunt door een groot systeem heen kijken om te beoordelen wat er precies allemaal gebeurt (en wat voor interacties er plaats vinden) zonder dat je een expert bent van alle subsystemen. Dit laatste is belangrijk omdat er anders geen mogelijkheid is om alle kennis bij een ‘debuggend’ persoon onder te brengen.

Een ander leuk effect is dat Dapper een aantal usecases had die niet waren voorzien gedurende de bouw van het systeem. Het kan bijvoorbeeld eenvoudig gebruikt worden om te bepalen hoeveel netwerk verkeer een individuele dienst gebruikt (inclusief effecten door andere systemen heen). Ook het bepalen van dependencies en de bijbehorende intensiteit van het gebruik ervan is iets wat vrij eenvoudig kan met Dapper en de BigTable dataset.

Ik kan iedereen van harte aanraden om deze paper te lezen. Inmiddels is het niet meer state-of-the-art, maar de ideeën die besproken worden zijn nog steeds uitermate relevant. Daarnaast is hij heel praktisch en zijn er, omdat het een populaire paper is, op internet veel andere analyses over te vinden [1, 2]. Wil je liever wat code zien wat geïnspireerd is op Dapper dan kan dat natuurlijk ook, bijvoorbeeld bij zipkin.


Over deze papers we love: binnen Sogyo hebben we de traditie dat we maandelijks een nieuwsbrief rondsturen. De nieuwsbrief gaat veel over interne feitjes, echter sinds februari 2015 verzorgd Kevin van der Vlist het topic: papers we love. Hij deelt met ons papers die interessant zijn om te lezen en ons kunnen inspireren. De topic is uitgegroeid tot een mooie verzameling die we ook graag hier willen delen.


Werken met ?
Kijk dan bij onze mogelijkheden voor zowel starters als ervaren engineers.


Categorieën: Gave Technologie