Docker - 4. rész: Együttműködő konténerek alkalmazása

Attila | 2022. 12. 29. 11:57 | Olvasási idő: 3 perc

Címkék: #Adatbázis (Database) #Container #Docker #Image #MySQL #Virtualizáció (Virtualization) #Volume

Ebben a bejegyzésben már több, együttműködő konténert fogunk kezelni, méghozzá úgy, hogy hálózatba kapcsoljuk őket. Az alkalmazásunk változatlan, de az adattárolónkat lecseréljük MySQL adatbáziskezelőre, amit rögtön össze is hangolunk a "todo" alkalmazásunkkal.
docker-multi-container

Előzmények, bevezetés, motivációk, tervek

Az előzőekben megismertünk többféle adattárolási módot (szöveges fájl alapút, SQLite adatbázis alapút), most viszont meg nézzük azt, hogy hogyan lehet és érdemes egy robosztusabb adatbáziskezelővel dolgozni Docker virtualizációs környezetben. Az alkalmazásunk továbbra is a "todo" alkalmazás lesz.

Gyakran felmerül a kérdés, hogy ha már MySQL adatbáziskezelőt használunk, akkor azt melyik konténerbe helyezzük el? Ugyanabba vagy egy sajátot készítsünk neki? A válasz erre pedig az, hogy minden konténer csak egy dologért legyen felelős, de azt az egy dolgot tökéletesen csinálja (vonatkozások elve az OOP-ben). Milyen előnyökkel jár, ha például a MySQL adatbáziskezelőt egy külön konténerbe helyezzük? Néhány válasz erre:

  • Lehetőségünk van az alkalmazásunkat (pl. "todo") külön fejleszteni az adatbázistól függetlenül és elszigetelten.
  • Amíg egy konténert használsz az adatbáziskezelésre helyileg, addig esetleg az éles rendszerben lévő adatbázisra egy másik konténert tudsz létrehozni.
  • És még számos oka van ennek, amelyekre a jövőben tudomást szerzünk.

Legyen tehát az architektúránk ilyen (két konténerből álló):

Két különböző konténer, de majd egy csomagba helyezzük őket: a köztük lévő hálózat pedig majd segít az adatok eltárolása és lekérdezése során.


Megvalósítás

A megvalósítás folyamatát az alábbiakban részletezem.

Konténer hálózat

Ne feledjük, hogy a konténerek izoláltan, egymástól elszigetelten működnek. Nem tudnak egymásról, de még a gazda gép egyéb folyamatairól sem.

A hálózat (network) lesz a segítségünkre abban, hogy kapcsolatot építsünk ki a konténerek között és tudjanak azon keresztül kommunikálni egymással. Nem kell hálózati szakembereknek lennünk (szerencsére), de emlékezzünk arra, hogy ha két konténer egy hálózaton működik, akkor fognak tudni kommunikálni egymással, egyébként pedig nem.

MySQL konténer hálózatba kapcsolása

Kezdjünk a hálózat létrehozásával, majd kapcsoljuk hozzá a MySQL konténert induláskor:

docker network create todo-app

A parancs elég egyszerű és érthető, a hálózat neve lesz a "todo-app".

Következhet a MySQL konténer létrehozása úgy, hogy megadunk neki néhány környezeti változót (nagyon hasonlóan, mint a Laravel keretrendszernél az .env fájlok attribútumainak értékadása volt: root (adminisztrátor szintű) jelszót és adatbázist adunk meg, plusz a verziószámát), és rögtön hozzá is kapcsoljuk az új hálózatunkhoz. Itt van hozzá a PowerShell-es script parancs, amit kiadhatunk a terminálban:

