Csináld magad stressztesztelés
Csináld magad stressztesztelés - teljesen rugalmas és realisztikus.
Korábban írtunk a stressztesztelésről, ahol a Blazemeter-t mutattuk be, ahol megtanulhattad, hogyan crasheld le az oldaladat anélkül, hogy aggódnál az infrastruktúra miatt. Szóval miért is fáradtam azzal, hogy megírjam ezt a bejegyzést a csináld magad megközelítésről? Van egy komplex frontend alkalmazásunk, ahol szinte lehetetlen lenne hűen szimulálni az összes hálózati tevékenységet hosszú időn keresztül. Böngésző-alapú tesztelési keretrendszert akartunk használni, nevezetesen WebdriverI/O-t néhány egyedi Node.js csomaggal a Blazemeter-en, és gyorsabbnak bizonyult elkezdeni az infrastruktúra kezelését és teljes kontrollt szerezni a környezet felett. Mi lett végül? Egy publikus felhőszolgáltatót használva (esetünkben a Linode-ot), programozott módon elindítottuk a szükséges számú gépet ideiglenesen, felkészítettük őket a megfelelő stack-kel, és a WebdriverI/O teszt végrehajtásra került. Az Ansible, Linode CLI és WebdriverIO segítségével az egész folyamat megismételhető és skálázható, lássuk hogyan!
Infrastruktúra fázis
Bármely tisztességes felhőszolgáltatónak van interfésze a felhőgépek kódból történő provizionálásához és kezeléséhez. Erre építve, ha tetszőleges számú számítógépre van szükséged a teszt indításához, megkaphatod 1-2 órára (100 végpont egy kávé áráért, hogy hangzik?).
Sok lehetőség van virtuális gépek dinamikus és programozott létrehozására stressztesztelés céljából. Az Ansible dinamikus inventory-t kínál, azonban a választott felhőszolgáltatónk nem volt benne az Ansible legújabb stabil verziójában (2.7) ennek a bejegyzésnek az írásakor. Valamint az alábbi megoldás az infrastruktúra fázist függetlenné teszi, bármilyen provizionálás (akár tisztán shell szkriptek) lehetséges minimális adaptációval.
Kövessük a lépéseket a Linode CLI telepítési útmutatójában. A kulcs az, hogy legyen a konfigurációs fájl a ~/.linode-cli-nél a hitelesítő adatokkal és gép alapértelmezésekkel. Ezután létrehozhatsz egy gépet egyetlen sorral:
linode-cli linodes create --image "linode/ubuntu18.04" --region eu-central --authorized_keys "$(cat ~/.ssh/id_rsa.pub)" --root_pass "$(date +%s | sha256sum | base64 | head -c 32 ; echo)" --group "stress-test"
A megadott publikus kulccsal jelszó nélküli bejelentkezés lesz lehetséges. Azonban ez messze nem elég a provizionálás előtt. A bootolás időbe telik, az SSH szerver nem elérhető azonnal, és a mi speciális helyzetünk az, hogy a stresszteszt után azonnal el szeretnénk dobni a példányokat, a teszt végrehajtással együtt a költségek minimalizálása érdekében.
A gép bootolására várás egy kicsit hosszabb kódrészlet, a CSV output robusztusan elemezhető:
## Várakozás a bootra, hogy SSH-zhassunk.
while linode-cli linodes list --group=stress-test --text --delimiter ";" --format 'status' --no-headers | grep -v running
do
sleep 2
done
Azonban az SSH kapcsolat valószínűleg még nem lehetséges, várjuk meg, amíg a port megnyílik:
for IP in $(linode-cli linodes list --group=stress-test --text --delimiter ";" --format 'ipv4' --no-headers);
do
while ! nc -z $IP 22 < /dev/null > /dev/null 2>&1; do
sleep 1
done
done
Észreveheted, hogy ez átfed a gép bootolás várakozással. Az egyetlen előny az, hogy a kettő szétválasztása kifinomultabb hibakezelést és jelentést tesz lehetővé.
Ezután a csoportunkban lévő összes gép törlése triviális:
for ID in $(linode-cli linodes list --group=stress-test --text --delimiter ";" --format 'id' --no-headers);
do
linode-cli linodes delete "$ID"
done
Tehát miután mindent egy szkriptbe csomagoltunk, és egy Ansible meghívást is beletettünk a közepébe, ez lesz a stress-test.sh:
#!/bin/bash
LINODE_GROUP="stress-test"
NUMBER_OF_VISITORS="$1"
NUM_RE='^[0-9]+$'
if ! [[ $NUMBER_OF_VISITORS =~ $NUM_RE ]] ; then
echo "error: Nem szám: $NUMBER_OF_VISITORS" >&2; exit 1
fi
if (( $NUMBER_OF_VISITORS > 100 )); then
echo "warning: Biztos, hogy $NUMBER_OF_VISITORS linode-ot akarsz létrehozni?" >&2; exit 1
fi
echo "Az inventory fájl visszaállítása."
cat /dev/null > hosts
echo "A szükséges linode-ok létrehozása, az inventory fájl feltöltése."
for i in $(seq $NUMBER_OF_VISITORS);
do
linode-cli linodes create --image "linode/ubuntu18.04" --region eu-central --authorized_keys "$(cat ~/.ssh/id_rsa.pub)" --root_pass "$(date +%s | sha256sum | base64 | head -c 32 ; echo)" --group "$LINODE_GROUP" --text --delimiter ";"
done
## Várakozás a bootra.
while linode-cli linodes list --group="$LINODE_GROUP" --text --delimiter ";" --format 'status' --no-headers | grep -v running
do
sleep 2
done
## Várakozás az SSH portra.
for IP in $(linode-cli linodes list --group="$LINODE_GROUP" --text --delimiter ";" --format 'ipv4' --no-headers);
do
while ! nc -z $IP 22 < /dev/null > /dev/null 2>&1; do
sleep 1
done
### Az IP összegyűjtése az Ansible hosts fájlhoz.
echo "$IP" >> hosts
done
echo "Az SSH szerverek elérhetővé váltak"
echo "A playbook végrehajtása"
ansible-playbook -e 'ansible_python_interpreter=/usr/bin/python3' -T 300 -i hosts main.yml
echo "A létrehozott linode-ok takarítása."
for ID in $(linode-cli linodes list --group="$LINODE_GROUP" --text --delimiter ";" --format 'id' --no-headers);
do
linode-cli linodes delete "$ID"
done
Provizionálási fázis
Ahogy korábban írtam, az Ansible csak egy lehetőség, bár népszerű lehetőség gépek provizionálására. Egy ilyen teszthez még egy csomó shell parancs is elegendő lenne a stack beállításához. Azonban miután valaki megkóstolja a deklaratív infrastrukturális munkát, ez lesz az első választás.
Ha ez az első tapasztalatod az Ansible-lel, nézd meg a hivatalos dokumentációt. Dióhéjban, egyszerűen YAML-ben deklaráljuk, hogyan nézzen ki a gép(ek), és milyen csomagokkal rendelkezzen.
Véleményem szerint egy ilyen egyszerű playbook, mint az alábbi, olvasható és érthető önmagában, előzetes tudás nélkül is. Tehát a main.yml a következő:
- name: WDIO-alapú stresszteszt
hosts: all
remote_user: root
tasks:
- name: Apt csomagok frissítése és upgrade-elése
become: true
apt:
upgrade: yes
update_cache: yes
cache_valid_time: 86400
- name: WDIO és Chrome függőségek
package:
name: "{{ item }}"
state: present
with_items:
- unzip
- nodejs
- npm
- libxss1
- libappindicator1
- libindicator7
- openjdk-8-jre
- name: Chrome letöltése
get_url:
url: "https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb"
dest: "/tmp/chrome.deb"
- name: Chrome telepítése
shell: "apt install -y /tmp/chrome.deb"
- name: Chromedriver beszerzése
get_url:
url: "https://chromedriver.storage.googleapis.com/73.0.3683.20/chromedriver_linux64.zip"
dest: "/tmp/chromedriver.zip"
- name: Chromedriver kicsomagolása
unarchive:
remote_src: yes
src: "/tmp/chromedriver.zip"
dest: "/tmp"
- name: Chromedriver indítása
shell: "nohup /tmp/chromedriver &"
- name: A WDIO teszt forráskódjának szinkronizálása
copy:
src: "wdio"
dest: "/root/"
- name: WDIO telepítése
shell: "cd /root/wdio && npm install"
- name: Kezdési dátum
debug:
var=ansible_date_time.iso8601
- name: Végrehajtás
shell: 'cd /root/wdio && ./node_modules/.bin/wdio wdio.conf.js --spec specs/stream.js'
- name: Befejezési dátum
debug:
var=ansible_date_time.iso8601
Telepítjük a Chrome függőségeit, magát a Chrome-ot, a WDIO-t, majd végrehajthatjuk a tesztet. Erre az egyszerű esetre ez elég. Ahogy korábban utaltam rá:
ansible-playbook -e 'ansible_python_interpreter=/usr/bin/python3' -T 300 -i hosts main.yml
Mi az előnye a shell szkriptekkel szemben? Erre a konkrét felhasználási esetre főleg az, hogy az Ansible biztosítja, hogy minden párhuzamosan történhessen, és elegendő hibakezelésünk és jelentésünk legyen.
Teszt fázis
Szeretjük a teszteket. A starter kit-ünkben vannak WebdriverIO tesztek (sok más típusú teszt mellett), így ezt választottuk a teljes stack stresszteszteléséhez. Ha ismered a JavaScript-et vagy a Node.js-t, a tesztkód könnyen érthető lesz:
const assert = require('assert');
describe('podcasts', () => {
it('should be streamable', () => {
browser.url('/');
$('.contact .btn').click();
browser.url('/team');
const menu = $('.header.menu .fa-bars');
menu.waitForDisplayed();
menu.click();
$('a=Jobs').click();
menu.waitForDisplayed();
menu.click();
$('a=Podcast').click();
$('#mep_0 .mejs__controls').waitForDisplayed();
$('#mep_0 .mejs__play button').click();
$('span=00:05').waitForDisplayed();
});
});
Ez a spec fájlunk, ami a lényeg, a konfigurációval együtt.
Megtehettük volna egy csomó kéréssel jMeter-ben vagy Gatling-ban? Majdnem. A hab a tortán az, ahol a podcast streamelését stresszteszteljük. Szimulálunk egy felhasználót, aki 10 másodpercig hallgatja a podcastot. Bármely frontend-nehéz alkalmazáshoz a realisztikus stressztesztelés igazi böngészőt igényel, a WDIO pontosan ezt biztosítja számunkra.

