Dockerbe zárva – 1. rész
Akik nem egyetlen monolit alkalmazással dolgoznak éveken át, azok valószínűleg szembesültek már azzal a problémával, hogy bizony menedzselni kell az eszközök, fordítók, stb. verzióit a projektek és szolgáltatások között.
Erre vannak már persze megoldások, node.js-re ott az nvm, javara ott a jenv, vagy minden másra ott az asdf, amiről írtam is már korábban. Ezek azonban ugyanúgy elszórtan a home könyvtárunkban tárolják a dolgokat, ha takarítani akarunk utánuk, akkor mindenhova be kell nézni, egyik gépről a másikra a konfigurációt nem mindig lehet jól átvinni, sőt némelyik még igen lassú is, pl az ASDF segítségével a PHP telepítése igen körülményes, le kell tölteni egy plugint, amihez kellenek ilyen-olyan csomagok is, le kell fordítani az egész PHP-t hozzá, és így tovább. Így valami jobban használható megoldás után kell nézni. Itt jött a képbe a Docker, a Docker Compose és az aliasok.
Ez a cikk eredetileg Ádám ötlete volt, de mivel nemigen haladt az elmúlt egy évben vele, ezért átvettem, sőt igazából újraírtam, ugyanis teljesen másként közelítettük meg, vagy éppen másban láttuk az alapproblémát. Az itt leírtak megértéséhez a Docker + Docker Compose alapvető ismerete és a bash vagy más shell ismerete szükséges.
Szóval az alapprobléma számomra az, hogy habár sok mindent fel tudok telepíteni a gépemre scriptelve - ahogy arról már írtam is itt -, akadnak olyan eszközök, amikből a verziók ütköznének, nehézkes lenne a váltás a verziók között, vagy éppen hosszadalmas folyamat.
Docker nélkül
A példáimban egy Ubuntu 22.04-et fogok használni.
Tegyük fel, hogy PHP fejlesztőként dolgozunk, az eszközök, amiket használunk a napi munkához a PHP, Apache, MySQL, na meg a Composer. Ez még nem is teszi szükségessé a dockerizálást, de majd mindjárt bonyolítjuk a dolgot. Ha Docker nélkül állnánk neki, akkor ezt megoldanánk egy:
1
$ sudo apt install -y php8.1 mysql-server composer
paranccsal és már futna is az apache 2.4, PHP 8.1, egy MySQL 8.0 és a composer 2.2.6. Na igenám, de mi van, ha nekünk 7.4-es PHP kell meg 5.7-es MySQL? Itt már kezd bonyolódni a dolog. Kezdjük a PHP-vel:
Először is kellenek bizonyos csomagok:
1
$ sudo apt install -y software-properties-common ca-certificates lsb-release apt-transport-https
Utána hozzá kell adni egy ppa-t, amiben a php verziók vannak:
1
$ LC_ALL=C.UTF-8 sudo add-apt-repository ppa:ondrej/php
Azután pedig tudjuk telepíteni azt:
1
$ sudo apt install -y php7.4
Most jöhet a MySQL!
Itt már nem lesz lehetőségünk ppa-t használni, direktben kell letölteni a mysql oldaláról a .deb fájlt és telepíteni azt:
1
2
$ wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-community-server_5.7.38-1ubuntu18.04_amd64.deb
$ dpkg -i mysql-community-server_5.7.38-1ubuntu18.04_amd64.deb
Annyira nem is volt bonyolult, akkor meg minek ez a nagy felhajtás? Hát például azért, ugyanis most vagy az 5.7-es vagy a 8-as MySQL fut a gépen, ahogy a PHP-ből is már a 7.4-es fog menni a 8.1 helyett. Persze ez lehet nem is baj, ha úgy gondolod, hogy neked ez elég, a Docker pedig istenkáromlás, akkor ne is olvasd tovább a cikket.
Docker Compose
Ha már maradtál, akkor nézzük hogy is lehet ezt megoldani dockerben?
Első lépésként jó lenne, ha meg tudnánk tekinteni a weboldalt lokálban, működés közben. Ehhez a Dockert átugorva rögtön a Docker Composet fogjuk segítségül hívni és írunk egy docker-compose.yml
-t a projektünk gyökerébe:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
services:
php:
image: php:7.4-apache
volumes:
- ./www:/var/www/html
network_mode: host
mysql:
image: mysql:5.7
network_mode: host
volumes:
- mysql_data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=password
volumes:
mysql_data:
A fenti konfig a hosztgép hálózatát használja, szóval feltételezi, hogy a 80-as, 443-as és a 3306-os port szabad. Ha egyszerre több projektet futtatunk, akkor természetesen nem így használjuk, de van ahol jól jöhet ( ha mondjuk localhostként akarunk hivatkozni az adatbázisra, vagy ha nem apache-al, hanem a PHP built-in webszerverével dolgozva localhostként akarjuk azt elérni). Indítsuk el:
1
$ docker-compose up -d
A www
mappában pedig a projektünk fog elhelyezkedni, ami most egy sima index.php
lesz, amivel lecsekkoljuk, hogy valóban tudunk-e csatlakozni az adatbázishoz:
1
2
3
4
$connection = mysqli_connect("localhost:3306", "root", "password");
if (!$connection) {
echo mysqli_connect_error();
}
Ezt ha elindítjuk. akkor egy újabb problémával szembesülünk, mégpedig azzal, hogy a mysqli
kiterjesztés hiányzik. Ezt meg tudjuk oldani úgy, hogy a projektünk mappájában elhelyezünk egy Dockerfile
-t, amiben minden benne lesz, ami az alkalmazáshoz kell:
1
2
FROM php:7.4-apache
RUN docker-php-ext-install mysqli
Jelenleg ez nem valami sok, de azért is jobb így, mert még ha az éles környezetben nem is Dockerben fut, így dokumentálva van, hogy mire is van szüksége az alkalmazásnak. Leállítani pedig a
1
docker-compose down
segítségével tudjuk. Ebben az esetben az adatbázis tartalma megmarad, ha pedig szeretnénk azt is eltüntetni, akkor a
1
docker-compose down -v
lesz a megoldás, ami a volumeokat is törli.
Composer
Oké, de mi a helyzet például a tesztek futtatásával vagy éppen a függőségek telepítésével? Mivel nincs PHP telepítve a gépen, ezért nem tudjuk konténeren kívül futtatni a teszteket, sem a függőségeket telepíteni. Na de akkor mégis hogy fogjuk ezt megoldani? Kezdjük a composerrel, ugyanis tesztek futtása teszt lib nélkül úgysem fog menni.
Először is fogjuk és belevarázsoljuk a Composert a Docker képfájlunkba:
1
2
3
4
5
FROM composer/composer # itt érdemes legalább minor verzióig befixálni
FROM php:7.4-apache
COPY --from=composer/composer /usr/bin/composer /usr/bin/composer
RUN docker-php-ext-install mysqli
Itt lényegében a composer imageből átemelünk egy fájlt, ami nekünk kell és ezután az általunk épített képfájlban is elérhető lesz.
Újrabuildeljük az imageket és elindítjuk ismét:
1
2
$ docker-compose build
$ docker-compose up -d
Ezután a composer már elérhető a futó php
konténerben.
docker-compose exec módszer
Tehát fut két konténer, egyikben ott a MySQL, a másikban pedig az Apache, valamint a PHP mysqli kiterjesztéssel és a composerrel.
Itt elválik az út kétfelé, az egyik módszer, hogy nyitunk egy shellt a konténeren belül és ott dolgozunk:
1
2
$ docker-compose exec -it -u $(id -u):$(id -g) php bash
$ composer init
Itt a Docker Compose segítségével indítunk egy shellt, mégpedig úgy, hogy a saját felhasználónkat és csoportunkat használjuk a konténeren belül is. Ezáltal a programok úgy lesznek futtatva, mintha mi futtatnánk azokat, így nem lesz gond a létrehozott fájlok jogosultságával. Az egyetlen hátránya, hogy habár ID van, de konténeren belül a mi felhasználónk nem létezik, így a whoami parancs hibát okoz és a bash is azt írja, hogy “I have no name”.
Ezzel az a baj, hogyha nincs még egyetlen projekt sem, ahol futna a docker-compose
, akkor nem fog menni a composer, tehát projektet sem tudunk vele létrehozni.
A másik módszerrel viszont kívülről szeretnénk futtatni a composert vagy éppen a PHP-t:
1
$ docker-compose exec -it -u $(id -u):$(id -g) php composer init
Viszont ez nem valami szép, nem várhatjuk el, hogy a felhasználó mindig beírja ezt a hosszú parancsot, nemde? Itt jönnek képbe az aliasok!
1
$ alias composer="docker compose exec -u $(id -u):$(id -g) php composer"
A fránya mappák
Mindkét megoldással az a baj, hogyha egy másik mappában vagyunk éppen, akkor bizony nem úgy fog működni, mint ahogy mi szeretnénk. Tegyük fel, hogy a /home/ubuntu/work/project_1
mappában van a projektünk, ahol a docker-compose
-t indítottuk. Ha most visszalépünk a /home/ubuntu/work
mappába és kiadjuk a composer init
parancsot, akkor az ugyanúgy a /home/ubuntu/work/project_1
mappát fogja látni, ugyanis a docker-compose
indításakor az a mappa lett bemountolva.
Erre a megoldás az lehet, hogyha a composer külön konténerben indul, mindig az adott mappát megkapva kontextusként, ahol indítottuk. Ebbe az irányba fogunk tovább haladni a következő részben.