From cd997b8ef6cbf16c0544aad6491897c2d9554c27 Mon Sep 17 00:00:00 2001 From: marcsello Date: Sat, 18 Dec 2021 02:43:35 +0100 Subject: [PATCH] Checkpoint I just got struck by the tought of loosing my work again. --- drawio/workload_move | 1 + src/content/scheduling.tex | 98 ++++++++++++++++++++++++++++++---- src/figures/workload_move.pdf | Bin 0 -> 8247 bytes 3 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 drawio/workload_move create mode 100644 src/figures/workload_move.pdf diff --git a/drawio/workload_move b/drawio/workload_move new file mode 100644 index 0000000..286ffed --- /dev/null +++ b/drawio/workload_move @@ -0,0 +1 @@ +5VhNc6M4EP01HJMCYTAc/ZXJVmVqpiqH3TnKIEATGXmFiO38+pVAfAjBjp3YO5XaHBKpkVrQ7/Xrjix3tTt+YXCffaUxIhaw46Plri0A5r4tfkvDqTZ4HqgNKcNxbXI6wzN+Q8qo9qUljlGhLeSUEo73ujGieY4irtkgY/SgL0so0U/dwxQZhucIEtP6J455pqyObXcPHhFOM3V04KkHO9gsVoYigzE99EzuxnJXjFJej3bHFSIydk1c6n0PE0/bF2Mo5+dsePz2lQZR/gUuyx+AFD8f4tdvd45y8wpJqb7YWrnWwuEcsUxAKSdhgWU0twS+qE/hpyY+jJZ5jOQRtuUuDxnm6HkPI/n0IAghbBnfETFzxLDgjL6gFSWUCUtOc7FsmWBCGpMF3CRJQBRJO8254oPjibl6UcQ4Ok6GwGkDKwiJ6A5xdhJL1AYwV1goMoJAzQ89aBtb1kO1YTFUbEpb113AxUDF/IL4z42AoljwT00p4xlNaQ7JprMu9ZB3a54o3atA/0Scn1TwYMmpDkN9pjzo0jgyRCDHr/q+saCord8pFh7b+PszPf4zexBXDlmKuNrV5/LAUfALPwUtWYQMPxVE7ee8H7XASBqxfOVZoTMK5xPcikzSIIAEp7kYRyLiSDB/KXmNhews1IMdjuMabVTgN5F7SOG9lx9Vfaa3tLz1IFECMSfyuCWMXtKKKYN0m0wjpaDqqE63+rSY5vBkzt3Z98D1Qw2vO3XkB+nkAk9z64S6B5okBboJARxgyuYQ+ahkr1WWSrxRHi9kNZKQE1gUONL5oOd0H1Ff7v6lJgiE2Okvtb2a/OhP1kdtdlKziinfaYE5ppJ0rFa8loxPg+dcCkzLXYISPsLcLeWc7kxhjz0UxDOjCognAdi6vv8hYeoJuDei343tw4Qb1A/3PN0xHIlCdG/3fua62/l5sigYBU+9ZUoaJl/fKH+u1jGIQe3xuqnijqSKTyRzij3MtZzx/y5lN7TURqn8+0ceV63ImtftSdH4EO9Uu6nXGVkoVI6PS+8EfS8QXllZC5Wm53Q3jWmQ3XLeywbfDrzZalrHY5TAknQf+6GOyGA0MDsifyShwK0aIsf7T5V1KvYXKK7TV9x2MqG4w2Y3iFDV7BqauBUv4tmfQBO9AYPc4J2aOJuF92HvJ9DderfRxJk3/vq31cTQJPkGWOLocLNoRsJqP9LijV+gaddoJweqNuwuryA6wzLke4bmhCOEHRbbq2kOGPknuIFj+b+Do+2qfxscZnP9hOqyT8iwC/j05X6xnj1UJecKSBpaPDer+dj1xs2qORhr/gaQ9e+HmisyCUAMi6yt2+dnlPCxl553x1ReSN7Xl4Cg/ivdNqW4uqJ05CinPMqs3r9E/9Z0nQvnCBOugLChnSMIu2PV/nYQn9Gw/VaI+wg7nwBixx8ksWtCPCrI74BYTLsL6Lq76W7x3c0/ \ No newline at end of file diff --git a/src/content/scheduling.tex b/src/content/scheduling.tex index 8310f71..620d512 100644 --- a/src/content/scheduling.tex +++ b/src/content/scheduling.tex @@ -4,24 +4,103 @@ \label{chapter:dynamic_scheduling} %---------------------------------------------------------------------------- +Az előző fejezetekben ismertetett, átalakított alkalmazások jól ki tudják használni a peremhálózati informatikai infrastruktúra adta lehetőségeket. Mint azt a \aref{sec:peremhalozati_rendszerek}.\ szekcióban is ismertettem a peremhálózati rendszerek a hagyományos adatközpontokkal szemben számos különbséget képviselnek. Egyik ilyen különbség a telepítésük sűrűsége. Ez a tulajdonsága lehetővé teszi, hogy a megvalósított alkalmazás szempontjából bizonyos szolgáltatásokat a felhasználókhoz \enquote{közel} futtasson. Ennek a \enquote{közelségnek} önmagában számos előnye tud lenni, mint például a csökkentett hálózati késleltetés és terhelés. Tipikusan a peremhálózati infrastruktúrákra jellemző, hogy a felhő végtelennek tűnő számítási kapacitásához képest erősen limitált erőforrások állnak rendelkezésre. +Könnyen beláthatjuk ezekből adódóan, hogy fontos szerepe van annak a kérdésnek, hogy egyes szolgáltatásokat mikor és melyik peremhálózati rendszeren futtatunk, vagy hogy megéri-e kihelyezni őket a felhő által nyújtott futtatókörnyezetből. Egy rosszul megválasztott szolgáltatás elhelyezés könnyen lehet, hogy az előnyök teljes ellentétét idézi elő megnövekedett költségek mellett. Mivel a peremhálózati rendszerek sokkal nagyobb számban állnak rendelkezésre és a helyes elhelyezés is kockázatos feladat, ezért az elhelyezési feladatokat minden esetben \enquote{kézzel} megoldani jelentős kihívást tud jelenteni. -\section{Felhős keretrendszer kiválasztása} +Megoldást jelenthet a problémára ha az egyes szolgáltatás komponenseket automatikusan tudjuk peremhálózati infrastruktúrába kihelyezni. Egy olyan ütemező algoritmusra van szükség, amely képes kiválasztani az ideális futtatási helyet a megfelelő komponenseknek és automatikusan áthelyezni (azaz ütemezni) oda. Így nincs szükség emberi beavatkozásra a megfelelő peremhálózati adatközpont kiválasztásához. + +A dinamikus ütemezés több kérdést és problémát is felvet, hiszen nem egyértelmű, hogy melyik alkalmazás szempontjából mi számít ideális környezetnek. Illetve ha tudjuk, hogy mi számít ideális környezetnek, hogy tudjuk felmérni, hogy melyik környezet lehet ideális jelölt az alkalmazás futtatására. + +Ebben a fejezetben megvizsgálom a dinamikus áthelyezés megvalósításának lehetőségét a korábban ismertetett alkalmazásokon, illetve egy egyszerű megoldást is tervezek, amely a probléma megoldását mutatja be. + +\section{Alkalmas felhős keretrendszer kiválasztása} \label{sec:cloud_framework} -% TODO: Itt nagyon sok mindent át kell írni - \Aref{sec:frameworks}.\ szekcióban több keretrendszert is megvizsgáltam. Ezek közül a \textit{KubeFed}-et választottam. +A kiválasztásnál elsősorban azt vettem figyelembe, hogy milyen lehetőségeket adnak az egyes keretrendszerek a szolgáltatások dinamikus átütemezésére felhő és perem, illetve perem és perem adatközpontok között. -% Itt leírom, hogy az összes egy nagy kula, és a kubefedet is csak azért választom, hogy legyen egységes controlplane +A \textit{KubeFed} mellett a \textit{Kubeedge} volt a másik keretrendszer, amely ideálisnak tűnt, ám mélyebb vizsgálatot követően elvetettem. Ennek oka a korábban is ismertetett hiányosságai kezdetleges állapota volt. -% támogatja az ütemezést -% saját ütemezője nincs -% +Az \textit{EdgeX Foundry} és \textit{Porject EVE} használatát korán elvetettem. Tipikusan erős különbségeket feltételeznek a perem és a felhő adatközpontokkal szemben támasztott elvárásoktól. Emiatt olyan megoldásokat kínálnak, amelyek teljesen más igényeket próbálnak kielégíteni, mint amiket az én eredeti problémafelvetésem támaszt. Emiatt ezeknek a megoldásoknak a használata csak felesleges nehézségeket jelentene. -\section{Komponensek dinamikus ütemezése} +A megvizsgált \acrshort{paas} rendszereket azért nem szerettem volna használni, mert célom volt egy teljesen nyílt megoldás készítése, ami nyílt szoftverekre épül és nem függ egy konkrét megoldástól. Továbbá így előfizetésre se volt szükségem. + +A \textit{KubeFed} saját ütemezőt nem implementál (mint ahogy egyik másik vizsgált megoldás sem). Viszont az általa megvalósított egységes vezérlő felületnek köszönhetően nagyon könnyen képesek vagyunk az egyes komponenseket adatközpontokra mozgatni, így könnyen tudunk olyan saját ütemezőt készíteni, ami megvalósítja a feladatot. Emiatt választottam ezt a keretrendszert. + +\section{Szolgáltatás létesítés és mozgatás \textit{KubeFed} környezetben} + +Konténerizált alkalmazás környezetben egy futó konténer mozgatása csak korlátozott értelemben lehetséges. Mivel az alkalmazás nem egy virtualizált hardveren, hanem magán a hoszt operációs rendszeren fut egy konténerizált környezetben, így nincs egyszerű arra, hogy egy alkalmazást a memória tartalmával együtt mozgassunk, úgy, hogy az egy másik hoszton ugyan úgy tudja folytatni a működését. + +Ezért az áthelyezés az adott konténer leállítását és egy másik hoszton (másik klaszterben) való elindítását jelenti. Ez állapotmentes szolgáltatásoknál nem jelent nagyobb problémát, hiszen nincs belső állapot amit meg kell őrizni a mozgatás során. Belső állapottal rendelkező alkalmazások esetén viszont ez komplex problémát is jelenthet akár. + +Állapotmentes szoftverek esetén lehetőségünk van arra, hogy a konténerek \enquote{mozgatása} alatti kieséseket minimalizálhatjuk azzal, hogy az új konténereket előbb indítjuk el, mint hogy a régit leállítanánk. Ez idő alatt lehetőség van a még futó kéréseket befejeznie a leállítandó alkalmazásnak, miközben az új kérések már az újonnan indított alkalmazáshoz érkeznek. Egy ilyen átterheléses áthelyezés időbeni lefutását mutatja be \aref{fig:workload_move}.\ ábra. Ideális esetben egy ilyen mozgatás teljes mértékben szolgáltatás kiesés nélkül megoldható. + +\begin{figure}[h!] + \centering + \includegraphics[width=0.7\textwidth]{figures/workload_move} + \caption{Állapotmentes alkalmazás mozgatása \enquote{A} hosztról \enquote{B} hosztra.} + \label{fig:workload_move} +\end{figure} + +Az átterheléses mozgatás belső állapottal rendelkező alkalmazások esetén nem könnyen oldható meg, hiszen a belső állapot szinkronizálása komoly feladat és többnyire nem is oldható meg az alkalmazás közreműködése nélkül. + + +Az alkalmazások leállítását és másik klaszterben való elindítását a \textit{KubeFed} teljes mértékben támogatja. Az összes federált \acrshort{api} objektum esetén lehetőséget ad arra, hogy kiválasszuk, hogy melyik klaszterben hozza létre. De még arra is lehetőséget nyújt, hogy bizonyos tulajdonságait az objektumoknak klaszterre szabva változtassuk meg. + +Ezeknek a konstrukcióknak a segítségével az ütemező szoftvernek csak egy \acrshort{api} hívást kell intéznie a \textit{Kubernetes} \acrshort{api}-hoz és ezzel meg tudja változtatni az ütemezést. A fentebb vázolt átterheléses mozgatást magától nem tudja megvalósítani a \textit{KubeFed}, de megfelelő \acrshort{api} hívásokkal ez is megoldható. + +\section{Alkalmazás komponensek dinamikus ütemezésének vizsgálata} + +Egy alkalmazás komponens dinamikus ütemezésének megvalósítása nagyon sok esetben támogatást igényel az alkalmazástól. Annak eldöntése, hogy mikor ütemezzük a komponenseket, hova és mit nyerünk az ütemezéssel mind-mind az alkalmazás saját tulajdonságaitól és belső működésétől függ. + +Emellett a konkrét ütemezés megvalósítása is erősen függ az alkalmazás tulajdonságaitól működésétől. + +A következőkben a korábban bemutatott alkalmazásokat fogom megvizsgálni abból a szempontból, hogy ezeket a problémákat, hogy lehet megoldani. Illetve milyen támogatásra van szükség az alkalmazástól. + +\subsection{Intelligens madárhang felismerés} + +Ebben az alkalmazásban a peremhálózati adatközpontba egy előszűrést megvalósító komponens kerül. Ennek a komponensnek a feladata, hogy a sok \acrshort{iot} eszköz által feltöltött hangminták által generált hálózati terhelést csökkentse azzal, hogy intelligens felismerést futtatva csak a releváns hangmintákat küldi tovább. + +Ez a komponens állapotmentes és + +\subsubsection{Ütemezési döntés meghozása} + +\subsubsection{Ütemezés megvalósítása} + +\subsection{Robot vezérlés} + +A felhő alapú robotkarok alkalmazásánál a peremhálózati adatközpontba egy vagy több alacsony szintű mozgást vezérlő komponens kerülhet. Ezek egymással egy osztott memóriát megvalósító \textit{Redis} adatbázison keresztül kommunikálnak. Ezek a komponensek belső állapottal rendelkeznek. Létrehozásukkor megkezdik a hozzájuk rendelt \textit{program} letöltését és futtatását. Ha a \textit{program} véget ért, akkor a komponensek kilépnek. + +\subsubsection{Ütemezési döntés meghozása} + +Ebben az alkalmazásban annak a megállapítása, hogy milyen logika alapján lehet eldönteni, hogy hol futtassuk az alkalmazásunkat jelentősen egyszerűbb: A valósidejű vezérléshez az elérhető legkisebb késleltetést kell elérnünk. Ahhoz, hogy meg tudjuk állapítani, hogy melyik az a peremhálózati adatközpont, amely ilyen tulajdonságokkal rendelkezik, ismernünk kell a robotkarokat tartalmazó telephely és az összes számításba jövő peremhálózati adatközpont közötti késleltetést. + +Ha ezt ismerjük, akkor egy egyszerű minimum függvényt használva, ki tudjuk választani az ideális futtatás helyét. + +\subsubsection{Ütemezés megvalósítása} + +A vezérlést megvalósító komponens belső állapottal rendelkezik. Futás közbeni átütemezése ezért nem megvalósítható. A robotkarok mozgatása nem engedhet meg kiesést, hiszen a vezérlés elvesztése akár katasztrofális következményekkel is járhat. A belső állapottal rendelkező komponensek áthelyezése kiesés nélkül viszont komplex feladat. + +Mivel \aref{sec:edge_layer_plans}.\ szekcióban kikötöttük, hogy minden programnak biztonságos állapotba kell helyeznie a robotkarokat lefutása végén. Illetve ebből az ismert állapotból legyen képes megkezdeni a vezérlést, ezért lehetőségünk van arra, hogy két lefutás között helyezzük át a programot. + +Az áthelyezés így jelentősen leegyszerűsödik, hiszen csak arra van szükség, hogy a vezérlő komponenseket a megfelelő adatközpontban indítsuk el. + +A vezérlést megvalósító szolgáltatás komponensek indításáért ebben az alkalmazásban külön szolgáltatás felel, ezért ez az a szoftverkomponens, amelynek támogatnia kell a dinamikus ütemezési döntések alkalmazását. + + +\section{Ütemező rendszer} + + +\subsection{Algoritmus} + +Egy ilyen algoritmusnak fel kell tudnia mérni, hogy melyik a legideálisabb környezet futtatni a szolgáltatást. Sajnos az, hogy mi számít ideális környezetnek, az erősen alkalmazás függő. Vannak alkalmazások, amelyeknél a késleltetés minimalizálása a kliensek felé a cél, de olyan is van, ahol pont a hálózati forgalom aggregálása a cél. Ezért a megfelelő algoritmus megalkotásához szükség van támogatásra a konkrét alkalmazástól. + +%Egy ilyen algoritmusnak fel kell tudnia mérni azokat + +\subsection{Megvalósítás} \begin{figure}[h!] @@ -29,4 +108,5 @@ \includegraphics[width=0.5\textwidth]{figures/turbomemer-legit} \caption{asd} \label{fig:turbomemer} -\end{figure} \ No newline at end of file +\end{figure} + diff --git a/src/figures/workload_move.pdf b/src/figures/workload_move.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b22dc414c11874b791d63acb1e1e89244ff180ec GIT binary patch literal 8247 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