SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

Prestatieanalyse en -afstemming is een krachtig hulpmiddel voor het verifiëren van de prestatie-compliance voor klanten.

Prestatieanalyse kan worden gebruikt om te controleren op knelpunten in een programma door een wetenschappelijke benadering toe te passen bij het testen van afstemmingsexperimenten. Dit artikel definieert een algemene benadering van prestatieanalyse en -afstemming, waarbij een Go-webserver als voorbeeld wordt gebruikt.

Go is hier vooral goed omdat het over profileringstools beschikt pprof in de standaardbibliotheek.

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

strategie

Laten we een samenvattende lijst maken voor onze structurele analyse. We zullen proberen bepaalde gegevens te gebruiken om beslissingen te nemen in plaats van wijzigingen aan te brengen op basis van intuïtie of giswerk. Om dit te doen zullen we dit doen:

  • Wij bepalen de optimalisatiegrenzen (eisen);
  • We berekenen de transactiebelasting voor het systeem;
  • Wij voeren de test uit (creëren data);
  • We observeren;
  • Wij analyseren: wordt aan alle eisen voldaan?
  • We zetten het wetenschappelijk op, stellen een hypothese op;
  • We voeren een experiment uit om deze hypothese te testen.

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

Eenvoudige HTTP-serverarchitectuur

Voor dit artikel gebruiken we een kleine HTTP-server in Golang. Alle code uit dit artikel is te vinden hier.

De applicatie die wordt geanalyseerd is een HTTP-server die Postgresql voor elk verzoek opvraagt. Daarnaast zijn er Prometheus, node_exporter en Grafana voor het verzamelen en weergeven van applicatie- en systeemstatistieken.

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

Ter vereenvoudiging zijn we van mening dat voor horizontaal schalen (en het vereenvoudigen van berekeningen) elke service en database samen worden ingezet:

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

Doelen definiëren

Bij deze stap bepalen we het doel. Wat proberen we te analyseren? Hoe weten we wanneer het tijd is om te eindigen? In dit artikel stellen we ons voor dat we klanten hebben en dat onze dienst 10 verzoeken per seconde zal verwerken.

В Google SRE-boek Selectiemethoden en modellering worden in detail besproken. Laten we hetzelfde doen en modellen bouwen:

  • Latentie: 99% van de verzoeken moet in minder dan 60 ms worden voltooid;
  • Kosten: De dienst moet de minimale hoeveelheid geld verbruiken die wij redelijkerwijs mogelijk achten. Om dit te doen maximaliseren we de doorvoer;
  • Capaciteitsplanning: vereist begrip en documentatie van hoeveel exemplaren van de applicatie moeten worden uitgevoerd, inclusief de algehele schaalfunctionaliteit, en hoeveel exemplaren nodig zijn om te voldoen aan de vereisten voor initiële belasting en inrichting redundantie n+1.

Latency kan naast analyse ook optimalisatie vereisen, maar de doorvoer moet duidelijk worden geanalyseerd. Bij gebruik van het SRE SLO-proces komt het uitstelverzoek van de klant of het bedrijf, vertegenwoordigd door de producteigenaar. En onze service zal vanaf het begin aan deze verplichting voldoen, zonder enige instellingen!

Het opzetten van een testomgeving

Met behulp van een testomgeving kunnen we ons systeem gemeten belasten. Voor analyse worden gegevens over de prestaties van de webservice gegenereerd.

Transactiebelasting

Deze omgeving gebruikt vegeteren om een ​​aangepaste HTTP-verzoeksnelheid te maken totdat deze wordt gestopt:

$ make load-test LOAD_TEST_RATE=50
echo "POST http://localhost:8080" | vegeta attack -body tests/fixtures/age_no_match.json -rate=50 -duration=0 | tee results.bin | vegeta report

kijken

Transactionele belasting wordt tijdens runtime toegepast. Naast de statistieken van de applicatie (aantal verzoeken, responslatentie) en het besturingssysteem (geheugen, CPU, IOPS), wordt applicatieprofilering uitgevoerd om te begrijpen waar er problemen zijn en hoe de CPU-tijd wordt verbruikt.

Profilering

Profilering is een soort meting waarmee u kunt zien waar de CPU-tijd naartoe gaat wanneer een applicatie actief is. Hiermee kunt u precies bepalen waar en hoeveel processortijd wordt besteed:

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

Deze gegevens kunnen tijdens de analyse worden gebruikt om inzicht te krijgen in verspilde CPU-tijd en onnodig werk dat wordt uitgevoerd. Go (pprof) kan profielen genereren en deze visualiseren als vlamgrafieken met behulp van een standaardset tools. Ik zal later in het artikel praten over hun gebruiks- en installatiehandleiding.

