Stressztesztelés - Nyugodtan crasheld le az oldaladat

Néhány elérhető eszközt használva a weboldal stresszteszteléshez, tanuljuk meg, hogyan crasheljük le az oldalunkat, mielőtt a látogatóink tennék!

Ha kihívás elé szeretnéd állítani az infrastruktúra szolgáltatódat, a fejlesztői csapatodat, vagy egyszerűen csak szeretnéd optimalizálni a webalkalmazásod skálázhatóságát, csatlakozz hozzánk egy rövid kalandhoz, ahol stresszteszteljük a gizra.com-ot.

Az elején néhány koncepcionális tanulságot tárgyalunk, így ugorhatsz közvetlenül a megvalósítás magyarázatához is.

Ki?

Kire bízzuk a stressztesztelést? Egy régi játékosra, mint a JMeter? Vagy újoncokra, mint a Gatling.io vagy a Locust.io?

A legújabb stressztesztelési feladatunkhoz végül a Gatling.io-t választottuk. A JMeter-t elvettük, mivel a felülete nehezen érthető és XML-alapú, nem emberbarát fájlformátumot használ. Vannak apró technikai különbségek a háttérben a kapcsolatkezelés és a teszt végrehajtás összteljesítménye terén; de a mi forgatókönyvünkben messze voltunk attól a helyzettől, hogy az eszköz korlátozná a képességünket reális képet kapni az alkalmazás viselkedéséről.

A Locust.io viszont kifejező nyelvet használ. Elkezdtünk teszteket építeni vele, de amikor erőforrás-feldolgozásra, változókezelésre, bővítményekre került sor, kiderült, hogy a Gatling.io sokkal szélesebb ökoszisztémával rendelkezik, mint a Locust.io. Amikor tényleges problémát kell megoldanod, nagyobb valószínűséggel találsz példát vagy megoldást a Gatling.io-hoz. Mondanom sem kell, ez a jövőben könnyen változhat.

A hab a tortán az, hogy a Gatling.io egy keretrendszert biztosít a tesztek funkcionális nyelven történő írásához - valami, amit mindannyian szeretünk!

Mit?

Mi a stressztesztelés?

A stressztesztelés valójában nem webfejlesztés-specifikus, stressztesztelheted a CPU-dat, a hálózatodat és természetesen a weboldaladat. A stressztesztelésnek különböző céljai vannak:

  • Az elérhető kapacitás meghatározása.
  • Annak megértése, hogyan degradálódik a rendszer, amikor nem képes tökéletesen teljesíteni.
  • Annak ismerete, mikor válik a rendszer teljesen elérhetetlenné.

Mit szeretnél stressztesztelni?

Weboldal stressztesztelésekor dönteni kell, hogy pontosan mit tesztelünk. Például választhatod:

  • az infrastruktúrát, ahol az oldal adott példánya hosztolva van.
  • a jelenlévő gyorsítótárazási rétegek egyikét (CDN, Drupal page cache stb.).
  • anonim vagy bejelentkezett felhasználóknak.
  • a weboldal frontend kódjának végrehajtásával vagy anélkül.

Keverheted a célkészletedet, de a megvalósítás során figyelned kell erre a döntésre. Például, ha ki szeretnéd zárni a Varnish cache-t a tesztelésből, de továbbra is szeretnéd, ha a Drupal page cache-t használná, trükköket kell alkalmaznod, például érvénytelen session cookie fejléc beállítását, ami megakadályozza, hogy a Varnish gyorsítótárazzon, de a Drupal-nak megengedi, hogy a page cache-t a szokásos módon használja.

Mikor?

Mikor a megfelelő idő a tesztek végrehajtására?

Mondhatnánk, hogy “mindig”, de az árnyaltabb válasz az alkalmazásodtól függ. Ha főleg anonim felhasználóknak szól, a production-szerű környezetben egyszer, közvetlenül az indítás előtt végzett stressztesztelés megfelelőnek tűnik. Sok bejelentkezett felhasználóval rendelkező rendszereknél, ahol a teljesítmény nagyot változhat kiadásról kiadásra, értelmesebb lenne minden nagyobb kiadás előtt végrehajtani.

Hol?

