Der Weg zur Magento Docker – Entwicklungsumgebung

von Andreas Pointner, 11. Januar 2016
Nachdem wir uns im Beitrag „Vagrant halt, docker run“ damit beschäftigt haben welche Vorteile Docker gegenüber Vagrant hat, widmen wir uns in diesem Teil nun der tatsächlichen Umsetzung.

Sämtliche dafür benötigten Docker-Images findet ihr auf unserem Docker-Hub und die dazugehörigen Dockerfiles sind auf GitHub verfügbar.

Für die nachfolgenden Beispiele benötigt man Docker als auch Docker-Compose. Um diese zu installieren folgt einfach folgender URL: https://docs.docker.com/compose/install/

Wie sind nun die Docker Images aufgebaut?

Möchte man seinen ersten eigenen Docker Container erstellen steht zu Beginn immer die Frage: Ist ein Container eine logische Einheit wie zum Beispiel ein Nginx + PHP-FPM oder eine einzelne Komponente?

Hier gibt es eine eindeutige Antwort darauf: Es kommt auf den Einsatzzweck an. Will man zum Beispiel eine möglichst flexible Entwicklungsumgebung haben, bei der die PHP-Version auf Knopfdruck geändert werden kann, so ist es sinnvoller im oben genannten Beispiel Nginx + PHP-FPM auf unterschiedliche Container aufzuteilen.

Will man aber in Bezug auf Ausfallsicherheit, Skalierbarkeit usw. jeden Container als selbständig lauffähige Instanz betreiben, so macht es mehr Sinn alles in einen Container zu packen.

Wir haben für beide Ansätze Images in unserem Docker Hub Account vorbereitet mit denen Ihr experimentieren könnt. In diesem Beitrag werden wir aber nur auf die Variante eingehen bei der ein Docker Container als logische Einheit behandelt wird.

Der Aufbau des Applikations-Stacks sieht folgendermaßen aus:

Docker Architektur
Jeder Docker Container ist genau für eine Aufgabe zuständig. So ist der App-Container für das Rendern und Ausliefern der Applikation zuständig. Der DB-Container regelt die Datenbankzugriffe und der Redis-Cache ist für sämtliche Datenhaltung im Cache zuständig.

App-Container

