ProHoster > Blog > podávání > SRE: Analýza výkonu. Metoda konfigurace pomocí jednoduchého webového serveru v Go
SRE: Analýza výkonu. Metoda konfigurace pomocí jednoduchého webového serveru v Go
Analýza a ladění výkonu je výkonným nástrojem pro ověřování souladu výkonu pro klienty.
Analýzu výkonu lze použít ke kontrole úzkých míst v programu použitím vědeckého přístupu k testování experimentů ladění. Tento článek definuje obecný přístup k analýze a ladění výkonu, jako příklad používá webový server Go.
Go je zde obzvláště dobrý, protože má nástroje pro profilování pprof ve standardní knihovně.
strategie
Vytvořme souhrnný seznam pro naši statickou analýzu. Pokusíme se použít některá data k rozhodování namísto provádění změn na základě intuice nebo odhadů. K tomu provedeme toto:
Stanovíme hranice optimalizace (požadavky);
Vypočítáme transakční zatížení pro systém;
Provedeme test (vytvoříme data);
Pozorujeme;
Analyzujeme - jsou splněny všechny požadavky?
Stanovili jsme to vědecky, vytvořili jsme hypotézu;
Provádíme experiment, abychom tuto hypotézu ověřili.
Jednoduchá architektura HTTP serveru
Pro tento článek použijeme malý HTTP server v Golangu. Celý kód z tohoto článku lze nalézt zde.
Analyzovaná aplikace je HTTP server, který se dotazuje Postgresql pro každý požadavek. Kromě toho existuje Prometheus, node_exporter a Grafana pro shromažďování a zobrazování metrik aplikací a systému.
Pro zjednodušení uvažujeme, že pro horizontální škálování (a zjednodušení výpočtů) jsou každá služba a databáze nasazeny společně:
Definování cílů
V tomto kroku rozhodujeme o cíli. Co se snažíme analyzovat? Jak poznáme, že je čas skončit? V tomto článku si představíme, že máme klienty a že naše služba zpracuje 10 000 požadavků za sekundu.
В Google SRE Book Podrobně jsou diskutovány metody výběru a modelování. Udělejme totéž a sestavme modely:
Latence: 99 % požadavků by mělo být dokončeno za méně než 60 ms;
Cena: Služba by měla spotřebovat minimální množství peněz, které je podle nás rozumně možné. Abychom toho dosáhli, maximalizujeme propustnost;
Plánování kapacity: Vyžaduje pochopení a dokumentaci, kolik instancí aplikace bude potřeba spustit, včetně celkové funkce škálování, a kolik instancí bude potřeba ke splnění požadavků na počáteční zatížení a zřizování. redundance n+1.
Latence může kromě analýzy vyžadovat optimalizaci, ale propustnost je jednoznačně potřeba analyzovat. Při použití procesu SRE SLO přichází požadavek na zpoždění od zákazníka nebo firmy, kterou zastupuje vlastník produktu. A tuto povinnost naše služba splní od samého začátku bez jakéhokoli nastavování!
Nastavení testovacího prostředí
Pomocí testovacího prostředí budeme schopni umístit naměřenou zátěž na náš systém. Pro analýzu budou vygenerována data o výkonu webové služby.
Transakční zatížení
Toto prostředí využívá vegetovat vytvořit vlastní rychlost požadavků HTTP, dokud nebude zastavena:
$ 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
Pozorování
Transakční zatížení bude aplikováno za běhu. Kromě metrik aplikace (počet požadavků, latence odezvy) a operačního systému (paměť, CPU, IOPS) bude spuštěno profilování aplikací, aby se zjistilo, kde má problémy a jak se spotřebovává čas CPU.
Profilování
Profilování je typ měření, který vám umožňuje zjistit, kam běží čas CPU, když je aplikace spuštěna. Umožňuje přesně určit, kde a kolik času procesoru stráví:
Tato data lze použít během analýzy k získání přehledu o plýtvání časem CPU a zbytečné práci, která se provádí. Go (pprof) dokáže generovat profily a vizualizovat je jako plamenné grafy pomocí standardní sady nástrojů. O jejich použití a průvodci nastavením budu mluvit dále v článku.
Provedení, pozorování, rozbor.
Udělejme experiment. Budeme provádět, pozorovat a analyzovat, dokud nebudeme s výkonem spokojeni. Zvolme libovolně nízkou hodnotu zatížení, kterou použijeme pro získání výsledků prvních pozorování. V každém následujícím kroku zvýšíme zátěž s určitým měřítkovým faktorem, zvoleným s určitou obměnou. Každý běh zátěžového testování se provádí s upraveným počtem požadavků: make load-test LOAD_TEST_RATE=X.
50 požadavků za sekundu
Věnujte pozornost dvěma horním grafům. Vlevo nahoře je vidět, že naše aplikace zpracovává 50 požadavků za sekundu (myslí) a vpravo nahoře je zobrazena doba trvání každého požadavku. Oba parametry nám pomáhají dívat se a analyzovat, zda jsme v rámci našich výkonnostních hranic, či nikoli. Červená čára na grafu Latence požadavku HTTP ukazuje SLO na 60 ms. Čára ukazuje, že jsme hluboko pod naší maximální dobou odezvy.
Podívejme se na nákladovou stránku:
10000 50 požadavků za sekundu / 200 požadavků na server = 1 serverů + XNUMX
Toto číslo můžeme ještě zlepšit.
500 požadavků za sekundu
Zajímavější věci se začnou dít, když zatížení dosáhne 500 požadavků za sekundu:
Opět v levém horním grafu vidíte, že aplikace zaznamenává běžnou zátěž. Pokud tomu tak není, je problém na serveru, na kterém je aplikace spuštěna. Graf latence odezvy je umístěn vpravo nahoře a ukazuje, že 500 požadavků za sekundu mělo za následek zpoždění odezvy 25–40 ms. 99. percentil stále dobře zapadá do výše zvoleného 60ms SLO.
Z hlediska nákladů:
10000 500 požadavků za sekundu / 20 požadavků na server = 1 serverů + XNUMX
Vše se dá ještě vylepšit.
1000 požadavků za sekundu
Skvělý start! Aplikace ukazuje, že zpracovala 1000 požadavků za sekundu, ale limit latence byl porušen SLO. To je vidět na řádku p99 v grafu vpravo nahoře. Navzdory skutečnosti, že linka p100 je mnohem vyšší, skutečné zpoždění je vyšší než maximum 60 ms. Pojďme se vrhnout na profilování, abychom zjistili, co aplikace vlastně dělá.
Profilování
Pro profilování nastavíme zatížení na 1000 požadavků za sekundu a poté použijeme pprof k zachycení dat, abyste zjistili, kde aplikace tráví čas CPU. To lze provést aktivací koncového bodu HTTP pprofa poté při zatížení uložte výsledky pomocí curl:
$ go tool pprof -http=:12345 cpu.1000_reqs_sec_no_optimizations.prof
Graf ukazuje, kde a kolik aplikace tráví čas CPU. Z popisu od Brendan Gregg:
Osa X je populace profilu zásobníku seřazená abecedně (toto není čas), osa Y ukazuje hloubku zásobníku, počítáno od nuly nahoře [nahoře]. Každý obdélník je rámeček. Čím širší je rám, tím častěji je přítomen v hromadách. To, co je nahoře, běží na CPU a to, co je níže, jsou podřízené prvky. Barvy obvykle nic neznamenají, ale jsou jednoduše vybrány náhodně, aby odlišily rámečky.
Analýza - hypotéza
Při ladění se zaměříme na hledání ztraceného času CPU. Budeme hledat největší zdroje zbytečných výdajů a odstraníme je. Vzhledem k tomu, že profilování velmi přesně odhaluje, kde přesně aplikace tráví svůj procesorový čas, možná to budete muset udělat několikrát a také budete muset změnit zdrojový kód aplikace, znovu spustit testy a uvidíte, že se výkon blíží cíli.
Podle doporučení Brendana Gregga budeme číst graf shora dolů. Každý řádek zobrazuje rámec zásobníku (volání funkce). První řádek je vstupní bod do programu, rodič všech ostatních volání (jinými slovy, všechna ostatní volání jej budou mít ve svém zásobníku). Další řádek je již jiný:
Pokud najedete kurzorem na název funkce v grafu, zobrazí se celkový čas, kdy byla v zásobníku během ladění. Funkce HTTPServe tam byla 65 % času, ostatní runtime funkce runtime.mcall, mstart и gc, zabral zbytek času. Zajímavost: 5 % z celkového času je věnováno DNS dotazům:
Adresy, které program hledá, patří do Postgresql. Klikněte na FindByAge:
Zajímavé je, že program ukazuje, že v zásadě existují tři hlavní zdroje, které přidávají zpoždění: otevírání a zavírání připojení, vyžádání dat a připojení k databázi. Graf ukazuje, že požadavky DNS, otevírání a zavírání spojení zabírají asi 13 % z celkové doby provádění.
Hypotéza: Opětovné použití připojení pomocí sdružování by mělo zkrátit dobu jednoho požadavku HTTP, což umožní vyšší propustnost a nižší latenci.
Nastavení aplikace - experiment
Aktualizujeme zdrojový kód, snažíme se u každého požadavku odstranit připojení k Postgresql. První možností je použít přípojný bazén na aplikační úrovni. V tomto experimentu jsme pojďme to nastavit sdružování připojení pomocí ovladače sql for go:
Po restartování testu s 1000 požadavky za sekundu je jasné, že úrovně latence p99 se vrátily do normálu se SLO 60 ms!
kolik to stojí?
10000 1000 požadavků za sekundu / 10 požadavků na server = 1 serverů + XNUMX
Pojďme to udělat ještě lépe!
2000 požadavků za sekundu
Zdvojnásobení zátěže ukazuje to samé, levý horní graf ukazuje, že aplikace zvládá zpracovat 2000 požadavků za vteřinu, p100 je nižší než 60ms, p99 vyhovuje SLO.
Z hlediska nákladů:
10000 2000 požadavků za sekundu / 5 požadavků na server = 1 serverů + XNUMX
3000 požadavků za sekundu
Zde aplikace dokáže zpracovat 3000 požadavků s latencí p99 menší než 60 ms. SLO není porušeno a náklady jsou akceptovány následovně:
10000 3000 požadavků za sekundu / na 4 1 požadavků na server = XNUMX servery + XNUMX (autor zaokrouhlil nahoru, Cca. překladatel)
Zkusme další kolo analýzy.
Analýza - hypotéza
Shromažďujeme a zobrazujeme výsledky ladění aplikace rychlostí 3000 požadavků za sekundu:
Stále je 6 % času stráveno navazováním spojení. Nastavení fondu zlepšilo výkon, ale stále můžete vidět, že aplikace nadále pracuje na vytváření nových připojení k databázi.
Hypotéza: Připojení, navzdory přítomnosti fondu, jsou stále zrušena a vyčištěna, takže je aplikace musí resetovat. Nastavení počtu čekajících připojení na velikost fondu by mělo pomoci s latencí tím, že minimalizuje čas, který aplikace stráví vytvářením připojení..
Nastavení aplikace - experiment
Pokus o instalaci MaxIdleConns rovná velikosti bazénu (také popsáno zde):
Kontrola plamene ukazuje, že spojení již není patrné! Pojďme to zkontrolovat podrobněji pg(*conn).query - také si nevšimneme navazování spojení zde.
Závěr
Analýza výkonu je zásadní pro pochopení toho, že jsou splněna očekávání zákazníků a nefunkční požadavky. Analýza porovnáním pozorování s očekáváním zákazníků může pomoci určit, co je přijatelné a co ne. Go poskytuje výkonné nástroje zabudované do standardní knihovny, díky kterým je analýza jednoduchá a přístupná.