Uitvoering, observatie, analyse.

Laten we een experiment doen. We zullen uitvoeren, observeren en analyseren totdat we tevreden zijn met de prestatie. Laten we een willekeurig lage belastingswaarde kiezen om deze toe te passen om de resultaten van de eerste waarnemingen te verkrijgen. Bij elke volgende stap zullen we de belasting verhogen met een bepaalde schaalfactor, gekozen met enige variatie. Bij elke belastingtest wordt het aantal verzoeken aangepast: make load-test LOAD_TEST_RATE=X.

50 verzoeken per seconde

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

Let op de bovenste twee grafieken. Linksboven zie je dat onze applicatie 50 verzoeken per seconde verwerkt (denkt hij) en rechtsboven zie je de duur van elk verzoek. Beide parameters helpen ons te kijken en analyseren of we binnen onze prestatiegrenzen vallen of niet. Rode lijn op de grafiek Latentie van HTTP-verzoeken toont SLO op 60 ms. Uit de lijn blijkt dat we ruim onder onze maximale responstijd zitten.

Laten we eens naar de kostenkant kijken:

10000 verzoeken per seconde / 50 verzoeken per server = 200 servers + 1

We kunnen dit cijfer nog verbeteren.

500 verzoeken per seconde

Er gebeuren interessantere dingen wanneer de belasting 500 verzoeken per seconde bereikt:

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

Nogmaals, in de grafiek linksboven kun je zien dat de applicatie de normale belasting registreert. Als dit niet het geval is, is er sprake van een probleem op de server waarop de applicatie draait. De responslatentiegrafiek bevindt zich rechtsboven en laat zien dat 500 verzoeken per seconde resulteerden in een responsvertraging van 25-40 ms. Het 99e percentiel past nog steeds mooi in de hierboven gekozen SLO van 60 ms.

Qua kosten:

10000 verzoeken per seconde / 500 verzoeken per server = 20 servers + 1

Alles kan nog verbeterd worden.

1000 verzoeken per seconde

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

Geweldige lancering! Uit de applicatie blijkt dat deze 1000 verzoeken per seconde heeft verwerkt, maar dat de latentielimiet is overschreden door de SLO. Dit is te zien in regel p99 in de grafiek rechtsboven. Ondanks dat de p100-lijn veel hoger is, zijn de werkelijke vertragingen hoger dan het maximum van 60 ms. Laten we eens kijken naar profilering om erachter te komen wat de applicatie eigenlijk doet.

Profilering

Voor profilering stellen we de belasting in op 1000 verzoeken per seconde en gebruiken we vervolgens pprof om gegevens vast te leggen om erachter te komen waar de applicatie CPU-tijd doorbrengt. Dit kan worden gedaan door het HTTP-eindpunt te activeren pprofen sla vervolgens, onder belasting, de resultaten op met behulp van curl:

$ curl http://localhost:8080/debug/pprof/profile?seconds=29 > cpu.1000_reqs_sec_no_optimizations.prof

De resultaten kunnen als volgt worden weergegeven:

$ go tool pprof -http=:12345 cpu.1000_reqs_sec_no_optimizations.prof

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

De grafiek laat zien waar en hoeveel de applicatie CPU-tijd besteedt. Uit de beschrijving van Brendan Gregg:

De X-as is de stapelprofielpopulatie, alfabetisch gesorteerd (dit is niet de tijd), de Y-as toont de diepte van de stapel, geteld vanaf nul bij [boven]. Elke rechthoek is een stapelframe. Hoe breder het frame, hoe vaker het in de stapels aanwezig is. Wat bovenaan staat, draait op de CPU, en wat eronder staat zijn de onderliggende elementen. De kleuren betekenen meestal niets, maar worden eenvoudigweg willekeurig gekozen om frames te onderscheiden.

Analyse - hypothese

Voor het afstemmen zullen we ons concentreren op het vinden van verspilde CPU-tijd. We zullen op zoek gaan naar de grootste bronnen van nutteloze uitgaven en deze verwijderen. Welnu, aangezien profilering heel nauwkeurig onthult waar de applicatie precies zijn processortijd aan besteedt, moet je dit misschien meerdere keren doen, en je zult ook de broncode van de applicatie moeten wijzigen, de tests opnieuw moeten uitvoeren en zien dat de prestaties het doel benaderen.

In navolging van de aanbevelingen van Brendan Gregg zullen we de grafiek van boven naar beneden lezen. Elke regel geeft een stapelframe weer (functieaanroep). De eerste regel is het toegangspunt tot het programma, de ouder van alle andere oproepen (met andere woorden: alle andere oproepen zullen deze op hun stapel hebben). De volgende regel is al anders:

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

