DockerTesting - Del 2: Ytelsestesting med Docker

19. oktober 2021

Av Richard Rostad, Promis Qualify

Gjennom etter hvert ganske mange år i konsulentbransjen har man hørt mange uttalelser om ytelse- og avbruddstest. “Vi kjører en ytelsestest slik at vi kan verifisere at applikasjonen tåler det trykket vi forventer i produksjon”. “Vi kjører en ytelsestest så vi kan rapportere at vi har kjørt en ytelsestest for vi vet allerede at ytelsen er god nok” (Akkurat den der gir som regel morsomme overraskelser).

Men nei. Den beste grunnen til å kjøre ytelsestester er å undersøke hvordan systemet oppfører seg under press slik at det dukker opp feil som er der hele tiden, men er vanskelige å oppdage når systemet har overskudd av ressurser. Typiske problemer som dukker opp er flaskehalser, memorybruk, bruk av swapspace på disk. Det som mange går glipp av i en slik sammenheng er at ytelsestesting dermed er en utforskende aktivitet med verktøystøtte.

Når man utfører en ytelsestest er det to kategorier av parametere man kan justere:

  • Last - hvor mange forespørsler, hvor store forespørsler, forespørsler som resulterer i større eller mindre grad av prosessering etc. Disse parameterne er enkle å sette opp i verktøy som Jmeter, Gatling eller lignende.
  • Ressurser - Hvor mye CPU, minne, nettverkskapasitet etc. som er tilgjengelig for systemet under test.

Når undertegnede gjør en ytelsestest begynner jeg gjerne med en relativt kraftig belastning. Eksempelvis dobbelt av forventet maksbelastning/sekund i 30-60 minutter. Det pleier gjerne å avdekke områder som bør undersøkes nærmere.

Så justerer jeg testoppsettet, oftest i samarbeid med utviklere, slik at vi får testet nærmere de bitene vi er usikre på. Hvis vi i første runde så tendenser til høyt minnebruk kan vi eksempelvis kjøre med minne-intense forespørsler og moderat last over lengre tid for å undersøke om det kanskje er noe merkelig i forbindelse med Garbage Collection.
Nå er det ikke alltid man kan reprodusere og dermed feilsøke problemer som oppsto i første runde. Tross alt ble det kjørt med rimelig høy last. I slike tilfeller er det ofte nyttig å begrense ressursene som er tilgjengelige for systemet under test. I Docker desktop kan man enkelt styre ressursbruken til Docker miljøet:

Ulempen er at dette styrer ressursene for hele Docker miljøet. Hvis man har et oppsett der testverktøyet kjører som en egen docker kontainer vil man jo også begrense ressursene som er tilgjengelig for lastgenerering. Det er sjelden formålstjenlig.
Heldigvis er vi i den situasjonen at docker-compose styres via en ganske enkel konfigurasjonsfil; docker-compose.yml.
Den har en seksjon for hver tjeneste der man kan definere hvordan den skal kjøres;

          services:
            service:
              image: LastGenerator
                deploy:
                  resources:
                    limits:
                      cpus: 2
                      memory: 8192M
          service:
            image: SystemUnderTest
              deploy:
                resources:
                  limits:
                    cpus: 0.1
                    memory: 128M


I dette konkrete eksemplet har lastgeneratoren tilgang på opptil 2 CPU-er og 8G minne mens systemet under test får bare 1/10 CPU og 1/8G minne slik at det skal være godt rom for å generere en forholdsmessig betydelig last.

Docker compose gir også gode muligheter for automatisering av ytelsestester og for den saks skyld alle andre typer tester man ønsker å kjøre i et integrert miljø, men der man ikke ønsker å ta kostnaden med å drifte et kontinuerlig kjøretidsmiljø med egen hardware etc. Dette er både tids og kostnadsbesparende og gjør at man kan utføre sin testing uten å ødelegge for andre som tester samtidig da hver tester har sitt eget midlertidige miljø.

Hvis vi går tilbake til ytelsestesting kan man eksempelvis bruke en prekonfigurert jmeter kontainer (eller en kontainer for et hvilket som helst annet ytelsestestverktøy. Undertegnede er kjent med Jmeter og benytter det verktøyet til mer enn det strengt tatt er tiltenkt, så som modellbasert massegenerering av syntetiske testdata.) Det er så enkelt som;

          docker pull justb4/jmeter

Det finnes flere alternative kontainere på docker hub, men denne aksepterer de samme argumentene som jmeter i ‘headless’ mode slik at du kan bruke dine allerede eksisterende jmeter filer direkte:

          docker run -name justb4/jmeter —t my_test_plan.jmx -l my_results.jtl

Der my_test_plan.jmx er den jmeter konfigurasjonen du på forhånd har testet grundig.

Når man så legger inn jmeter som en del av det midlertidige docker-compose miljøet så fyres ytelsestesten av hver gang det midlertidige miljøet starter opp. Siden du har konfigurert med fornuftige ressursbegrensninger så kjører ytelsestesten raskt gjennom og fungerer nå både som ytelsestest, smoke-test for å kontrollere at siste versjon av systemet under test er klar til utforskende manuell test og en enkel systemintegrasjonstest.

Og det viktigste av alt er at dette er en realistisk simulering av produksjonsmiljøet. Siden docker kontainere tar med seg alle konfigurasjoner og parametere og dermed kjører uendret i produksjon har vi med noen enkle kommandoer testet at systemet vårt fungerer (eller ikke fungerer) også i produksjon helt uten at vi har et parallellmiljø kjørende til enhver tid.

Docker, kubernetes og tilsvarende teknologier er primært myntet på enklere produksjonssetting og drift, men det betyr ikke at vi testere ikke kan benytte oss av anledningen til å få enklere testing på kjøpet.

I forrige artikkel nevnte jeg at vi kunne foreta avbruddstesting med ett klikk. Når docker compose miljøet er oppe og kjører kan vi ganske enkelt kikke i docker-desktop applikasjonen og trykke ‘stop’ på en av tjenestene som kjører der;


Og så enkelt kan vi stoppe tjenesten og observere hvordan, i dette tilfellet, dependency track brukergrensesnittet fungerer uten back-end og database. (Den oppdager ikke engang at serveren er borte og bare henger uten feilmeldinger). I en moderne mikrotjenestebasert applikasjon er det ganske mange tjenester som i større eller mindre grad avhenger av hverandre og man kan ofte finne ut ganske mange interessante ting ved å forstyrre verdikjeden ved avbruddstesting. Tradisjonelt har slik avbruddstesting krevet større innsats og ofte nedkobling av hardware (trekk ut strømkabelen til en maskin og se hva som skjer-testing). Med docker-compose er avbruddstesting så enkelt at det bør være en del av den normale utforskende testaktiviteten.

 Del 1 finner du her: Introduksjon til docker-testing