SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Ang pagsusuri at pag-tune ng pagganap ay isang mahusay na tool para sa pag-verify ng pagsunod sa pagganap para sa mga kliyente.

Maaaring gamitin ang pagsusuri sa pagganap upang suriin ang mga bottleneck sa isang programa sa pamamagitan ng paglalapat ng siyentipikong diskarte sa pagsubok ng mga eksperimento sa pag-tune. Tinutukoy ng artikulong ito ang isang pangkalahatang diskarte sa pagsusuri at pag-tune ng pagganap, gamit ang isang webserver ng Go bilang isang halimbawa.

Ang Go ay lalong mabuti dito dahil mayroon itong mga tool sa pag-profile pprof sa karaniwang aklatan.

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

estratehiya

Gumawa tayo ng listahan ng buod para sa ating pagsusuri sa istruktura. Susubukan naming gumamit ng ilang data upang gumawa ng mga desisyon sa halip na gumawa ng mga pagbabago batay sa intuwisyon o hula. Upang gawin ito gagawin namin ito:

  • Tinutukoy namin ang mga hangganan ng pag-optimize (mga kinakailangan);
  • Kinakalkula namin ang pagkarga ng transaksyon para sa system;
  • Ginagawa namin ang pagsubok (lumikha ng data);
  • Aming sinusunod;
  • Sinusuri namin - natutugunan ba ang lahat ng mga kinakailangan?
  • Itinakda namin ito nang siyentipiko, gumawa ng hypothesis;
  • Nagsasagawa kami ng isang eksperimento upang subukan ang hypothesis na ito.

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Simpleng Arkitektura ng HTTP Server

Para sa artikulong ito gagamit kami ng maliit na HTTP server sa Golang. Ang lahat ng code mula sa artikulong ito ay matatagpuan dito.

Ang application na sinusuri ay isang HTTP server na nag-poll sa Postgresql para sa bawat kahilingan. Bukod pa rito, mayroong Prometheus, node_exporter at Grafana para sa pagkolekta at pagpapakita ng mga sukatan ng application at system.

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Upang pasimplehin, isinasaalang-alang namin na para sa pahalang na pag-scale (at pagpapasimple ng mga kalkulasyon) ang bawat serbisyo at database ay magkakasamang naka-deploy:

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Pagtukoy sa mga layunin

Sa hakbang na ito, nagpasya kami sa layunin. Ano ang sinusubukan nating pag-aralan? Paano natin malalaman kung oras na para matapos? Sa artikulong ito, aakalain namin na mayroon kaming mga kliyente at ang aming serbisyo ay magpoproseso ng 10 kahilingan bawat segundo.

Π’ Google SRE Book Ang mga paraan ng pagpili at pagmomodelo ay tinalakay nang detalyado. Gawin natin ang pareho at bumuo ng mga modelo:

  • Latency: 99% ng mga kahilingan ay dapat makumpleto nang wala pang 60ms;
  • Gastos: Dapat gamitin ng serbisyo ang pinakamababang halaga ng pera na sa tingin namin ay makatwirang posible. Upang gawin ito, pinalaki namin ang throughput;
  • Pagpaplano ng kapasidad: Nangangailangan ng pag-unawa at pagdodokumento kung gaano karaming mga instance ng application ang kailangang patakbuhin, kasama ang pangkalahatang pagpapaandar ng pag-scale, at kung gaano karaming mga pagkakataon ang kakailanganin upang matugunan ang mga kinakailangan sa paunang pag-load at provisioning kalabisan n+1.

Maaaring mangailangan ng pag-optimize ang latency bilang karagdagan sa pagsusuri, ngunit malinaw na kailangang suriin ang throughput. Kapag ginagamit ang proseso ng SRE SLO, ang kahilingan sa pagkaantala ay nagmumula sa customer o negosyo, na kinakatawan ng may-ari ng produkto. At tutuparin ng aming serbisyo ang obligasyong ito mula pa sa simula nang walang anumang mga setting!

Pagse-set up ng isang kapaligiran sa pagsubok