Als u de cursor over de naam van een functie in de grafiek beweegt, wordt de totale tijd dat deze op de stapel stond tijdens het debuggen weergegeven. De HTTPServe-functie was er 65% van de tijd, andere runtime-functies runtime.mcall, mstart и gc, nam de rest van de tijd in beslag. Leuk weetje: 5% van de totale tijd wordt besteed aan DNS-query’s:

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

De adressen waarnaar het programma zoekt, behoren tot Postgresql. Klik op FindByAge:

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

Interessant genoeg laat het programma zien dat er in principe drie belangrijke bronnen zijn die voor vertraging zorgen: het openen en sluiten van verbindingen, het opvragen van gegevens en het verbinden met de database. Uit de grafiek blijkt dat DNS-verzoeken, het openen en sluiten van verbindingen ongeveer 13% van de totale uitvoeringstijd in beslag nemen.

Hypothese: Het hergebruiken van verbindingen met behulp van pooling zou de tijd van een enkel HTTP-verzoek moeten verkorten, waardoor een hogere doorvoer en een lagere latentie mogelijk zijn.

De applicatie opzetten - experimenteren

We updaten de broncode en proberen bij elk verzoek de verbinding met Postgresql te verwijderen. De eerste optie is gebruiken aansluiting zwembad op applicatieniveau. In dit experiment hebben wij laten we het opzetten verbindingspooling met behulp van sql-stuurprogramma voor go:

db, err := sql.Open("postgres", dbConnectionString)
db.SetMaxOpenConns(8)

if err != nil {
   return nil, err
}

Uitvoering, observatie, analyse

Na het herstarten van de test met 1000 verzoeken per seconde is het duidelijk dat de latentieniveaus van p99 weer normaal zijn met een SLO van 60 ms!

Wat zijn de kosten?

10000 verzoeken per seconde / 1000 verzoeken per server = 10 servers + 1

Laten we het nog beter doen!

2000 verzoeken per seconde

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

Een verdubbeling van de belasting laat hetzelfde zien, de grafiek linksboven laat zien dat de applicatie 2000 verzoeken per seconde weet te verwerken, p100 is lager dan 60 ms, p99 voldoet aan de SLO.

Qua kosten:

10000 verzoeken per seconde / 2000 verzoeken per server = 5 servers + 1

3000 verzoeken per seconde

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

Hier kan de applicatie 3000 verzoeken verwerken met een p99-latentie van minder dan 60 ms. De SLO wordt niet geschonden en de kosten worden als volgt aanvaard:

10000 verzoeken per seconde / per 3000 verzoeken per server = 4 servers + 1 (de auteur heeft afgerond, ca. vertaler)

Laten we nog een analyseronde proberen.

Analyse - hypothese

We verzamelen en tonen de resultaten van het debuggen van de applicatie met 3000 verzoeken per seconde:

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

Nog steeds wordt 6% van de tijd besteed aan het leggen van verbindingen. Het opzetten van de pool heeft de prestaties verbeterd, maar je kunt nog steeds zien dat de applicatie blijft werken aan het maken van nieuwe verbindingen met de database.

Hypothese: Verbindingen worden, ondanks de aanwezigheid van een pool, nog steeds verbroken en opgeschoond, dus de applicatie moet ze opnieuw instellen. Het instellen van het aantal in behandeling zijnde verbindingen op basis van de poolgrootte zou de latentie moeten helpen verminderen door de tijd die de applicatie besteedt aan het maken van een verbinding te minimaliseren.

De applicatie opzetten - experimenteren

Proberen te installeren MaxIdleConns gelijk aan de zwembadgrootte (ook beschreven hier):

db, err := sql.Open("postgres", dbConnectionString)
db.SetMaxOpenConns(8)
db.SetMaxIdleConns(8)
if err != nil {
   return nil, err
}

Uitvoering, observatie, analyse

3000 verzoeken per seconde

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

p99 is minder dan 60 ms met aanzienlijk minder p100!

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

Bij het controleren van de vlamgrafiek blijkt dat de verbinding niet meer merkbaar is! Laten we het nader bekijken pg(*conn).query — we merken ook niet dat de verbinding hier tot stand wordt gebracht.

SRE: Prestatieanalyse. Installatiemethode met behulp van een eenvoudige webserver in Go

Conclusie

Prestatieanalyse is van cruciaal belang om te begrijpen dat aan de verwachtingen van de klant en niet-functionele eisen wordt voldaan. Analyse door observaties te vergelijken met de verwachtingen van klanten kan helpen bepalen wat acceptabel is en wat niet. Go biedt krachtige tools die zijn ingebouwd in de standaardbibliotheek en die analyse eenvoudig en toegankelijk maken.

Bron: www.habr.com

Voeg een reactie