all the figures

This commit is contained in:
Torma Kristóf 2019-12-04 22:13:01 +01:00
parent 6b77b25dd7
commit d3d1a1cce5
Signed by: tormakris
GPG Key ID: DC83C4F2C41B1047
4 changed files with 244 additions and 28 deletions

View File

@ -21,4 +21,4 @@ deploy:
token: $GITHUB_KEY
file:
- pdf/thesis.pdf
cleanup: false
skip_cleanup: true

View File

@ -10,6 +10,100 @@
\numberwithin{lstlisting}{section}
%\numberwithin{tabular}{section}
\section{Knative Autoscaler Napl\'o feldolgoz\'as\'at v\'egző k\'od}
\label{sec:log-analyze}
\begin{lstlisting}[label=code:log-analyze]
class LogAnalyzer(Analyzer):
def __init__(self):
super().__init__(typeof='.txt')
self.concurrencypersec = []
self.podpersec = []
self.start = datetime.datetime.now()
self.end = datetime.datetime.now()
def listtodict(self, inlist: list) -> dict:
it = iter(inlist)
res_dct = dict(zip(it, it))
return res_dct
def processfile(
self,
fname,
shouldprint: bool = False) -> dict:
dictofsecs = {}
if 'date' in fname:
return {}
with open(fname, 'r') as inputFile:
line = inputFile.readline()
while line:
try:
linedict = json.loads(line)
try:
currdate = linedict['ts'].split(
'.')[0].replace('T', ' ')
dateformatted = datetime.datetime.strptime(
currdate, '%Y-%m-%d %H:%M:%S')
if self.start < dateformatted < self.end:
message = linedict['msg']
messagelist = re.split('[ =]', message)
messagedict = self.listtodict(messagelist)
messagedict['ts'] = dateformatted
if 'ObservedStableValue' in messagedict:
if messagedict['ts'] not in dictofsecs:
dictofsecs[messagedict['ts']] = {
'pod': [], 'cc': []}
dictofsecs[messagedict['ts']]['pod'].append(
float(messagedict['PodCount']))
dictofsecs[messagedict['ts']]['cc'].append(
float(messagedict['ObservedStableValue']))
except Exception as exception:
print(exception)
except json.JSONDecodeError:
continue
finally:
line = inputFile.readline()
return dictofsecs
def readconfigdates(self, directory='.'):
dates = []
with open(directory + "/dates.txt", 'r') as inputFile:
line = inputFile.readline().rstrip()
currline = 0
while line:
dateformatted = datetime.datetime.strptime(
line, '%Y-%m-%d %H:%M:%S')
dates.append(dateformatted)
line = inputFile.readline().rstrip()
currline += 1
self.start = dates[0]
self.end = dates[1]
def averagepersec(
self,
dictoftimes: dict,
shouldprint: bool = False) -> None:
for key, value in dictoftimes.items():
pod = value['pod']
concurrency = value['cc']
avgpod = average(pod)
avgcc = average(concurrency)
self.podpersec.append(avgpod)
self.concurrencypersec.append(avgcc)
if shouldprint:
print(avgpod)
print(avgcc)
def work(self, directory: str = '.') -> None:
files = super().getfiles(directory)
self.readconfigdates(directory)
filelines = {}
for afile in files:
filelines.update(self.processfile(afile))
self.averagepersec(filelines, False)
\end{lstlisting}
%TODO Remove this shit
%----------------------------------------------------------------------------
\clearpage\section{Válasz az ,,Élet, a világmindenség, meg minden'' kérdésére}
%----------------------------------------------------------------------------

View File

@ -1,21 +1,59 @@
\chapter{F\"uggv\'enyek l\'etrehoz\'asa}
A rendszerek sajátosságai miatt ugyanazt a kódot nem lehet változtatás nélkül felhasználni a két eltérő rendszerben. Mindkét rendszerre két tesztfüggvényt valósítottam meg. Az egyik célja a lehető legrövidebb válaszidő és a legnagyobb áteresztőképesség. A másik célja a kérésenként nagyobb processzorterhelés generálása.
Az első egy Go nyelven megvalósított tesztfüggvény, amely a meghívás után “Hello Go!” szöveggel tér vissza. A Kubeless rendszerhez írt függvény szignatúrája egyezik a korábban bemutatott egyszerű “Hello World!” függvényével. Függőségként importálni kell a Kubeless sztenderd könyvtárát.
Az első egy Go nyelven megvalósított tesztfüggvény, amely a meghívás után “Hello Go!” szöveggel tér vissza. A Kubeless rendszerhez írt függvény szignatúrája egyezik a korábban bemutatott egyszerű “Hello World!” függvényével. Függőségként importálni kell a Kubeless sztenderd könyvtárát.
%TODO
<hello-go Dockerfile ide>
Knative rendszerben szükség van a Go sztenderd könyvtárban megtalálható HTTP szerver implementációra, valamint egy belépési függvényre, amely elindítja a webszervert és beköti a függvényünket úgy, hogy az alapértelmezett URL, ahol elindul a webszerver, az ide érkező kéréseket továbbítsa neki. Mivel a Knative-ba Docker Image-eket lehet telepíteni, ezért azt is létre kell hozni. Annak érdekében, hogy a végső Image a lehető legkisebb méretű legyen, a Go program binárissá fordítását külön végezzük el, a végső Image-be csak bemásoljuk azt. Erre a Docker ad eszközt, az úgynevezett multi-stage buildek segítségével. Ez azt jelenti, hogy egy külön végrehajtási láncot leírva lefordítjuk a kódot, majd a végső Image-be csak bemásoljuk a fordítással végzett konténer Image-éből. Az elkészült Image-et - melyet le\'ir\'o Docker file \aref{code:dockerfile-hello-go} k\'odr\'eszleten l\'athat\'o - a Docker Hubra feltöltöttem, hogy ne kelljen minden frissen telepített Workernek megépítenie az Image-et.
Knative rendszerben szükség van a Go sztenderd könyvtárban megtalálható HTTP szerver implementációra, valamint egy belépési függvényre, amely elindítja a webszervert és beköti a függvényünket úgy, hogy az alapértelmezett URL, ahol elindul a webszerver, az ide érkező kéréseket továbbítsa neki. Mivel a Knative-ba Docker Image-eket lehet telepíteni, ezért azt is létre kell hozni. Annak érdekében, hogy a végső Image a lehető legkisebb méretű legyen, a Go program binárissá fordítását külön végezzük el, a végső Image-be csak bemásoljuk azt. Erre a Docker ad eszközt, az úgynevezett multi-stage buildek segítségével. Ez azt jelenti, hogy egy külön végrehajtási láncot leírva lefordítjuk a kódot, majd a végső Image-be csak bemásoljuk a fordítással végzett konténer Image-éből. Az elkészült Image-et a Docker Hubra feltöltöttem, hogy ne kelljen minden frissen telepített Workernek megépítenie az Image-et.
\begin{lstlisting}[float=!ht,caption={Echo t\'ipus\'u f\"uggv\'eny Docker Image-\'et le\'ir\'o Dockerfile},label=code:dockerfile-hello-go]
FROM golang:1.13 as builder
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . ./
RUN CGO_ENABLED=0 GOOS=linux go build -v -o server
FROM alpine:3
RUN apk add --no-cache ca-certificates
COPY --from=builder /app/server /server
CMD ["/server"]
\end{lstlisting}
A várhatóan nagyobb processzorterhelést generáló függvényként egy Python nyelven implementált prímszám számoló szerepelt. E függvény viselkedése egyezik mindkét rendszerbe telepített verzió esetében, csupán a bemeneti érték kinyerése tér el. A függvény erről a bemeneti értékről dönti el, hogy az prímszám-e. Kubeless esetében a Go-val ellentétben nincs szükség semmilyen függőség megjelölésére, a megírt függvényt egyből lehet telepíteni.
A Knative esetében szintén eltérő módon érdemes Docker Image-et készíteni a megírt függvényből. Szükség van a Flask nevű könyvtárra, amely segítségével webes alkalmazásokat lehet készíteni Pythonban. Használata igen hasonló a Go-ban megtalálható implementációhoz. Úgynevezett dekorátor segítségével lehet a Flask számára jelezni, hogy az adott függvény milyen URL meghívásakor fusson le, valamint a használni kívánt port számát is itt lehet megadni. A Docker Image építése során multi-stage build használatára nincsen szükség, hiszen a Python értelmezőnek mindenképpen jelen kell lennie a végső Image-ben is. A Flask telepítése mellett úgy döntöttem, hogy a gunicorn Pythonban implementált webszervert is telepítem, valamint azt használom, mint webszervert a Docker Image-ben. Használatának előnye, hogy képes kihasználni a Python által nyújtott multiprocessing lehetőségeket, amely azt jelenti, hogy több Python értelmezőt indítva, több folyamat használatával jobb teljesítményt lehet elérni, mint több szál használatával, ugyanis a Python értelmező egyszerre egy szálon futó programkódot értelmez.
%TODO
<isprime-python skálázós service YAML ide>
Minden Knative függvényhez definiáltam egy-egy YAML állományban egy olyan Service-t, amelyhez tartozó Podok számát nullára képes leskálázni a rendszer, valamint egy konkurencia alapú skálázást használó Service-t. Az ut\'obbit le\'ir\'o YAML \'allom\'any \aref{code:yaml-isprime-py} k\'odr\'eszleten l\'athat\'o.
Minden Knative függvényhez definiáltam egy-egy YAML állományban egy olyan Service-t, amelyhez tartozó Podok számát nullára képes leskálázni a rendszer, valamint egy konkurencia alapú skálázást használó Service-t.
\begin{lstlisting}[float=!ht,caption={P\'irm\'asz\'aml\'al\'o f\"uggv\'eny Knative Service obejktum\'at le\'ir\'o YAML},label=code:yaml-isprime-py]
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
name: isprime-py-sc-cc
namespace: default
spec:
template:
metadata:
annotations:
autoscaling.knative.dev/class: kpa.autoscaling.knative.dev
autoscaling.knative.dev/metric: concurrency
autoscaling.knative.dev/target: "10"
autoscaling.knative.dev/minScale: "1"
autoscaling.knative.dev/maxScale: "100"
spec:
containers:
- image: tormachris/isprime-python
env:
- name: TARGET
value: "Py"
\end{lstlisting}
Az elkészített függvényen, valamint a Docker Image-eket leíró Dockerfile-okat Git verziókezelőrendszerrel kezeltem, a létrehozott repository-t a GitHub szolgáltatásba feltöltöttem. Ez lehetővé tette a Travis Continuos Integration/Continuos Delivery rendszer használatát. Ezt arra használtam, hogy a változások nyugtázása után, valamint azok GitHubra történő feltöltése után a Docker Image-ek automatikusan épüljenek meg, valamint kerüljenek fel a Docker Hubra. Fontos, hogy ez nem feltétlen jelenti, hogy a Kubernetesben éppen futó, vagy később létrejövő Podok a legfrissebb Image-et fogják használni. Ahhoz szükség van vagy a latest nevű címkét használni, vagy az ImagePullPolicy nevű opciót bekapcsolni a Knative Service, vagy Kubernetes Deployment definiálásánál.

View File

@ -1,13 +1,13 @@
\chapter{M\'er\'esi k\"ornyezet ismertet\'ese}
\section{Topol\'ogia}
A mérési infrastruktúra kialakításánál a fő szempont a potenciális szűk keresztmetszetek elkerülése volt, ennek okán a méréseket három nagy teljesítményű szerveren végeztem. A Kubernetes klaszter egy Masterből és egy Workerből állt, a harmadik számítógépen pedig a mérőeszközök futottak. Az így kialakított mérési klaszter előnye, hogy az egyik gépen futó, nagy erőforrás igényű folyamatok nem vesznek el egy másik gépen futó folyamattól erőforrást. Több Kubernetes Worker használatára volt lehetőség, de mivel egyszerre egy mérés futott, valamint a Workerek közötti elosztást nem volt cél vizsgálni, egyet is elégségesnek ítéltem.
A mérési infrastruktúra kialakításánál a fő szempont a potenciális szűk keresztmetszetek elkerülése volt, ennek okán a méréseket három nagy teljesítményű szerveren végeztem. A Kubernetes klaszter egy Masterből és egy Workerből állt, a harmadik számítógépen pedig a mérőeszközök futottak. A klaszter fizikai fel\'ep\'it\'ese \aref{fig:phys-cluster} \'abr\'an l\'athat\'o. Az így kialakított mérési klaszter előnye, hogy az egyik gépen futó, nagy erőforrás igényű folyamatok nem vesznek el egy másik gépen futó folyamattól erőforrást. Több Kubernetes Worker használatára volt lehetőség, de mivel egyszerre egy mérés futott, valamint a Workerek közötti elosztást nem volt cél vizsgálni, egyet is elégségesnek ítéltem.
\begin{figure}[!ht]
\centering
\includegraphics[width=120mm, keepaspectratio]{figures/physical_cluster.png}
\caption{A m\'er\'esekhez haszn\'alt fizikai klaszter fel\'ep\'it\'ese}
\label{fig:phys-clustert}
\caption{A m\'er\'esekhez haszn\'alt fizikai klaszter fel\'ep\'it\'ese}
\label{fig:phys-cluster}
\end{figure}
A számítógépek két tíz gigabites hálózati interfészen keresztül is össze vannak kötve. Egyiken keresztül csatlakoznak az internetre, ezen az interfészen publikus IP címmel rendelkeznek. A másikon csak egymást érik el, itt privát IP címük volt. Mindkét interfészen egy Layer 2-es szórási tartományban voltak. Szakdolgozatom elkészítése során több klaszterrel is dolgoztam, melyek fizikailag nem feltétlen voltak egy switchre bekötve, viszont ez legrosszabb esetben is csak minimálisan befolyásolhatja a méréseket.
@ -93,17 +93,55 @@ A bash szkript megírásánál a hey automatizált telepítése is fontos volt,
\item valamint a cél IP címét és port számát külön változókban tároltam.
\end{itemize}
A mérendő függvények nevét egy tömbben tároltam, így egymás után több mérés is elvégezhető ugyanazon paraméterekkel. A mérés típusát egy parancssori kapcsolóval lehet kiválasztani. Annak érdekében, hogy a szkript használatát kényelmessé tegyem, úgy döntöttem, mindkét típusú mérést el lehet végezni egy futtatással. A szkript elkészítésénél igyekeztem figyelni arra, hogy új típusú mérés bevezetéséhez ne legyen szükséges olyan részeket módosítani, amik nem kapcsolódnak közvetlen az új funkcionalitáshoz.
A mérendő függvények nevét egy tömbben tároltam, így egymás után több mérés is elvégezhető ugyanazon paraméterekkel. A mérés típusát egy parancssori kapcsolóval lehet kiválasztani. Annak érdekében, hogy a szkript használatát kényelmessé tegyem, úgy döntöttem, mindkét típusú mérést el lehet végezni egy futtatással. A szkript elkészítésénél igyekeztem figyelni arra, hogy új típusú mérés bevezetéséhez ne legyen szükséges olyan részeket módosítani, amik nem kapcsolódnak közvetlen az új funkcionalitáshoz.
%TODO
<bash szkript részlet, for típusú mérésről>
A folyamatos terhel\'est gener\'al\'o m\'er\'es \aref{code:bash-banchmark-for} k\'odr\'eszleten l\'athat\'o. A hey saj\'at\'oss\'agai miatt alkalmaztam azt a megold\'ast, hogy a k\'iv\'ant hossz\'us\'ag\'u fut\'as el\'er\'ese \'erdek\'eben t\"obbsz\"or elind\'itottam az eszk\"ozt. Látható, hogy a Kubeless-be és a Knative-ba telepített függvények meghívása eltér nem csak az alkalmazott http metódusban és a hosztnév sémájában, de a Content-Type fejléc, melynek értékét a T kapcsolóval lehet megadni, megléte is különbözik.
A fenti kódrészleten látszik, hogy a heyben lévő maximum lekérdezési limit miatt a konstans terhelést generáló méréshez a hey többszöri futtatására van szükség. Látható, hogy a Kubeless-be és a Knative-ba telepített függvények meghívása eltér nem csak az alkalmazott http metódusban és a hosztnév sémájában, de a Content-Type fejléc, melynek értékét a T kapcsolóval lehet megadni, megléte is különbözik.
\begin{lstlisting}[float=!ht,caption={Folyamatos terhel\'est gener\'al\'o m\'er\'est v\'egző szkriptr\'eszlet Bash nyelven},label=code:bash-banchmark-for]
if [[ $* == *"--for"* ]]; then
for num in 1 2 3 4 5 6 7 8 9 10; do
echo -e "for $num\n"
if $kubeless; then
if [[ $* == *"--loadtest"* ]]; then
loadtest -k -H "Host: $function.kubeless" --rps $rps -c $connection -t $time -p "$function_firendly".body http://$kuberhost/"$function" >./data/"$function"."$num".txt
else
hey -c "$connection" -q $rps -z "$time" -m POST -o csv -host "$function.kubeless" -D "$function_friendly".body -T "application/json" http://$kuberhost/"$function" >./data/"$function"."$num".csv
fi
else
if [[ $* == *"--loadtest"* ]]; then
loadtest -k -H "Host: $function.default.example.com" --rps $rps -c $connection -t $time http://$kuberhost/ >./data/"$function"."$num".for.csv
else
hey -c "$connection" -q $rps -z "$time" -m POST -o csv -host "$function.default.example.com" http://$kuberhost/ >./data/"$function"."$num".for.csv
fi
fi
done
fi
\end{lstlisting}
%TODO
<bash szkript részlet, cilmb típusú mérésről>
Mint az \aref{code:bash-banchmark-climb} kódrészleten látszik, a növekvő terhelésű mérés implementációja igen hasonló az egyenletes terhelésűhöz. A hey működését kihasználva, a rps kapcsoló segítségével beállított limit nem változik a mérés során, csupán a connection objektumok száma emelkedik.
Mint az a fenti kódrészleten látszik, a növekvő terhelésű mérés implementációja igen hasonló az egyenletes terhelésűhöz. A hey működését kihasználva, a rps kapcsoló segítségével beállított limit nem változik a mérés során, csupán a connection objektumok száma emelkedik.
\begin{lstlisting}[float=!ht,caption={Emelkedő terhel\'est megval\'os\'itő m\'er\'es Bash szkriptnyelven},label=code:bash-banchmark-climb]
if [[ $* == *"--climb"* ]]; then
while [[ $climb -lt $climb_max ]]; do
climb_rps=$((rps * climb))
echo -e "Rps: $climb_rps"
if $kubeless; then
if [[ $* == *"--loadtest"* ]]; then
loadtest -k -H "Host: $function.kubeless" --rps $climb_rps -c 1 -t $time -p "$function_firendly".body http://$kuberhost/"$function" >./data/"$function"."$climb_rps".climb.txt
else
hey -c $climb -q $rps -z $time -m POST -o csv -host "$function.kubeless" -D "$function_friendly".body -T "application/json" http://$kuberhost/"$function" >./data/"$function"."$climb_rps".climb.csv
fi
else
if [[ $* == *"--loadtest"* ]]; then
loadtest -k -H "Host: $function.default.example.com" --rps $climb_rps -c 1 -t $time http://$kuberhost/ >./data/"$function"."$climb_rps".climb.txt
else
hey -c $climb -q $rps -z $time -m POST -o csv -host "$function.default.example.com" http://$kuberhost/ >./data/"$function"."$climb_rps".climb.csv
fi
fi
climb=$((climb + 1))
done
fi
\end{lstlisting}
\section{M\'er\'esi eredm\'enyek automatiz\'alt elemz\'ese}
Egy mérés eredményeként a mérés típusától függően akár több, nagy méretű csv állomány keletkezik. Ezen fájlok feldolgozása függ attól, hogy azt a Jmeter vagy a hey generálta. A fájlok kézi feldolgozása nyilvánvalóan lehetetlen. Az is előfordulhat, hogy már korábban feldolgozott méréseket egy új szempontból is fel kell dolgozni. Ez elő is fordult, ugyanis a program első verziója még nem volt képes az észlelt késleltetés feldolgozására. Szerencsére a Python programnyelv ilyen feladatok elvégzésére kiváló választás.
@ -112,26 +150,72 @@ A program célja az volt, hogy az összes összegyűjtött mérésből generálj
Mivel egy méréshez több állomány is tartozhat, az egybe tartozó állományok egy mappába kerültek. A mappa neve reflektálja a mérőeszköz nevét, a mért rendszer és függvény nevét. Például a jmeter-knative-hello-scale-1 egy lehetséges mappanév. Az így strukturált adatok feldolgozása nem függ másik méréstől. A program indulása után az adatokat tartalmazó mappákat kigyűjti, majd a jmeter kulcsszót tartalmazó mappák feldolgozását továbbítja a Jmeter kimenetét feldolgozni képes objektumnak. Az összes többi könyvtárat pedig a hey által generált állományok feldolgozásáért felelő objektumnak továbbítja. Mivel a Knative Autoscaler naplófájljainak feldolgozása, mint igény, később jelent meg, a könyvtárak neve nem jelzi, hogy az adott objektum számára tartalmaz-e releváns információt, ezért az minden mappa tartalmát megvizsgálja, tartalmaz-e általa feldolgozandó állományokat.
%TODO
<jmeter class részlet>
Ahogy \aref{code:jmeter-analyze} kódrészleten látszik, hogy a Python eléggé megkönnyíti egy csv fájl feldolgozását. A csv.reader függvény egy generátorral tér vissza, melyen végig iterálva lehet soronként feldolgozni az állományt. Egy sort a Python egy tuple objektumként reprezentál. Az első sorban az oszlopok nevét adja meg a Jmeter, így azt külön tuple objektumba mentve a zip hívással egy dictionary-t kapunk. Ebből a kívánt oszlop nevét megadva lehet kinyerni a keresett mezőt.
A kódrészleten látszik, hogy a Python eléggé megkönnyíti egy csv fájl feldolgozását. A csv.reader függvény egy generátorral tér vissza, melyen végig iterálva lehet soronként feldolgozni az állományt. Egy sort a Python egy tuple objektumként reprezentál. Az első sorban az oszlopok nevét adja meg a Jmeter, így azt külön tuple objektumba mentve a zip hívással egy dictionary-t kapunk. Ebből a kívánt oszlop nevét megadva lehet kinyerni a keresett mezőt.
\begin{lstlisting}[float=!ht,caption={Jmeter kimenet\'et feldolgoz\'o k\'odr\'eszlet, Python nyelven},label=code:jmeter-analyze]
class JmeterAnalyzer(CsvAnalyzer):
def __init__(self):
super().__init__()
self.responsepersec = {}
def processfile(
self,
fname,
shouldprint: bool = False):
with open(fname, 'r') as f:
data = csv.reader(f)
fields = next(data)
for row in data:
items = zip(fields, row)
item = {}
for (name, value) in items:
item[name] = value.strip()
sec = datetime.datetime.fromtimestamp(
int(item['timeStamp']) / 1000.0).strftime('%c')
if sec not in self.responsepersec:
self.responsepersec[sec] = []
self.responsepersec[sec].append(float(item['Latency']))
def collectinfo(self, shouldprint: bool = False) -> None:
self.walkresponsepersec(self.responsepersec, shouldprint)
\end{lstlisting}
A JmeterAnalyzer objektum létrejöttekor létrehoz egy dictionary objektumot, melyben a beolvasott fájl timeStamp mezője lesz a kulcs, az érték pedig az adott másodpercben érzékelt válaszok körbefordulási ideje. Mivel a Jmeter egy Java-ban írt szoftver, a timeStamp mező egy java.utils.Date objektum szerializálva. Ahhoz, hogy ebből Python datetime objektumot lehessen készíteni, az adott számot el kell osztani ezerrel. Az így kapott datetime már másodperc pontosan fogja tárolni az időt. Az adott másodpercben visszaérkezett válaszok számát pedig az adott másodpercben tárolt késleltetési értékek száma adja meg.
A feldolgozás végeztével egy-egy listába kigyűjti a másodpercenként összegyűjtött adatokat tartalmazó listák hosszát, valamint az értékek átlagát.
%TODO
<hey class részlet>
Amint \aref{code:hey-analyze} hey esetében máshogy kell csinálni, ugyanis itt a mérés több fájlra bomlik, amelyekben viszont nem lehet feltételezni, hogy pontosan harminc másodpercnyi, vagy egyéb konstans időtartamnyi mérés adatát tartalmazza egy-egy fájl, ugyanis a munka során ez változott. Emiatt egy fájl feldolgozása után gyűjti ki két listába az összegyűjtött adatokat tartalmazó lista hosszát és a késleltetések átlagát.
A hey esetében máshogy kell csinálni, ugyanis itt a mérés több fájlra bomlik, amelyekben viszont nem lehet feltételezni, hogy pontosan harminc másodpercnyi, vagy egyéb konstans időtartamnyi mérés adatát tartalmazza egy-egy fájl, ugyanis a munka során ez változott. Emiatt egy fájl feldolgozása után gyűjti ki két listába az összegyűjtött adatokat tartalmazó lista hosszát és a késleltetések átlagát.
\begin{lstlisting}[float=!ht,caption={Hey kimenet\'et feldolgoz\'o k\'odr\'eszlet, Python nyelven},label=code:hey-analyze]
class HeyAnalyzer(CsvAnalyzer):
def __init__(self):
super().__init__()
def processfile(
self,
fname,
shouldprint: bool = False):
with open(fname, 'r') as f:
data = csv.reader(f)
fields = next(data)
responsepersec = {}
for row in data:
items = zip(fields, row)
item = {}
for(name, value) in items:
item[name] = value.strip()
sec = int(item['offset'].split('.')[0])
if sec not in responsepersec:
responsepersec[sec] = []
else:
responsepersec[sec].append(float(item['response-time']))
self.walkresponsepersec(responsepersec, shouldprint)
\end{lstlisting}
Miután a Knative Autoscaler naplóállománya analizálásának igénye felmerült, a méréseket automatizáló bash szkript módosítva lett úgy, hogy minden mérés kezdetének és végeztének másodpercre pontos dátumát egy külön fájlba menti. Ez által a naplófájl bejegyzéseit lehet szűrni a két dátum köztire.
%TODO
<LogAnalyzer class részlet>
A Knative Autoscaler a naplóbejegyzéseket json objektumként menti, melyből a Python képes dictionary objektumot készíteni. Amennyiben az adott bejegyzés ts mezője a mérés kezdési és befejezési ideje közé esik, akkor az msg mezőben lévő üzenet feldolgozásra kerül. Az üzenetben kulcs-érték párok vannak szóközzel elválasztva egymástól. A kulcs és az érték között egyenlőségjel van. Ezt egy reguláris kifejezéssel listává lehet konvertálni. Sajnos a Python reguláris kifejezés API-jában nincs arra lehetőség, hogy ilyen esetben dictionary objektumot adjon vissza, így azt kézzel kell konvertálni kihasználva azt, hogy az értékek mindig egy kulcs után következnek. Ezután a Podok száma, valamint a megfigyelt stabil konkurencia érték letárolható.
A Knative Autoscaler a naplóbejegyzéseket json objektumként menti, melyből a Python képes dictionary objektumot készíteni. Amennyiben az adott bejegyzés ts mezője a mérés kezdési és befejezési ideje közé esik, akkor az msg mezőben lévő üzenet feldolgozásra kerül. Az üzenetben kulcs-érték párok vannak szóközzel elválasztva egymástól. A kulcs és az érték között egyenlőségjel van. Ezt egy reguláris kifejezéssel listává lehet konvertálni. Sajnos a Python reguláris kifejezés API-jában nincs arra lehetőség, hogy ilyen esetben dictionary objektumot adjon vissza, így azt kézzel kell konvertálni kihasználva azt, hogy az értékek mindig egy kulcs után következnek. Ezután a Podok száma, valamint a megfigyelt stabil konkurencia érték letárolható. Ennek folyamat\'at \aref{sec:log-analyze} f\"uggel\'ekben l\'atni.
A feldolgozott adatokból a matplotlib Python könyvtár segítségével készíthető grafikon úgy, hogy az adatokat tartalmazó listát átadjuk a megfelelő függvény számára. A grafikon mentése egy másik függvényhívással lehetséges. Szintén külön függvényhívással lehet a grafikon címét, valamint a tengelyek feliratát elhelyezni a grafikonon.