Jenkins, Kubernetes kérdés

N Jenkins munkával, M párhuzamosra generált Jenkins stage-ekben helm chart csoportok telepítése avagy létezik-e kubernetes shared queue?

Sziasztok!

Egy érdekes problémába futottam bele Kubernetes, Jenkins, helm chart témakörben.

Adott egy szoftver ami microservice-ekké lett átalakítva, a különböző egységei mind különböző helm chart-ok.

Egy teszt-automatizálási rendszert készítettem - Adott sok-sok yaml amely tulajdonképpen a termék különböző lehetőségeit tartalmazza, a rendszer gyakorlatilag ezekből a változókból generál values file-okat a helm chart-ok részére telepítés céljából.

Egy teszt nem más, mint egy csoportja a helm chart-ok telepítésének (majd további teendőket végez), nyilván a változóknak megfelelően beállított telepítést eredményezve. Mindegyik "helm chart csoport" multi-tenant jelleggel, különböző namespace-ekbe kerül, így nem különösebben zavarják egymást.

Jenkins, amikor ezt a teszt-automatizálási feladatot lefuttatja, sajnos mivel nincs végtelen erőforrás, nem telepíthet párhuzamosan "végtelen" tesztet, azaz a helm chart-ok csoportját.

Mivel a Jenkins-ben a parallel stage nem korlátozható könnyedén, én ezt a feladatot egyelőre (később majd automatizálom) konkrét számú (úgyis 99%-ban egyelőre X erőforrás kell nekik szóval később ráér számolgatni), egy LinkedBlockingQueue-val oldottam meg, amelynek segítségével igaz, hogy 1 Jenkins munkafeladat 200 tesztet szeretne futtatni, egy időben a LinkedBlockingQueue miatt, csak Integer maxParallel-t fog egy időben telepíteni, tekintve, hogy nem szeretném hogy a telepítések a sorrendbeli telepítés miatt úgymond "deadlock"-oljanak. Ilyen deadlock lenne, ha mondjuk 200-szor próbálja telepíteni a helm chart csoport első elemét, és tegyük fel épp a 147-ig jut el, mire kifogy az erőforrásból, és hát az első 147 nem tudja folytatni a második chart telepítésével, a többi pedig az előző befejezésére vár hogy az elsőt telepítse - itt pedig a deadlock, egymásra várnak. Tehát ezt ezzel a LinkedBlockingQueue-val sikerült megoldani, viszont ez csak az egyik fele a problémának.

Nyilván van az az eset, hogy nem 1 Jenkins munka kér 200 tesztet, hanem mondjuk 200 kér 300-at, azaz N Jenkins feladat kér M tesztet.

A probléma viszont az, hogy adott Jenkins feladatnak fogalma sincs hogy az épp előtte sorban álló (vagy épp konkurrensen futó többinek) még mennyi terve van hogy telepítsen és abból is, mennyi helm chart-ot. Erre jönne jól, ha nem a Jenkins feladat uralná a queue-t, hanem mondjuk a kubernetes, azaz, hogy ha a Kubernetes-ben lenne egy úgymond shared queue, ahol a tesztek nevei és a Jenkins munkák kezdeti időpontjai hogy a sorrend különböző branch-ekből meghívásra is választ adva működjön (JobId nem lenne szerencsés ugye).

Ergo oké, hogy a LinkedBlockingQueue adott Jenkins-ben működik, csak hát a Jenkins feladatok egymás között kellene hogy ismerjék a Queue-t.

A "helmfile" nem megoldás;

Egy Kubernetes-ben létrehozott "locking megoldás" addig amíg egy adott feladat a queue-t lekérdezi hogy hány névtér került már létrehozásra (pl. ezzel ki lehetne szűrni hogy hány teszt fut épp) szintén elég gáz megoldás lenne több szempontból is.

