Az előző két cikkben szó volt egyszerű konténer indításáról felcsatolt mappákkal, fájlokkal és Docker image készítéséről. A Docker Compose-zal a felcsatolt mappát is a "volumes" blokkban definiáltuk, de valójában volume-ként inkább arra hivatkozunk, amikor a Dockerre bízzuk a mappa létrehozását, és nem is feltétlenül csak lokális fájlrendszerben lehet gondolkodni. A cikkben és a mellékelt videóban viszont csak lokális volume-okról lesz szó az előző részekben elkészült webszervert kiegészítve egy fájlfeltöltő alkalmazással, ám továbbra is a hoszt rendszer hálózatán futtatva a szolgáltatásokat. Ezért egy rettentő egyszerű HTTPD Proxy-t is konfigurálnunk kell, amivel még egy lépéssel közelebb kerülünk ahhoz a szemlélethez, amit előbb-utóbb mindenkinek meg kell ismernie, aki egyszerre több webes szolgáltatással dolgozik konténerekbe szervezve.
Tartalomjegyzék
- Törölhető konténerek
- Volume-ok áttekintése
- Mappastruktúra optimalizálása
- HTTPD konfiguráció
- Fájlfeltöltő készítése
- Docker Compose fájl módosítása
- Böngésző teszt
- Releváns, hasznos parancsok
- Végszó
Törölhető konténerek
[Tartalom]
Docker konténereknél az is szempont, hogy a konténer bármikor letörölhető legyen és ez a törlés ne okozzon maradandó kárt az alkalmazásban. Természetesen, amíg a konténerek nem élnek, addig a szolgáltatások sem érhetők el (a magas rendelkezésre állású, skálázott szolgáltatásoktól tekintsünk el), de a következő indításkor onnan lehet folytatni, ahol abbahagytuk. Ezt többféle módon is el lehet érni.
- Állapotmentes alkalmazások: Ilyen lehet akár egy statikus HTML oldal vagy egy webes számológép, aminek nincs tárolandó adata, csak az inputot várja és visszaadja az eredményt.
- Nem érdekel az állapot: Lehet az alkalmazásnak állapota, de nem érdekel az aktuális állapot, illetve nem fontos, hogy a törlések között is megtartsuk. Ilyen lehet egy demo alkalmazás, amit egyszer elindítok, dolgozom benne, törlöm, és legközelebb mindent előlről akarok kezdeni.
- Perzisztens tárhely: Ha fontos az állapotok megtartása, akkor valamilyen perzisztens tárhelyről kell gondoskodni. Ez lehet akár egy külső adatbázis szerver, vagy az alkalmazás konténerében egy volume definíciója.
A továbbiakban pedig a volume lesz a téma.
Volume-ok áttekintése
[Tartalom]
A volume tulajdonképpen arról szól, hogy a konténerben kijelölünk egy mappát, aminek a tartalmát akkor is szeretnénk megtartani, ha a konténert törüljük. Többféle volume definíció létezik. Lehet akár távoli fájlrendszert is felcsatolni a hálózaton keresztül, de használhatunk lokális volume-okat, amik fizikailag is azon a szerveren vannak, ahol a konténer is fut. Első körben a lokális volume-okkal éppen elég megismerkedni.
Ezek a lokális volume-ok viszont hasonlók a bind mounttal felcsatolt mappákhoz olyan értelemben, hogy mindkét mappa a hoszt operációs rendszer fájlrendszerén lesz. A bind mounttal felcsatolt mappát viszont többnyire mi hozzuk létre, mi menedzseljük. A Docker is létrehozza, ha a mappa nem létezett, de a jogosultságok nem feltétlenül lesznek jók. A lokális volume viszont mindenképpen a Dockeren keresztül lesz létrehozva és fizikailag a fájlrendszeren alapértelmezés szerint valahol a /var/lib/docker
mappa alatt jön létre. Mivel ebben az esetben a létrehozott mappa mindig üres, szintén alapértelmezés szerint, de opcionálisan a Docker kimásolja az image-ből annak a mappának a tartalmát, amit a konténer számára volume-ként megjelöltünk.
Bár a volume-ok minden szempontból tárgyalása számunkra most feleslegesen bonyolítaná a téma megértését, de megkülönböztetünk még névtelen és nevesített volume-okat is. Valójában a névtelen volume-nak is van neve, de ezt nem mi adjuk neki, hanem automatikusan kap egy generált hasht. Ez alapján viszont nehezebben azonosítható be, hogy a volume melyik konténerhez tartozik és mi van rajta, amikor a volume-ok listáját böngésszük. Névtelen volume például a Dockerfile-ban is definiálható már az image fejlesztője által, így azokat a mappákat, amiknek feltétlenül szükséges maradandó tárhely, a biztonság kedvért előre fel lehet sorolni. Én mégis azt javaslom, hogy amikor csak lehet, és ha nem csak egy gyors kipróbálásról van szó, mindig definiáljuk névvel a volume-okat. Ezt persze mindenkinek a saját helyzetében kell mérlegelni, de a következőkben mindkettőre lesz példa.
Mappastruktúra optimalizálása
[Tartalom]
Mielőtt viszont volume-okat definiálnánk, át kell alakítanunk a korábbi példát úgy, hogy egy kicsit logikusabban szervezzük a konfigurációs fájlokat és forráskódokat az új célnak is megfelelően, ami egy PHP alapú fájlfeltöltő és egy, a feltöltött fájlokat listázó szolgáltatás lesz.
- A HTTPD-hez szükséges fájlokat mind betesszük egy "
httpd
" nevű mappába. - A gyökérben levő Dockerfile-t egyből lehet is mozgatni ebbe a mappába.
- A két konfigurációs fájl a gyökérből átkerül a "
httpd/conf
" mappába, a "httpd-vhosts.conf
" fájl viszont a konténerbeni helyéhez hasonlóan a "httpd/conf/extra
" almappába. - A "
vhosts
" mappa úgyszintén bekerül a "httpd
" alá - A "
vhosts/blog
" mappa pedig átnevezhető "downloads"-ra, tehát a "httpd/vhosts/downloads
" néven lesz ezentúl elérhető.
A "downloads" mappában levő “index.html
” fájl új tartalma viszont egy link lesz, ami majd a letöltések mappájára mutat
<a href="files">Downloads</a>
</p>
HTTPD konfiguráció
[Tartalom]
A korábban két COPY
-val másolt konfigurációs fájlokat a Dockerfile-ban már lehet egy utasítással másolni, vagyis az alábbi két sor
COPY ./httpd-vhosts.conf /usr/local/apache2/conf/extra
változik erre:
Alapértelmezetten a HTTPD szerver a konténer standard outputjára és error stream-be logol, de a virtuális hostoknak a logjait fájlba irányítottuk, ezért ezt érdemes legalább egy névtelen volume-ra kitenni. Ehhez a Dockerfile-ba bekerül a végére a következő sor:
A teljes Dockerfile
COPY conf /usr/local/apache2/conf
COPY vhosts /var/vhosts
VOLUME [ "/usr/local/apache2/logs" ]
A virtuális hostok konfigurációját is változtatni kell, ahol viszont majd használjuk a “ProxyPass
“ és “ProxyPassReverse
” direktívákat. Ezeket viszont előbb engedélyeznünk kell a “httpd.conf
” -ban. A következő két sort kell beilleszteni a fájlba még a “http-vhosts.conf
” betöltése előtt, vagy a már benne levő, de kikommentelt sorok elől kivenni a kettőskeresztet:
LoadModule proxy_http_module modules/mod_proxy_http.so
A "httpd-vhosts.conf
"-ban az aldomaineket kell korrigálni, azaz a "blog" helyett mindenhol "downloads" lesz, ahogy a log fájlok neveiben és a mappa útvonalakban is. Ezen kívül az előző részekben bemutatott "xip.io
" szolgáltatás egy ideje nem érhető el, ezért lecseréltem "nip.io
"-ra. A PHP feltöltőt viszont nem tudja kiszolgálni a HTTPD, mert a PHP modult nem engedélyeztük. A PHP-t egyébként is általában külön konténerben érdemes futtatni, ezért most egy, az egyébként élesben ajánlott, de komplikáltabban konfigurálható megoldás helyett használjuk a proxy funkcióját a HTTPD-nek. Ehhez a "127.0.0.1.nip.io
virtuális host végén az alábbi két sort kell beilleszteni, ami minden kérést átirányít a virtuális hosztról a PHP konténerbe, ami majd a 8080-as porton lesz indítva a host network-ön.
ProxyPassReverse "/" "http://127.0.0.1:8080/"
A "files" almappára, amit majd volume-ként fogunk felcsatolni, a HTTPD-ben engedélyezni kell a fájlok listázását "index.html
" hiányában. Ehhez a "Directory" blokkba kerül be a Options +Indexes
Teljes httpd-vhosts.conf:
DocumentRoot "/usr/local/apache2/htdocs"
</VirtualHost>
<VirtualHost *:80>
ServerAdmin webmaster@127.0.0.1.nip.io
DocumentRoot "/var/vhosts/downloads"
ServerName downloads.127.0.0.1.nip.io
ErrorLog "logs/downloads-error_log"
CustomLog "logs/downloads-access_log" common
<Directory "/var/vhosts/downloads">
Require all granted
Options +Indexes
</Directory>
</VirtualHost>
<VirtualHost *:80>
ServerAdmin webmaster@127.0.0.1.nip.io
DocumentRoot "/var/vhosts/www"
ServerName 127.0.0.1.nip.io
ErrorLog "logs/www-error_log"
CustomLog "logs/www-access_log" common
<Directory "/var/vhosts/www">
Require all granted
</Directory>
ProxyPass "/" "http://127.0.0.1:8080/"
ProxyPassReverse "/" "http://127.0.0.1:8080/"
</VirtualHost>
Fájlfeltöltő készítése
[Tartalom]
A fájlfeltöltőhöz a "httpd" almappához hasonlóan létrehozunk egy "uploader"nevű mappát a projekt gyökerében. Ebben lesz a Dockerfile, amiben leírjuk a PHP-val indított szerver működését, illetve egy "index.php
", ami bekerül majd szintén az image-be.
Dockerfile
COPY index.php /var/www/index.php
CMD [ "php", "-S", "0.0.0.0:8080", "-t", "/var/www" ]
A PHP image-nek több változata létezik. A 8.0.5-ös PHP verzióból a "cli", azaz parancssori PHP értelmező változatot használjuk, de demonstrálásra egy egyszerű szervert azzal is tudunk indítani. Ezt a szervert pedig a konténer indításakor el kell tudnunk indítani. A "CMD
" kulcsszóval definiálható egy ilyen parancs. A parancsnak minden részét idézőjelbe kell tenni és vesszőkkel elválasztva, szögletes zárójelek között megadni tulajdonképpen JSON formátumban. A -S
után a szolgáltatás IP-je és portja áll, a -t
után pedig a gyökérkönyvtár útvonala. Az index.php
másolása pedig már biztosan nem okoz meglepetést a Dockerfile-ban, aminek a forráskódja az alábbi 5 soros PHP kódból és utána következő rövid HTML-ből áll:
if (!empty($_FILES['file'])) {
move_uploaded_file($_FILES['file']['tmp_name'], __DIR__ . '/files/' . $_FILES['file']['name']);
}
?>
<html>
<head>
<title>Uploader</title>
<meta charset="utf-8">
</head>
<body>
<form enctype="multipart/form-data" method="post">
<input type="file" name="file"><br>
<input type="submit" value="Upload">
</form>
</body>
</html>
Ez a feltöltő kód is csak a demonstrációra szolgál. Élesben azért ennél sokkal összetettebb lenne a kód.
Docker Compose fájl módosítása
[Tartalom]
Már használatra kész lenne a forráskód, de a Docker Compose-t még fel kell készítenünk az új szolgáltatásra és a volume használatára, ahol a feltöltött fájlokat fogjuk tárolni. A feltöltő része a "services
" blokkon belül a "docker-compose.yml
" fájlban a következő:
build:
context: uploader
dockerfile: Dockerfile
network_mode: host
A volume-ot is definiálni kell a "services
" blokkal egy szinten (sorban alatta vagy felette), ahol csak a volume nevét kell megadni egy kettősponttal a végén.
files:
Majd mindkét service blokkján belül hozzá kell adni a volume csatolásának definícióját, ahol a mappa elérések különbözni fognak. Ezen kívül a HTTPD-ből nem kell tudni írni a fájlokat, így ezt csak olvashatóra korlátozzuk a HTTPD konténerben, valamint a "type"-nál a "bind" helyett, amit a bind mounttal felcsatolásnál megadtunk, most a "volume" érték lesz. A teljes új fájl most a következő:
volumes:
files:
services:
httpd:
build:
context: httpd
dockerfile: Dockerfile
network_mode: host
volumes:
- type: volume
source: files
target: /var/vhosts/downloads/files
read_only: true
uploader:
build:
context: uploader
dockerfile: Dockerfile
network_mode: host
volumes:
- type: volume
source: files
target: /var/www/files
Böngésző teszt
[Tartalom]
A teszteléshez most el kell indítani a konténereket, illetve az image-eket újra kell buildelni. A HTTPD image már az előző részek után ott lehet a gépen, ezért a "--build
" paraméter hozzáadásával indítjuk a konténereket, hogy biztosan lefusson az új build.
Ezek után a böngészőből megnyitva a "127.0.0.1.nip.io
" weboldalt a fájlfeltöltő űrlapon betallózunk egy fájlt.
Majd a "downloads.127.0.0.1.nip.io
" weboldalon a "Downloads" linkre kattintva már látható is a feltöltött fájl annak ellenére, hogy a szolgáltatások elzárva, külön konténerben futnak.
Bár ez már túlmutat a cikken, de ugyanez a logika akár távoli fájlrendszereket felcsatoló volume-okkal is alkalmazható és így akár fizikailag több gépet is lehet használni a terhelés elosztására, amíg a volume mindegyik gépen elérhető.
Releváns, hasznos parancsok
[Tartalom]
Volume-ok listázásával látható a "httpd-vhosts_files
" nevű volume, ami tartalmazza a projekt nevét éppúgy, mint a konténerek. Valamint egy névtelen volume is megjelenik egy hosszú hash-sel, amit nem illesztek be, mert úgyis mindenkinél más.
A volume-ok adatai is megnézhetők az inspect paranccsal:
Ami egy json kimenetet ad, amiben a "Labels" kulcsszóra érdemes felfigyelni, ahol a Docker Compose-zal kapcsolatos adatok vannak, mivel abban definiáltuk.
{
"CreatedAt": "2021-05-04T20:13:27+02:00",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "httpd-vhosts",
"com.docker.compose.version": "1.28.5",
"com.docker.compose.volume": "files"
},
"Mountpoint": "/var/lib/docker/volumes/httpd-vhosts_files/_data",
"Name": "httpd-vhosts_files",
"Options": null,
"Scope": "local"
}
]
A névtelen volume-nál is meg lehet nézni ugyanezt, viszont ott hiányozni fog a "Labels", mivel annak semmi köze nem volt a Compose-hoz.
A virtuális hoszt logokat a konténerekbe belépve lehet megnézni, de akár a belépést kihagyva közvetlenül a fájl tartalmát is ki lehet listázni
A nem virtuális hoszthoz köthető logokat, mint például a konfigurációs fájl elírását, amitől a konténer el sem indul, az alábbi paranccsal lehet kiírni.
Arra az esetre, ha már nagyon hosszú lenne a log, érdemes a "--tail
" paraméterben limitálni a kiírandó legfrissebb sorok számát.
A névtelen volume-okat az alábbi paranccsal JSON-ban le lehet kérni:
De az összes felcsatolt mappa, beleértve a bind mountokat és a volume-okat, listázható JSON-ban az alábbi paranccsal:
Végszó
[Tartalom]
Bár az előző részekhez képest ez most egy nagyobb ugrás volt, de a fókusz a névtelen és nevesített volume-okon volt. Ezeket mindenképp érdemes megismerni és használni. Ha mindeközben olyan akadályba ütköznél a volume-okkal kapcsolatban, amit nem értesz, mert látszólag működnie kellene, egy részt ne aggódj, időnként a tapasztaltabbak is belefutnak speciális helyzetekbe, amiket ilyenkor ki kell nyomozni. Más részt pedig bátran írj kommentet a hiba minél részletesebb leírásával és segítek. Igény esetén pedig egy újabb cikkben és videóban egy konkrét problémára is kitérek.
A cikk, a videó és az oldal like-olásával, valamint a Youtube csatornára feliratkozással is támogathatod az ingyenes tartalmak mögötti munkát, amit töretlenül igyekszem készíteni az igényeid alapján is. Ezeket az igényeket, észrevételeket is várom bármilyen csatornán továbbra is.