Sa tulong ng isang pagsubok na kapaligiran, makakapaglagay kami ng nasusukat na pagkarga sa aming system. Para sa pagsusuri, bubuo ng data sa pagganap ng serbisyo sa web.

Pag-load ng transaksyon

Ginagamit ng kapaligirang ito Vegeta para gumawa ng custom na rate ng kahilingan sa HTTP hanggang sa tumigil:

$ 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

Pagmamasid

Ang pag-load ng transaksyon ay ilalapat sa runtime. Bilang karagdagan sa mga sukatan ng application (bilang ng mga kahilingan, latency ng pagtugon) at operating system (memorya, CPU, IOPS), tatakbo ang profile ng application upang maunawaan kung saan ito may mga problema at kung paano ginagamit ang oras ng CPU.

Pag-profile

Ang pag-profile ay isang uri ng pagsukat na nagbibigay-daan sa iyong makita kung saan pupunta ang oras ng CPU kapag tumatakbo ang isang application. Pinapayagan ka nitong matukoy nang eksakto kung saan at gaano karaming oras ng processor ang ginugol:

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Maaaring gamitin ang data na ito sa panahon ng pagsusuri upang makakuha ng insight sa nasayang na oras ng CPU at hindi kinakailangang gawaing ginagawa. Ang Go (pprof) ay maaaring bumuo ng mga profile at mailarawan ang mga ito bilang mga flame graph gamit ang isang karaniwang hanay ng mga tool. Pag-uusapan ko ang tungkol sa kanilang paggamit at gabay sa pag-setup mamaya sa artikulo.

Pagpapatupad, pagmamasid, pagsusuri.

Gumawa tayo ng isang eksperimento. Kami ay magpe-perform, magmamasid at mag-analyze hanggang kami ay masiyahan sa performance. Pumili tayo ng di-makatwirang mababang halaga ng pagkarga upang mailapat ito upang makuha ang mga resulta ng mga unang obserbasyon. Sa bawat kasunod na hakbang ay tataas namin ang load na may tiyak na scaling factor, na pinili nang may ilang pagkakaiba-iba. Ang bawat pagtakbo ng pagsubok sa pag-load ay isinagawa nang may naayos na bilang ng mga kahilingan: make load-test LOAD_TEST_RATE=X.

50 kahilingan kada segundo

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Bigyang-pansin ang dalawang nangungunang mga graph. Ang kaliwang itaas ay nagpapakita na ang aming application ay nagpoproseso ng 50 mga kahilingan sa bawat segundo (sa palagay nito) at ang kanang itaas ay nagpapakita ng tagal ng bawat kahilingan. Ang parehong mga parameter ay tumutulong sa amin na tingnan at suriin kung kami ay nasa loob ng aming mga hangganan ng pagganap o hindi. Pulang linya sa graph Latency ng Kahilingan ng HTTP nagpapakita ng SLO sa 60ms. Ipinapakita ng linya na mas mababa tayo sa maximum na oras ng pagtugon.

Tingnan natin ang bahagi ng gastos:

10000 kahilingan kada segundo / 50 kahilingan kada server = 200 server + 1

Mapapabuti pa natin ang figure na ito.

500 kahilingan kada segundo

Mas maraming kawili-wiling bagay ang magsisimulang mangyari kapag ang pag-load ay umabot sa 500 kahilingan bawat segundo:

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Muli, sa itaas na kaliwang graph makikita mo na ang application ay nagre-record ng normal na pagkarga. Kung hindi ito ang kaso, may problema sa server kung saan tumatakbo ang application. Ang graph ng latency ng tugon ay matatagpuan sa kanang bahagi sa itaas, na nagpapakita na ang 500 kahilingan sa bawat segundo ay nagresulta sa pagkaantala ng pagtugon na 25-40ms. Ang 99th percentile ay angkop pa rin sa 60ms SLO na pinili sa itaas.

Sa mga tuntunin ng gastos:

10000 kahilingan kada segundo / 500 kahilingan kada server = 20 server + 1

Mapapabuti pa ang lahat.