Tehát a projekted elején jársz és kritikus a kiváló teljesítmény és skálázhatóság biztosítása, akkor integrálhatod a Gatling.io-t a Travis-be. Amikor az oldalad bootstrap-elve van, teljesen lehetséges stressztesztet végrehajtani és küszöbértéket definiálni az áteresztőképességre, és hagyni, hogy a Travis hibát jelezzen, ha az eredmény nem haladja meg a meghatározott értéket. Készülj fel, hogy ez kissé növeli a Travis build-jeid instabilitását, mivel a Travis által biztosított környezetben elérhető erőforrások nem teljesen stabilak, és időnként sporadikus hibák lesznek. Ennek ellenére nagyon vonzó lehetőség lehet annak biztosítására, hogy a teljesítmény és a skálázhatóság ne degradálódjon a sprintek során.

Ha production-szerű környezetben szeretnél tesztet végezni, a Travis nem fogja megmondani, hogy az infrastruktúrád vagy szolgáltatód lemarad-e az ígéretéhez képest. Egy production-szerű környezet:

  • ugyanannyi sávszélességgel rendelkezik, mint a production.
  • ugyanazzal az adatbázissal rendelkezik, mint a production (kivéve a szanálást).
  • ugyanolyan szintű hardver erőforrásokkal rendelkezik.

Nyugodtan figyelmen kívül hagyhatod, hogy az éles oldalnak valódi felhasználói vannak, a másik környezetednek viszont nincs, mivel a stresszteszt ideális esetben eléri a stack korlátait, függetlenül a meglévő emberi felhasználóktól.

A Travis-en kívül esetleg végre tudod hajtani a teszteket a local környezetedből, de sokat profitálhatsz egy hosztolt szolgáltatásból, mint a BlazeMeter (írj kommentet, ha tudsz jobb alternatívát). Miért nem lokálisan? A szolgáltatás biztosítani tudja és monitorozni tudja, hogy a teszt végrehajtását nem korlátozza:

  • az elérhető sávszélesség.
  • a teszt-végrehajtó gép számítási erőforrásai.
  • a célponttól való földrajzi távolság.

Ezek és más tényezők befolyásolják a realisztikus végrehajtást, és egy hosztolt szolgáltatás használata segíthet elkerülni ezeket a csapdákat.

Miért?

Mivel ezt a bekezdést olvasod, valószínűleg megvannak a saját válaszaid. A rövid válaszom az lenne, hogy képesek legyünk észrevenni a problémákat, mielőtt megtörténnek. Olyan “gotcha”-k, amiket a múltban láttam:

  • Alulteljesítő hosting szolgáltató.
  • Rosszul konfigurált Varnish.
  • Nem működő anonim page cache.
  • Nem skálázható algoritmus egyedi/contributed kódban.
  • Elégtelen elérhető sávszélesség (az oldalon lévő statikus asset-ekhez és a várt forgalomhoz képest).

Valójában izgalmas problémamegoldó játék az output elemzése és annak megértése, milyen problémák vannak, ha vannak.

Hogyan?

Miután elolvastad a stresszteszteléssel kapcsolatos különböző gondolataimat, készítsünk együtt egy tesztet a Gizra.com ellen.

Észreveheted, hogy Scala-t használunk a tesztek írásához. Ne aggódj, az összes remek tutorial és példa mellett nincs szükség Scala tudásra, egy kis funkcionális programozási tapasztalat biztosan segít. Adjunk hozzá fokozatosan új darabokat egy kis építőelemhez, amíg végrehajtható tesztünk nem lesz.

Egy egyszerű HTTP kérés

Egyetlen statikus HTTP kérés kiadásához lehet ilyen egyszerű:

val scn = scenario("Gizra")
    .exec(http("request_0")
      .get("/blog/")
    )

Itt egyetlen HTTP kérést adunk ki a host felé, hogy lekérjük a blog oldalt. A képek, CSS vagy JS fájlok egyike sem kerül lekérésre, csak ez az egyetlen URL, semmi más. Az alap URL kezelést egy későbbi lépésben adjuk hozzá.

Fejlécek

A valódi böngészők különböző fejléceket küldenek a kérés mellett, realisztikusabbá tehetjük így:

