Samlar stockar från Loke

Samlar stockar från Loke

Vi på Badoo övervakar ständigt ny teknik och utvärderar om vi ska använda dem i vårt system eller inte. Vi vill dela en av dessa studier med samhället. Det är tillägnat Loki, ett logaggregationssystem.

Loki är en lösning för att lagra och visa loggar, och denna stack tillhandahåller också ett flexibelt system för att analysera dem och skicka data till Prometheus. I maj släpptes ytterligare en uppdatering, som aktivt främjas av skaparna. Vi var intresserade av vad Loki kan göra, vilka möjligheter det ger och i vilken utsträckning det kan fungera som ett alternativ till ELK, den stack som vi använder nu.

Vad är Loke

Grafana Loki är en uppsättning komponenter för ett komplett loggningssystem. Till skillnad från andra liknande system är Loki baserad på idén att endast indexera loggmetadata - etiketter (precis som i Prometheus), och komprimera själva loggarna sida vid sida i separata bitar.

Hemsida, GitHub

Innan jag går in på vad du kan göra med Loki vill jag förtydliga vad som menas med "idén att endast indexera metadata". Låt oss jämföra Loki-metoden och indexeringsmetoden i traditionella lösningar, som Elasticsearch, med exemplet på en linje från nginx-loggen:

172.19.0.4 - - [01/Jun/2020:12:05:03 +0000] "GET /purchase?user_id=75146478&item_id=34234 HTTP/1.1" 500 8102 "-" "Stub_Bot/3.0" "0.001"

Traditionella system analyserar hela raden, inklusive fält med många unika värden för user_id och item_id, och lagrar allt i stora index. Fördelen med detta tillvägagångssätt är att du kan köra komplexa frågor snabbt, eftersom nästan all data finns i indexet. Men du måste betala för detta i och med att indexet blir stort, vilket leder till minneskrav. Som ett resultat är loggarnas fulltextindex jämförbar i storlek med själva loggarna. För att snabbt kunna söka igenom det måste indexet laddas in i minnet. Och ju fler loggar, desto snabbare ökar indexet och desto mer minne förbrukar det.

Loki-metoden kräver att endast nödvändig data extraheras från strängen, vars antal värden är litet. På så sätt får vi ett litet index och kan söka i data genom att filtrera det efter tid och indexerade fält, och sedan skanna resten med reguljära uttryck eller delsträngssökningar. Processen verkar inte vara den snabbaste, men Loki delar upp begäran i flera delar och exekverar dem parallellt och bearbetar en stor mängd data på kort tid. Antalet skärvor och parallella förfrågningar i dem är konfigurerbart; sålunda beror mängden data som kan behandlas per tidsenhet linjärt på mängden resurser som tillhandahålls.

Denna avvägning mellan ett stort snabbt index och ett litet parallellt brute-force-index tillåter Loki att kontrollera kostnaden för systemet. Den kan flexibelt konfigureras och utökas efter dina behov.

Loki-stacken består av tre komponenter: Promtail, Loki, Grafana. Promtail samlar in loggar, bearbetar dem och skickar dem till Loki. Loke behåller dem. Och Grafana kan begära data från Loke och visa det. I allmänhet kan Loki användas inte bara för att lagra loggar och söka igenom dem. Hela stacken ger stora möjligheter att bearbeta och analysera inkommande data med hjälp av Prometheus sätt.
En beskrivning av installationsprocessen finns här.

Loggsökning

Du kan söka i loggarna i ett speciellt gränssnitt Grafana — Explorer. Frågorna använder LogQL-språket, som är mycket likt PromQL som används av Prometheus. I princip kan det ses som ett distribuerat grep.

Sökgränssnittet ser ut så här:

Samlar stockar från Loke

Själva frågan består av två delar: väljare och filter. Selector är en sökning med indexerad metadata (etiketter) som tilldelas loggarna, och filter är en söksträng eller regexp som filtrerar bort de poster som definieras av väljaren. I det givna exemplet: Inom parentes - väljaren, allt efter - filtret.