Teszt végrehajtási fázis
Miután a shell szkriptet végrehajthatóvá tettük (chmod 750 stress-test.sh), képesek vagyunk végrehajtani a tesztet akár:
- egy látogatóval egy virtuális gépről:
./stress-test.sh 1 - 100 látogatóval 100 virtuális gépről mindegyikhez:
./stress-test.sh 100
ugyanolyan egyszerűséggel. Azonban nagyon nagyszabású teszteknél gondolnod kell néhány szűk keresztmetszetre, például a tesztelési oldal adatközpontjának kapacitására. Értelmesebb lehet véletlenszerűen választani egy adatközpontot minden tesztelő géphez.
A teszt végrehajtás két fő részből áll: a környezet bootstrap-elése és maga a teszt végrehajtása. Ha a környezet bootstrap-elése túl nagy százalékot vesz igénybe, az egyik stratégia egy Docker image előkészítése, és ahelyett, hogy újra és újra létrehoznánk a környezetet, csak használjuk az image-et. Ebben az esetben érdemes konténer-specifikus hosting megoldást keresni önálló virtuális gép helyett.
Szeretnéd most kipróbálni? Csak csinálj egy git clone https://github.com/Gizra/diy-stress-test.git-et!
Eredmény elemzés
Egy ilyen elosztott DIY tesztnél az eredmények elemzése kihívást jelenthet. Például hogyan mérnéd a kérés/másodpercet egy adott böngésző-alapú teszthez, mint a WebdriverI/O?
Esetünkben az elemzés a másik oldalon történik. Szinte minden hosting megoldás, amivel találkozunk, támogatja a New Relic-et, ami sokat segíthet egy ilyen elemzésben. A tesztünk DIY volt, de az eredménykezelést kiszerveztük. A hab a tortán az, hogy segít a szűk keresztmetszetek felkutatásában is, így hasonló megoldás alkalmazható a te hosting platformodra is.
Azonban mi van, ha össze szeretnéd gyűjteni az eredményeket egy ilyen elosztott teszt végrehajtás után?
Anélkül, hogy részletekbe mennénk, tanulmányozhatod az Ansible fetch modulját, így összegyűjthetsz egy eredménylogot az összes teszt szerverről és helyben egy központi helyen tarthatod.
Következtetés
Nagyszerű tapasztalat volt, hogy miután némi nehézségbe ütköztünk egy hosztolt stresszteszt platformmal; végül képesek voltunk újraalkotni egy megoldást a nulláról sokkal több fejlesztési idő nélkül. Ha az alkalmazásodnak is speciális, szokatlan eszközökre van szüksége a stresszteszteléshez, fontold meg ezt a megközelítést. Az összes választott komponens, mint a Linode, WebdriverIO vagy Ansible könnyen lecserélhető a kedvenc megoldásodra. Földrajzilag elosztott stressztesztelés, teljesen realisztikus weboldal látogatók nehéz frontend logikával, alacsony költségű stressztesztelés - úgy tűnik, most már le vagy fedve!
Áron Novák