SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

A teljesítmény-elemzés és konfigurálás egy erőteljes eszköz az ügyfelek teljesítményének ellenőrzésére.

A teljesítmény-elemzés alkalmazható a programok szűk keresztmetszeteinek azonosítására, tudományos megközelítést alkalmazva a konfigurálási kísérletek vizsgálatakor. Ez a cikk a teljesítmény-elemzés és konfigurálás általános megközelítését határozza meg, egy Go alapú webszerver példáján keresztül.

A Go különösen jól alkalmazható, mivel rendelkezik profilozási eszközökkel pprof a standard könyvtárban.

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

Stratégia

Hozzunk létre egy összefoglaló listát a strukturált elemzésünkhöz. Megpróbálunk adatokat felhasználni a döntéshozatalhoz, ahelyett, hogy intuitív vagy találgatásokon alapuló változtatásokat végeznénk. Ezt a következőképpen fogjuk csinálni:

  • Határozzuk meg az optimalizálás határait (követelmények);
  • Számítsuk ki a rendszer tranzakciós terhelését;
  • Futtassunk tesztet (adatokat generálunk);
  • Megfigyelünk;
  • Elemzünk — minden követelmény teljesül?
  • Tudományos módon konfigurálunk, hipotézist alkotunk;
  • Kísérletet végzünk ennek a hipotézisnek az ellenőrzésére.

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

Egyszerű HTTP-szerver architektúra

Ebben a cikkben egy kis HTTP-szervert fogunk használni Golangon. Az összes kódot, amelyet ebben a cikkben bemutatunk, megtalálhatók itt.

Az elemzés alatt lévő alkalmazás egy HTTP-szerver, amely minden kéréskor a Postgresql adatbázist kérdezi le. Továbbá van Prometheus, node_exporter és Grafana az alkalmazás és a rendszer metrikáinak gyűjtésére és megjelenítésére.

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

Egyszerűsítés céljából tekinthessük úgy, hogy a horizontális skálázás (és a számítások egyszerűsítése) érdekében minden szolgáltatás és adatbázis együtt kerül telepítésre:

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

Célok meghatározása

Ebben a lépésben meghatározzuk a célt. Mit próbálunk elemezni? Hogyan tudjuk, mikor kell befejezni? Ebben a cikkben azt fogjuk feltételezni, hogy ügyfeleink vannak, és szolgáltatásunk 10 000 kérést fog feldolgozni másodpercenként.

A Google SRE Könyv részletesen bemutatja a választás és a modellezés módszereit. Hasonló módon járunk el, modelleket építünk:

  • Késleltetés: a kérések 99%-ának 60 ms-nál kevesebb időn belül kell végrehajtódnia;
  • Költség: a szolgáltatásnak a lehető legkevesebb pénzt kell fogyasztania, ami számunkra ésszerűnek tűnik. Ennek érdekében maximalizáljuk a sávszélességet;
  • Kapacitás-tervezés: meg kell érteni és dokumentálni, hogy hány példányt kell elindítani az alkalmazásból, beleértve az általános skálázási funkciót, valamint hány példányra van szükség az induló terhelési követelmények kielégítésére és a redundancia biztosítására. n+1 redundancia.

A késlekedés optimalizálása szükséges lehet az elemzés mellett, de a sávszélességet mindenképpen ki kell elemezni. Az SRE SLO folyamat használatakor a késleltetés követelménye az ügyfelet és/vagy az üzletet képviselő terméktulajdonostól származik. Szolgáltatásunk ezt az elkötelezettséget már a kezdetektől betartja, bármiféle beállítás nélkül!

Tesztkörnyezet beállítása

A tesztkörnyezet segítségével mennyiségi terhelést tudunk kiadni a rendszerünkre. Az elemzéshez teljesítményadatok jönnek létre a webszolgáltatásról.

Tranzakciós terhelés

Ez a környezet a következőket használja Vegeta a testreszabott HTTP-kérdések frekvenciájának létrehozására, amíg meg nem állítják:

$ 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

Megfigyelés

A tranzakciós terhelés alkalmazásra kerül végrehajtás közben. Az alkalmazás metrikái (kérések száma, válaszidők) és az operációs rendszer (memória, CPU, IOPS) mellett alkalmazásprofilozás fog futni, hogy megértsük, hol vannak a problémák, és hogyan használják a CPU-időt.

Profilozás

A profilozás egyfajta mérés, amely lehetővé teszi, hogy lássuk, hová megy a CPU-idő az alkalmazás futása alatt. Megtudhatjuk, hogy pontosan hol és mennyi CPU-idő megy el:

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

