HU

Miért nem elég a sikeres kiadáshoz, ha a fejlesztői gépen működik a kód

Ha a kód a fejlesztői gépen működött, élesben mégis összeomlik, a hiba többnyire nem a kódban van, hanem a környezetek közötti különbségben.

aerial view of shipping container yard
Fotó: CHUTTERSNAP (Unsplash)

Egy szoftver életciklusában a legveszélyesebb és leginkább félrevezető mondat úgy hangzik, hogy a programozó gépén még tökéletesen működött minden. Ez a mondat ugyanis tényszerűen igaz, és pontosan megmutatja az elakadás okát. A kód valóban hibátlan volt abban a szeparált környezetben, ahol hetekig dolgoztak rajta. A hiba abból a tényből fakad, hogy az a hardver és operációs rendszer, ahol a fejlesztő a munkáját végzi, fizikai és szoftveres paramétereiben sem egyezik meg azzal a szerverrel, ahol a vásárlók végül használják a rendszert.

Mi történik a háttérben egy feszült kiadási napon

Amikor az elkészült új verzió kikerül az éles szerverre, a folyamat ideális esetben nem lenne több, mint egy előre beállított, automatizált lépéssorozat. A valóság ehhez képest a legtöbb kkv esetében egy azonnali, tervezetlen tűzoltás. A kért funkció a tesztelésen még gond nélkül átment, a belső csapat kipróbálta, a megrendelő jóváhagyta, a kód mégis azonnal hibára fut, amint éles forgalmat kap. A cégvezető ilyenkor teljesen jogosan a fejlesztőt vonja felelősségre a leállásért, a fejlesztő pedig a szerver beállításait vagy a rendszergazdát hibáztatja. A helyzet abszurditása az, hogy mindkét félnek igaza van a saját technikai nézőpontjából.

A probléma gyökere a rejtett és dokumentálatlanul maradt eltérő konfiguráció. Az éles szerveren teljesen életszerű, hogy egy régebbi, de stabil adatbázis-verzió fut, más a memóriakiosztás mértéke, eltérő a PHP verziója, vagy jóval szigorúbbak a fájlokra vonatkozó írási jogosultságok. A fejlesztő ezeket a szigorú paramétereket a saját munkakörnyezetében egyszerűen nem látta. A hiba tehát az esetek döntő többségében nem logikai, hanem pusztán fizikai jellegű. A programkód egy olyan ismeretlen környezetbe érkezett, amire senki sem készítette fel.

Ilyenkor kezdődik a kapkodás és az ideiglenes megoldások keresése. A rendszergazda próbálja menet közben, éles forgalom mellett átállítani a szervert, miközben a fejlesztő a kódot próbálja a szerver aktuális állapotához igazítani. Az eredmény szinte mindig egy olyan gyors kompromisszum, amit a rohanás miatt senki sem tesztelt le rendesen.

A hiba elhárítása azért vesz igénybe hosszú órákat, mert a leállás okát egy vakfoltban, a két rendszer közötti különbségekben kell keresni. A hagyományos tesztkörnyezet nem azonos az éles szerverrel, csupán egy ahhoz nagyon hasonló felület. Amikor a rendszer összeomlik, a cég számára a kiesett bevétel, az ügyfélszolgálatra zúduló panaszáradat és a belső frusztráció azonnal jelentkezik. Ezt a feszültséget nem lehet azzal megoldani, hogy a csapat legközelebb jobban odafigyel a részletekre. A megoldás kizárólag az lehet, ha technológiai szinten zárjuk ki a különbséget a fejlesztői és az éles gép között.

A lokális gép és az éles szerver közötti vakfolt

