Teljesítménytesztelés (majdnem) ingyen

Minden fejlesztő rémálma, hogy az általa írt kód összeomlik valódi terhelés alatt, sokszor azonban ez (sajnos) elkerülhetetlen. Amikor egy új projekt elindul szinte lehetetlen a majdani felhasználók számát megjósolni, ami teljesítmény szempontjából igencsak megnehezíti az alkalmazás és az infrastruktúra megtervezését, arról nem is beszélve, hogy nem mindig … átfogalmazom: soha nem áll rendelkezésre elég idő és pénz a tökéletesen skálázható szoftver létrehozására A teljesítmény problémája végigkíséri a szoftvert egész életén át, ezért nem árt tisztában lenni annak korlátaival. Ebben segít a teljesítmény tesztelés.

Teljesítménytesztelésről általában

A teljesítménytesztelés lényege tömören, hogy megfigyeljük a rendszerünk viselkedését egy bizonyos terhelés mellett. Mivel rendszer szintű tesztről van szó, a megfigyelésünk tárgya nem csak az alkalmazásunk, hanem a mögötte lévő infrastruktúra (hálózat, storage, stb) is, ezáltal a kapott információk nem csak a fejlesztők, de az üzemeltetők számára is értékesek.

Teljesítménytesztek során a leggyakarabban az alábbiakra szeretnénk választ kapni:

  • adott (folyamatos) terhelés mellett a rendszer helyesen működik-e (load/soak tesztelés)
  • a rendszer a vártnál (jóval) nagyobb terhelése esetén milyen viselkedést mutat (stress/spike tesztelés)
  • a rendszer külöböző komponenseinek módosítása által optimálisabb működést kapunk-e (adott terhelés mellett) (configuration tesztelés)

Az ilyen típusú teszteknél rendkívül fontos, hogy a tesztelési környezet a lehető legjobban hasonlítson az éles környezethez, ellenkező esetben (a nagy számú faktor miatt) a valóságtól igencsak eltérő eredményekhez juthatunk. Talán az is belátható, hogy megfelelő infrastruktúrával kell ellátnunk a tesztek futtatását végző környezetet is, ellenkező esetben ezen rendszer tulajdonságai (például hálózat sebessége) is nagyban befolyásolhatják a kapott adatokat. Nem célszerű például nagy terhelést előállító teszteket egy egyszerű számítógépről futtatni, mert ennek erőforrásai valószínűleg jóval limitáltabbak a szükségesnél.

A JMeterről

Jó pár teljesítménytesztelő eszköz áll rendelkezésünkre, ezek közül az egyik legnépszerűbb a JMeter. A program Java nyelven íródott (ami garancia a “szemet gyönyörködtető” felületre) és elég sok funkciót kapunk tőle. Talán az egyik leghasznosabb ezek közül, hogy a teszteket (ún. “Test Plan”-t) létrehozhatjuk a program felületét használva, majd a futtatáshoz használhatjuk a konzolos üzemmódot, ezzel rengeteg erőforrást takarítva meg (hiszen ezáltal nem vagyunk asztali operációs rendszerekhez kötve). További hasznos funkiója a távoli futtatás, ami annyit tesz hogy a program képes kommunikálni máshol (más gépeken) futó példányaival, és a teszteket azok segítségével is elvégezni. Végül, de nem utolsó sorban: a tesztek futtatása után kapott adatokat a felhasználói felületen feldolgozhatjuk, akár vizuálisan ábrázolhatjuk.

Fizetős szolgáltatásokkal (például Loader.io) összehasonlítva a JMeter rendelkezik azoknak legtöbb (ha nem több) funkciójával, emellett pénztárcabarát is, így valódi alternatívát nyújthat.

Megjegyzés: A szoftver minden funkciójának és beállításának tárgyalása sajnos jelen írás keretein túlmutat, de szerencsére a dokumentéció elég használható és részletes, így érdemes nézegetni.

A teljesítménytesztelésről és a JMeterről itt található még jó, magyar nyelvű leírás.

Prerequisites

Mint említettem az optimális eredmények érdekében érdemes a teszteket saját gépünk helyett egy erre dedikált, megfelelő erőforrásokkal rendelkező rendszeren végezni. Esetünkben ezt most virtuális szerverek fogják adni, melyeket például a DigitalOcean szolgáltatónál is egyszerűen létrehozhatunk.

A JMeter terminológiájában a távoli futtatás esetén a résztvevőket Master és Slave nevekkel illetik.