docker run -d `
  --network todo-app --network-alias mysql `
  -v todo-mysql-data:/var/lib/mysql `
  -e MYSQL_ROOT_PASSWORD=secret `
  -e MYSQL_DATABASE=todos `
  mysql:8.0

A parancs második sorában van egy --network-alias kapcsoló, amelyre majd visszatérünk, hogy miért is volt rá szükség.

A parancs hatására letölti a Docker Hub-on lévő MySQL image-t a gépünkre (a vezérlőpulton láthatjuk is a letöltés végeztével). Részlet az image-ek listájából:

De nem csak az image van itt meg, hanem hozzá létrejött rögtön egy konténer is, ami el is indult, fut.

A MySQL Volume a todo-mysql-data nevet kapja így, és be is csatoljuk (mount) a /var/lib/mysql könyvtárba, ami a MySQL adatainak tárolására szolgál. Habár mi sosem futtattuk itt a docker volume create parancsot, a Docker felismeri, hogy mi egy named volume-ot akarunk használni, és automatikusan létre is hozza számunkra. Ezt ellenőrizhetjük is a Docker Desktop vezérlőpultjában a Volumes menüpont alatt.

Most be is tudunk lépni a MySQL adatbáziskezelőbe és a MySQL Console alkalmazás segítségével tudjuk kezelni, lekérdezni az adatainkat:

Itt kilistáztuk a futó konténereinket, kimásoltuk a mysql konténer azonosítóját és a következő parancsot futtattuk hozzá:

docker exec -it <mysql-container-id> mysql -p

Megjegyzés: a sárga háttérrel jelölt azonosító mindenkinek egyedi, azt a listából érdemes kimásolni.

Jelszót fog kérni hozzá, amit a konténer létrehozó parancsban mi megadtunk, ez a következő: secret

A helyes jelszó megadása után meg is jelenik a mysql prompt. A példa kedvéért listázzuk ki a meglévő adatbázisainkat:

SHOW DATABASES;

A parancs hatására visszakaptuk a rendszerszintű adatbázisokat és a saját todos nevű adatbázisunkat is mutatja nekünk. Ennek örülünk, utána pedig az exit; utasítással ki is léphetünk a MySQL Console-ból.

Kapcsolódjunk a MySQL szerverhez

De… hogyan? Ha ugyanazon a hálózaton vannak, akkor hogyan találja meg egymást a két konténer? Ehhez egy hálózati menedzselő eszközt fogunk alkalmazni, ami számos módon tudja segíteni a networking-gel kapcsolatos munkánkat: https://github.com/nicolaka/netshoot

Természetesen, ezt is virtuálisan, Docker image-ként és konténerizálva tudjuk futtatni, méghozzá úgy, hogy rögtön a "todo-app" hálózathoz kapcsoljuk be.

docker run -it --network todo-app nicolaka/netshoot

A parancs hatására letöltődik az eszköz képe (image) és a konténere már fut is, aminek a terminálja meg is jelenik nekünk ott, ahol az imént az indító parancsot kiadtuk. Utána adjuk ki az utasítást:

dig mysql

Az "ANSWER SECTION"-ben láthatjuk az eredményt, ami egy A record a mysql számára és ott van az IP címem (ami nálatok nagy valószínűséggel eltér az enyémtől). A "mysql" nem egy érvényes hostname, viszont emlékezzünk vissza arra, amikor az utasításban kiadtuk a --network-alias kapcsolót, ez gyakorlatilag egy alias névvel elfedi az IP címet… ez annyit jelent, hogy nekünk mindössze hozzá kell kapcsolnunk az alkalmazásunkat ehhez hostname-hez: mysql és már menni is fog.

Kapcsolódjunk a MySQL adatbázishoz a todo alkalmazásunkkal!

Mindenekelőtt viszont állítsuk le és akár kukázzuk az eddig meglévő konténerjeinket, kivéve a MySQL konténerjét. Így tiszta lappal tudunk indulni.

A kapcsolódási paramétereket – akárcsak a Laravel esetében – környezeti változók segítségével tudjuk beállítani. Ez fejlesztői környezetben megoldható úgy, hogy a konténer létrehozó parancsba belefűzöm ezeket a paramétereket és értékadásokat, viszont éles környezetben ez túl nagy biztonsági kockázat lenne, úgyhogy a környezeti változók megadásához éles környezetben hozzunk létre egy fájlt, ami tartalmazza a "kényes" paramétereket (például jelszó). Erről Diogo Monica készített egy fantasztikus blogbejegyzést, ami itt olvasható: https://blog.diogomonica.com//2017/03/27/why-you-shouldnt-use-env-variables-for-secret-data/

Mi viszont most még csak fejlesztünk, úgyhogy a konténert létrehozó utasításba helyezzük el ezeket a paramétereket és az értékeiket: kell a MySQL host, user, password és db, tehát a gazdagép címe, felhasználónév, amivel tudunk csatlakozni, az ő jelszava és az adatbázis, amihez kapcsolódunk.

docker run -dp 3000:3000 `
  -w /app -v "$(pwd):/app" `
  --network todo-app `
  -e MYSQL_HOST=mysql `
  -e MYSQL_USER=root `
  -e MYSQL_PASSWORD=secret `
  -e MYSQL_DB=todos `
  node:18-alpine `
  sh -c "yarn install && yarn run dev"

Létrejön az új todo alkalmazás konténerünk, amit akár meg tudunk nézni a böngészőben is, nem fog tartalmazni egyetlen feladatot sem. Tudjuk viszont naplózni az alkalmazást a korábbi parancsunkkal: előbb kilistázzuk a futó konténereket (docker ps), majd a node:18-alpine image-t használó konténert logoljuk (docker logs -f <node:18-alpine-container-id>):

A képen látható, hogy már a mysql host-hoz kapcsolódik, nem a korábban látott SQLite adatbázishoz.

A böngészőben vigyünk fel néhány vadonatúj feladatot, amit a MySQL adatbázisunk fog most már eltárolni.

Lépjünk be a MySQL szervert futtató konténerünkbe, és kapcsolódjunk a todos adatbázishoz rögtön:

docker exec -it <mysql-container-id> mysql -p todos

Jelszó megadása (secret) után rögtön ki is tudjuk listázni a todo_items tábla tartalmát:

SELECT * FROM todo_items;

Így megkapjuk azt a három mysql-es feladatot, amit az imént a weboldalon keresztül feltöltöttem. Minderről egy összefoglaló ábra látható alább:


Továbblépés

Ha megnézzük a Docker Desktop vezérlőpultjában a konténereket, akkor láthatjuk, hogy több konténerünk is fut egymás mellett (alatt), gondolok itt most elsősorban a todo alkalmazást futtató és a MySQL szervert futtató konténerre… de milyen jó lenne, ha ezek egy "csomagban" futnának és együttesen tudnánk őket kezelni. Ezt is megtanuljuk majd a későbbiekben!

Kövessetek a Facebook-on, és ha tetszik a munkám, akkor támogassátok néhány euróval a blog és az oldal fennmaradását a "buymeacoffee" (kávé) ikon útmutatásait követve.