Software Engineering Verziókövetés: git & git flow PDF
Document Details
![HumaneBoltzmann8945](https://quizgecko.com/images/avatars/avatar-16.webp)
Uploaded by HumaneBoltzmann8945
bbte-mi-se
Sulyok Csaba
Tags
Summary
This document is about version control using git and git flow. It covers topics such as repositories, centralize and distributed version control systems, and git commands. The document potentially belongs to a software engineering course or a similar course at a university or college level.
Full Transcript
Software Engineering Verziókövetés: git & git flow Sulyok Csaba [email protected] Software Engineering Verziókövetés: git & git flow Verziókövetés csapatok által haszná...
Software Engineering Verziókövetés: git & git flow Sulyok Csaba [email protected] Software Engineering Verziókövetés: git & git flow Verziókövetés csapatok által használt közös kódbázis kód megosztására, vizsgálatára, karbantartására hasznos biztonsági mentésekre is pár legelterjedtebb verziókövető rendszer: Software Engineering Verziókövetés: git & git flow Tároló (repository) egy mappa, melyben a változásokat monitorizálja a verziókövető rendszer minden állapot elmentődik fejlesztéshez szükséges állományokat tartalmaz: forráskód-állományok, dokumentumok, erőforrások, ábrák, stb. egyes állományok nincsenek követve, pl.: bináris kompilált állományok, külső könyvtárak, stb. tárolók élhetnek: lokális számítógépen: lokális tároló (local repository) külső számítógépen: távoli tároló (remote repository) Software Engineering Verziókövetés: git & git flow Centralizált verziókövető rendszerek egy lokális tárolót szinkronban tarthatunk távoli tároló(k)kal ha egy verziókövető rendszer csak egy távoli tárolót támogat, ez centralizált rendszer nem kerül minden verzió a lokális tárolóba, csak az amin jelenleg dolgozik a fejlesztő pl. SVN Remote origin Local machine Local machine Hátrányok: csak a központi szerveren van biztonsági mentés ⇒ single point of failure Internet kapcsolat lehet szükséges minden mozzanathoz Software Engineering Verziókövetés: git & git flow Osztott verziókövető rendszerek több távoli tároló karbantartása lehetséges az összes állapot másolódik a lokális tárolóba inicializáláskor (clone) minden kliensgép teljes biztonsági mentéssel szolgálhat nem szükséges Internet kapcsolat pl. git, mercurial Hátrány: hosszabb clone idő Software Engineering Verziókövetés: git & git flow git Linus Torvalds készítette az első verzióját 2005-ben parancssorból alkalmazható git [...] formájú parancsokkal pl. aktív verzió lekérése: git version tanulásra ajánlott a parancssor használata, később felhasználói felületet nyújtó segédalkalmazások is használhatóak: GitKraken, SourceTree, fejlesztői környezetbe beépített git támogatás online git tárolókat támogató platformok: GitHub, GitLab, BitBucket, stb. Windows alatt egy szimulált UNIX ter- minálkörnyezetet is nyújt: Git Bash Software Engineering Verziókövetés: git & git flow.gitignore Egy soronkénti lista állománynevekkel és/vagy szűrőkkel, melyek szerint bizonyos állományokat figyelmen kívül hagy a git. Bárhova elhelyezhető a tárolón–a tartalom érvényes az állomány mappájára és minden almappára. Ajánlott mindig legalább egy ilyen állományt elhelyezni egy tároló gyökerében. Példák kihagyandó állományokra: kompiláció eredménye, bináris állományok (pl. *.exe, *.jar) IDE beállítások (pl..idea,.vscode folder) log állományok (pl. *.log) Kiindulópont sablonok: https://github.com/github/gitignore Software Engineering Verziókövetés: git & git flow git tárolók inicializálása inicializálás = egy mappa megjelölése mint lokális tároló git init megjelöli a jelenlegi mappát (pwd) mint lokális git tároló minden kezdetleges metadata a.git almappában jön létre git clone [ ] lehet URL többféle protokollal (http, https, ssh), vagy lokális mappa kimeneti mappa neve - alapértelmezetten a forrás neve Software Engineering Verziókövetés: git & git flow Beállítások git config --list section1.key11=value11 section1.key12=value12 section2.key21=value21 section2.key22=value22 A következő állományokban találhatóak a beállítások (prioritási sorrendben) 1. lokális, tároló-specifikus - /.git/config git config 2. felhasználó szintű - /.gitconfig git config --global 3. rendszerszintű - /etc/gitconfig git config --system Fontos kulcsok (az első fontos beállítás): user.name - teljes név user.email - e-mail cím Software Engineering Verziókövetés: git & git flow Külső tárolók karbantartása egy tároló szinkronban tud maradni több külső tárolóval - a git nomenklatúra szerint ezeket remote-oknak nevezzük egy remote áll: rövid névből (alias) - alapértelmezetten origin a távoli tároló címéből URL vagy másik lokális mappa egy URL használhat különböző protokollokat (leggyakrabban HTTPS vagy SSH) https:////.git https://@//.git ssh://git@:/.git releváns parancsok: git remote - listázza a jelenlegi tároló remote-jainak nevét; a -v opcióval a címeket is git remote add git remote set-url git remote remove Software Engineering Verziókövetés: git & git flow Változások modellezése: DAG A git által használt adatszerkezet a változások modellezésére: irányított körmentes gráf (directed acyclical graph–DAG). A gráf csomópontjai jelképezik a tároló egy-egy állapotát–nevezzük reviziónak vagy commitnak is. A git minden reviziót egyedi bináris azonosítóval lát el. Él b-ből az a-ba = a b állapot az a-n alapszik 629f2ef 29c7fef 99479db 0ea64a7 a2f5330 Software Engineering Verziókövetés: git & git flow Változások modellezése: DAG HEAD master 629f2ef origin/master 29c7fef 99479db 0ea64a7 a2f5330 a gráf továbbá tartalmaz névvel ellátott pointereket (e.g. master) egy commitra mutat, s mozgatható a gráf további változtatása nélkül / (e.g. origin/master) - ahova gondoljuk hogy a master ág pointer mutat az origin remote-on Software Engineering Verziókövetés: git & git flow Változások modellezése: DAG HEAD master 629f2ef origin/master 29c7fef 99479db 0ea64a7 a2f5330 HEAD ez a különleges pointer jelképezi a lokális tárolóban látható állapotot a git a HEAD állapothoz képest küveti a változásokat állapota nincs szinkronizálva a távoli tároló(k)kal általában másik ág pointerre mutat, így marad követhető, de mutathat egyenesen egy revizióra is (headless állapot) Software Engineering Verziókövetés: git & git flow Változások feltöltése git add megjelöl HEAD-hez képest változtatásokat, melyeket szeretnénk a következő commitban látni ennek érdekében egy metaállatpotba helyezi a változásokat: staging area minták és mappanevek is használhatóak (pl. git add *.java, git add.) git commit -m "Üzenet" commit létrejön a lokális tárolóban a változások mellett a commithoz csatolódik: megadott üzenet (Mindig írjunk segítőkész üzeneteket) időpont felhasználó neve és e-mail címe (a konfigurációból) nem szinkronizál távoli tárolóval Software Engineering Verziókövetés: git & git flow Változások feltöltése Ellenálljunk ennek Software Engineering Verziókövetés: git & git flow Változások feltöltése New commit HEAD mybranch Old commit HEAD mybranch otherbranch otherbranch Old commit commit előtt commit után Új commit esetén: egy új csomópont jön létre a gráfban minden megjelölt (add) változással a HEAD és a jelenlegi ág pointer átvándorol az új csomópontra az új commit szüleje a korábbi HEAD állapot lesz–él jön létre. Software Engineering Verziókövetés: git & git flow Változások feltöltése git push [remote] [ágNév] lokális ág pointert szinkronizál egy távolival minden ághoz tartozik egy alapértelmezett remote (upstream) alapértelmezett ág: amelyik éppen aktív (amelyikre a HEAD mutat) minden a távoli tárolón nem létező commitot szinkronizál Local Remote HEAD mybranch New commit New commit Sync mybranch origin/mybranch Old commit Old commit Software Engineering Verziókövetés: git & git flow Változások feltöltése git push [remote] -U [branch] beállítja az adott ág upstreamjét a megadott remote-ra, majd végrehajtja a feltöltést ezután a push meghívható paraméterek nélkül egy lokális ág létrehozása utáni első push-kor kötelező megadni git push --force a standard push hibával lép ki, hogyha a lokális tárolón nincs meg minden kinti változatás a erőltett (force) push fölülírja a távoli ágat nem minden ágon megengedett–függ a felhasználó szerepkörétől csak extrém esetekben és óvatosan használjuk Software Engineering Verziókövetés: git & git flow Változások letöltése git fetch külső commitok letöltése, amelyek nem elérhetőek a lokális tárolóban a HEAD pointert nem mozdítja el–a lokális tároló állapota nem változik ehelyett a legújabb commitra egy újabb metapointer fog mutatni: FETCH_HEAD minden külső branchet szinkronizál, nem csak az aktívnak megfelelőt Local Remote FETCH_HEAD origin/mybranch New commit mybranch New commit Sync HEAD mybranch Old commit Old commit Software Engineering Verziókövetés: git & git flow Változások letöltése git pull letölti a külső változásokat és integrálja őket ekvivalens hívások: git fetch git merge FETCH_HEAD Local Remote origin/mybranch New commit mybranch New commit Sync HEAD mybranch Old commit Old commit Software Engineering Verziókövetés: git & git flow További git parancsok git status lokális tároló állapotáról nyújt információt jelenlegi ág, változott állományok, aktív ág állapota a megfelelő távoli ághoz képest git diff részletes változtatások listázása állományonként alapértelmezetten az összes különbséget megmutatja a lokális tároló és a HEAD között. git log jelenlegi ág commitjait listázza–rálátást nyújt a gráfra mutatja az ág pointereket, commit ID-kat, üzeneteket, szerzőt, stb. git log --oneline - kompakt megjelenítés git log --graph - tényleges gráf kirajzolása git log -n - csak az utolsó n commitot listázza Software Engineering Verziókövetés: git & git flow git log példa Merge commit HEAD master Commit 3a Commit 3b branch1 Commit 2 Commit 1 $ git log --oneline --graph * 1f8272a (HEAD -> master) Merge commit |\ | * 4864a95 (branch1) Commit 3b * | 0f4e525 Commit 3a |/ * 288ee65 Commit 2 * 5c7f256 Commit 1 Software Engineering Verziókövetés: git & git flow git log példa GitLabon is látható a teljes gráf minden ággal: Repository → Graph Software Engineering Verziókövetés: git & git flow Összevonás (merge) git merge Két commit “history”-ját vonja össze: az első a jelenleg aktív (ahova a HEAD mutat), a második a megadott célpont. A célpont sosem mozdul a parancs hatására, csak a HEAD és a jelenleg aktív ág. A célpont lehet egy commit hex ID, ágnév vagy meta-revizió (pl. FETCH_HEAD). A git alapértelmezetten megpróbálja automatikusan végrehajtani az összevonást. Software Engineering Verziókövetés: git & git flow Összevonás (merge) Jelenleg a master ágon vagyunk, s bevonnánk a branch1-en készült változásokat. 1. példa: A célpont már szüleje a HEAD-nek HEAD master Commit 4 Commit 3 branch1 Commit 2 Commit 1 Software Engineering Verziókövetés: git & git flow Összevonás (merge) Jelenleg a master ágon vagyunk, s bevonnánk a branch1-en készült változásokat. 1. példa: A célpont már szüleje a HEAD-nek HEAD master Commit 4 Commit 3 branch1 Commit 2 Commit 1 $ git merge branch1 Already up-to-date. Software Engineering Verziókövetés: git & git flow Összevonás (merge) Jelenleg a master ágon vagyunk, s bevonnánk a branch1-en készült változásokat. 2. példa: A HEAD szüleje a célpontnak: branch1 Commit 4 Commit 3 HEAD master Commit 2 Commit 1 merge előtt Software Engineering Verziókövetés: git & git flow Összevonás (merge) Jelenleg a master ágon vagyunk, s bevonnánk a branch1-en készült változásokat. 2. példa: A HEAD szüleje a célpontnak: branch1 Commit 4 branch1 Commit 4 Commit 3 Commit 3 HEAD master Commit 2 HEAD master Commit 2 Commit 1 Commit 1 merge előtt merge után a jelenlegi ág (s ezáltal a HEAD is) a célpontra ugranak a gráfban nem jönnek létre új állapotok fast-forward merge Software Engineering Verziókövetés: git & git flow Összevonás (merge) Jelenleg a master ágon vagyunk, s bevonnánk a branch1-en készült változásokat. 3. példa: A HEAD és a célpont elágaztak egymástól. HEAD master Commit 3a Commit 3b branch1 Commit 2 Commit 1 merge előtt Software Engineering Verziókövetés: git & git flow Összevonás (merge) Jelenleg a master ágon vagyunk, s bevonnánk a branch1-en készült változásokat. 3. példa: A HEAD és a célpont elágaztak egymástól. Merge commit HEAD master Commit 3a Commit 3b branch1 Commit 2 Commit 1 merge után Software Engineering Verziókövetés: git & git flow Összevonás (merge) Jelenleg a master ágon vagyunk, s bevonnánk a branch1-en készült változásokat. 3. példa: A HEAD és a célpont elágaztak egymástól. Új commit jön létre két szülővel–a merge commit egy speciális commit ezzel a tulajdonsággal. A git megpróbálja a változásokat automatikusan összefésülni–ha különböző állományokban vagy ugyanazon állományok különböző soraikban történt változás. Ha van átfedés, merge konfliktus lép fel. A git megjelöli a problémás sorokat, ezeket kézzel fel kell oldani. Feloldozás után a következő commit merge commit lesz. line 1 line 1 line 1 line 1 line 2 line two line deux > branch1 line 3 Commit Software Engineering 2 Commit 3a Commit 3b Conflict git & git flow Verziókövetés: Elágazás (branching) Ágak segítenek a munka szétválasztásában és párhuzamosításában A git esetén az ágak csak névvel ellátott pointerek a gráfban–más rendszerek más megoldásokat alkalmazhatnak. Aktív branch (checked out branch) = a lokális mappában található verzió (ahova a HEAD mutat). Alapértelmezett ág: master Az online verziókövető platform különbséget tehet védett ágak között (protected branches)–ezekre csak előre meghatározott jogosultság jelenlétében lehet push-olni vagy merge-elni Software Engineering Verziókövetés: git & git flow Elágazás (branching) git branch listázza a lokális ágakat, s jelöli a jelenleg aktívat -a - a külső ágakat is listázza -v - minden ágnál listázza a commitot, ahova mutat HEAD master Commit 3a Commit 3b branch1 Commit 2 Commit 1 $ git branch -av branch1 5af2242 Commit 3b * master 881ab23 Commit 3a remotes/origin/branch1 5af2242 Commit 3b remotes/origin/master 881ab23 Commit 3a Software Engineering Verziókövetés: git & git flow Elágazás (branching) git branch létrehoz egy új ágat az adott névvel az új pointer eleinte oda mutat ahova a HEAD a létrehozás idejekor nem vált át erre az ágra, nem teszi aktívvá git checkout [-b] elmozgatja a HEAD pointert a célponthoz; átvált a megadott ágra ágnéven kívül a ref lehet egy hex commit ID is–headless state -b - ha nem létezik ilyen nevű ág, hiba helyett létrehozza ajánlott ezzel a paranccsal létrehozni ágakat, elkerülendő az átváltás elfelejtése Software Engineering Verziókövetés: git & git flow Clean & reset git clean -xfd minden nem követett állományt töröl egy lokális tárolóban–pl. minden ignore-olt állományt git reset [mode] target az aktív ágat visszaállítja a megadott állapotba módok: --soft - megtartja az add-del megjelölt változtatásokat a staging area-ban --mixed (alapértelmezett) - megtartja a változtatásokat, de kiveszi a staging area-ból (felfogható mint az add ellentéte) --hard - mindent visszaállít, nem commitolt változások elvésznek; használhatjuk arra, hogy egy ágat elmozgassunk hard reset példák git reset --hard HEAD - minden lokális változtatás feltakarítása git reset --hard HEAD~1 - a jelenlegi ágat az eggyel Software Engineering korábbi commmitra ugrasztja Verziókövetés: git & git flow Git flow: konvencionális git csapatmunka folyamat A csapatmunka folyamán kialakulhat egy munkamódszer a git használatát illetően. Preferált elágazási stratégia: funkcionalitás szerint (nem felhasználó szerint) Minden user story-t/funkcionalitást a saját ágán valósítunk meg–feature branch Sosem push-olunk a master ágra, csak merge-eljük a feature branch-ek tartalmát ide Összeköthetjük az összeolvasztást a kód átnézésével és folyamatos integrációval is (több információ később) Software Engineering Verziókövetés: git & git flow Git flow: konvencionális git csapatmunka folyamat sprint eleje master M M feature/1-login feature/2-list-things Példa feature branch névre: feature/- A master ág mindig stabil állapotot tartalmaz, nincs benne félkész funkcionalitás Egy-egy sprint végén a master ág tartalma mindig átadható a kliensnek, még ha nincs is minden feature ága beleolvasztva Software Engineering Verziókövetés: git & git flow GitLab merge request Online eszköz, mellyel két ágat összeolvaszthatunk Áttekinthető összefoglalót kapunk az ágat közötti különbségekről Kommentek hagyhatóak a különböző tartalmon (code review) Feltételek szabhatóak a merge-re, pl. minden komment legyen feloldva, fusson le helyesen a CI, stb. Git flow kontextusában: egy-egy funkcionalitáshoz tartozó ágat olvasztunk a masterbe (vagy másik dedikált ágba) IDDE laborfeladatok: a diák létrehoz egy merge requestet laborfeladatonként–ennek merge-elése jelenti a laborfeladat elfogadását Software Engineering Verziókövetés: git & git flow Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Sulyok Csaba [email protected] Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Példaprogramok: https://gitlab.com/bbte-mmi/softeng/examples/gradle-examples Software Engineering Automatizált build és függőségkezelés: Maven, Gradle 1. rész Bevezető Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Build rendszerek szükségessége a build parancsok legtöbb programozási nyelvnél repetitívek, hibaérzékenyek src/hello/HelloWorld.java: package hello; public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World"); } } futtatható JAR állomány készítése és futtatása: # compile javac -sourcepath src -d build/classes src/hello/HelloWorld.java # próba: class állomány futtatása java -cp build/classes hello.HelloWorld # MANIFEST írása echo "Main-Class: hello.HelloWorld" > MANIFEST.mf # JAR állomány készítése jar cvfm build/jar/HelloWorld.jar MANIFEST.mf -C build/classes/. # JAR állomány futtatása java -jar build/jar/HelloWorld.jar Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Build rendszerek szükségessége ezen folyamatok automatizálása már elérhető a Make eszköz óta a meghívható parancsok valamilyen leíró (deszkriptor) állományban találhatóak Make esetén Makefile a leíró állomány verziókövetőre feltölthető, így egy projekten dolgozó összes csapattag garantáltan ugyanazt a folyamatot hívja egy projekt gyökerébe kerül, általában elkülönítve a forráskódtól további taszkok, melyek repetitívek, s segíthet az automatizálásuk: futtatás tesztek futtatása kitelepítés külső szerverre Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Függőségkezelés vállalati szintű rendszerek esetén legtöbbször nem elegendő a nyelvbe beépített függvények sokasága külső (third-party) könyvtárak és keretrendszerek beépítése szükséges kompilálás és más aktivitások automatizálása (linkelés, összecsomagolás, tesztelés, futtatás konzolról, stb.) pontos verziók leszedése minden alkalommal nem akarjuk verziókövetőn tárolni a függőségeket ugyanazon deszkriptorban rögzítjük, hogy melyek a függőségek (ez az állomány bekerül verziókövetés alá) minden nagyobb nyelv rendelkezik (legalább egy) ilyen eszközzel Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Build és függőségkezelési eszközök Legtöbb programozási nyelv nyújt egyet, pl.: Java: Maven és Gradle node.js: npm, yarn Python: pip.NET/C#: NuGet Egyes eszközök nem nyújtanak függőségkezelést, csak build automatizálást (pl. Make, Ant) Nem kompilált nyelvek esetén gyakran a függőségkezelési aspektus kerül előtérbe (pl. npm) Kompilált nyelvek esetén a modern eszközök mindkét feltételnek eleget tesznek (pl. Maven, Gradle) Software Engineering Automatizált build és függőségkezelés: Maven, Gradle 2. rész Groovy, a gradle szkriptek nyelve Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Groovy A Groovy egy Java alapú interpretált szkriptnyelv, amely egyszerűbben tanulható nyelvekből inspirálódik (pl. Python) Standard Java kód direkten értelmezhető Groovy-ban, de egyszerűsíthető Egyszerűsítések: Nem szükséges egy main függvény, egy szkript belsejében levő kód enkapszulálódik egy main függvénybe. Elhagyhatóak a pontosvesszők. A System.out stream metódusai prefix nélkül meghívhatóak. Függvényhívások zárójelei hanyagolhatóak. Getter és setter metódusok kezelhetőek mint adattag. A return kulcsszó mellőzhető metódusok utolsó sorában. Szimpla idézőjelek használhatóak, dupla idézőjelekbe bash-stílusú változóneveket tehetünk. Nem erősen típusos (def szócskával adhatunk meg változókat). List és Map példányok megadhatóak inline zárójelekkel. Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Groovy összehasonlítás test.groovy – Java-kompatibilis kód public class MyClass { private String myAttribute; public MyClass(String myAttribute) { this.myAttribute = myAttribute; } public String getMyAttribute() { System.out.println("Retrieving attribute"); return myAttribute; } public static void main(String[] args) { System.out.println("Hello, World!"); MyClass myObject = new MyClass("myAttributeValue"); System.out.println("Retrieved attribute is " + myObject.getMyAttribute()); } } Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Groovy összehasonlítás test.groovy – Groovy egyszerűsített kód class MyClass { def myAttribute def getMyAttribute() { println "Retrieving attribute" myAttribute } } println 'Hello, World!' def myObject = new MyClass(myAttribute: "myAttributeValue"); println "Retrieved attribute is ${myObject.myAttribute}" Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Groovy closures Closure = lambda kifejezés = egy névtelen kódblokk, mely különböző kontextusokban újrahasználható Fogadhat tetszőleges számú paramétert // defining closures def c1 = { println "hello from closure" } def c2 = { param1 -> println "hello from closure, param1 = ${param1}" } // executing closures c1(); c2(42) // using closures as arguments println "iterating through 1 to 10" (1..10).forEach { it -> println it } Software Engineering Automatizált build és függőségkezelés: Maven, Gradle 3. rész Gradle (és Maven) Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Gradle (és Maven) Mindkettő Automatizált build, függőség- és projektmenedzsment eszközök Nyílt forráskódú Több projektet menedzselhetünk (multimodul) egyszerre Konvenciót diktál folderek és állományok elhelyezésére, nevére és strukturájára Számos külső kiegészítő plugin Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Gradle (és Maven) Gradle https://github.com/gradle/ Maven gradle https://github.com/apache/ deszkriptor: maven build.gradle (Groovy nyelv) vagy deszkriptor: pom.xml (XML for- build.gradle.kts (Kotlin) mátumú) célpont mappa: build célpont mappa: target kiterjeszthető convention over configuration – nincs saját függőségi rendszere, rigid szabványok, nehezen ter- támogatja a Maven és Ivy-t jeszthetőek, de legtöbb helyze- tre megoldást nyújtanak sok másik programozási saját függőségi rendszer nyelvhez nyújt plugint, illetve Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Gradle taskok A gradle egy command-line eszköz, amely taskok futtatását teszi lehetővé. gradle tasks – egy task mely kiírja a lehetséges taskokat (a tasks is egy task) gradle - lefuttatja az adott nevű taskot. A taskot a task kulcsszóval adjuk hozzá a projekthez, s a benne definiált doLast kulcsszóval adjuk meg a teendőjét. A taskok között függőségek lehetnek (dependsOn kulcsszóval). A taskok leírását két kulcsszón keresztül állítjuk be: group és description. Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Példa: Gradle taskok Példa: gradle-tasks description = ''' Példaprojekt Gradle taszkokkal ''' // létrehozunk egy "a" nevű taszkot task a { group 'Example' // a taszk kategóriája description 'Example task' // leírása doLast { // a taszk tényleges kódja println 'Hello from task a' } } // alternatív írásmód taszk létrehozására task('b', group: 'Example', description: 'Another example task', dependsOn: ['a']) { doLast { println 'Hello from task b' } } // a b taszkot állítjuk alapértelmezettnek Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Gradle pluginok Egy plugin létrehoz új taszkokat, megjelöl forráskódszetteket, vagy elvégez más projektkonfigurációt. Példa: java, cpp, npm, application, maven-publish, etc. Hivatalosan támogatott pluginok: https: //docs.gradle.org/current/userguide/standard_plugins.html Plugin érvénybe léptetése: az apply metódussal: apply plugin: 'java' a plugins closure segítségével (újabb, megadható verzió is a pluginoknak): plugins { id 'java' } Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Példa: gradle java plugin Megjelöli a Maven konvenciók által diktált forráskódmappákat (src/main/java, src/main/resources, src/test/java, src/test/resources) Készít taszkokat buildelésre, tesztelésre, takarításra, stb., amelyek között jól követhető függőségi gráfot alakít ki (pl. a jar csomagoló task függ a classes-től, amely burkolja a forráskódok kompilálását – compileJava – és erőforrások másolását – processResources) Listázhatjuk a meghívott taskokat a gradle --console=verbose taskname flaggel Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Példa: gradle application plugin application plugin: létrehoz taszkokat, amelyekkel: run - futtathatjuk a projektet egy main metódust tartalmazó osztály megadásával installDist, distZip - összecsomagolhatjuk a projektet s az összes függőséget egy kiadható distributable-be további információ Példa: gradle-application plugins { id 'java' id 'application' } application { // a main metódust tartalmazó osztály megjelölése mainClass = 'edu.bbte.gradleex.application.HelloWorld' } Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Konvencionális projektstruktúra build.gradle (Gradle esetén) vagy pom.xml (Maven esetén) - jelen projekt telepítésdeszkrip- tora; build (Gradle esetén) vagy target (Maven esetén) - minden build eredmény, köztes állományok, re- portok, stb.; nem kerül be verziókövetés alá; src/main/java - kompilálandó forráskód; bekerül a végső termékbe; src/main/resources - extra erőforrások (konfig- urációs fájlok, képek, stb.); nem kompiláljuk; bek- erül a végső termékbe; src/test/java - unit tesztek forráskódjai; lefutnak buildeléskor, de nem kerülnek be a végső termékbe; src/test/resources - unit teszteknél használatos erőforrások; nem kerülnek be a végső termékbe; Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Konvencionális projektstruktúra a src/test/java-n belüli osztályok látják a src/main/java-n belüli osztályokat, illetve a src/main/resources-en belüli erőforrásokat fordítva nem érvényes: a src/main/java- n belüli osztályok nem látják a src/test/java-n belüli osztályokat, illetve a src/test/resources-en belüli erőforrásokat Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Konvencionális csomagstruktúra Java forráskódmappák struktúrájára vonatkozó konvenciók (pl. src/main/java-n belül) mélyebb csomaghierarchia alkalmazott ⇒ garantált, hogy nincs átfedés a függőségekben megjelenő osztálynevekkel egyezik a hálózati domainnevek hierarchiájában, de a domainnevek fordított sorrendben alkalmazzák ezt a csomagok sorrendben tartalmazzák a következő információt: 1. a projekt típusa kereskedelmi (commercial–com) tanítási jellegű (educational–edu) non-profit (organizational–org) 2. a tulajdonos intézmény neve (cég vagy egyetem neve) 3. a projekt neve 4. további finomított szétbontás a projekten belül, általában osztályok célja szerint (több részlet később) példák teljes osztálynevekre: edu.bbte.blog.views.BlogPostView com.google.calendar.controllers.MainController Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Maven projekt példa HelloWorld.java: package edu.bbte.mavenex.structure; import java.util.Properties; import java.io.InputStream; import java.io.IOException; public class HelloWorld { public static void main(String[] args) throws IOExcep // nyitunk egy streamet a classpathből // nem feltétlenül egyezik a lokális // fájlrendszer tartalmával InputStream propsStream = HelloWorld.class.getResourceAsStream("/hello.properties") // betöltjük a kulcs-érték párokat Properties props = new Properties(); props.load(propsStream); // kulcs szerinti olvasás System.out.println("Hello " + props.get("name")); } } Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Gradle függőségek függőség: jelen modultól független modul, amelyben definiált osztályokra szükségünk van a folyamat valamely szakaszában (pl. kompiláláskor, futtatáskor, teszteléskor) a Gradle nem nyújt saját függőségi mechanizmust, csak támogatja a Maven vagy Ivy stílusú tárolókból való behúzást függőségek használatához szükséges a java plugin aktiválása Maven modulokra történő függőségek esetén szükséges ismerni a csomag Maven ID-ját, melynek komponensei: groupId - alap csoport csomagnév–konvencionálisan egyezik a korábban említett csomaghierarchia első tagjaival artifactId - a projekt/almodul neve version - a modul verziószáma packaging (opcionális) - a modul végtermékének típusa, alapértelmezetten jar (alternatív értékek: zip, war, ear, stb.) classifier (opcionális) - ha több végterméket készítünk, ez az utolsó string különbséget tesz közöttük Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Függőségek és tárolók a függőségeknek megfelelő csomagokat a Gradle és Maven először egy lokális tárolóban keresi (alapértelmezett a home folderen belül), majd a deklarált külső repokban a repositories szekcióban deklarálunk Maven stílusú tárolókat, ahonnan függőségeket kereshetünk – segédmetódusok: mavenCentral() => https://repo.maven.apache.org/maven2/ google() => https://maven.google.com/ vizuális keresés a Central repóban: https://mvnrepository.com/ vagy https://search.maven.org/ első letöltés után a csomagok cache-elődnek a lokális tárolóban ⇒ első használat lassú lehet más tárolókat is megadhatunk URL alapján: repositories { mavenCentral() maven { url "http://jcenter.bintray.com/" } } Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Függőség hatókörök A projektéletciklus melyik fázisában van szükség egy-egy függőségre? A Gradle java pluginja külön configuration-eket definiál aszerint, hogy mikor van szükségünk függőségekre. Ez a lista kiterjeszthető. Fontosabb értékek: implementation - mind kompiláláskor, mind futáskor szükséges, s mindkét source setben runtimeOnly - kompiláláskor nem szükséges, de futáskor igen compileOnly - kompiláláskor szükséges, de futáskor elvárjuk egy külső konténertől, hogy szolgáltassa a függőséget (pl. webkonténer, EE szerver) testImplementation, testCompileOnly, testRuntimeOnly - csak teszteléskor szükséges (csak a src/test/java-beli osztályok látják) Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Gradle függőségek a dependencies szekcióban deklaráljuk a függőségeket két lehetséges szintaxis: single String, “:” karakter elválasztással: configuration 'group:artifact:version' kulcs-érték párosok használata configuration group: 'group', name: 'name', version: 'version' Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Gradle függőségek példa Példa: gradle-dependencies plugins { id 'java' } repositories { // define Maven Central as main repository mavenCentral() } dependencies { // API for logging necessary for compilation implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' // implementation for SLF4J logging only necessary when running the application runtimeOnly group: 'ch.qos.logback', name:'logback-classic', version:'1.2.3' // JUnit only necessary in testing testImplementation group: 'junit', name: 'junit', version: '4.12' } Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Gradle függőségek példa függőségek vizsgálata a gradle dependencies taszkkal: $ gradle dependencies runtimeClasspath - Runtime classpath of source set 'main'. +--- org.slf4j:slf4j-api:1.7.25 \--- ch.qos.logback:logback-classic:1.2.3 +--- ch.qos.logback:logback-core:1.2.3 \--- org.slf4j:slf4j-api:1.7.25 testRuntimeClasspath - Runtime classpath of source set 'test'. +--- org.slf4j:slf4j-api:1.7.25 +--- ch.qos.logback:logback-classic:1.2.3 | +--- ch.qos.logback:logback-core:1.2.3 | \--- org.slf4j:slf4j-api:1.7.25 \--- junit:junit:4.12 \--- org.hamcrest:hamcrest-core:1.3 Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Példa: függőségek Gradle-ben src/main/java/.../HelloWorld.java: public class HelloWorld { // logger betöltése // ez az osztály csak akkor látszik, ha van az slf4j-api-ra implementation hatáskörű public static final Logger LOG = LoggerFactory.getLogger(HelloWorld.class); public void logHello() throws IOException { InputStream propsStream = HelloWorld.class.getResourceAsStream("/hello.properties Properties props = new Properties(); props.load(propsStream); // logger használata LOG.info("Hello " + props.get("name")); } public static void main(String[] args) throws IOException { new HelloWorld().logHello(); } } Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Példa: függőségek Gradle-ben src/test/java/.../HelloWorldTest.java: @RunWith(JUnit4.class) public class HelloWorldTest { private HelloWorld helloWorld = new HelloWorld(); @Test public void testLogHello() throws IOException { // meghívjuk a tesztelendő kódot helloWorld.logHello(); // tesztelünk valamit vele kapcsolatban // assert(...); } } Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Példa: gradle maven-publish plugin nem szükséges ahhoz, hogy Maven stílusú függőségeket használjunk a projektünkben–az natívan beépített cserébe generál egy Mavennel kompatibilis csomagot a mi projektünkből, hogy más Maven-alapú projektek hivatkozhassanak rá generál egy pom.xml-t a build.gradle-ben található információ alapján, majd a már meglévő JAR-csomagoló taszkot kiegészíti a POM becsomagolásával a publikálandó eredmények Maven stílusú ID-ját a publishing szekcióban adjuk meg taszkok: publishToMavenLocal - feltölti a lokális Maven cache-be az eredményezett erőforrást (alapértelmezetten ~/.m2/repository) publish - feltölti az összes külső Maven tárolóba az erőforrást további információ Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Példa: gradle maven-publish plugin Példa: gradle-mavenplugin build.gradle: plugins { id 'java' id 'maven-publish' // megadja a 'publish*' taskokat, pl. `publishToMavenLocal` } publishing { publications { // a generált pom információja itt módosítható maven(MavenPublication) { groupId = 'edu.bbte.gradleex.mavenplugin' artifactId = 'gradleex-mavenplugin' version = '1.0.0-SNAPSHOT' from components.java } } } Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Minőségbiztosítás gradle-lel A java plugin magával hoz két fontos taszkot: 1. test lefuttatja az összes JUnit tesztet, melyet felfedez a src/test/java forrásmappában olvasható reportot generál a build/reports kimeneti mappában 2. check általános burkoló taszk, mely függőségek révén lefuttat minden minőségbiztosítási taszkot a build függ a checktől a check alapértelmezetten csak a testtől függ, de új QA-s taszkokkal bővíthetjük a projekt életciklusát Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Statikus kódelemzés gradle-lel Statikus kódelemző eszközök beépíthetőek a projektbe pluginok által. Ismertebb Java eszközök: apply plugin: 'checkstyle' apply plugin: 'pmd' apply plugin: 'spotbugs' Mindegyik eszköz raportokat generál a build/reports kimeneti mappába Alapértelmezetten bármely eszközben észlelt hiba a build teljes eséséhez vezet. Ez a viselkedés egy-egy flag beállításával konfigurálható. További konfigurációs opciók: kimeneti formátum (HTML, XML), ruleset-ek be- és kikapcsolása, mappák vagy osztályok átugrása, stb. Az általános check tasktól automatikusan függőség kerül egy-egy eszközt futtató taszkra. Ezen eszközök használhatóak a fejlesztői környezetekben is, közös beállításokkal. Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Multimodul projektek Több alprojektre bonthatjuk egy-egy nagy projektünket. Előnyök: Segít a modularizálásban Segít a függőségek szétválasztásában (egy-egy modul csak attól függ, amit ténylegesen használ) Modulok függhetnek egymástól, de nem körkörösen, így kényszerítjük a tiszta kódolást Egyszerre adhatunk ki karbantartási parancsokat, s ez minden alprojektre lefut A Gradle natívan támogatja a multimodul projekteket. Egy gyökérmodul alprojekteket (modulokat) importál a settings.gradle állományon keresztül // almodulok definiciója relatív elérési útvonal szerint include 'module1', 'module2' Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Multimodul projektek Beállításokat globálisan lehet végezni minden projekten az allprojects closure-rel, vagy csak az alprojekteken a subprojects-szel A Gradle projects taszkjával információt kaphatunk a projekt hierarchiáról Ha a multimodulon belül másik modulra szeretnénk függőséget tenni, hivatkozhatunk a multimodulon belüli nevére: dependencies { implementation project(':other_project_name') } Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Példa: Multimodul projektek gradle-lel Példa: gradle-multimodule settings.gradle: // almodulok definiciója relatív elérési útvonal szerint include 'module1', 'module2' gyökér build.gradle: // ez a kódrészlet minden projektre érvényes allprojects { group = 'edu.bbte.gradleex' version = '1.0.0-SNAPSHOT' } // ez a kódrészlet minden alprojektre érvényes subprojects { apply plugin: 'java' repositories { mavenCentral() } } Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Példa: Multimodul projektek gradle-lel module1/build.gradle: // csak a függőségeket szükséges megadni, a többi beállítás a szülő deszkriptorban ta dependencies { implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' runtimeOnly group: 'ch.qos.logback', name:'logback-classic', version:'1.2.3' } module2/build.gradle: plugins { id 'application' } dependencies { implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' runtimeOnly group: 'ch.qos.logback', name:'logback-classic', version:'1.2.3' // belső projekt implementation project(':module1') } application { mainClass = 'edu.bbte.gradleex.frontend.HelloWorld' } Software Engineering Automatizált build és függőségkezelés: Maven, Gradle 4. rész Naplózás – egy példa külső függőségekre Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Naplózás Miért nem elég a System.out.println? üzenetek szűrése - néha csak fontos üzenetek jelenjenek meg, néha a legcsekélyebbek is (debugging) üzenetek több kimenetre küldése - konzolra való kiírás mellett állományba, vagy hálózati célpontra üzenetek formázása - automatikusan hozzátenni a forrásosztályt/sort, timestampet, stb. előbbieket kombinálni, pl. konzolra csak fontos üzeneteket csak timestamppel ellátva állományba minden üzenetet több információval. Software Engineering Automatizált build és függőségkezelés: Maven, Gradle slf4j A Java nyújt primitív natív naplózást (java.util.logging csomag), de elegánsabb egy dedikált keretrendszer használata Különböző naplózási keretrendszerek fölötti absztrakciós szintet képez. A fejlesztőknek lehetősége van a telepítéskor “bekötni” a kívánt naplózási rendszert. import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HelloWorld { private static final Logger LOG = LoggerFactory.getLogger(HelloWorld.class); public static void main(String[] args) { LOG.info("Hello World"); } } Software Engineering Automatizált build és függőségkezelés: Maven, Gradle slf4j slf4j-api függőség szükséges, ezen kívül valamilyen konkrét naplózási keretrendszernek megfelelő csomag (binding). PoC példa, produkcióra nem alkalmas: slf4j-simple. implementáció hiányában a következő üzenet jelenik meg: SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. Software Engineering Automatizált build és függőségkezelés: Maven, Gradle logback spirituális utódja a jól ismert log4j loggolási keretrendszernek natív slf4j támogatás XML vagy Groovy szintaxisú komplex konfiguráció automatikus beolvasása hot reload: konfigurációs állomány újraolvasása változás esetén, a JVM újraindítása nélkül szükséges függőségek: logback-core-.jar logback-classic-.jar (függ az előbbitől, így elegendő csak ezt behúzni) Software Engineering Automatizált build és függőségkezelés: Maven, Gradle logback.xml konzolra naplózás %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n Software Engineering Automatizált build és függőségkezelés: Maven, Gradle logback.xml állományba naplózás myLogFile.log %d{yyyy-MM-dd HH:mm:ss} - %msg%n Software Engineering Automatizált build és függőségkezelés: Maven, Gradle logback.xml RollingFileAppender... myLogFile.log %d{yyyy-MM-dd HH:mm:ss} - %msg%n myLogFile.log.%d{yyyy-MM-dd}.%i.log 10MB... Software Engineering Automatizált build és függőségkezelés: Maven, Gradle Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Sulyok Csaba [email protected] Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Példaprogramok: https: //gitlab.com/bbte-mmi/softeng/examples/docker-examples Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose 1. rész Virtualizáció Docker Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Kitelepítés = szoftvertermék futtatása a végső tervezett környezetében Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Kézi kitelepítés Komplex alkalmazások kézi kitelepítésének esetén sok hátulütő léphet fel: operációs rendszer inkompatibilitás eszközök pontos verziója külső könyvtárak és keretrendszerek jelenléte környezeti változók admin jogok tűzfal beállítások foglalt portok stb. Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Virtualizáció Modern alkalmazások válasza az oprendszer-szintű virtualizáció Konténerekben futtatjuk az alkalmazásainkat Absztrakciós szint az alkalmazás és függőségei fölött Hasonlóak a virtuális gépekhez, de a hardver mellett az oprendszert is virtualizálja, így kifejezetten pehelysúlyú Részlegesen újrahasznosítják a host gép hardver adottságait (pl. kernel), így sok konténer futtatható párhuzamosan ugyanazon erőforrásokkal Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Virtualizáció Minimális oprendszert (amely épít a host oprendszerre) és a tervezett alkalmazásunkat tartalmazzák, együtt minden függőséggel, runtime-mal, stb. Tartalmazhatnak több eszközt és futó alkalmazást is (mint egy VM), de pehelysúlyuk miatt elterjedt minta, hogy egy konténeren egy szolgáltatást futtassunk (pl. az adatbázis és az alkalmazásszerverünk 2 külön konténer lenne). Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Docker 2013-ban megjelent konténerizációs eszköz Eleinte önálló eszköz volt, amellyel Docker image-eket lehetett buildelni, illetve konténereket lehetett általa futtatni. Az image-ek formátuma átalakult egy nyílt specifikációvá: Open Container Initiative Ezáltal több eszköz van, amely ezt implementálja, így interoperabilis működést kaphatunk, ilyen a Podman, Buildah, kaniko, stb. Fontos fogalmak a Dockernél: image, konténer, volume, hálózat. Mindegyik monitorizálható pl. a lazydocker segítségével Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Docker használati statisztika kiegészített statisztika Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Docker image-ek lefagyasztott állapota egy virtuális gépnek image nevek: SHA hash az egyedi azonosítója alapértelmezetten nincs neve, lehet tag(ek)et rendelni hozzá a tagek hasonlóan működnek a git ágakhoz (pointerek egy image-re) tagek tartalmaznak egy verziót, : karakterrel elválasztva - alapértelmezett érték: latest lokálisan cache-elhetőek önmagában nem futtathatóak, konténereket hozunk létre belőlük Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Docker image-ek és registry-k egy image le-/feltölthető registry(k)re alapértelmezett registry: docker.io, melyen lehet keresni a Docker Hubon a tag elején tartalmazza a registry-t, pl: alpine:3.10 - Docker Hub hivatalos image myuser/alpine:3.10 - a myuser Docker Hub felhasználó hozzájárulása myregistry.com/alpine:3.10 - a myregistry.com hoston futó registryből származó image alternatív registry-k pl. RedHat quay.io, Google GAR sajátot is futtathatunk, pl. Harbor példák: csak oprendszert tartalmazó image-ek: alpine, ubuntu, debian eszközt/kompilátort tartalmazó image-ek: node, python, openjdk, gradle adatbázist vagy más eszközt tartalmazó image-ek: mongo, redis Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Docker image parancsok az image-ekkel kapcsolatos parancsok: docker image [] docker image pull image letöltése a megfelelő registry-ből legacy variáns: docker pull docker image push image feltöltése a megfelelő registry-be legacy variáns: docker push docker image ls lokális gépen lévő image-ek listázása legacy variáns: docker images docker image rm image törlése ha egy image több rámutató taggel létezik lokálisan, csak a tag törlődik legacy variáns: docker rmi Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Docker image parancsok docker image tag tag megadása egy forrás image-nek az eredeti tag megarad legacy variáns: docker tag docker image build image építese Dockerfile segítségével legacy variáns: docker build docker login/logout registryre való be/kijelentkezés konzolból alapértelmezett registry: docker.io Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Docker konténerek Egy Docker image futtatható példánya Infrastruktúrától függetlenül futó rendszer Indítható/leállítható, de nem migráljuk gépről gépre A Docker Engine saját IP címmel látja el, s behelyezi egy általa karbantartott virtuális hálózatba Azonosító: ugyancsak van hex ID-ja van egy neve is - megadható készítéskor, másképp a Docker generál egyet tetszőlegesen Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Docker konténer parancsok docker run készít egy konténert az adott image-ből, s elindítja azt releváns paraméterek: --name -p : - a lokális port1-re érkező hívások továbbítva vannak a konténer port2 portjára -it - interaktív mód - STDIN/STDOUT/STDERR továbbítása -d - detached állapot - futtassuk háttérben a konténert --rm - töröljük a konténert, amikor kilépünk belőle Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Docker konténer parancsok további konténerekkel kapcsolatos parancsok: docker container [] docker container ls vagy docker ps futó konténerek listázása összes konténer listázása a -a flaggel docker container start/stop/restart/kill karbantartó operációk docker container attach kapcsolódás a detached konténer konzoljához lokális gépen lévő image-ek listázása docker container logs kiírja a konténer által megjelenített összes konzolkimenetet limitálható sorok száma vagy kezdő idő szerint docker container create vagy docker create konténer létrehozása/előkészítése, elindítás nélkül Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Docker konténer parancsok docker exec egy már futó konténernek futtatja a kiadott parancsot -it itt is szükséges az interaktív futásért pl. nyithatunk konzolt a konténerben, ha a command értéke bash vagy sh különbözik a container attach-től, mivel az a főfolyamat konzoljához csatlakozik, míg az exec új folyamatot indít a futó konténerben, így több folyamat is indítható párhuzamosan Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Docker volume-ok perzisztens adatok Dockerben tekinthetünk rájuk úgy, mint osztott mappák a host gép és egy vagy több konténer között pl. adatbázis tartalma kerülhet ide docker run -v /localdir:/remotedir megosztja a lokális /localdir mappát a készülő konténerrel - ott a /remotedir útvonal alatt érjük el :ro végződéssel a volume-ot a konténer csak olvashatja (read-only) további parancsok: docker volume create docker volume ls/rm/prune Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Docker hálózatok névvel ellátott virtuális hálózatok, melyben konténerek kommunikálhatnak egymással a hoszt gép mellett segíti a konténerek elszigetelésének kontrollját a host gép kapja a 172.xx.0.1 IP címet, a konténerek kapnak további inkrementális IP-kat releváns parancsok: docker network create docker network ls/rm/prune docker network connect/disconnect Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose 2. rész Saját Docker image-ek Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Saját Docker image-ek saját alkalmazásaink futtatásához szükséges saját Docker image-ek elkészítése manuálisan létrehozhatunk image-eket meglévő konténerekből a docker container commit parancs segítségével - nem automatizálható Dockerfile DSL állomány segítségével szkriptelhetünk image készítést - ez egyszerű parancsok sorozata: # Comment INSTRUCTION arguments Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Saját Docker image-ek mindig meglévő image-ből indulunk ki, amelyiken legalább egy oprendszer megtalálható docker [image] build a contextRoot a mappa, amelynek tartalma alapján építjük az image-et csak ebből a mappából és almappáiból másolhatunk állományokat az új image-re - ezért általában egy projekt gyökérkönyvtárát adjuk meg a contextRoot gyökerében keressük a Dockerfile állományt -t - kezdeti tag megadása pl. docker build -t myImage. = jelenlegi mappában levő Dockerfile alapján készítünk image-et, melynek kezdő tagje myImage lesz Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Dockerfile node.js “Hello World” FROM node:18.16.0-alpine WORKDIR /usr/app COPY index.js./ CMD node index.js FROM (mindig az első parancs) megadja a kiinduló forrás-image-et konkrét verzió megadása tanácsos a reprodukálhatóság kedvéért Docker Hubon kereshetünk megfelelő image-et ajánlott kicsi base image-et keresni lehet egy apró Linux distro, pl. Alpine ki lehet indulni a még kisebb, még üresebb scratch image-ből is Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Dockerfile node.js “Hello World” FROM node:18.16.0-alpine WORKDIR /usr/app COPY index.js./ CMD node index.js WORKDIR - dolgozó konyvtár beállítása COPY másolás a lokális gépről az image-re (vigyázat: 2 külön fájlrendszer).dockerignore állománnyal kihagyhatóak bizonyos állományok (.gitignore formátumával azonos) CMD mi fusson, mikor indítunk egy konténert nem fut buildeléskor le, hanem csak az innen létrejött konténer indításakor ha több ilyen sor van megadva, csak az utolsó lesz érvényes Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Dockerfile node.js “Hello World” Karbantartási konzolparancsok: # Docker image készítése az "exercise2" taggel # A Dockerfile és a kontextus-gyökér a jelenlegi mappa, így az argumentum: "." docker build -t exercise2. # Konténer futtatásának kipróbálása # --name - személyre szabott nevet ad a konténernek # -it - interaktív # --rm - törli a konténert futás végén docker run --name exercise2container -it --rm exercise2 # Új taget adunk az image-nek, hogy betartsa a Docker Hub elnevezési konvencióját: us # Adhattuk volna ezt a taget egyenesen a build parancsnak is docker tag exercise2 csabasulyok/nodejs-helloworld # Bejelentkezés a Docker Hubba docker login -u csabasulyok # Publikus image feltöltése docker push csabasulyok/nodejs-helloworld Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Példa: node.js hálózati alkalmazás FROM node:18.16.0-alpine WORKDIR /usr/app # Csak a függőségeket tartalmazó állományok másolása COPY package*.json./ # Külső függőségek letöltése RUN npm install # Maradék állományok átmásolása COPY.. ENV PORT=3000 EXPOSE 3000 CMD node index.js RUN - ez a parancs lefut az image építésekor (a CMD-vel ellentétben) ENV - környezeti változó beállítása EXPOSE - megjelöl egy portot, hogy az image-ből készült konténerek ott fognak figyelni hálózati hívásokra teljesen szemantikus - nem nyílnak meg ezek a portok automatikusan (továbbra is szükséges a -p) szabad nem megjelölt portok felé is továbbítani Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Cache-elés kihasználása A Dockerfile-ban leírt parancsokat lépésről lépésre hajtja végre a Docker Engine, s minden lépés után elmenti (cache) a köztes állapotot. Minden image-készítési folyamat alatt ellenőrzi, hogy mely lépések hagyhatóak ki a cache újrafelhasználásának segítségével. Megkeresi az első lépést, melyet újra végre kell hajtani. Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Cache-elés kihasználása Az image-en izolációban végrehajtott műveletek (pl. RUN, WORKDIR, CMD, ENV) sosem kérnek újrafuttatást. A host kontextustól függő műveletek opcionálisan kérnek újrafuttatást, pl. a COPY parancs akkor fut le újra, ha változtak a másolandó állományok az ezelőtti build óta. Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Cache-elés kihasználása Elkerülhető plusz build: a korábbi példában előbb felmásoltuk a projektet, majd meghívtuk az npm install-t, noha ennek meghívása csak akkor szükséges, ha változott a package.json vagy package-lock.json tartalma. Jelen variánsban bármely forráskód-állomány megváltozása újradiktálja a buildet. Hogyan kerülhetjük ezt el? Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Cache-elés kihasználása FROM node:18.16.0-alpine WORKDIR /usr/app # Csak a függőségeket tartalmazó állományok másolása COPY package*.json. # Külső függőségek letöltése # CSAK AKKOR FUT LE HA A package*.json VÁLTOZOTT RUN npm install # Maradék állományok átmásolása COPY../ ENV PORT=3000 EXPOSE 3000 CMD node index.js Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Többlépcsős image-készítés Multi-stage builds Több különböző konténer játszhat szerepet egy végső image elkészítésében Intuició: szükségünk van Gradle-re egy Java projekt buildelésére, de futtatáskor már nincs feltétlenül szükség a jelenlétére ⇒ nem szükséges, hogy része legyen a végső image-nek. egy opció kompilálni lokális gépen, de akkor elveszítjük az eszköztártól való függetlenséget használhatunk 2 külön Dockerfile-t, amely hibaveszélyes Megoldás: többlépcsős Dockerfile Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Többlépcsős Dockerfile több FROM parancsot tartalmaz, ezek különböző fázisokra bontják a buildet fázisok között lehet állományokat másolni a COPY parancs --from=... argumentumával az utolsó fázis kimenete lesz a de facto builderedmény a fázisok alapértelmezetten számszerű azonosítót kapnak, de string nevet is adhatunk nekik az as kulcsszó segítségével számszerű ID karakterlánc ID # első fázis # első fázis FROM image1 FROM image1 AS buildImage...... # második fázis # második fázis FROM image2 FROM image2 COPY --from=0 src dest/ COPY --from=buildImage src dest/ Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Többlépcsős példa Mavennel Naív megközelítés (működőképes, de redundáns): FROM gradle:8.10.0-jdk21-jammy COPY../ RUN gradle build CMD gradle bootRun Többlépcsős megközelítés: # 1. fázis: kompilálás s distributable előkészítése FROM gradle:8.10.0-jdk21-jammy AS build-env WORKDIR /src/ COPY../ RUN gradle --no-daemon bootJar # 2. fázis: distributable átmásolása a Gradle konténerből egy egyszerű JRE-sre FROM eclipse-temurin:21-jre-alpine WORKDIR /app COPY --from=build-env /src/build/libs/spring-gradle-multistage.jar./ CMD java -jar spring-gradle-multistage.jar Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose 3. rész Konténerek orkesztrációja docker-compose, Kubernetes, Docker Swarm Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Need more containers! A mikroszervíz-orientált architektúrák egyre elterjedtebbek – a nagyobb projekteket kisebb külön folyamatokra bontjuk, amelyek jól meghatározott protokollok és kommunikációs modellek segítségével kommunikálnak egymással (REST, üzenetsorok). Ha ezeket mind konténerekben futtatjuk s közös hálózatba helyezzük, össze tudjuk őket varrni komplexebb rendszerek felépítéséhez. Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Need more containers! Ez segíti a skálázhatóságot, osztott s redundáns rendszereket hozhatunk létre, ill. használhatunk különböző programozási nyelveket komponenseinknek. DE a megfelelő docker run parancsok felépítése nehezen karbantarthatóvá válik Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Docker compose több Docker konténer beállítására és karbantartására alkalmas eszköz UNIX rendszereken nem telepítődik együtt a Dockerrel, külön letöltés szükséges YAML szintaxisú leíró állománnyal konfiguráljuk Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose docker-compose.yml version: '3.8' # a compose API verziója services: # szolgáltatások listája # készül egy-egy konténer mindegyiknek # közös hálózatban lesznek, s a szervíz név lesz a host amivel megtalálják egymást service1: # első szervíz neve image: "imageName1:tag1" # használandó image container_name: "cName" # létrehozott konténer neve (opcionális) environment: # környezeti változók - ENV_VAR_NAME=env_var_value restart: always # automatikus újraindítási elv service2: # második szervíz neve build:. # ha mi adjuk a megfelelő Dockerfile-t # i.e. a mi karbantartásunk alatt van ez a szervíz image: "imageName2:tag2" # buildkor használt image-név ports: - localPort:containerPort Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Docker compose parancsok docker compose [ ] alapértelmezetten minden service-re érvényes a parancs megadható egy service-név, mely esetben csak arra lesz végrehajtva docker compose up elkészíti és indítja az összes konténert alapból interaktív (nem adja vissza a konzolt), -d paraméterrel fölülírható alapértelmezetten készít egy új dedikált hálózatot, melyhez mindegyik itt definiált konténert hozzáadja docker compose down lezárja a rendszert docker compose logs logok megjelenítése docker compose pull/push/build service image-ekre vonatkozó parancsok Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Compose példa node.js + mongo-val index.js: //... // connect to MongoDB const mongoURL = process.env.MONGO_URL || 'mongodb://127.0.0.1'; const client = new MongoClient(mongoURL); await client.connect(); const db = client.db('example'); const collection = db.collection('documents'); console.log(`Connected to MongoDB on ${mongoURL}`); // GET requests return the content of the DB app.get('/documents', async (req, res) => { const result = await collection.find({}).toArray(); res.send(result); }); // POST requests insert a new entry into the DB app.post('/documents', express.json(), async (req, res) => { await collection.insertOne(req.body); res.send('Inserted successfully'); }); //... Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose docker-compose.yaml példa node.js + mongo-val version: '3.8' services: mongo: # one of the images in the composition # we do not build this image image: mongo:6.0.9 container_name: compose-example-mongo # automatically restart container restart: unless-stopped server: # second image in the composition: our server # we build this image build:./server # the context root of the associated Docker build image: compose-example-node-server # use this name/tag when building image container_name: compose-example-node-server # when compose starts this container, nam ports: - "8080:8080" # expose port from this image, we need it environment: # set URL of database used in web application, so hostname coincides MONGO_URL: mongodb://mongo Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Compose példa node.js + mongo-val: parancsok # elindítja a teljes orchestrationt (minden service-t) a háttérben docker compose up -d # ellenőrízzük hogy valóban futnak docker container ls... compose-example-node-server Up 4 seconds 0.0.0.0:8080->8080/tcp compose-exampl... mongo:6.0.9 Up 4 seconds 27017/tcp compose-exampl # GET HTTP hívás próba curl -i http://localhost:8080/documents response: [] # POST HTTP hívás próba curl -i -X POST http://localhost:8080/documents -H 'Content-Type: application/json' \ --data-raw '{"name": "My Name", "age": 42}' # GET újra curl -i http://localhost:8080/documents response: [{"_id":"64df70967ed470b443f14039","name":"My Name","age":42}] # szervízek lezárása docker compose down Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Compose-on túl A compose csak egy szerveren képes konténereket futtatni, míg egy produkciós kitelepítési stratégiához szükséges lenne egy sok szerverből álló cluster Egy ilyen rendszerben több csomópont (node) közösen van kezelve, s konténerek skálázódnak fekete doboz formájában rajtuk A legelterjedtebb ilyen orkesztrációs szolgáltatások: Kubernetes - CNCF certified Docker Swarm - külön telepítés nem szükséges, ugyanezen Docker mechanizmusokat használja Sok cloud szolgáltató is nyújt saját módszereket erőforrásaik menedzselésére, pl. AWS CloudFormation Ha pedig kombinálni szeretnénk a konténerizált és cloud-alapú erőforrások automatikus kezelését közös konfigurációval, alkalmazhatunk Infrastructure as Code eszközt. Elterjedtebbek: Terraform Pulumi Software Engineering Virtualizáció, kitelepítés: Docker, docker-compose Software Engineering Folyamatos integráció: GitLab CI Sulyok Csaba [email protected] Software Engineering Folyamatos integráció: GitLab CI Példaprogramok: https://gitlab.com/bbte-mmi/softeng/examples/ci-examples hello-world gradle-lint-and-unittest node-docker-build Software Engineering Folyamatos integráció: GitLab CI 1. rész (Folyamatos) Integráció Software Engineering Folyamatos integráció: GitLab CI Integráció Egy-egy funkcionalitás integrálása egy már meglévő jól meghatározott rendszerbe Hogyan garantáljuk hogy korábban jól működő funkcionalitások továbbra is működnek? Növekvő alkalmazásokban egyre nő a manuális integrációra fordított idő - automatizálás szükséges, ahol lehetséges Software Engineering Folyamatos integráció: GitLab CI Folyamatos integráció (continuous integration) Integráció folyamatának automatizálása Minőségbiztosítási lépések elvégzése Verziókövető eszközzel való összekötés minden pushkor elvégezhetünk verifikációs lépéseket Automatikusan elvégezhető jobok: kompilálhatóság ellenőrzése statikus kódanalízis unit tesztek coverage mérése Ismert CI rendszerek: CircleCI GitLab CI Jenkins Mivel ezek a lépések izolált környezetben kell lefussanak, általában konténerizált környezetben futnak Software Engineering Folyamatos integráció: GitLab CI Pipeline egy-egy job futhat külön izolált környezetben (i.e. külön konténerben), de függőségek lehetnek közöttük (pl. tesztelni nem is érdemes, ha a kompilálás nem lehetséges) pipeline - jobok jól meghatározott sorozata/gráfja egymástól függő jobok sorrendben futnak le csak akkor sikeresek, ha mindegyik job sikeres egy hibás job rövidre zárja a pipeline-t Software Engineering Folyamatos integráció: GitLab CI Kitelepítési jobok kitelepíti/előkészíti kitelepítésre a terméket staging vagy production szerverre folyamatos integráció + kitelepítési job = folyamatos kitelepítés (continuous deployment) utolsó fázisban fut le - amikor minden QA eszköz helyesnek nyílvánítja a terméket dedikált ágakon futtatható Software Engineering Folyamatos integráció: GitLab CI 2. rész GitLab CI Software Engineering Folyamatos integráció: GitLab CI GitLab CI a GitLab saját CI rendszert biztosít az integrációs feladatok GitLab runner példányokon futnak le - a runnerek futhatnak akárhol a GitLab pushkor kiadja a futtatandó jobokat runnereknek, amelyek képesek lefuttatni a CI-t a tároló gyökerében található.gitlab-ci.yml állománnyal konfiguráljuk Software Engineering Folyamatos integráció: GitLab CI GitLab CI: Pipeline, stage és job minden pushkor indul egy pipeline egy pipeline áll egy vagy több stádiumból (stage) a stage-ek szigorúran a megadott sorrendben hajtódnak végre egy stage áll egy vagy több jobból egy job egy teendő lefuttatására alkalmas (buildelés, tesztelés, lintelés, stb.) egy stage-en belüli összes job futtatható párhuzamosan, nincs köztük függőség, de mindnek sikeresen le kell futnia a következő stage-be való belépésért egyes jobok manuálisra állíthatóak - kézzel indíthatóvá válnak a GitLab felületről Software Engineering forrás: https://docs.gitlab.com Folyamatos integráció: GitLab CI.gitlab-ci.yml példa stages: # 2 CI stage definiciója - stage1 - stage2 job1: # egy job neve stage: stage1 # hozzákötjük ezt a jobot egy stage-hez image: alpine:3.10 # Docker image, melyből létrehozott konténeren lefut a job script: # a job szkriptje - a fő parancsok - echo "I am performing the job2 CI job" # ha ezen parancsok egyike sikertelenül végződik, a job (s innen a stage és pipeline) # elbuknak, és későbbi stage-ek kimaradnak teljesen job2: stage: stage1 image: alpine:3.10 script: - echo "I am performing the job2 CI job" job3: stage: stage2 # csak ha mind a `job1`, mind a `job2` sikeresen lefut image: alpine:3.10 script: - echo "I am performing the job3 CI job" Software Engineering Folyamatos integráció: GitLab CI.gitlab-ci.yml környezeti változók a joboknak be vannak állítva alapértelmezett környezeti változók a projektről és a futásról: https://docs.gitlab.com/ee/ci/ variables/predefined_variables.html további környezeti változó adható meg a GitLab projekt vagy group beállításai között my_job1: image: alpine:3.10 script: - echo "I am performing the CI job" - echo "The repository URL is ${CI_REPOSITORY_URL}" - echo "The git branch for this pipeline is ${CI_COMMIT_BRANCH}" - echo "Custom env var is ${CUSTOM_VAR}" # must be set in settings Software Engineering Folyamatos integráció: GitLab CI.gitlab-ci.yml node.js példa stages: - lint - docker lint: stage: lint image: "node:18.16.0-alpine" script: - npm install - npx eslint. build-and-push-image: stage: docker image: "buildah/buildah" script: - buildah build -t ${CI_REGISTRY_IMAGE}. - buildah login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY} - buildah push ${CI_REGISTRY_IMAGE} Software Engineering Folyamatos integráció: GitLab CI Jobok szűrése manuális jobok (nem futnak le a pipeline részeként): job1: manual: true bizonyos branch/tag szerinti bevonás: job2: only: - branchName bizonyos branch/tag szerinti kizárás: job3: except: - branchName reguláris kifejezés szerinti bevonás: job4: only: - /^issue-.*$/ # branch names beginning with "issue-" kulcsszavak szerinti bevonás: job5: only: - tag # run only for pushed tags, not branches Software Engineering Folyamatos integráció: GitLab CI Erőforrások raktározása/publikálása cache állomány- és mappaneveket jelöl meg, melyeket elraktároz (cache) ugyanazon job következő futásáig más jobok nem érhetik el pl.: a külső függőségeket nem szükséges minden pipeline futásakor újratölteni jelentősen felgyorsíthatják a jobok lefutását lint-job: stage: "lint" image: "node:18.16.0-alpine" script: - npm install # still call npm install, dependencies may change - npx eslint. cache: paths: - node_modules/ # cache node_modules until next run Software Engineering Folyamatos integráció: GitLab CI Erőforrások raktározása/publikálása artifacts állomány- és mappaneveket jelöl meg, melyek elmentődnek és elérhetővé válnak a job lefutása után pl. kompilálási eredmények, reportok, stb. későbbi stage-ek elérhetik az állományokat pl. Egy job buildel egy erőforrást, míg a következő használja azt egy Docker image készítésére. A 2 job különböző image-eket használ - átvitel szükséges beállítható egy lejárati dátum, mely után az állományok törlődnek release-job: script: - mvn package # create Maven package artifacts: paths: - target/*.jar # create artifact for package for later download expire_in: 1 week # only save artifacts for 1 week Software Engineering Folyamatos integráció: GitLab CI SOFTWARE ENGINEERING DESIGN Gyula Uszkai - 2024 / 2025 START 1 SOFTWARE REQUIREMENTS A condition or capability that must be met or possessed by a system or