Egyelőre lehet tekerni az erőforrásokon: megfelezem adott Jenkins feladat queue-ját és 2 párhuzamos feladatot engedek be Jenkins-el. Ez viszont nem igazából skálázható, nem is natív és ráadásul elég problémás ha egy adott teszt időben túl hosszú ideig fut és emiatt az adott queue üresen áll, miközben a többi pedig rá vár.

Ennél még a Jenkins node-ok is jobban teljesítenek, igaz, ott a queue-t a Jenkins maga uralja, ahol az adott Node-ok Executor-okkal oldják ezt meg.

Arra is gondoltam, hogy a Kubernetes-t nem "Cloud"-ként állítom be, hanem Node-ként és "hadd SSH-zzon bele aztán futtasson PodMan-el egy konténert", de ezt "talán inkább hagyjuk".

Rátaláltam a Kueue-ra, ezzel kapcsolatban az jutott eszembe, hogy lehet hogy egy meta-helm-chart-ot kellene generálnom ami függ az adott csoport tagjaitól, ergo ha a termék adott konfigurációja 5 chart-ot akar telepíteni, hozzak létre egy meta-helm-chart-ot amelyben a deployment-ek resource-ai listázva vannak és "egyként" adjam deployment-ként oda Kueue-nak.

Azt viszont látom hogy a kueue-t alapvetően "Job"-ra tervezték és nem helm chart-ra. Az is elég sajnálatos, hogy a Jenkins támogatása a Kubernetes pluginnal hírül sem olyan korrekt mint a Node-ok esetében.

Valakinek van erre ötlete?

Hozzászólások

Elsőre nehéz megérteni, de ha jól értem, akkor minden microservice-nek van saját Helm chartja. Nekem ez ütötte meg a szemem elsőre. 30+ microservice-t deployolunk egy Helm chart tudásával. Nem láttam soha indokoltnak, hogy ennyi Helm chartot tartsunk fent, mert a deployment oldala hasonló minden microservice-nek, ezért nagyon minimális az eltérés a deployment konfigurációban (ami nem azonos a microservice futás idejű konfigurációval). Minden microservice deploymentnek megvan a külön values.yaml fájlja és ugyanazt a helm chartot hívogatjuk a különböző konfigurációkkal. Azt javaslom, hogy fontoljátok meg, hogy lehetne-e ezt egy Helm charttal megoldani.

Jenkinsben a Parallel és a Throttle Concurrent Builds pluginnel szépen beszabályozható, hogy hány build/teszt fusson egyszerre. Ha túl sok a dinamikus job, akkor előfordulhat, ami a leírásban is probléma,  hogy elfogy az executor. Lehet használni a parallel: matrix megoldást,  akkor ugyan csak egy build executor lesz használatban, de akkor meg rejtve a háttérben foglalja a nem kiszámítható mennyiségű erőforrást,  tehát ez csak a probléma elfedése lenne.

 

Ami jó irányba viheti a témát:

- egy Helm chart az összes microservice-hez.

- optimalizálni a dinamikus jobokat.

Egy vs. sok helm chart temahoz: azert ez erosen fugg attol, hogy a microservice-ek mennyire vannak reuse-olva a ceg kulonbozo termekei kozott. Ha van A, B es C termek, amiknek kozos az auth resze, akkor nyilvan jobb, ha az kulon van kezelve (repo, verziozas, etc.). Ha minden microservice-nek kulon repoja van (ami az ajanlott), akkor onnantol az kulon "eletet el". Kulon verziozva lehet csak azt release-elni, stb.

Ha sok kis helm chart van, azokat is bele lehet tenni egy nagy helm chartba subchartokkent, bar az nem tudom mennyire jo otlet, sose hasznaltam meg olyat.

Nálunk hasonló a felállás, sok hasonló microservice. A megoldás erre a szitura a Helm-nek a library chart lehetősége, minden, ami közös, egy külön library chart-ban van definiálva, és az van behivatkozva minden egyes microservice Helm chart-ból, amik így csak vékony wrapperek.