Ezeket az adatokat az elemzés során fel lehet használni, hogy betekintést nyerjünk a haszontalanul elköltött CPU-időbe és a végzett szükségtelen munkába. A Go (pprof) képes profilokat generálni és azokat lánggrafikonná vizualizálni a szabványos eszközkészlettel. Kicsit később a cikkben beszélek az ezek használatáról és a beállítási útmutatóról.

Végrehajtás, megfigyelés, elemzés.

Kísérletet végzünk. Fognak végrehajtani, figyelni és elemezni, amíg a teljesítményünk meg nem felel. Véletlenszerűen alacsony terhelést választunk, hogy azzal első megfigyeléseket végezzünk. Minden egyes lépésnél növelni fogjuk a terhelést egy skálázási tényezővel, amelyet egy bizonyos szórással választunk. Minden egyes terheléses tesztet a kérés száma beállításával hajtunk végre: make load-test LOAD_TEST_RATE=X.

50 kérés másodpercenként

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

Figyeljük meg a két felső grafikon. A bal felső azt mutatja, hogy az alkalmazásunk másodpercenként 50 kérést dolgoz fel (szerinte), a jobb felső pedig a kérések időtartamát. Mindkét paraméter segít nekünk abban, hogy elemezzük: beleférünk-e a teljesítményhatárainkba vagy sem. A grafikon piros vonala HTTP Kérés Késleltetés az SLO-t 60 ms-ra mutatja. A vonalon látszik, hogy jóval a maximális válaszidőnk alatt vagyunk.

Nézzük a költségeket:

10000 kérés másodpercenként / 50 kérés szerveren = 200 szerver + 1

Még mindig lehet javítani ezen a mutatón.

500 kérés másodpercenként

Érdekesebb dolgok történnek, amikor a terhelés 500 kérés másodpercenként:

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

Ismét a bal felső grafikonon látható, hogy az alkalmazás normál terhelést rögzít. Ha nem így van, akkor probléma van a szerveren, ahol az alkalmazás fut. A válaszidő késleltetés grafikonja a jobb felső oldalon található, amely azt mutatja, hogy a 500 kérés másodpercenként 25-40 ms késleltetést okozott. A 99. percentilis még mindig szépen belefér az előzőleg választott 60 ms SLO-ba.

Költségek szempontjából:

10000 kérés másodpercenként / 500 kérés szerveren = 20 szerver + 1

Még mindig lehet javítani.

1000 kérés másodpercenként

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

Nagyszerű indítás! Az alkalmazás 1000 kérés másodpercenkénti feldolgozást mutat, azonban a késleltetési határ megsértésre került az SLO oldaláról. Ezt a p99 vonal mutatja a jobb felső grafikonon. Bár a p100 vonal magasabb, a valós késleltetések meghaladták a 60 ms maximális értéket. Merüljünk el a profilozásba, hogy megtudjuk, valójában mi történik az alkalmazással.

Profilozás

A profilozáshoz 1000 kérés másodpercenkénti terhelést állítunk be, majd használjuk pprof az adatok rögzítésére, hogy megtudjuk, hol tölti az alkalmazás a processzoridőt. Ezt az HTTP végpont aktiválásával lehet megtenni. pprof, majd a terhelés alatt a curl segítségével mentse el az eredményeket:

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

Az eredmények a következőképpen jeleníthetők meg:

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

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

A grafikon megmutatja, hol és mennyi CPU időt töltenek a programmal. A leírás szerint Brendan Gregg:

Az X tengelyen a stack profil kitöltése, ábécé sorrendben (ez nem idő), az Y tengely a stack mélységét mutatja, nullától kezdve a [top]-ig. Minden téglalap egy stack keretet jelent. Minél szélesebb a keret, annál gyakrabban szerepel a stackekben. A felső rész a CPU-t használja, az alsó rész pedig a gyerekelemeket mutatja. A színek általában nem jelentenek semmit, egyszerűen véletlenszerűen választják ki a keretek megkülönböztetésére.

Elemzés — hipotézis

A beállítás során arra fogunk összpontosítani, hogy megpróbáljuk megtalálni a haszontalan CPU időtöltéseket. Keressük a legnagyobb haszontalan időtöltési forrásokat, és távolítsuk el őket. Nos, mivel a profilozás meglehetősen pontosan feltárja, hol költi a program a CPU idejét, lehet, hogy ezt többször is meg kell tenni, valamint szükséges lesz a program forráskódját módosítani, a teszteket újraindítani és figyelni, hogy a teljesítmény közelít a kitűzött célhoz.

Brendan Gregg ajánlásait követve a grafikonon fentről lefelé fogunk olvasni. Minden sor egy stack keretet (függvényhívást) jelenít meg. Az első sor a program belépési pontja, a szülője az összes többi hívásnak (más szavakkal, minden más hívásnak szerepelni fog a stackjében). A következő sor már eltér:

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