A Master irányító szerepet tölt be: koordinálja a Slave-eket, átadja nekik a végrehajtandó teszt feladatokat, majd összegyűjti az eredményeket. Elméletileg Master-ként funkcionálhat akár a saját gépünkön futó példánya a programnak, de mint azt fentebb említettem, ez nem javasolt.

A Slave végzi el a munka oroszlánrészét: fogadja a Master utasításait, végrehajtja a teszteket, majd továbbítja az eredményeket a Master felé. Mivel a tesztek végrehajtása eléggé erőforrásigényes is lehet, érdemes a Slave-nek elegendő erőforrást adni.

Az elején az egyszerűség kedvéért kezdjünk két droplettel (DigitalOcean terminológiában ez a VM), melyek az alábbi konfigurációval rendelkezzenek: Ubuntu 14.04 2GB RAM. (A fenti linkre kattintva rögtön ez lesz az alapbeállítás). A későbbiekben be kell majd jelentkeznünk SSH-n keresztül, úgyhogy vagy írjuk fel a root jelszót vagy adjunk hozzá egy SSH kulcsot a dropletekhez.

Szükség lesz továbbá a JMeter telepítésére a saját számítógépünkön, aminek a segítségével majd a tesztjeinket létrehozzuk.

Az alábbi opciók konfigurálásával a későbbiekben szereplő parancsok perszonalizálhatóak.

Slave telepítése

Kezdjük a Slave-ekkel, telepítsük rájuk a Java környezetet és a JMetert:

$ ssh root@SLAVE
$ apt-get update -qq
$ apt-get install -y -qq openjdk-7-jre-headless
$ cd /tmp
$ wget https://www.apache.org/dist/jmeter/binaries/apache-jmeter-3.1.tgz
$ wget https://www.apache.org/dist/jmeter/binaries/apache-jmeter-3.1.tgz.md5
$ md5sum -c apache-jmeter-3.1.tgz.md5
$ mkdir -p /opt/jmeter
$ tar -xf apache-jmeter-3.1.tgz -C /opt/jmeter --strip-components=1
$ rm -rf apache-jmeter-3.1.tgz*

Amikor ez kész van, állítsuk be, hogy a JMeter induljon el szerver (Slave) módban, valamint adjuk meg neki, hogy a saját publikus IP címén figyeljen bejövő kapcsolatokra:

$ wget https://gist.githubusercontent.com/kamermans/2830209/raw/9ecee450adce01ef1767d04ec97d0d79235c0d03/jmeter-server.sh
$ mv jmeter-server.sh /etc/init.d/jmeter
$ sed -i -e 's/JMETER_IP=.*/JMETER_IP=SLAVE/g' /etc/init.d/jmeter
$ chmod +x /etc/init.d/jmeter
$ /etc/init.d/jmeter start

Ezzel készen is volnánk. A fenti lépéseket többször (több VM-en) megismételve több Slave-hez jutunk, mi több erőforrást és nagyobb teszt kapacitást jelent.

Master telepítése

A Master beállítása még ennél is könnyebb. Itt is a Java és a JMeter telepítésével kezdünk:

$ ssh root@MASTER
$ apt-get update -qq
$ apt-get install -y -qq openjdk-7-jre-headless
$ cd /tmp
$ wget https://www.apache.org/dist/jmeter/binaries/apache-jmeter-3.1.tgz
$ wget https://www.apache.org/dist/jmeter/binaries/apache-jmeter-3.1.tgz.md5
$ md5sum -c apache-jmeter-3.1.tgz.md5
$ mkdir -p /opt/jmeter
$ tar -xf apache-jmeter-3.1.tgz -C /opt/jmeter --strip-components=1
$ rm -rf apache-jmeter-3.1.tgz*

Ahhoz, hogy a Slave-ek jó IP címre próbáljanak meg csatlakozni (és ne a helyi 127.0.1.1 címre) a következő paranccsal távolítsuk el az erre vonatkozó sort a Master hosts fájljából:

$ sed -i -e '/127.0.1.1.*/d' /etc/hosts

(Erre a lépésre azért van szükség, mert a Master a saját hosztnevének a feloldásával igyekszik megtudni a publikus IP címét, amire majd a választ kéri a Slave-ektől. Mivel ez alap esetben egy lokális címre van állítva, a sort eltávolítva a probléma megoldódik, de megjegyezném, hogy ennél egy fokkal szebb megoldás lenne, ha az eltávolítás helyett a lokális IP cím helyére beírnánk a kapott külső IP címet)

Utolsó (egyben opcionális) lépésként beállíthatjuk a Slave-ek IP címeit a JMeter konfigurációs fájljában:

$ sed -i -e 's/remote_hosts=.*/remote_hosts=SLAVE/g' /opt/jmeter/bin/jmeter.properties

Erre lehetőségünk lesz később is, a program futtatásakor a Master szerveren.

Teszt tervezés

A JMeter terminológiájában a tesztek futtatásához szükséges információkat tartalmazó valamit teszt tervnek (test plan) hívják. Egy ilyen tervet kell készítenünk, amit a legegyszerűbb módon a program grafikus felületén tehetünk meg. Kezdjünk is neki: hozzunk létre egy tesztet, ami összehasonlítja jelen, illetve egy másik (http://httplug.io), általam készített weboldal válaszidejeit.

A program első megnyitásakor a bal oldalon egy “Test Plan” feliratot kell látnunk:

Empty Test Plan

Erre kattintva elsőként egy Thread Group-ot kell létrehoznunk:

Create Thread Group

Ez felel majd magának a terhelésnek a “létrehozásáért”: itt állíthatjuk be a konkurens felhasználók számát, illetve hogy mennyi ideig/hányszor fusson a teszt (kezdjünk mondjuk 30 felhasználóval és 100 iterációval):

Thread Group

Ezek után hozzáadhatjuk azokat a modulokat, amik a valódi teszt futtatásért felelnek (JMeter terminológiában Sampler-ek), esetünkben ez HTTP Request Sampler lesz:

Create HTTP Request

Ismételjük meg a fenti lépést és állítsuk be a sagikazarmark.hu és a httplug.io domain neveket:

HTTP Request

Ezzel készen is van az első teszt tervünk. Mentsük el egy fájlba a továbbiakhoz.

Ha valaki nem akar a fenti lépésekkel bíbelődni, letöltheti az általam készített tervet innen.

Futás!

Tervünk elkészültével gyarkolatilag készen is vagyunk, a munka innentől a szoftvereké. A kész tervet töltsük fel a Master szerverünkre és indítsuk el a programot, majd annak végeztével töltsük le az eredményeket:

$ scp path/to/test.jmx root@MASTER:
$ ssh root@MASTER /opt/jmeter/bin/jmeter -n -r -t test.jmx -l results.jtl
$ scp root@MASTER:results.jtl path/to/results.jtl

(a tesztektől függően a folyamat akár több órát is igénybe vehet, így ebben az esetben érdemes a parancsokat külön-külön futtatni)

A korábban a Master beállításánál említett opcionális konfigurációt - ha akkor nem - akkor most be kell állítanunk: a -r kapcsoló helyett a -R SLAVE[,SLAVE2]: kapcsolót kell használnunk:

$ ssh root@MASTER /opt/jmeter/bin/jmeter -n -R SLAVE -t test.jmx -l results.jtl

TA tesztek végeztével az adatokat a grafikus felületen elemezhetjük. Esetünkben például a válaszidők elemzéséhez adjunk hozzá egy Response Time Graph, ún. Listener elemet a teszt tervünkhöz és nyissuk meg vele a kapott fájlt:

Add Response Time Graph to the Test Plan

Egy ehhez hasonló grafikont kellene kapnunk:

Response Time Graph

Automatizáljunk

A most létrehozott teszt infrastruktúra rendelkezik a fizetős szolgáltatások legtöbb (ha nem az összes) funkciójával, de jelen formájában még azért nem tökéletes.

Például (a cím igéretével ellntétben) egyáltalán nem ingyenes, hiszen a használt dropletekért fizetnünk kell. A DigitalOcean árazási modeljében szerencsénkre szerepel az óránkénti számlázás, ami lehetővé teszi, hogy pár óra használat után egyszerűen kitörölhetjük a dropleteket és csak arra az időszakra fizessünk, ameddig használtuk őket.

Ezzel azonban rögtön el is jutunk a második problémához, nevezetesen a telepítgetés macerájához. Személy szerint én bármilyen szerver telepítésére sajnálom az időt, arról nem is beszélve, hogy még állandóan törölgessem meg létrehozzam őket. Szerencsére az ilyen jellegű feladatokat ma már tudjuk automatizálni, esetünkben a Terraform nevű eszköz lesz a segítségünkre. A telepítési feladatok mellet képes akár a dropletek létrehozására és törlésére is (a DigitalOcean API-ján keresztül).

A Terraform konfigurációjával egy egész, sőt két cikket is meg lehetne tölteni, így ettől most inkább tekintsünk el és ugorjunk oda ahol csak lefuttatunk két parancsot és kész az infrastruktúránk: íme.

Automate all the things