A szoftverfejlesztés hagyományos, konténerek nélküli modelljében három teljesen különálló világ létezik egymás mellett. Az első a programozó saját számítógépe, a második a cég tesztszervere, a harmadik pedig a tényleges éles környezet, ami a forgalmat kiszolgálja. Ez a három gép operációs rendszerében, szoftververzióiban és memóriakiosztásában szinte sosem azonos, hacsak nincsenek az alapoktól kezdve szigorúan szinkronizálva. A lokális gépen a leggyakrabban egy friss, asztali operációs rendszer fut, hatalmas rendelkezésre álló memóriával és olyan hibakereső eszközökkel, amik azonnali visszajelzést adnak. A tesztszerver ezzel szemben jellemzően egy olcsóbb, megosztott erőforrású virtuális gép. Az éles szerver pedig egy szigorúan védett, kifejezetten a teljesítményre optimalizált környezet, ahonnan minden felesleges eszközt kitiltottak.

A nyers kódnak mind a három helyen tökéletesen és ugyanazt az eredményt produkálva kellene futnia. Ez a fizikai valóságban szinte képtelenség, amíg a környezeteket a rendszergazdák kézzel, külön-külön állítják be. Vegyünk egy tipikus példát. A fejlesztő feltelepít egy új, hasznos könyvtárat a saját gépére a gyorsabb képfeldolgozás érdekében, ami automatikusan bekerül a szoftver függőségei közé. A kód átmegy a tesztszerverre, de ott ez a specifikus könyvtár hiányzik az operációs rendszerből. Ha a tesztelést végző kolléga észleli az elakadást, egy parancssoros beavatkozással manuálisan pótolja a hiányt, és a munka megy tovább. Az élesítéskor ugyanez a hibás folyamat ismétlődik meg, de ott már a vásárlók jelenlétében derül ki, hogy a képfeltöltés nem működik.

Az operációs rendszerek apró frissítései, a telepített szoftververziók minimális eltérései és a szerverek memóriakiosztásának különbségei láthatatlan repedéseket hoznak létre az architektúrában. Ezek a repedések pontosan a kiadás pillanatában, terhelés alatt válnak kritikus töréssé. A Docker és a konténerizálás technológiája pontosan ezt a fizikai különbséget szünteti meg az alapoknál.

Ebben a rendszerben a kód nem önmagában, egy fájlcsomagként utazik az egyik gépről a másikra. A szoftver megkapja a saját, méretre vágott operációs rendszerét, a hajszálpontos szoftververziókat és az összes szükséges szerverbeállítást egyetlen zárt, hordozható dobozban. Ez a doboz a konténer. Ennek a technológiának a legnagyobb előnye a kiszámíthatóság. Szigorúan ugyanaz a zárt doboz indul el a fejlesztő laptopján a reggeli kávé mellett, a tesztszerveren az ügyfélbemutató alatt, és az éles környezetben is a legnagyobb forgalom közepén. A vakfolt megszűnik, mert a futtatókörnyezet végre együtt utazik a kóddal.

A szoftverkörnyezetek szinkronizálásának valódi ára

A manuális beállítások kézi fenntartása és a folyamatos hibakeresés rengeteg láthatatlan munkaórát emészt fel egy cég életében. Ha egy vállalkozás három különálló partnertől veszi meg a fejlesztést, a webes tárhelyet és a szerverek üzemeltetését, az egyeztetés önmagában komoly adminisztrációs teher. A fejlesztőcégek elküldik a kész kódot, az üzemeltető megpróbálja futtatni az általa karbantartott gépen, majd hiba esetén visszaküldi a csomagot kivizsgálásra. Ez az oda-vissza mutogatás nem termel üzleti értéket, csupán a hiányzó, egységes infrastruktúrát próbálja drága emberi munkával pótolni. A szakaszok határán menetrendszerűen vész el az idő és a felelősség.

Ennek az állandó szinkronizációs kényszernek az egyik leglátványosabb eleme a személyi változások kezelése. Amikor egy projekt növekszik, és egy új programozó lép be a csapatba, az első feladata a saját számítógépének felkészítése a munkára. Letölti a forráskódot, megpróbálja elindítani, majd gyakran napokig küzd a különböző hibaüzenetekkel. Az adatbázis nem kapcsolódik, a környezeti változók hiányoznak, a jelszavak nem egyeznek meg a dokumentációval, vagy egy régebbi szoftververzió akadályozza az elindulást. Az új kolléga az első hetében szinte egyáltalán nem fejleszt, hanem a régi kollégákat kérdezi a rejtett beállításokról. Ez a kiesés kifizetett fejlesztői munkaidő, ami alatt a projekt egyetlen sort sem halad előre, a költségek viszont ketyegnek.