Nekiem meg valami eszembe jutott. Azt irtad, hogy van egy leiro yaml allomanyod, amit arra hasznalsz, hogy csomo values.yaml-t generalj adott helm chartoknak, majd ezekbol X-et telepitesz. Azaz mondjuk van X, Y, Z helm chartod, amikbol egy tesztnek kell az X es a Z, ezeknek generalsz values.yaml-oket es aztan deployolod. Jol ertem?

Mert en inkabb arra fele mennek, hogy teszt tipusonkent van egy helm chartom, amiben dependenciakban ott az osszes child chart. Azaz van egy tesztA nevu chartod amiben dependencia az X,Y meg van egy tesztB amiben dependencia az X,Y,Z,V chartok. Igy teszte tipusokkent kellene a values.yaml-t legyartani es nem child chart szinten. A telepitendo chart-ok szama is messze kisebb lenne, mivel X,Y es X,Y,Z,V helyett csak ket chart a TestA es a TestB lenne telepitve. Persze valijaban ugye huzza maga utan a dependenciakat, de azok nem chartok lennenek ugye, hanem K8S resourcok, amiket a c hart templatek generalnak. A resourcok minden esetben ugytanazok, de egyetlen chart-al telepithetoek lennenek a 4-5 chart helyett.

De lehet nem jol ertettem

Próbáltam többször is átrágni magam azon, hogy pontosan mit akarsz megoldani a topiknyitó leírás alapján, de még nem állt össze a teljes kép, hogy mikor mi a hatásköre a Jenkins pipeline-nak, mi mentén párhuzamosítasz, mik a lépések, amiket a Kubernetes felé végrehajt (helm release? teszt indítás?). Azt feltételezem, hogy nemcsak a Kubernetesbe rak ki valamit a Jenkins job, hanem a Jenkins executor is Kubernetes alapú.

Inkább csak néhány részlethez tudok tippeket írni.

 

Mivel a Jenkins-ben a parallel stage nem korlátozható könnyedén

Ezt hogy kell érteni? A parallel pont akkora párhuzamosságol fut, ahány elemű map-et átadsz neki. Ha tudod, hogy egyszerre 20 dolgot csinálhatsz, akkor adj neki 20 darab belső stage-et. Ezt elő lehet állítani programozottan is, scripted pipeline használatával.

 

Ilyen deadlock lenne, ha mondjuk 200-szor próbálja telepíteni a helm chart csoport első elemét, és tegyük fel épp a 147-ig jut el, mire kifogy az erőforrásból, és hát az első 147 nem tudja folytatni a második chart telepítésével, a többi pedig az előző befejezésére vár hogy az elsőt telepítse

Ha egy részfeladat arra vár, hogy mások befejeződjenek, akkor az a részfeladat ne is kerüljön bele semmilyen queue-ba addig. És akkor el sem indul ez a deadlock-os várakozós szitu. Csak akkor kerüljön bele a queue-ba, hogy aztán később valamikor végrehajtásra is kerüljön, ha a függőségei befejeződtek. Tehát úgymond arra ne legyen várakozás soha, hogy valami befejeződjön. Várakozás mindig csak arra történjen, hogy legyen szabad feldolgozó.

 

Még általánosságban: elég sok rétegben lehet trükközni párhuzamosságal Jenkins és Kubernetes executor viszonyában. Az alap eset, hogy van egy Jenkins node-od, ami megfelel egy Kubernetes pod-nak egy containerrel, és ezen belül fut minden sorosan. Aztán lehet ezen belül parallel blokkod párhuzamosításra. Aztán lehet olyan is, hogy egy pod-ban van több containered, és megmondhathod, hogy egy stage melyik containerben fusson. Még akár olyan is lehet, hogy egy Jenkins job-on belül több pod-ot kérsz a további stage-ek végrehajtására, és mondjuk ezeket kezeled parallel-lel. De hogy a te esetedre melyik a legjobb, ahhoz jobban át kéne látni a problémát.