{image_name="nginx.promtail.test"} |= "index"

På grund av hur Loki fungerar kan du inte göra förfrågningar utan en väljare, men etiketter kan göras godtyckligt generiska.

Väljaren är nyckel-värdet för värdet i hängslen. Du kan kombinera väljare och ange olika sökvillkor med hjälp av operatorerna =, != eller reguljära uttryck:

{instance=~"kafka-[23]",name!="kafka-dev"} 
// Найдёт логи с лейблом instance, имеющие значение kafka-2, kafka-3, и исключит dev 

Ett filter är en text eller regexp som kommer att filtrera bort all data som tas emot av väljaren.

Det är möjligt att erhålla ad-hoc-grafer baserade på mottagen data i måttläget. Till exempel kan du ta reda på frekvensen av förekomsten i nginx-loggarna för en post som innehåller indexsträngen:

Samlar stockar från Loke

En fullständig beskrivning av funktionerna finns i dokumentationen LogQL.

Logganalys

Det finns flera sätt att samla in loggar:

  • Med hjälp av Promtail, en standardkomponent i stacken för att samla stockar.
  • Direkt från hamnarcontainern med hjälp av Loki Docker Logging Driver.
  • Använd Fluentd eller Fluent Bit som kan skicka data till Loki. Till skillnad från Promtail har de färdiga parsers för nästan alla typer av stockar och kan hantera flerradsloggar också.

Vanligtvis används Promtail för att analysera. Den gör tre saker:

  • Hittar datakällor.
  • Fäst etiketter på dem.
  • Skickar data till Loke.

För närvarande kan Promtail läsa loggar från lokala filer och från systemd journal. Den måste installeras på varje maskin från vilken stockar samlas in.

Det finns integration med Kubernetes: Promtail tar automatiskt reda på tillståndet för klustret genom Kubernetes REST API och samlar in loggar från en nod, tjänst eller pod, och lägger omedelbart upp etiketter baserade på metadata från Kubernetes (podnamn, filnamn, etc.).

Du kan också hänga etiketter baserat på data från loggen med hjälp av Pipeline. Pipeline Promtail kan bestå av fyra typer av steg. Mer information - in officiell dokumentation, jag kommer omedelbart att notera några av nyanserna.

  1. Parsing stadier. Detta är scenen för RegEx och JSON. I detta skede extraherar vi data från loggarna till den så kallade extraherade kartan. Du kan extrahera från JSON genom att helt enkelt kopiera fälten vi behöver till den extraherade kartan, eller genom reguljära uttryck (RegEx), där namngivna grupper "mappas" till den extraherade kartan. Extraherad karta är en nyckel-värdelagring, där nyckel är namnet på fältet och värde är dess värde från loggarna.
  2. Förvandla stadier. Det här steget har två alternativ: transform, där vi ställer in transformationsreglerna, och source - datakällan för transformationen från den extraherade kartan. Om det inte finns något sådant fält i den extraherade kartan kommer det att skapas. Det är alltså möjligt att skapa etiketter som inte är baserade på den extraherade kartan. I detta skede kan vi manipulera data i den extraherade kartan med en ganska kraftfull golang mall. Dessutom måste vi komma ihåg att den extraherade kartan är fulladdad under parsningen, vilket gör det möjligt att till exempel kontrollera värdet i den: "{{if .tag}tag value exists{end}}". Mall stöder villkor, loopar och vissa strängfunktioner som Replace och Trim.
  3. Handlingsstadier. I det här skedet kan du göra något med det extraherade:
    • Skapa en etikett från de extraherade data, som kommer att indexeras av Loki.
    • Ändra eller ställ in händelsetiden från loggen.
    • Ändra data (loggtext) som kommer att gå till Loke.
    • Skapa mätvärden.
  4. Filtreringssteg. Matchstadiet, där vi antingen kan skicka poster som vi inte behöver till /dev/null, eller skicka dem för vidare bearbetning.