Als Basis-Image für den App-Container haben wir uns für das BaseImage von Phusion entschieden (https://github.com/phusion/baseimage-docker). Einige Vorteile dieses Images sind der flexible Aufbau (Init-Scripts bei Containererstellung, SSHd und Cron optional) und das einfache Einbinden von eigenen Services über runit.

Aufbauend auf diesem Image wurde das Image copex/php erstellt. Dieses Image beinhaltet eine PHP5.6 Instanz und sämtliche PHP-Erweiterungen (inklusive Redis) um Magento ausführen zu können.

dockerfile php
Auf Basis des PHP-Images wurde dann ein konfigurierbares Image mit Nginx und PHP-FPM erstellt. Über Umgebungsvariablen kann dieses angepasst werden.
nginx-php-fpm
Die Dockerfiles beinhalten zum Großteil shell Anwendungen die als Docker-Root ausgeführt werden. Wie man am Besten solche Dockerfiles erstellt, kann man auf der offiziellen Docker Seite hier nachlesen. In diesem Beitrag werden wir aber nicht genauer darauf eingehen.

In die bash des copex/nginx-php-fpm-Container gelangt man mit folgendem Befehl:

Wie das Image verwendet wird, erklären wir an einem Beispiel im Abschnitt Docker-Compose.

Redis und MySql-Container

Sowohl der Redis-Container als auch der MySql-Container werden von den offiziellen Docker-Images erstell. (redis:latest und mysql:latest)

Docker-Compose

Wie schafft man es nun am Einfachsten für eine Entwicklungsumgebung diese Images miteinander zu verbinden? Für diesen Einsatzzweck wurde docker-compose (ehemals fig) entwickelt. Docker-Compose ist ein Tool, um mehrere Container miteinander zu verbinden und diese laufen zu lassen. Die Konfiguration der einzelnen Container und die Verlinkung untereinander erfolgt mit Hilfe einer YAML-Datei, des docker-compose.yml – Files.

Anhand des nachfolgenden Beispiels werde ich erklären wie die CopeX Docker Container funktionieren.

docker-compose.yml
Der erste Absatz des Docker-Compose Files definiert den Applikationscontainer und heißt deshalb auch „app„. Mit Hilfe der „image“ Eigenschaft wird das zu verwendende Docker Image deklariert. In unserem Fall ist das unser copex/nginx-php-fpm Image.

Das Schlüsselwort „ports“ definiert welche Container-Ports auf andere Netzwerkinterfaces gebunden werden sollen, damit diese auch zum Beispiel von außen erreichbar sind. In unserem Beispiel binden wir die Ports für HTTP (80) und HTTPS (443) des Applikations-Docker-Containers auf das lokale Interface, also auf 127.0.0.1 (localhost). Somit ist der App-Container über die IP erreichbar. Für Magento empfehlen wir sich einen hosts-Eintrag (/etc/hosts) zu machen und so mit den Domainnamen, die auch in der Datenbank hinterlegt sind, zu arbeiten.

Ein weiterer interessanter Bereich ist die Verlinkung zu anderen Docker-Containern. Unter dem Keyword „links“ wird ein Container-Identifier (in unserem Fall „app“) auf einen Hostnamen im aktuellen Docker-Container verlinkt und sämtliche Verbindungen automatisch angelegt, damit die beiden Container miteinander kommunizieren können.

In unserem Beispiel ist somit der Container „cache“ im „app“-Container mit dem Hostnamen „cache“ erreichbar.

Im Bereich „environment“ werden Umgebungsvariablen definiert über die man die Images anpassen und provisionieren kann. So wird zum Beispiel in unserem Container die Domain in die NGINX – Konfiguration übernommen als auch das MAGENTO_ROOT Verzeichnis gesetzt.

Der letzte in unserem Beispiel verwendete Abschnitt einer Container-Deklaration sind die beiden Bereiche „volumes_from“ und „volumes„. Bei volumes_from wird ein „Verzeichnis“ eines anderen Docker-Containers eingebunden und auf das aktuelle Dateisystem „darübergelegt“ (Stichwort: Union File System). Bei „volumes“ wird ein lokales Verzeichnis im Docker Container eingebunden, so bedeutet „.:/var/www“, dass das aktuelle Verzeichnis (.) im Verzeichnis „/var/www“ des Containers eingebunden wird.

Wie starte ich nun meine Magento – Docker Entwicklungsumgebung?

Um die gesamte Entwicklungsumgebung zu starten, muss man docker-compose am Besten als Dämon ausführen.

Mit diesem Befehl ist es möglich eine Entwicklungsumgebung auf einfache Weise laufen zu lassen.

Die großen Vorteile dieser Methode sind unserer Meinung nach folgende:

  • Kein Sychronisieren der Dateien durch rync oder NFS
  • Geringer Ressourcenverbrauch, da keine Virtuelle Maschine instantiiert werden muss
  • Schnelle Anpassbarkeit an die Produktivumgebung (PHP-Version kann per Konfiguration ausgetauscht werden, Redis-Cache ebenfalls per Konfiguration hinzufügbar, …)
  • Auslagerung von einzelnen wiederverwendbaren Funktionen in einzelne Docker-Container (z.B: CRON)
  • Möglichkeit die Container auch im Produktivsystem einzusetzen. Derzeit raten wir noch von dieser Methode ab, da immer noch Sicherheitslücken auftreten und auch Stabilitätsprobleme auftreten können
  • Die Images müssen nur einmal heruntergeladen werden und die Container speichern nur die veränderten Daten, wodurch auch der benötigte Speicherplatz auf ein Minimum reduziert wird

Wie sieht das ganze nun bei Magento2 aus?

Bei Magento2 ist das ganze Setup im Grunde ident. Unsere Images wurden mit sämtlichen Erweiterungen für Magento1 und Magento2 erstellt und auch getestet. Benötigt man jedoch eigene Anpassungen so kann sich jeder unser Repository auf GitHub klonen und eigene Dockerfiles schreiben. Um diese dann am lokalen System auszutesten und zu ersetzen muss man nur das Image neu erstellen lassen.


Die anschließende Image-ID muss dann noch für das Lokale System getagged werden:

Ein etwas komplexeres Beispiel könnte dann folgendermaßen aussehen:
Magento Docker Architektur 2