Im vorherigen Artikel ging es um Docker-Grundlagen, jetzt wollen wir uns damit beschäftigen, wie man eine PHP-Anwenung mit etwas spezifischeren Anforderungen als „PHP & Apache“ in ein eigenes Dockerimage packt und in Docker Hub pusht. Wir nehmen hier als Beispiel unser CMS.
Zuerst brauchen wir einen neuen Ordner. In diesem legen wir eine Datei namens „Dockerfile“ an. Dieses Dockerfile enthält nachher alle Anweisungen, die für das fertige Image wichtig sind. Es ist wichtig, dass der Ordner nur für das Image relevante Dateien enthält, da der gesamte Ordner, inklusive Unterordner zu den Dockerservern gesendet wird, um dann „in der cloud“ in ein ausführbares Image verwandelt wird.
Zuallererst (und ganz am Anfang der Datei) kommt die Anweisung „FROM <image>“. Jedes neue Image basiert auf einem andern, vorherigen (das sozusagen unterste Image hat dann den Namen „scratch“). Unsere Anwendung benötigt PHP 5.6 mit Apache, also schreiben wir
FROM php:5.6-apache
als Start hin.
Beginnen wir damit, das eigentliche CMS hinzuzufügen. Dazu benutzen wir das „ADD“ Kommando: (Vorrausgesetzt, dass das CMS im Unterordner „cms“ im aktuellen Ordner gespeichert ist)
ADD cms/ /var/www/html
Als nächstes brauchen wir ein paar Volumes. Volumes lassen sich auch direkt im Container anlegen, dann werden die Dateien vom Dockerdaemon irgendwo auf dem Host gespeichert, wenn beim Ausführen des Images keine Volumes angegeben wurden.
Unser CMS speichert wichtige Daten in /content, /apps und /config. Wir brauchen also drei Volumes:
VOLUME /var/www/html/apps VOLUME /var/www/html/content VOLUME /var/www/html/config
Das sieht doch gut aus! Speichern wir die Datei ab und bauen das Image mit
docker build .
. Dabei sollten wir uns im selben Ordner mit dem Dockerfile befinden.
Das müsste dann in etwa so aussehen:
docker build . Sending build context to Docker daemon 3.223 MB Step 1/5 : FROM php:5.6-apache 5.6-apache: Pulling from library/php 6d827a3ef358: Already exists 87fe8fbc743a: Already exists f6d1a8d304ab: Already exists caf3547d9b73: Already exists 1004db2760ff: Already exists 66e2d66a547e: Already exists bbfaa62c234a: Already exists 19ce8807f4d1: Already exists 65dc0142d59b: Pull complete a95f4fa43ae2: Pull complete f4edd48c3730: Pull complete d58fc629c9d1: Pull complete c75a71ebb7f4: Pull complete Digest: sha256:fdd934917130261fe59e51fe303934cdb8657e458ff7b139a4e76caf4526bcb7 Status: Downloaded newer image for php:5.6-apache ---> 7371a90e1216 Step 2/5 : ADD cms/ /var/www/html ---> 340466aa2e3e Removing intermediate container a9243bafb75e Step 3/5 : VOLUME /var/www/html/apps ---> Running in 38ed9a9be9c4 ---> 09ff1566cd06 Removing intermediate container 38ed9a9be9c4 Step 4/5 : VOLUME /var/www/html/content ---> Running in 3809c65bb5ac ---> ac15bec9c7f0 Removing intermediate container 3809c65bb5ac Step 5/5 : VOLUME /var/www/html/config ---> Running in ba8939e3c391 ---> 7d2e36e48359 Removing intermediate container ba8939e3c391 Successfully built 7d2e36e48359
Die letzte Zeile ist wichtig, dort steht der Name des neuen Images. Den können wir jetzt mit
docker run -p 8080:80 7d2e36e48359
ausführen. Kurz mal probehalber localhost:8080 im Browser aufrufen uuuuuuund…
Hier Bild einfügen
Es geht nicht. Mist. Hilft nur nix, über das Problem zu jammern, gucken wir lieber mal nach, wie's im Container aussieht.
Mit
docker exec -it <name des containers> /bin/bash
können wir einen Bash im Container öffnen und uns drinnen umsehen. (Den Namen kriegen wir, wenn wir vorher keinen vergeben haben mit „docker ps“ raus.
Auf der Konsole sagt der Container: (Der läuft ja immernoch im Vordergrund, alle Logs werden direkt an uns weitergegeben)
[core:alert] [pid 17] [client 172.17.0.1:39640] /var/www/html/.htaccess: Invalid command 'RewriteEngine', perhaps misspelled or defined by a module not included in the server configuration
Es fehlt also die RewriteEngine, die wir als benötigt in unserer .htaccess definiert haben. Von der Übersichtsseite auf Docker Hub über das PHP-Apache Image erfahren wir, dass das Image absichtlich minimal gehalten wurde, um die Imagegröße klein zu halten. (Das entspricht der 'Philosophie' von Docker, weshalb die meisten Images nur das haben, was sie brauchen um die Anwendung laufen zu lassen, nichtmal rudimentäre Systemwerkzeuge sind meist dabei)
Da wir das nun wissen, wird auch die PHP-Mysqli Erweiterung nicht vorhanden sein, die das CMS ebenfalls benötigt.
Um diese und mod_rewrite hinzuzufügen brauchen wir dieses Kommando:
RUN docker-php-source extract \ && docker-php-ext-install mysql mysqli pdo pdo_mysql \ && a2enmod rewrite \ && docker-php-source delete
Mit der RUN-Anweisung lassen sich belibige Anweisungen auf der Konsole des Containers ausführen. Generell ist es eine gut Praxis, mehrere Anweisungen in einem RUN-Kommando zusammenzufassen, das minimiert die verschiedenen Images, die für jede Anweisung erstellt werden.
Wenn wir den Container jetzt bauen, laufen lassen und anschließend im Browser localhost:8080 aufrufen, sollten wir was in dieser Richtung hier sehen:
[Hier zweites Bild einfügen]
Der Container scheint also zu funktionieren! (Das CMS ist noch nicht installiert, wir müssten also als nächstes im /config Volume eine entsprechende Config-datei anlegen.
Momentan sind wir noch auf die Standard-PHP.ini angewiesen. In der steht z.B. drin, dass Dateien bis zu einer maximalen größe von 2M hochgeladen werden dürfen… im Produktiveinsatz ist das devinitiv zu wenig. Das müssen wir also anpassen. Dazu erstellen wir eine eigene php.ini-Datei (Download hier) und bauen die mit folgendem Befehl in unser Image ein:
COPY php.ini /usr/local/etc/php/
Hier müssten wir auch Error-Reporting für einen Entwicklungscontainer anschalten.
Das fertige Image können wir nun zu Dockerhub pushen, um es später wiederzuverwenden, und nicht nocheinmal bauen zu müssen. Dazu brauchen wir zunächst ein Benutzerkonto auf Dockerhub
Anschließend können wir uns mittels
docker login
einloggen.
Um das Image zu pushen, müssen wir es zunächst erstmal taggen, momentan hat es noch keinen Namen, nur etwas kryptischen ID wie „bb9bec94fd60“. Um diese rauszufinden, gibt es
docker images</docker> Damit werden alle Images, die auf diesem PC vorhanden sind, (also auch unser vorhin erstelltes) angezeigt. Um dieses Image nun mit einem "richtigen" Namen zu versehen, führt man <code>docker tag <image-ID von gerade Eben> <repository, also z.B. Nutzername>/<Imagename>
aus. Konkret bei uns wäre das
docker tag bb9bec94fd60 rkcsd/cms
Wenn wir nun nochmal
docker images
ausführen, sehen wir dass unser Image jetzt ein Tag hat:
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE rkcsd/cms latest bb9bec94fd60 7 hours ago 375 MB
Jetzt können wir das Image mit
docker push rkcsd/cms
in Docker Hub pushen, anschließend kann jeder das Image mit
docker run rkcsd/cms
herunterladen und starten. Es gibt auch die Möglichkeit „private“ Images in Docker Hub zu laden, eins davon ist kostenlos, die anderen kosten. Man kann auch sein eigenes Docker Repository hosten und dort dann seine Images privat halten.
Dieses Tutorial (und das vorherige sollte einen Einstieg in das große Thema „arbeiten mit Docker“ geben. Ich habe sicherlich nur die Oberfläche angekratzt, wer mag kann sich noch z.B. in den Docs weiterbilden, dort gibt es auch noch tiefergehende (ganz gute) Tutorials und eine Erklärung der Befehle. Im Netz findet man ebenfalls eine Vielzahl an Tutorials.