1000 kahilingan kada segundo

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Mahusay na paglulunsad! Ipinapakita ng application na nagproseso ito ng 1000 kahilingan kada segundo, ngunit ang limitasyon sa latency ay nilabag ng SLO. Ito ay makikita sa linya p99 sa kanang itaas na graph. Sa kabila ng katotohanan na ang p100 na linya ay mas mataas, ang aktwal na mga pagkaantala ay mas mataas kaysa sa maximum na 60ms. Sumisid tayo sa pag-profile upang malaman kung ano talaga ang ginagawa ng application.

Pag-profile

Para sa pag-profile, itinakda namin ang pag-load sa 1000 kahilingan sa bawat segundo, pagkatapos ay gamitin pprof upang makuha ang data upang malaman kung saan ginugugol ng application ang oras ng CPU. Magagawa ito sa pamamagitan ng pag-activate ng HTTP endpoint pprof, at pagkatapos, sa ilalim ng pagkarga, i-save ang mga resulta gamit ang curl:

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

Ang mga resulta ay maaaring ipakita tulad nito:

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

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Ipinapakita ng graph kung saan at kung gaano kalaki ang ginugugol ng application sa oras ng CPU. Mula sa paglalarawan mula sa Brendan Gregg:

Ang X axis ay ang stack profile populasyon, pinagsunod-sunod ayon sa alpabeto (ito ay hindi oras), ang Y axis ay nagpapakita ng lalim ng stack, pagbibilang mula sa zero sa [itaas]. Ang bawat parihaba ay isang stack frame. Kung mas malawak ang frame, mas madalas itong naroroon sa mga stack. Ang nasa itaas ay tumatakbo sa CPU, at ang nasa ibaba ay ang mga elemento ng bata. Karaniwang walang ibig sabihin ang mga kulay, ngunit pinipili lamang nang random upang maiba ang mga frame.

Pagsusuri - hypothesis

Para sa pag-tune, magtutuon kami sa pagsubok na maghanap ng nasayang na oras ng CPU. Hahanapin natin ang pinakamalaking pinagmumulan ng walang kwentang paggasta at aalisin ang mga ito. Buweno, dahil ang pag-profile ay nagpapakita ng napakatumpak na kung saan eksaktong ginugugol ng application ang oras ng processor nito, maaaring kailanganin mong gawin ito nang maraming beses, at kakailanganin mo ring baguhin ang source code ng application, muling patakbuhin ang mga pagsubok at makita na ang pagganap ay lumalapit sa target.

Kasunod ng mga rekomendasyon ni Brendan Gregg, babasahin natin ang tsart mula sa itaas hanggang sa ibaba. Ang bawat linya ay nagpapakita ng stack frame (function call). Ang unang linya ay ang entry point sa programa, ang magulang ng lahat ng iba pang mga tawag (sa madaling salita, lahat ng iba pang mga tawag ay magkakaroon nito sa kanilang stack). Iba na ang susunod na linya:

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Kung i-hover mo ang cursor sa pangalan ng isang function sa graph, ipapakita ang kabuuang oras na nasa stack habang nagde-debug. Ang HTTPServe function ay naroon 65% ng oras, iba pang runtime function runtime.mcall, mstart ΠΈ gc, kinuha ang natitirang oras. Nakakatuwang katotohanan: 5% ng kabuuang oras ang ginugol sa mga query sa DNS:

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Ang mga address na hinahanap ng programa ay nabibilang sa Postgresql. Mag-click sa FindByAge:

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Kapansin-pansin, ipinapakita ng programa na, sa prinsipyo, mayroong tatlong pangunahing mapagkukunan na nagdaragdag ng mga pagkaantala: pagbubukas at pagsasara ng mga koneksyon, paghiling ng data, at pagkonekta sa database. Ipinapakita ng graph na ang mga kahilingan sa DNS, pagbubukas at pagsasara ng mga koneksyon ay tumatagal ng humigit-kumulang 13% ng kabuuang oras ng pagpapatupad.

Hypothesis: Ang muling paggamit ng mga koneksyon gamit ang pooling ay dapat mabawasan ang oras ng isang kahilingan sa HTTP, na nagbibigay-daan sa mas mataas na throughput at mas mababang latency.

