Aki már hallott a Dockerről beszélni, előadni, az tudja, hogy én elsősorban a gyors fejlesztői környezet felállítása miatt tettem le emellett a technológia mellett a voksomat. Mint minden rendszernek és módszertannak, a konténerizált fejlesztőkörnyezetnek is vannak hátrányai, nem csak előnyei. Ebben a rövid írásban azt szeretném bemutatni, hogy mire kell készülnünk, ha belevágunk.
Operációs rendszer függetlenség
Nem, nem független az operációs rendszertől a Docker. Nem fog ugyanaz a fejlesztői környezet működni Linux, Windows és OSX rendszereken. A konténerek ugyanúgy fognak futni és a konténeren belüli működéssel nem is lesz különbség. A különbség a konténeren kívüli környezettel lesz. Nem Linuxon ugyanis mindig egy virtuális gépen fog futni a konténer. Így a fájlmegosztások, port csatolások plusz lépéseket, megoldásokat kívánnak. Ha be akarunk csatolni egy host gépen található könyvtárat a konténeren belülre, akkor az nem egy sima szimbolikus link lesz, mint Linux rendszeren, hanem először meg kell osztani fájlokat a host és a docker host között, és csak utána lehet becsatolni azokat. Ráadásul a fájl tulajdonosai is mások lesznek majd mint Linuxon. A portokkal ugyanez a helyzet és a host-host közötti csatolást is meg kell oldani. Ma már fényévekre vagyunk a kezdeti Boot2Docker-es időktől. A Docker for Windows/Mac a legtöbb korai problémára gyógyírt vagy tapaszt kínál.
Fájlrendszer sebessége
A fájlrendszer megoldás inkább egy tapaszhoz hasonlít. Ráadásul elég kellemetlen tud lenni, hogy amikor kipróbáljuk nem nagyon fog vészesen lassúnak tűnni a rendszerünk, de az első éles bevetésnél megtapasztaljuk milyen lassú is tud lenni a fájlmegosztás egy virtuális gép és a host gép között. Aki valamilyen virtualizációt használt már, pl. Vagrant, az tudja miről beszélek. Ha nem tudod annak ellenére, hogy használsz ilyen technológiákat, akkor valószínűleg a Dockerrel se lesz problémád. Azonban, ha olyan rendszereket fejlesztesz, ahol sok-sok kis fájlból áll a rendszered, bizony meg kell majd küzdeni a megfelelő sebességért.
Nézzünk egy példát: ha PHP alkalmazásod vendor könyvtárában, vagy a Node.js alkalmazásod node_modules könyvtárában számos külső függőség található, akkor jobban jársz, ha ezeket a könyvtárakat nem bind-olod, hanem a konténeren belül hozod létre, és szükség esetén (pl. kódkiegészítés vagy nyomkövetés) másolod kívülre is. Ez persze csak akkor megoldás, hogy ha a saját kód nem áll sok fájlból. Olyankor megoldás lehet a módosítások utáni image építés, ami a BuildKit megjelenésével vált egyáltalán alternatívává, mivel az félelmetesen felgyorsítja az image építését. Pár percről pár másodpercre csökkenti le a build idejét.
Mint fent érzékelhető ebben a tekintetben nincs egyetlen jó megoldás sem, mindig az adott projekt adottságaihoz kell majd szabnod a rendszered, ami sok-sok óra kísérletezéssel, próbálgatással fog járni.
Új függőség hozzáadása
Talán a legnagyobb különbséget az új függőségek hozzáadásának módszere fogja jelenteni. Míg eddig elég volt az, hogy egy fejlesztő gépén egyszer feltelepíts egy-egy függőséget, az most már nem lesz elég. Az a hagyományos megoldás, amikor az ops, vagy egy senior kolléga addig masszírozza az új kolléga gépét, amíg azon nem fog elfogadhatóan működni az adott kiegészítő. A konténerizált megoldás esetében kénytelen leszel leprogramozni azt, amit az ops vagy senior kolléga csinált.
Ha például szeretnénk használni a Facebook Webdriver csomagját, akkor az csak úgy nem fogjuk tudni használni, mert ahhoz kell a PHP zip támogatása. Amit pedig azért nem fogunk tudni feltenni, mert ahhoz meg az operációs rendszerben ott kell lennie a zlib csomagnak. Ami nem hangzik bonyolultnak, de mindjárt megértjük, hogy miért tenger idő, amikor ki is próbáljuk úgy, hogy a fentiekről nem tudunk. Mert, ha ezeket tudjuk, akkor csak annyi, hogy a megfelelő lépéseket berakjuk a PHP konténer Dockerfile-jába, vagy már eleve benne vannak, mert az évek alatt mindig szükségünk volt rá, ezért beraktuk az alap PHP konénerünkbe, amiből származtatjuk a projektjeinkben lévőket.
Na de nézzük mi van akkor, ha nem vagyunk ilyen szerencsések? Akkor első körben kiadjuk a composer require --dev facebook/webdriver
parancsot a konténeren belül, ami egy hibát fog kiírni, hogy hiányzik a PHP Zip kiterjesztése. Ekkor beletesszük a PHP konténer Dockerfile-jába a RUN docker-php-ext-install zip
sort, majd újrabuildejük az imaget. Itt lehet az is lassítani fog minket, hogy a hivatalos PHP image-t használjuk, és életünkben nem írtunk még Dockerfile-t. Ezután megkapjuk a zlib hiányára panaszkodó hibaüzenetet, amit legegyszerűbb, ha bedobunk egy internetes keresésbe, mert nem biztos, hogy tudjuk, hogy a libzip-dev csomagot kell feltennünk a hiány feloldásához. Itt az újrabuildelés nem egyszeri lesz, hisz nem biztos, hogy elsőre megtaláljuk a megfelelő megoldást.
Ha nem jön elő újabb függőség, akkor is érdemes rászánni egy kis időt, hogy csiszoljunk a Dockerfile-on. Egyrészt nem biztos, hogy minden függőségre szükségünk lesz, amit a neten ajánlatként találtunk, de ezt csak kísérletezgetéssel tudjuk eldönteni úgy, hogy az egyes fantasztikus javaslatokat kivesszük, töröljük, majd újrabuildelünk. A másik ami tipikus szokott lenni, legalábbis én mindig így csinálom, hogy a Dockerfile-t ilyenkor a build sebességére optimalizálom. Ha van valami új, akkor azt a fájl végére rakom (ha lehet) és ebben a körben nem figyelek arra, hogy optimális méretű image készüljön, itt inkább az lesz a célom, hogy gyorsan újra és újra tudjam építeni azt. A Docker ugyanis csak azokat a RUN lépéseket futtatja újra, amik változtak.
Ilyenkor például nem kell mindig végig várnom, hogy lefusson a composer install, elég csak a zlib és a zip kiterjesztés telepítésére várnom, az image-t használva tudom tesztelni, hogy a composer require parancs lefut-e. Azonban ahogy lefut, és beilleszti a composer.json-ba a függőséget, már nem lehet a require mögött a függőség telepítése. Azt értelemszerűen a legelejére kell raknom.
Ne felejtsünk el a végén egy teljesen friss és szűz git clone utáni tesztet, hogy lássuk az új kolléga tényleg olyan könnyedén be fog-e tudni kapcsolódni a fejlesztésbe mint ahogyan azt reméltük. Ha azt gondolod, hogy “á ilyen nálunk nem gyakran fordul elő”, akkor várd meg, hogy lefusson a következő build a CI szerveren. Ha nincs CI szervered sem, akkor se izgulj, legkésőbb az élesbeálláskor tesztelheted majd a helyes működést. Vagy tudod mit? Ne is teszteld, majd az ügyfél szól, ha nem megy valami!
Természetesen ez utóbbi egy picit drágább lesz mintha egy szűz telepítés teszttel elkaptad volna az elején a hibákat, de a döntés a tied. Ne higgy nekem, hanem teszteld ki.
Ugyanaz a konténer fut élesen mint amin fejlesztünk
A lehető legnagyobb lábon lövés, ha ezt elhiszed és azt a konténert teszed ki élesbe, mint amin a fejlesztés is zajlik. Nem véletlen, hogy a legtöbb csomagkezelőben van –dev, vagy valami hasonló kapcsoló, ami lehetővé teszi az éles és a teszt függőségek szétválasztását. Lehetővé kell tenni, hogy a staging az élessel minél jobban megegyező rendszer legyen és azon fusson az éles rendszerhez minél közelebb álló alkalmazás. Azonban jó, ha már itt is megoldjuk, hogy bizonyos fejlesztést segítő eszközök ide is kikerülhessenek. Gondolok itt a különböző gazdag információ gyűjtő kiegészítőkre, mint pl. az Xdebug.
Hogyan tudom ezeket a problémákat elkerülni?
Mondhatnám, hogy gyere el a Konténerizált Fejlesztőkörnyezet Kialakítása workshoppomra, de becsapni nem akarlak. Helyetted nem fogom tudni beletenni azt az energiát és munkát, ami ehhez az új eszköz és a hozzá tartozó módszertan megtanulásához szükséges. Támogatni, segíteni, irányt mutatni viszont fogok tudni.