Med hjälp av exemplet med att bearbeta vanliga nginx-loggar kommer jag att visa hur du kan analysera loggar med Promtail.

För testet, låt oss ta en modifierad nginx-bild jwilder/nginx-proxy:alpine som nginx-proxy och en liten demon som kan fråga sig själv via HTTP. Demonen har flera slutpunkter, som den kan ge svar av olika storlekar, med olika HTTP-status och med olika fördröjningar.

Vi kommer att samla in loggar från hamnarcontainrar, som kan hittas längs vägen /var/lib/docker/containers/ / -json.log

I docker-compose.yml ställer vi in ​​Promtail och anger sökvägen till konfigurationen:

promtail:
  image: grafana/promtail:1.4.1
 // ...
 volumes:
   - /var/lib/docker/containers:/var/lib/docker/containers:ro
   - promtail-data:/var/lib/promtail/positions
   - ${PWD}/promtail/docker.yml:/etc/promtail/promtail.yml
 command:
   - '-config.file=/etc/promtail/promtail.yml'
 // ...

Lägg till sökvägen till loggarna till promtail.yml (det finns ett "docker"-alternativ i konfigurationen som gör samma sak på en rad, men det skulle inte vara så självklart):

scrape_configs:
 - job_name: containers

   static_configs:
       labels:
         job: containerlogs
         __path__: /var/lib/docker/containers/*/*log  # for linux only

När denna konfiguration är aktiverad kommer Loki att ta emot loggar från alla behållare. För att undvika detta ändrar vi inställningarna för test nginx i docker-compose.yml - lägg till loggning i taggfältet:

proxy:
 image: nginx.test.v3
//…
 logging:
   driver: "json-file"
   options:
     tag: "{{.ImageName}}|{{.Name}}"

Redigera promtail.yml och ställ in Pipeline. Loggarna är som följer:

{"log":"u001b[0;33;1mnginx.1    | u001b[0mnginx.test 172.28.0.3 - - [13/Jun/2020:23:25:50 +0000] "GET /api/index HTTP/1.1" 200 0 "-" "Stub_Bot/0.1" "0.096"n","stream":"stdout","attrs":{"tag":"nginx.promtail.test|proxy.prober"},"time":"2020-06-13T23:25:50.66740443Z"}
{"log":"u001b[0;33;1mnginx.1    | u001b[0mnginx.test 172.28.0.3 - - [13/Jun/2020:23:25:50 +0000] "GET /200 HTTP/1.1" 200 0 "-" "Stub_Bot/0.1" "0.000"n","stream":"stdout","attrs":{"tag":"nginx.promtail.test|proxy.prober"},"time":"2020-06-13T23:25:50.702925272Z"}

pipeline stadier:

 - json:
     expressions:
       stream: stream
       attrs: attrs
       tag: attrs.tag

Vi extraherar stream, attrs, attrs.tag-fälten (om några) från den inkommande JSON och lägger in dem i den extraherade kartan.

 - regex:
     expression: ^(?P<image_name>([^|]+))|(?P<container_name>([^|]+))$
     source: "tag"

Om det var möjligt att placera taggfältet i den extraherade kartan, extraherar vi namnen på bilden och behållaren med hjälp av regexp.

 - labels:
     image_name:
     container_name:

Vi tilldelar etiketter. Om nycklarna image_name och container_name finns i de extraherade data, kommer deras värden att tilldelas till lämpliga etiketter.

 - match:
     selector: '{job="docker",container_name="",image_name=""}'
     action: drop

Vi kasserar alla loggar som inte har etiketterna image_name och container_name set.

  - match:
     selector: '{image_name="nginx.promtail.test"}'
     stages:
       - json:
           expressions:
             row: log

För alla loggar vars image_name är lika med nginx.promtail.test, extrahera loggfältet från källloggen och placera det i den extraherade kartan med radnyckeln.

  - regex:
         # suppress forego colors
         expression: .+nginx.+|.+[0m(?P<virtual_host>[a-z_.-]+) +(?P<nginxlog>.+)
         source: logrow

Vi rensar inmatningssträngen med reguljära uttryck och drar ut den virtuella nginx-värden och nginx-loggraden.

     - regex:
         source: nginxlog
         expression: ^(?P<ip>[w.]+) - (?P<user>[^ ]*) [(?P<timestamp>[^ ]+).*] "(?P<method>[^ ]*) (?P<request_url>[^ ]*) (?P<request_http_protocol>[^ ]*)" (?P<status>[d]+) (?P<bytes_out>[d]+) "(?P<http_referer>[^"]*)" "(?P<user_agent>[^"]*)"( "(?P<response_time>[d.]+)")?

Analysera nginx-logg med reguljära uttryck.

    - regex:
           source: request_url
           expression: ^.+.(?P<static_type>jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|html|htm)$
     - regex:
           source: request_url
           expression: ^/photo/(?P<photo>[^/?.]+).*$
       - regex:
           source: request_url
           expression: ^/api/(?P<api_request>[^/?.]+).*$

Analysera request_url. Med hjälp av regexp bestämmer vi syftet med begäran: till statik, till foton, till API och ställer in motsvarande nyckel i den extraherade kartan.

       - template:
           source: request_type
           template: "{{if .photo}}photo{{else if .static_type}}static{{else if .api_request}}api{{else}}other{{end}}"

Med hjälp av villkorliga operatorer i mallen kontrollerar vi de installerade fälten i den extraherade kartan och ställer in de nödvändiga värdena för fältet request_type: foto, statisk, API. Tilldela annat om det misslyckades. Nu innehåller request_type begärantypen.

       - labels:
           api_request:
           virtual_host:
           request_type:
           status:

Vi ställer in etiketterna api_request, virtual_host, request_type och status (HTTP-status) baserat på vad vi lyckades lägga i den extraherade kartan.

       - output:
           source: nginx_log_row

Ändra utgång. Nu går den rensade nginx-loggen från den extraherade kartan till Loke.

Samlar stockar från Loke

Efter att ha kört ovanstående konfiguration kan du se att varje post är märkt baserat på data från loggen.

Tänk på att extrahering av etiketter med ett stort antal värden (kardinalitet) kan bromsa Loki avsevärt. Det vill säga att du inte ska sätta i indexet till exempel user_id. Läs mer om detta i artikelnHur etiketter i Loki kan göra loggfrågor snabbare och enklare". Men detta betyder inte att du inte kan söka efter user_id utan index. Det är nödvändigt att använda filter när du söker ("grab" enligt data), och indexet här fungerar som en strömidentifierare.

Loggvisualisering

Samlar stockar från Loke

Loki kan fungera som en datakälla för Grafana-diagram med LogQL. Följande funktioner stöds:

  • hastighet - antal poster per sekund;
  • räkna över tid - antalet poster i det givna intervallet.

Det finns också aggregeringsfunktioner Sum, Avg och andra. Du kan bygga ganska komplexa grafer, till exempel en graf över antalet HTTP-fel:

Samlar stockar från Loke

Lokis standarddatakälla är lite mindre funktionell än Prometheus-datakällan (du kan till exempel inte ändra förklaringen), men Loki kan anslutas som en Prometheus-typkälla. Jag är inte säker på om detta är dokumenterat beteende, men att döma av responsen från utvecklarna "Hur konfigurerar man Loki som Prometheus-datakälla? · Nummer #1222 · grafana/loki”, till exempel, det är helt lagligt och Loki är fullt kompatibel med PromQL.

Lägg till Loki som datakälla med typen Prometheus och lägg till URL /loki:

Samlar stockar från Loke

Och du kan göra grafer, som om vi arbetade med mått från Prometheus:

Samlar stockar från Loke

Jag tror att diskrepansen i funktionalitet är tillfällig och utvecklarna kommer att fixa det i framtiden.

Samlar stockar från Loke

Metrik

Loki ger möjligheten att extrahera numeriska mätvärden från loggar och skicka dem till Prometheus. Till exempel innehåller nginx-loggen antalet byte per svar, och även, med en viss modifiering av standardloggformatet, tiden i sekunder som det tog att svara. Dessa data kan extraheras och skickas till Prometheus.

Lägg till ytterligare ett avsnitt till promtail.yml:

- match:
   selector: '{request_type="api"}'
   stages:
     - metrics:
         http_nginx_response_time:
           type: Histogram
           description: "response time ms"
           source: response_time
           config:
             buckets: [0.010,0.050,0.100,0.200,0.500,1.0]
- match:
   selector: '{request_type=~"static|photo"}'
   stages:
     - metrics:
         http_nginx_response_bytes_sum:
           type: Counter
           description: "response bytes sum"
           source: bytes_out
           config:
             action: add
         http_nginx_response_bytes_count:
           type: Counter
           description: "response bytes count"
           source: bytes_out
           config:
             action: inc

Alternativet låter dig definiera och uppdatera mätvärden baserat på data från den extraherade kartan. Dessa mätvärden skickas inte till Loki - de visas i Promtail /metrics-slutpunkten. Prometheus måste konfigureras för att ta emot data från detta stadium. I exemplet ovan samlar vi in ​​ett histogrammått för request_type="api". Med den här typen av mått är det bekvämt att få percentiler. För statik och foton samlar vi in ​​summan av byte och antalet rader där vi fick byte för att beräkna medelvärdet.

Läs mer om mätvärden här.

Öppna en port på Promtail:

promtail:
     image: grafana/promtail:1.4.1
     container_name: monitoring.promtail
     expose:
       - 9080
     ports:
       - "9080:9080"

Vi ser till att mätvärdena med prefixet promtail_custom har visats:

Samlar stockar från Loke

Installera Prometheus. Lägg till jobbreklam:

- job_name: 'promtail'
 scrape_interval: 10s
 static_configs:
   - targets: ['promtail:9080']

Och rita en graf:

Samlar stockar från Loke

På så sätt kan du till exempel ta reda på de fyra långsammaste frågorna. Du kan också konfigurera övervakning för dessa mätvärden.

Skalning

Loki kan vara i både singel binärt läge och sharded (horisontellt skalbart läge). I det andra fallet kan den spara data till molnet, och bitarna och indexet lagras separat. I version 1.5 är möjligheten att lagra på ett ställe implementerad, men det rekommenderas ännu inte att använda det i produktionen.

Samlar stockar från Loke

Bitar kan lagras i S3-kompatibel lagring, och horisontellt skalbara databaser kan användas för att lagra index: Cassandra, BigTable eller DynamoDB. Andra delar av Loki - Distributörer (för att skriva) och Querier (för frågor) - är statslösa och skalas även horisontellt.

På DevOpsDays Vancouver 2019-konferensen meddelade en av deltagarna Callum Styan att hans projekt med Loki har petabyte av loggar med ett index på mindre än 1% av den totala storleken: "Hur Loki korrelerar mätvärden och loggar - och sparar pengar".

Jämförelse av Loki och ELK

Indexstorlek

För att testa den resulterande indexstorleken tog jag loggar från nginx-behållaren för vilken Pipeline ovan konfigurerades. Loggfilen innehöll 406 624 rader med en total volym på 109 MB. Loggar genererades inom en timme, cirka 100 poster per sekund.

Ett exempel på två rader från loggen:

Samlar stockar från Loke

När det indexerades av ELK gav detta en indexstorlek på 30,3 MB:

Samlar stockar från Loke

I fallet med Loki gav detta cirka 128 KB index och cirka 3,8 MB data i bitar. Det är värt att notera att loggen skapades på konstgjord väg och inte innehöll en mängd olika data. En enkel gzip på den ursprungliga Docker JSON-loggen med data gav en komprimering på 95,4 %, och med tanke på att endast den rensade nginx-loggen skickades till Loki själv är komprimeringen till 4 MB förståelig. Det totala antalet unika värden för Loki-etiketter var 35, vilket förklarar indexets ringa storlek. För ELK rensades även stocken. Således komprimerade Loki originaldata med 96 % och ELK med 70 %.

Minnesförbrukning

Samlar stockar från Loke

Om vi ​​jämför hela stapeln av Prometheus och ELK, så "äter" Loke flera gånger mindre. Det är tydligt att Go-tjänsten förbrukar mindre än Java-tjänsten, och att jämföra storleken på Heap Elasticsearch JVM och det tilldelade minnet för Loki är felaktigt, men ändå är det värt att notera att Loki använder mycket mindre minne. Dess CPU-fördel är inte så uppenbar, men den finns också.

Скорость

Loke "slukar" stockar snabbare. Hastigheten beror på många faktorer - vilken typ av loggar, hur sofistikerade vi analyserar dem, nätverk, disk etc. - men den är definitivt högre än den för ELK (i mitt test - ungefär två gånger). Detta förklaras av det faktum att Loki lägger mycket mindre data i indexet och därför lägger mindre tid på indexering. I det här fallet är situationen den omvända med sökhastigheten: Loki saktar märkbart ner på data större än några gigabyte, medan för ELK är sökhastigheten inte beroende av datastorleken.

Loggsökning

Loki är betydligt sämre än ELK när det gäller loggsökningsmöjligheter. Grep med reguljära uttryck är en stark sak, men det är sämre än en vuxendatabas. Bristen på intervallfrågor, aggregering endast av etiketter, oförmågan att söka utan etiketter - allt detta begränsar oss i att söka efter information av intresse i Loki. Detta betyder inte att ingenting kan hittas med Loki, men det definierar flödet för att arbeta med loggar, när du först hittar ett problem på Prometheus-diagrammen och sedan letar efter vad som hände i loggarna med dessa etiketter.

gränssnitt

Först och främst är det vackert (förlåt, kunde inte motstå). Grafana har ett snyggt gränssnitt, men Kibana är mycket mer funktionellt.

Loki för- och nackdelar

Av plusen kan det noteras att Loki integrerar med Prometheus, respektive, vi får mätvärden och larm direkt ur lådan. Det är bekvämt att samla in loggar och lagra dem med Kubernetes Pods, eftersom det har en tjänsteupptäckt som ärvts från Prometheus och automatiskt fäster etiketter.

Av minusen - dålig dokumentation. Vissa saker, som funktionerna och funktionerna hos Promtail, upptäckte jag först i processen att studera koden, fördelen med öppen källkod. En annan nackdel är de svaga analysmöjligheterna. Till exempel kan Loki inte analysera flerradsloggar. Nackdelarna inkluderar också det faktum att Loki är en relativt ung teknik (släpp 1.0 var i november 2019).

Slutsats

Loki är en 100 % intressant teknik som är lämplig för små och medelstora projekt, vilket gör att du kan lösa många problem med loggaggregering, logsökning, övervakning och analys av loggar.

Vi använder inte Loki på Badoo, eftersom vi har en ELK-stack som passar oss och som har blivit övervuxen med olika skräddarsydda lösningar genom åren. För oss är stötestenen sökningen i loggarna. Med nästan 100 GB loggar per dag är det viktigt för oss att kunna hitta allt och lite till och göra det snabbt. För kartläggning och övervakning använder vi andra lösningar som är skräddarsydda efter våra behov och integrerade med varandra. Loki-stacken har påtagliga fördelar, men den kommer inte att ge oss mer än vad vi har, och dess fördelar kommer inte exakt att uppväga kostnaden för att migrera.

Och även om det efter forskning blev klart att vi inte kan använda Loki, hoppas vi att det här inlägget hjälper dig att välja.

Lagret med koden som används i artikeln finns här.

Källa: will.com

Lägg en kommentar