Pagse-set up ng application - eksperimento

Ina-update namin ang source code, subukang tanggalin ang koneksyon sa Postgresql para sa bawat kahilingan. Ang unang pagpipilian ay ang paggamit pool ng koneksyon sa antas ng aplikasyon. Sa eksperimentong ito kami i-set up natin koneksyon pooling gamit ang sql driver para sa go:

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

if err != nil {
   return nil, err
}

Pagpapatupad, pagmamasid, pagsusuri

Pagkatapos i-restart ang pagsubok na may 1000 kahilingan sa bawat segundo, malinaw na ang mga antas ng latency ng p99 ay bumalik sa normal na may SLO na 60ms!

Ano ang halaga?

10000 kahilingan kada segundo / 1000 kahilingan kada server = 10 server + 1

Pagbutihin pa natin!

2000 kahilingan kada segundo

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Ang pagdodoble sa pag-load ay nagpapakita ng parehong bagay, ang itaas na kaliwang graph ay nagpapakita na ang application ay namamahala upang iproseso ang 2000 mga kahilingan sa bawat segundo, ang p100 ay mas mababa sa 60ms, ang p99 ay nasiyahan sa SLO.

Sa mga tuntunin ng gastos:

10000 kahilingan kada segundo / 2000 kahilingan kada server = 5 server + 1

3000 kahilingan kada segundo

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Dito ang application ay maaaring magproseso ng 3000 kahilingan na may p99 latency na mas mababa sa 60ms. Ang SLO ay hindi nilalabag, at ang gastos ay tinatanggap tulad ng sumusunod:

10000 kahilingan kada segundo / kada 3000 kahilingan kada server = 4 server + 1 (inipon ng may-akda, tinatayang tagasalin)

Subukan natin ang isa pang round ng pagsusuri.

Pagsusuri - hypothesis

Kinokolekta at ipinapakita namin ang mga resulta ng pag-debug ng application sa 3000 kahilingan bawat segundo:

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

6% pa rin ng oras ang ginugugol sa pagtatatag ng mga koneksyon. Ang pag-set up ng pool ay nagpabuti ng pagganap, ngunit makikita mo pa rin na ang application ay patuloy na gumagana sa paglikha ng mga bagong koneksyon sa database.

Hypothesis: Ang mga koneksyon, sa kabila ng pagkakaroon ng pool, ay ibinabagsak at nililinis pa rin, kaya kailangang i-reset ng application ang mga ito. Ang pagtatakda ng bilang ng mga nakabinbing koneksyon sa laki ng pool ay dapat makatulong sa latency sa pamamagitan ng pagliit sa oras na ginugugol ng application sa paggawa ng koneksyon.

Pagse-set up ng application - eksperimento

Sinusubukang i-install MaxIdleConns katumbas ng laki ng pool (inilarawan din dito):

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

Pagpapatupad, pagmamasid, pagsusuri

3000 kahilingan kada segundo

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Ang p99 ay mas mababa sa 60ms na may makabuluhang mas kaunting p100!

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Ang pagsuri sa flame graph ay nagpapakita na ang koneksyon ay hindi na kapansin-pansin! Suriin natin nang mas detalyado pg(*conn).query β€” hindi rin namin napapansin ang pagkakaugnay dito.

SRE: Pagsusuri sa Pagganap. Paraan ng configuration gamit ang isang simpleng web server sa Go

Konklusyon

Ang pagsusuri sa pagganap ay kritikal sa pag-unawa na ang mga inaasahan ng customer at hindi gumaganang mga kinakailangan ay natutugunan. Ang pagsusuri sa pamamagitan ng paghahambing ng mga obserbasyon sa mga inaasahan ng customer ay maaaring makatulong na matukoy kung ano ang katanggap-tanggap at kung ano ang hindi. Nagbibigay ang Go ng mga mahuhusay na tool na binuo sa karaniwang library na ginagawang simple at naa-access ang pagsusuri.

Pinagmulan: www.habr.com

Magdagdag ng komento