NGINX ja HAProxy: Käyttäjäkokemuksen testaaminen pilvipalvelussa
Monet suorituskyvyn vertailuarvot mittaavat huippunopeutta tai pyyntöjä sekunnissa (RPS), mutta nämä mittarit voivat yksinkertaistaa liikaa suorituskyvyn tarinaa reaalimaailman sivustoilla. Harvat organisaatiot käyttävät palveluitaan huipputeholla tai lähellä sitä, jolloin 10 prosentin muutos suorituskyvyssä jompaankumpaan suuntaan voi tehdä merkittävän eron. Sivuston tarvitsema läpäisykyky tai RPS ei ole ääretön, vaan se määräytyy ulkoisten tekijöiden, kuten palveltavien samanaikaisten käyttäjien määrän ja kunkin käyttäjän aktiivisuustason mukaan. Loppujen lopuksi tärkeintä on, että käyttäjät saavat parhaan mahdollisen palvelutason. Loppukäyttäjät eivät välitä siitä, kuinka monta muuta ihmistä sivustollasi vierailee. He välittävät vain saamastaan palvelusta eivätkä anna anteeksi huonoa suorituskykyä siksi, että järjestelmä on ylikuormitettu.
Tämä johtaa meidät havaintoon, jonka mukaan tärkeintä on, että organisaatio tarjoaa kaikille käyttäjilleen johdonmukaista, matalan viiveen suorituskykyä myös suuressa kuormituksessa. Vertaillessamme NGINX:ää ja HAProxya, jotka toimivat Amazon Elastic Compute Cloudissa (EC2) käänteisproxyina, pyrimme tekemään kaksi asiaa:
- Määritimme, millaista kuormitusta kukin välityspalvelin pystyy mukavasti käsittelemään
- Keräsimme viiveen prosenttijakauman, joka on mielestämme metriikka, joka korreloi suorimmin käyttäjäkokemuksen kanssa
Testausprotokollat ja kerätyt metriikat
Käyttämämme kuormanmuodostusohjelmalla wrk2
emuloimme asiakasta, joka teki jatkuvia pyyntöjä HTTPS:n välityksellä määritellyn ajanjakson aikana. Testattava järjestelmä – HAProxy tai NGINX – toimi käänteisenä välityspalvelimena, joka loi salattuja yhteyksiä wrk
-säikeiden simuloimiin asiakkaisiin, välitti pyynnöt NGINX Plus R22:ta käyttävälle backend-verkkopalvelimelle ja palautti verkkopalvelimen tuottaman vastauksen (tiedoston) asiakkaalle.
Jokaista kolmesta komponentista (asiakas, käänteinen välityspalvelin ja verkkopalvelin) ajettiin Ubuntu 20.04.1 LTS:llä c5n.2xlarge Amazon Machine Image (AMI) EC2:ssa.
Kuten mainittiin, keräsimme jokaisesta testiajosta täydellisen latenssin prosenttijakauman. Latenssilla tarkoitetaan aikaa, joka kuluu asiakkaan pyynnön luomisen ja vastauksen saamisen välillä. Latenssiprosenttijakauma lajittelee testausjakson aikana kerätyt latenssimittaukset suurimmasta (suurin latenssi) pienimpään.
Testausmenetelmä
Client
Käyttämällä wrk2
-ohjelmaa (versio 4.0.0) ajoimme seuraavan skriptin Amazonin EC2-instanssissa:
taskset -c 0-3 wrk -t 4 -c 100 -d 30s -R requests_per_second --latency https://adc.domain.com:443/
Simuloidaksemme monia web-sovellusta käyttäviä asiakkaita, synnytettiin 4 wrk
säiettä, jotka yhdessä loivat 100 yhteyttä käänteiseen välityspalvelimeen. Skripti tuotti 30 sekunnin testiajon aikana tietyn määrän RPS:ää. Nämä parametrit vastaavat seuraavia wrk2
vaihtoehtoja:
-
‑t
-vaihtoehto – luotavien säikeiden määrä (4) -
‑c
-vaihtoehto – luotavien TCP-yhteyksien määrä (100) -
‑d
-vaihtoehto – testauksen sekuntimäärä (30 sekuntia) -
‑R
-vaihtoehto – – – – – – – – – – – – – – – – – – – – – -. Asiakkaan antamien RPS:ien määrä -
‑‑latency
-vaihtoehto – Tulos sisältää korjatun viiveprosenttitiedon
Lisäsimme RPS:ien määrää asteittain testijaksojen aikana, kunnes yksi välityspalvelimista saavutti 100 %:n suorittimen käyttöasteen. Lisätietoja on kohdassa Suorituskykytulokset.
Kaikki asiakkaan ja välityspalvelimen väliset yhteydet tehtiin HTTPS:n kautta TLSv1.3:lla. Käytimme ECC:tä 256-bittisellä avainkoolla, Perfect Forward Secrecyä ja salauspakettia TLS_AES_256_GCM_SHA384
. (Koska TLSv1.2 on edelleen yleisesti käytössä Internetissä, suoritimme testit uudelleen myös sillä; tulokset olivat niin samankaltaisia kuin TLSv1.3:lla, ettemme sisällytä niitä tähän.)
HAProxy: Configuration and Versioning
Varasimme käänteiseksi välityspalvelimeksi HAProxyn version 2.3 (stable).
Populaarisen verkkosivuston samanaikaisten käyttäjien määrä voi olla valtava. Suuren liikennemäärän käsittelemiseksi käänteisen välityspalvelimen on voitava skaalautua hyödyntämään useita ytimiä. Skaalaamiseen on kaksi perustapaa: moniprosessointi ja monisäikeistäminen. Sekä NGINX että HAProxy tukevat moniprosessointia, mutta niissä on tärkeä ero – HAProxyn toteutuksessa prosessit eivät jaa muistia (kun taas NGINX:ssä ne jakavat). Kyvyttömyydellä jakaa tilaa prosessien kesken on useita seurauksia HAProxylle:
- Konfiguraatioparametrit – mukaan lukien rajoitukset, tilastot ja nopeudet – on määriteltävä erikseen kullekin prosessille.
- Suorituskykymetriikat kerätään prosessikohtaisesti; niiden yhdistäminen vaatii ylimääräisiä konfiguraatioita, jotka voivat olla varsin monimutkaisia.
- Jokainen prosessi käsittelee terveystarkastukset erikseen, joten kohdepalvelimia tutkitaan prosessikohtaisesti eikä palvelinkohtaisesti, kuten odotetaan.
- Tilaisuuksien pysyvyys ei ole mahdollista.
- HAProxy Runtime API:n kautta tehty dynaaminen konfiguraatiomuutos koskee vain yhtä prosessia, joten API-kutsu on toistettava jokaista prosessia varten.
Näiden ongelmien vuoksi HAProxy ei vahvasti suosittele moniprosessoritoteutuksen käyttöä. Lainaan suoraan HAProxyn konfigurointikäsikirjasta:
MONIPUOLISEN PROSESSIN KÄYTTÄMINEN ON KOVEMPAA DEBUGATA JA SEN KÄYTTÄMINEN ON TODELLA KIELLETTY.
HAProxy otti käyttöön monisäikeistyksen versiossa 1.8 vaihtoehtona moniprosessoinnille. Monisäikeistäminen ratkaisee suurimmaksi osaksi tilanjako-ongelman, mutta kuten kohdassa Suorituskykytulokset käsitellään, monisäikeistämisessä HAProxy ei suoriudu yhtä hyvin kuin moniprosessitilassa.
HAProxy-konfiguraatiomme sisälsi varauksen sekä monisäikeistystilaa (HAProxy MT) että moniprosessitilaa (HAProxy MP) varten. Vaihtaaksemme tilojen välillä kullakin RPS-tasolla testauksen aikana kommentoimme ja poistimme kommentit asianmukaisista riveistä ja käynnistimme HAProxyn uudelleen, jotta konfiguraatio astuisi voimaan:
$ sudo service haproxy restart
Tässä on konfiguraatio, jossa HAProxy MT on provisioitu: Neljä säiettä luodaan yhden prosessin alaisuuteen, ja kukin säie on kiinnitetty suorittimeen. HAProxy MP:ssä (kommentoitu tähän) on neljä prosessia, joista jokainen on kiinnitetty CPU:lle.
global #Multi-thread mode nbproc 1 nbthread 4 cpu-map auto:1/1-4 0-3 #Multi-process mode #nbproc 4 #cpu-map 1 0 #cpu-map 2 1 #cpu-map 3 2 #cpu-map 4 3 ssl-server-verify none log /dev/log local0 log /dev/log local1 notice chroot /var/lib/haproxy maxconn 4096 defaults log global option httplog option http-keep-alive frontend Local_Server bind 172.31.12.25:80 bind 172.31.12.25:443 ssl crt /etc/ssl/certs/bundle-hapee.pem redirect scheme https code 301 if !{ ssl_fc } default_backend Web-Pool http-request set-header Connection keep-alive backend Web-Pool mode http server server1 backend.workload.1:80 check
NGINX: Konfigurointi ja versiointi
Käytimme NGINX Open Source -ohjelman versiota 1.18.0 käänteisenä välityspalvelimena.
Käyttääksemme kaikkia koneen käytettävissä olevia ytimiä (tässä tapauksessa neljää) lisäsimme worker_processes
-direktiiviin auto
-parametrin worker_processes
-parametrin, joka on myös oletusarvoisen, arkistostamme jaellun nginx.conf-tiedoston asetus. Lisäksi sisällytettiin worker_cpu_affinity
-direktiivi, jotta jokainen työprosessi kiinnitettäisiin suorittimeen (jokainen 1
toisessa parametrissa tarkoittaa koneen suorittimen).
user nginx;worker_processes auto;worker_cpu_affinity auto 1111;error_log /var/log/nginx/error.log warn;pid /var/run/nginx.pid;events { worker_connections 1024;}http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; keepalive_timeout 65; keepalive_requests 100000; server { listen 443 ssl reuseport; ssl_certificate /etc/ssl/certs/hapee.pem; ssl_certificate_key /etc/ssl/private/hapee.key; ssl_protocols TLSv1.3; location / { proxy_set_header Connection ' '; proxy_http_version 1.1; proxy_pass http://backend; } } upstream backend { server backend.workload.1:80; keepalive 100; }}
Tulokset suorituskyvystä
Käänteisen välityspalvelimen suorituskyky on kriittinen sovelluksen etupäänä.
Testasimme kutakin käänteistä välityspalvelinta (NGINX, HAProxy MP ja HAProxy MT) kasvavilla RPS-lukumäärillä niin kauan, kunnes yksi niistä saavutti 100 %:n suorittimen käyttöasteen. Kaikki kolme suoriutuivat samalla tavalla RPS-tasoilla, joilla suorittimen käyttöaste ei ollut loppuun käytetty.
100 prosentin suorittimen käyttöasteen saavuttaminen tapahtui ensimmäisenä HAProxy MT:n kohdalla 85 000 RPS:n kohdalla, ja siinä vaiheessa suorituskyky heikkeni dramaattisesti sekä HAProxy MT:n että HAProxy MP:n kohdalla. Seuraavassa esitetään kunkin käänteisen välityspalvelimen viiveen prosenttijakauma kyseisellä kuormitustasolla. Kaavio piirrettiin wrk
-skriptin tulosteesta käyttäen GitHubista saatavaa HdrHistogram-ohjelmaa.
Koska kuormitusnopeus on 85 000 RPS, HAProxy MT:llä latenssi nousee jyrkästi 90:nteen prosenttipisteeseen asti, minkä jälkeen se tasaantuu vähitellen noin 1100 millisekunnissa (ms).
HAProxy MP suoriutuu paremmin kuin HAProxy MT – latenssi nousee hitaammin 99. persentiiliin asti, jolloin se alkaa tasaantua noin 400 ms:ssa. (Vahvistuksena siitä, että HAProxy MP on tehokkaampi, havaitsimme, että HAProxy MT käytti hiukan enemmän suorittimen prosessoria kuin HAProxy MP jokaisella RPS-tasolla.)
NGINX:llä latenssia ei ole käytännöllisesti katsoen lainkaan millään persentiilillä. Suurin viive, jonka merkittävä määrä käyttäjiä voi kokea (99,9999. prosenttipisteessä), on noin 8 ms.
Mitä nämä tulokset kertovat meille käyttäjäkokemuksesta? Kuten johdannossa mainittiin, mittari, jolla on todella merkitystä, on vasteaika loppukäyttäjän näkökulmasta, eikä testattavan järjestelmän palveluaika.
On yleinen harhaluulo, että jakauman mediaaniviive edustaa parhaiten käyttäjäkokemusta. Itse asiassa mediaani on luku, jota noin puolet vasteajoista on huonompia! Käyttäjät tekevät tyypillisesti monia pyyntöjä ja käyttävät monia resursseja sivulatausta kohden, joten useiden heidän pyynnöistään viiveet ovat väistämättä kaavion ylempien prosenttilukujen (99. – 99,9999.) luokkaa. Koska käyttäjät eivät siedä huonoa suorituskykyä, he huomaavat todennäköisimmin yläpuolella olevat viiveet.
Ajattele asiaa näin: kokemuksesi ruokakaupan kassalla käymisestä määräytyy sen mukaan, kuinka kauan kaupasta poistuminen kestää siitä hetkestä lähtien, kun astuit kassajonoon, eikä vain sen mukaan, kuinka kauan kassanhoitajalta kesti laskea tavarasi. Jos esimerkiksi edessänne oleva asiakas kiistää jonkin tuotteen hinnan ja kassanhoitajan on pyydettävä jotakuta tarkistamaan se, kassallaoloaikasi on paljon tavallista pidempi.
Voidaksemme ottaa tämän huomioon latenssituloksissamme, meidän on korjattava jotain, jota kutsutaan koordinoiduksi laiminlyönniksi, jossa (kuten wrk2
README:n lopussa olevassa huomautuksessa selitetään) ”suuren latenssin vastaukset johtavat siihen, että kuormanmuodostaja koordinoi palvelimen kanssa välttääkseen mittauksen korkean latenssin aikana”. Onneksi wrk2
korjaa koordinoidun laiminlyönnin oletusarvoisesti (lisätietoja koordinoidusta laiminlyönnistä on README:ssä).
Kun HAProxy MT kuluttaa suorittimen loppuun 85 000 RPS:n nopeudella, monet pyynnöt kokevat suuren latenssin. Ne ovat oikeutetusti mukana tiedoissa, koska korjaamme koordinoidun laiminlyönnin. Tarvitaan vain yksi tai kaksi korkean viiveen pyyntöä viivästyttämään sivun latausta ja aiheuttamaan käsityksen huonosta suorituskyvystä. Kun otetaan huomioon, että todellinen järjestelmä palvelee useita käyttäjiä kerrallaan, vaikka vain yhdellä prosentilla pyynnöistä olisi korkea viive (arvo 99. persentiilissä), suuri osa käyttäjistä on potentiaalisesti kärsinyt siitä.
Johtopäätös
Yksi suorituskyvyn vertailuanalyysin pääkohdista on sen määrittäminen, reagoiko sovelluksesi tarpeeksi nopeasti tyydyttääkseen käyttäjiä ja pitääkseen heidät palaamassa.
Kumpikin NGINX ja HAProxy ovat ohjelmistopohjaisia ja niillä on tapahtumapohjainen arkkitehtuuri. Vaikka HAProxy MP tarjoaa paremman suorituskyvyn kuin HAProxy MT, tilan jakamisen puuttuminen prosessien kesken tekee hallinnasta monimutkaisempaa, kuten HAProxy-kohdassa kerroimme yksityiskohtaisesti: Konfigurointi ja versiointi. HAProxy MT korjaa nämä rajoitukset, mutta heikomman suorituskyvyn kustannuksella, kuten tuloksista käy ilmi.
NGINX:ssä ei ole kompromisseja – koska prosessit jakavat tilan, monisäikeistystilaa ei tarvita. Saat moniprosessoinnin ylivoimaisen suorituskyvyn ilman rajoituksia, jotka saavat HAProxyn luopumaan sen käytöstä.