val httpProtocol = http
  .acceptHeader("*/*")
  .acceptEncodingHeader("gzip, deflate")
  .acceptLanguageHeader("en-US,en;q=0.5")
  .doNotTrackHeader("1")
  .userAgentHeader("Mozilla/5.0 (X11; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0")

val headers_0 = Map(
  "Accept" -> "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
  "Upgrade-Insecure-Requests" -> "1")

val scn = scenario("Gizra")
    .exec(http("request_0")
      .get("/blog/")
      .headers(headers_0)
    )

Lehetőségünk van globális fejléceket definiálni a http protokollon keresztül és minden egyes kéréshez is. Jelenleg nem kötöttük össze a httpProtocol-t az scn szcenárióval, mivel ez akkor történik, amikor definiáljuk a felhasználóinkat.

Dinamikus HTTP kérés

Tegyük fel, hogy szeretnénk szimulálni egy felhasználót, aki megérkezik a blog oldalra és meglátogatja az első blog bejegyzést. Akkor mi az első elem URL-je? Ezt csak az első lekérdezés válaszából lehet megtudni. Szerencsére a Gatling lehetőséget ad az eredmény elemzésére és a következő kérésben való felhasználására:

  val scn = scenario("Gizra")
    .exec(http("request_0")
      .get("/blog/")
      .check(css("#blog-page .content:first-of-type a", "href").saveAs("blogentry"))
    )
    .exec(http("request_1")
      .get("${blogentry}")
    )

Ha webszolgáltatást tesztelsz és jsonPath-ot vagy xPath-ot kell használnod, az is megvalósítható.

Látogatók

Eddig amit csináltunk, pontosan úgy viselkedik, mint egy böngésző-alapú automatizált teszt. Végül definiáltuk, hogyan és mit kell letölteni HTTP kéréseken keresztül, valóban újszerű szintaxissal. Megvan a httpProtocol-unk és az scn szcenáriónk, most megkérhetjük a Gatling-ot, hogy hozzon létre néhány felhasználót és fokozatosan növelje a nyomást az oldalon és az infrastruktúrán:

  setUp(scn.inject(
    rampUsers(10) over(10 seconds),
  )).protocols(httpProtocol)

Ebben a szcenárióban megkérjük a Gatling-ot - 10 másodperc alatt - 10 felhasználó hozzáadására. Sok stratégia létezik, amit a Gatling követhet új session-ök injektálásakor a szimulációba.

Egy Simulation osztály

Most itt az ideje egy kicsit többet használni a Scala-ból, de valójában nem sokat változik tesztről tesztre, így a teljes osztályunk így néz ki:

import scala.concurrent.duration._

import io.gatling.core.Predef._
import io.gatling.http.Predef._
import io.gatling.jdbc.Predef._

class GizraCom extends Simulation {
  val testServerUrl = scala.util.Properties.envOrElse("GIZRA_COM_BASE_URL", "https://www.gizra.com")

  val httpProtocol = http
    .baseURL(testServerUrl)
    .acceptHeader("*/*")
    .acceptEncodingHeader("gzip, deflate")
    .acceptLanguageHeader("en-US,en;q=0.5")
    .doNotTrackHeader("1")
    .userAgentHeader("Mozilla/5.0 (X11; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0")

  val scn = scenario("Gizra")
    .exec(http("request_0")
      .get("/blog/")
      .check(css("#blog-page .content:first-of-type a", "href").saveAs("blogentry"))
    )
    .exec(http("request_1")
      .get("${blogentry}")
    )

  setUp(scn.inject(
    rampUsers(10) over(10 seconds),
  )).protocols(httpProtocol)

}

Kényelmes a local végrehajtáshoz, hogy felülírhatod az alap URL-t a GIZRA_COM_BASE_URL környezeti változón keresztül, így:

export GIZRA_COM_BASE_URL="http://gizra.local"
./run.sh

a helyi példányodat tesztelné az éles oldal helyett. Ehhez a run.sh-hoz olvasd tovább.

Végrehajtás lokálisan

A teszt implementálása során biztosan szeretnéd lokálisan végrehajtani, egy kis szkript kényelmes lehet, különösen ha nem vagy egyedül a projekteden:

#!/bin/bash