Ha az egérmutatót a grafikonon a függvény neve fölé helyezi, megjelenik a teljes idő, amennyit a stackben töltött a hibakeresés során. A HTTPServe függvény az idő 65%-ában ott volt, a többi futásidő függvény, runtime.mcall, mstart és gc, vette el a maradék időt. Érdekes tény: az összes eltöltött idő 5%-át a DNS lekérdezésekre fordították:

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

A program által keresett címek a Postgresql birtokába tartoznak. Kattintsunk a FindByAge:

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

Érdekes, hogy a program azt mutatja, hogy három fő forrása van a késleltetésnek: kapcsolatok megnyitása és zárása, adatok lekérdezése és az adatbázishoz való csatlakozás. A grafikonon látható, hogy a DNS lekérdezések, a kapcsolatok megnyitása és zárása a teljes futási idő körülbelül 13%-át teszik ki.

Hipotézis: A kapcsolatok újrahasznosítása a kapcsolópultra támaszkodva csökkenti a HTTP kérések idejét, lehetővé téve a magasabb átviteli sebességet és alacsonyabb késleltetést..

Alkalmazás beállítása - kísérlet.

Frissítjük a forráskódot, próbáljuk meg eltüntetni a PostgreSQL-hez való kapcsolódást minden egyes kérésnél. Az első lehetőség - a kapcsolópul használata az alkalmazás szintjén. Ebben a kísérletben mi beállítjuk a kapcsolópultot SQL meghajtóval Go-hoz:

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

if err != nil {
   return nil, err
}

Végrehajtás, megfigyelés, elemzés.

A 1000 kérés/másodperc teszt újraindítása után látható, hogy a p99 késések normális szintre álltak a SLO 60ms-vel!

Mi a költség?

10000 kérés/másodperc / 1000 kérés/szerver = 10 szerver + 1

Tegyük még jobban!

2000 kérés/másodperc

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

A terhelés megduplázása ugyanazt mutatja, a bal felső grafikonon látható, hogy az alkalmazás 2000 kérést dolgoz fel másodpercenként, a p100 kevesebb mint 60ms, p99 megfelel a SLO-nak.

Költségek szempontjából:

10000 kérés/másodperc / 2000 kérés/szerver = 5 szerver + 1

3000 kérés/másodperc

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

Itt az alkalmazás képes 3000 kérést kezelni, a p99 késleltetés kevesebb, mint 60ms. Az SLO nem sérül, a költséget így fogadták el:

10000 kérés/másodperc / 3000 kérés/szerver = 4 szerver + 1 (a szerző kerekítette felfelé, jegyzet: fordító)

Próbáljunk ki egy újabb elemzést.

Elemzés — hipotézis

Összegyűjtjük és megjelenítjük az alkalmazás hibakeresési eredményeit 3000 kérés/másodperc sebességen:

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

Még mindig 6% időt töltünk a kapcsolatok létrehozásával. A kapcsolópult beállítása javította a teljesítményt, de még mindig látszik, hogy az alkalmazás folytatja az új kapcsolatok létrehozását az adatbázissal.

Hipotézis: A kapcsolatok, a kapcsolatpult ellenére, még mindig elutasításra és tisztításra kerülnek, ezért az alkalmazásnak újra kell telepítenie őket. A várakozó kapcsolatok számának beállítása a pult méretéhez kell, hogy segítsen a késleltetés csökkentésében, minimalizálva az időt, amelyet az alkalmazás a kapcsolat létrehozására fordít..

Alkalmazás beállítása - kísérlet.

Próbáljuk meg beállítani a MaxIdleConns értéket a pult méretének megfelelően (szintén leírták itt):

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

Végrehajtás, megfigyelés, elemzés.

3000 kérés/másodperc

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

p99 kevesebb mint 60ms, jelentősen alacsonyabb p100-zal!

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

A flame graph ellenőrzése azt mutatja, hogy a kapcsolat létrehozása már nem észlelhető! Nézzük meg részletesebben. pg(*conn).query — itt sem észleljük a kapcsolat létrehozását.

SRE: Teljesítmény-elemzés. Konfigurálás egyszerű Go alapú webszerver alkalmazásával.

Zárás

A teljesítményelemzés alapvető fontosságú az ügyfélvárakozások és a nem funkcionális követelmények teljesülésének megértéséhez. Az elemzés, amely az ügyfélvárakozásokkal való összehasonlításon alapul, segíthet meghatározni, mi számít elfogadhatónak és mi nem. A Go hatékony eszközöket biztosít, amelyeket a szabványos könyvtárban találunk, így az elemzés egyszerűvé és hozzáférhetővé válik.

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster