Fejlesszünk konténerben I.
Bevezetés a Docker Compose csodálatos világába.
No nem arra gondolok, hogy költözzünk ki egy kényelmesen berendezett, jól felszerelt konténerházba. Bár lehet, hogy az is segít, de maradjunk a Docker-nél (azon belül is a Docker Compose nevezetű eszköznél) és annál, hogy hogyan tudja segíteni a fejlesztő-, teszt- és build környezetek létrehozását. Kezdjük is valami egyszerűvel.
Hello Compose
Tegyül fel, hogy van egy kis PHP-s alkalmazásunk, mindenféle függőség nélkül:
www/index.php
<?php
echo "Hello World";
Szó, ami szó, nem vittük túlzásba a dolgot. A hozzá tartozó Docker Compose konfiguráció valahogy így nézhetne ki hozzá:
docker-compose.yml
version: "3"
services:
app:
image: php:7.1-alpine
ports:
- 8080:8080
command: php -S 0.0.0.0:8080 -t /app/www
volumes:
- .:/app
Ezek után, ha egy docker-compose up
parancsot kiadunk a megfelelő könyvtárban, elindul a beépített PHP-s webszerver a 8080-as porton és a http://127.0.0.1:8080/ oldalra rápillantva láthatjuk is az ismerős szöveget.
Létezik pl. Apache vagy FPM alapú csomag is, de ha nincsenek nagy igényeink, a beépített webszerver használata talán a legegyszerűbb megoldás.
Hello Composer
Ez mind szép és jó, de nagy rá az esély, hogy szükségünk van valamilyen külső csomagra, amit Composer-rel szeretnénk telepíteni. Ennek a megvalósítására több lehetőségünk is van. Használhatunk például egy olyan Docker image-et, ami tartalmazza a composer
parancsot és telepíthetjük a függőségeket a konténeren belül:
docker-compose.yml
version: "3"
services:
app:
image: composer:1.3
volumes:
- .:/app
Telepíteni pedig a következő paranccsal tudunk:
$ docker-compose run --rm app composer require phpunit/phpunit
A megoldás szépséghibája, hogy a composer
által generált fájlok root
felhasználóval jönnek létre a host gépen. Ez nem feltétlen kell, hogy zavarjon minket, mivel írni csak a konténeren keresztül fogunk oda, olvasni pedig bárki tudja így is.
Szükség esetén elpakolhatjuk a generált fájlokat egy Docker volume-ra, így csak egy vendor könyvtárunk lesz root
-tal, ami üres.
docker-compose.yml
version: "3"
volumes:
vendor-files:
services:
app:
image: composer:1.3
volumes:
- .:/app
- vendor-files:/app/vendor
Már el is hangzott a megoldás fő problémája, a vendor könyvtár üres, a host gép nem látja a generált fájlokat, így például nem tudjuk igénybe venni ezekre az IDE kódkiegészítő funkcióit se.
Lehet persze a composer
parancsot simán Docker-en kívül is futtatni, így megfelelő felhasználóval jönnek létre a fájlok és az IDE is látja őket. Ebben az esetben viszont a fejlesztők gépein és a build gépeken is ott kell lennie a telepítéshez szükséges eszközöknek.
Hello MySQL
Feltelepítettük a kedvenc keretrendszerünket, ideje beszerezni egy adatbázist is hozzá. Először is építünk egy olyan PHP-s image-et, ami tartalmazza a megfelelő kiterjesztéseket:
Dockerfile
FROM php:7.1-alpine
RUN docker-php-ext-install pdo pdo_mysql
A konténerek alapesetben úgy működnek, hogy a bennük történt változások nem nevezhetőek maradandónak, ami esetünkben azt jelenti, hogy minden docker-compose up
hívásnál egy üres adatbázist kapunk. Ez felhasználástól függően akár még elvárt működés is lehet, de ha tartósabb megoldást szeretnénk, a volume-ok ebben is segítenek.
docker-compose.yml
version: "3"
volumes:
database-data:
services:
app:
build: .
ports:
- 8080:8080
depends_on:
- database
command: php -S 0.0.0.0:8080 -t /app/www
volumes:
- .:/app
database:
image: mysql:5.7
volumes:
- database-data:/data
environment:
MYSQL_ROOT_PASSWORD: test
MYSQL_DATABASE: test
MYSQL_USER: test
MYSQL_PASSWORD: test
Érdemes megfigyelni a depends_on
részt, amitől a database
konténer előbb fog elindulni, mint az app
. Itt érdemes megjegyezni, hogy csak előbb indul, nem várja azt meg, hogy be is fejezze az elindulását, így ha az app
elég gyors, előállhat olyan helyzet, hogy még nem éri el az adatbázist. Ennek kivédésére léteznek megoldások, de ezt meghagyom házi feladatnak.
A másik érdekesség a környezeti változók a database
konténer beállításainál. Az image-eknek egész sok beállítása lehet (pl. a mysql image esetén megadhatunk fájlokat is, amikből inicializálódik az adatbázis) és szolgáltathatnak extra parancsokat, hogy könnyebb legyen rájuk saját image-et építeni (mint pl. a php-s image docker-php-ext-install
parancsa). Ezekről az image Docker Hub-os oldalán vagy közvetlenül a Docker image forrásából is tájékozódhatunk.
A konténerek között a Docker saját hálózatot épít ki, aminek segítségével a PHP-s kódból egész egyszerűen elérhetjük az adatbázist (a docker-compose.yml
fájlban szereplő service név alapján):
www/index.php
<?php
$db = new PDO('mysql:host=database;dbname=test', 'test', 'test');
$stmt = $db->query('SHOW VARIABLES LIKE "%version%"');
var_dump($stmt->fetchAll(PDO::FETCH_ASSOC));
Első körben ennyi lett volna, a példák megtekinthetőek a kapcsolódó Github repóban. A következő részben egy kicsivel bonyolultabb példákkal folytatjuk.