BASE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
if [ ! -d "$BASE_DIR"/gatling-charts-highcharts-bundle-2.3.0 ]; then
  cd "$BASE_DIR" || exit 1
  wget https://repo1.maven.org/maven2/io/gatling/highcharts/gatling-charts-highcharts-bundle/2.3.0/gatling-charts-highcharts-bundle-2.3.0-bundle.zip
  unzip gatling-charts-highcharts-bundle-2.3.0-bundle.zip
  rm gatling-charts-highcharts-bundle-2.3.0-bundle.zip
  cd gatling-charts-highcharts-bundle-2.3.0 || exit 1
  ln -s "$BASE_DIR"/GizraCom.scala user-files/simulations/
fi
cd "$BASE_DIR"/gatling-charts-highcharts-bundle-2.3.0 || exit 1
./bin/gatling.sh -s GizraCom

A HTML riport részletes és kész megmutatni akár az üzleti érdekelteknek is, a szakértői meglátásaiddal együtt:

Egy riport a Gatling.io-tól a Gizra.com-ról.
Egy riport a Gatling.io-tól a Gizra.com-ról.

Végrehajtás BlazeMeter-en

Opcionálisan, ha realisztikus, nagyszabású végrehajtásokat célozsz ezzel a teszttel, felhőbe tehetjük. Az összetevők itt:

  • egy (ingyenes) BlazeMeter fiók
  • egy Taurus konfigfájl, test.yml:
execution:
- executor: gatling
  scenario: sample

scenarios:
  sample:
    script: GizraCom.scala
    simulation: GizraCom
    keepalive: true

Erre azért van szükség, hogy a BlazeMeter felismerje a Gatling tesztet, teljesen erre a szolgáltatásra specifikus. Ha csak lokálisan akarod végrehajtani, nincs rá szükséged.

Ezután feltöltheted a szimuláció fájlokat (Taurus tesztekként), és elvégezhetsz egy kis konfigurációt:

Fájl feltöltés és konfiguráció.
Fájl feltöltés és konfiguráció.

És még szebb riportokat kapsz, mint legutóbb - élvezd!

Egy BlazeMeter riport.
Egy BlazeMeter riport.

Ráadásul extra erőfeszítés nélkül triviálisan megbizonyosodhatsz arról, hogy a tesztet nem korlátozza az infrastruktúra:

A BlazeMeter monitorozza a végrehajtó gépeket helyetted.
A BlazeMeter monitorozza a végrehajtó gépeket helyetted.

Szimuláció rögzítő

Van egy lusta módja a szimuláció rögzítésének GUI-val, ha még csak kísérletezel a Gatling-gal. Próbáld ki, de bármilyen nem triviális teszthez érintened kell a Scala kódot. Olyan teszteknél, ahol sok statikus erőforrás letöltése érdekel és elég unalmas lenne manuálisan kódolni, adj neki egy esélyt.

A Gatling.io GUI rögzítője - jó társ a tényleges Scala osztályok írásában.
A Gatling.io GUI rögzítője - jó társ a tényleges Scala osztályok írásában.

Hab a tortán - Integrációs tesztek Travis-en

Szóval épp most fejlesztettél ki egy szép, komplex stresszteszt készletet az intraneted számára és azt tervezed, hogy a core projekted mellett karbantartod. Ez nagyszerű! Hozz létre egy apró Travis integrációt a stresszteszt repository-hoz, aztán lazíthatsz: ha a Travis zöld marad, a Scala kód lefordul és a stresszteszt végrehajtható. Ha minimális számú felhasználóval csinálod, valószínűleg elfogadható a production oldalon végezni, így a stressztesztelési repository-dnak lehet a szokásos Travis státusz képe, mint a többi repository-dnak.

Tanulság

Elérheted ezt a kis bemutatót a GitHub-on. Fork-old, csinálj belőle teljes értékű boilerplate-et, küldj PR-t, mindent nagyra értékelünk!

A stressztesztelésnek barátságos tanulási görbéje van, ha már megvan a böngésző-alapú tesztelés gondolkodásmódja. Kezdd el ma, és vállalásokat tehetsz az ügyfeleidnek, hogy az oldaluk skálázódni fog.

Áron Novák

Áron Novák

Devops Gatling Automatizált tesztek