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.