A dedikált szinkronizálás hiánya a felelősség teljes elmosódását is magával hozza. Ha a cég alaprendszeréhez ma senki nem mer hozzányúlni egy pénteki napon, mert félnek a leállástól, akkor pontosan tudod, milyen felárat fizet a vállalkozás a bizonytalanságért. A kiadás félelmetes, ritka eseménnyé válik, amit igyekeznek minél ritkábban meglépni. Minél ritkábban frissítik azonban a rendszert, annál több új kód gyűlik fel egyetlen hatalmas csomagban. Minél nagyobb a csomag, annál nagyobb az esélye annak, hogy valami váratlan dolog eltörik az élesítéskor.

Ez egy klasszikus ördögi kör, amit csak a technológiai alapok teljes rendbetételével lehet megszakítani. Az üzemeltetés átvétele a mi esetünkben pontosan azt jelenti, hogy a környezetet véglegesen a kódhoz igazítjuk. Megszüntetjük a kézi szinkronizálás szükségességét, és egy olyan keretet adunk a szoftvernek, amiben a fejlesztés és a kiadás folyamata végre elválik az emberi tényezőktől. Ezt az oldalt, amit éppen olvasol, szintén ezzel a technológiával szállítjuk és tartjuk karban, és az intrapp.io keretrendszer is ugyanezt a stabilitást hozza magával.

A kódként kezelt infrastruktúra azonnali hatásai

Az infrastruktúra kódként történő kezelése nem egy elvont informatikai fogalom, hanem egy nagyon is kézzelfogható, mérhető technológiai váltás. A gyakorlatban ez azt jelenti, hogy a szerver beállításait többé nem egy rendszergazda kattintgatja végig egy adminisztrációs felületen, emlékezetből vagy egy elavult dokumentáció alapján. A szerver pontos felépítése, a telepítendő csomagok listája, a memórialimitek, az adatbázis verziója és a teljes futtatókörnyezet egy egyszerű, olvasható szöveges fájlban szerepel. Ez a konfigurációs fájl ugyanúgy része a projekt verziókezelt repójának, mint maga a szoftver tényleges kódja.

A Docker és a CI/CD bevezetése azonnali, radikális módon alakítja át a belső munkafolyamatokat. Az egyik legkonkrétabb hatás a korábban említett beléptetésnél mutatkozik meg. Az infrastruktúra kódként történő kezelésével egy új fejlesztő projektbe vonása és gépének felállítása már nem napokig tartó kézi beállítás. Nem kell a lokális környezetet kézzel, lépésről lépésre konfigurálni, és nem kell a hiányzó jelszavakat keresgélni. A fejlesztő kiad egyetlen parancsot a terminálban, a Docker letölti a szükséges elemeket, és a gépén elindul pontosan ugyanaz a környezet, ami az éles szerveren is futni fog. Nincs többé olyan kifogás, hogy az adott funkció egy másik gépen még működött.

A másik azonnali és kritikus hatás a biztonság és a helyreállíthatóság drasztikus növekedése. Mivel a teljes környezet kódként van leírva, bármikor, bármilyen hardveren újraépíthető. Ha a fizikai szerver megsérül, vagy egy külső frissítés végzetes hibát okoz a rendszerben, a szoftver és a környezete visszaállítható a legutóbbi, bizonyítottan működő állapotba. Az adatokat továbbra is a mentés védi, a környezetet viszont nem kell kézzel, emlékezetből újratelepíteni.

A rendszer folyamatosan felügyeli önmagát. A healthcheck funkció megállás nélkül figyeli, hogy a konténerek megfelelően, a várt paraméterekkel futnak-e. Ha egy folyamat megakad vagy egy konténer leáll, a rendszer automatikusan újraindítja, sokszor még azelőtt, hogy a felhasználók észrevennék.

Az infrastruktúra kódként való kezelése a saját fejlesztéseinknél is megkerülhetetlen alapkövetelmény. A WordPress alapú WPXpert márka és a Docker és infrastruktúra szolgáltatásunk is erre a stabil architektúrára épül. A környezet megbízhatósága nem azon múlik, hogy ki ügyel éppen a szerverre a hétvégén, hanem a pontosan lefektetett, automatizált, verziókövetett szabályokon. Ezt a módszert alkalmazzuk a komplex egyedi fejlesztés során is. Ha a te szoftvered specifikus megoldást igényel, ezt a stabilitást akkor is megkapod, teljesen függetlenül attól, hogy a projekt a mi intrapp.io alapjainkra épül, vagy egy teljesen önálló Laravel alkalmazásról van szó. Meglévő, örökölt rendszert is átveszünk, akkor is, ha azt eredetileg nem mi fejlesztettük.

Hogyan válik a rettegett élesítés rutinfeladattá

Az élesítés nem maradhat az a stresszes pont a naptárban, amitől az egész cégvezetés és a fejlesztőcsapat retteg. A szoftvered kizárólag akkor hoz üzleti hasznot, ha az új funkciók folyamatosan és biztonságosan kikerülnek a felhasználók elé. A kiadás folyamatának észrevétlennek, unalmasnak és kiszámíthatónak kell lennie. Ehhez arra van szükség, hogy a szigorú automatizáció vegye át az esendő emberi munka helyét a kód mozgatásában és a szerverek beállításában. A deploy nem egy kivételes esemény, hanem egy szigorúan szabályozott rutin.

A CI/CD, vagyis a folyamatos integráció és folyamatos szállítás módszertana pontosan ezt a nyugalmat és észrevétlenséget biztosítja a mindennapokban. Amikor a programozó elkészül egy feladattal és beküldi a kódot, a rendszer emberi beavatkozás nélkül automatikusan lefuttatja a teszteket. Ezt ugyanabban a konténerben teszi, ami később élesben is futni fog. Ha a tesztek sikeresen lefutnak, a kód kikerül a staging környezetbe. A staging egy jelszóval védett felület, amin ugyanaz a konténer fut, mint élesben, és ahol te, mint cégvezető vagy a megrendelőd, nyugodtan kattintva ellenőrizheted a végeredményt. Ha itt mindent rendben találsz, a jóváhagyás után a kód automatikusan élesedik. Ha az új verzió nem indul el rendben, a healthcheck megállítja a kiadást, és élesben az előző, működő verzió marad.

Ha ma a kiadás a te cégednél is gyakran azonnali tűzoltást és hétvégi túlórákat jelent, a nyers kód további módosítása előtt az alapokat kell rendbe tenni. A jelenlegi, instabil rendszered konténerizálása az első, legfontosabb lépés ahhoz, hogy a fejlesztés ismét egy jól tervezhető, kiszámítható üzleti folyamat legyen. Ez az átállás egyáltalán nem igényli a meglévő szoftvered drága újraírását vagy eldobását. A már megírt és működő kódod megmarad, csupán a bizonytalan futtatókörnyezetet cseréljük ki alatta egy megbízható, zárt rendszerre.

Vedd számba azokat a kifizetett munkaórákat, amiket a csapatod az elmúlt egy hónapban hibakereséssel, egymásra mutogatással és a szerverek kézi beállításával töltött. Ha ezek a drága órák valódi funkciófejlesztésre fordíthatók lettek volna, a projektetek ma már egy teljesen más szakaszban tartana. Írj nekünk egy rövid, tényszerű összefoglalót a jelenlegi infrastruktúrátokról, az esetleges korábbi fejlesztői elakadásokról és a leggyakoribb hibaüzenetekről. Átnézzük a munkafolyamatotokat, és pontosan megtervezzük a Docker alapú, kódként kezelt környezetet a szoftveredhez. Megmutatjuk a lépéseket, hogy a következő verzió kiadása már ne egy kockázatos esemény legyen, hanem egy egyszerű, stresszmentes rutin.