Howto: WordPress im Docker Swarm Mode mit Nginx-Proxy auf einem Host

Nachdem ich in den letzten Artikeln das Deployment einer Laravel-Anwendung mit Nginx-Proxy und Nginx-Proxy-Companion auf einem Host beschrieben hatte, sollte nun WordPress auf demselben Host im Docker Swarm Mode platziert werden. Dabei gelten dieselben Einschränkungen, d.h. der Betrieb findet nur auf dem einzelnen Host und nicht im gesamten Swarm-Cluster statt. Die Vorarbeiten waren geleistet, der Nginx-Proxy-Container stellt den Reverse-Proxy dar, der Nginx-Proxy-Companion sorgt für die Verwaltung der Let’s-Encrypt-SSL-Zertifikate. Der folgende Artikel beschreibt die spezifischen Einstellungen für den Betrieb von WordPress und gibt Hinweise, wie sich einige Hürden umschiffen lassen. 

Die Komponenten

Zunächst einmal stellt sich die Frage nach den benötigten Komponenten für WordPress. Analog zur Laravel-Anwendung werden minimal benötigt:

  • eine Datenbank – hier MariaDB
  • PHP, dabei kommt erneut das php-fpm-swrm-Image zum Einsatz und
  • Nginx, ebenfalls wird auf das nginx-swrm-Image zurück gegriffen. 

Weitere mögliche Komponenten wie Redis oder Memcache können zu einem späteren Zeitpunkt ergänzt werden. 

Verzeichnisstruktur für verschiedene Services

Um für zukünftige bzw. weitere Services gerüstet zu sein, habe ich zunächst eine Verzeichnisstruktur angelegt. Dabei sollen alle Dateien, die für eine einzelne Anwendung benötigt werden, an einer Stelle versammelt sein. Die Struktur sieht für die WordPress-Anwendung wie folgt aus:

Ausgehend vom Verzeichnis “services” befinden sich in “geschkename” alle notwendigen Dateien bzw. Verzeichnisse. Darunter “html” für die Aufnahme der WordPress-Dateien, “mariadb/data” für die Datenbank und “nginx/sites-enabled” für die Nginx-Konfigurationsdatei. Im Hauptverzeichnis “geschkename” befindet sich die docker-compose-Datei, die den Docker Stack definiert. 

Einstellungen der Komponenten

MariaDB-Datenbank

MariaDB bietet nicht nur die Möglichkeit, ein root-Passwort beim Start des Containers zu definieren, sondern es kann auch direkt eine Datenbank mitsamt Datenbank-User und -Passwort angelegt werden. Damit diese Angaben, insbesondere das Passwort, nicht als Umgebungsvariable übergeben werden müssen, kommt erneut die Docker Secret-Funktion zum Einsatz. Wie sich beim ersten Start heraus stellte, muss der Parameter für das root-Passwort auf jeden Fall übergeben werden, es sei denn, dies wird explizit abgestellt, d.h. entweder ein leeres root-Passwort erlaubt, oder ein zufälliges gewählt. Insgesamt ergeben sich damit die folgenden vier Docker Secret Parameter:

Hat es funktioniert?

Das sieht insgesamt doch sehr gut aus. Die jeweiligen Identifier der Docker Secrets finden sich im docker-compose-File wieder. 

Nginx-Webserver

Die Konfiguration des Nginx-Webservers entspricht weitestgehend der bisher verwendeten. Die Datei “nginx/sites-enabled/default” sieht wie folgt aus:

Hierbei ist zu beachten – und daher sind die Stellen hier absichtlich kommentiert beibehalten, dass der “innere” Nginx nur auf Port 80 lauscht, insofern kein SSL kennt. Sämtliche SSL-Kommunikation findet nur auf zwischen Browser und Nginx-Proxy statt, intern hingegen erfolgt die Kommunikation unverschlüsselt. Zwar ist dies eine übliche Konfiguration eines Reverse-Proxys, doch bei WordPress führte es zunächst zu einigen Problemen, dazu später mehr. 

Um die grundlegende Funktionsfähigkeit zu testen, hatte ich in das “html“-Verzeichnis zunächst nur eine Test-Datei kopiert, aber es spricht auch nichts dagegen, WordPress direkt dort hinein zu packen. Dazu kann einfach eine aktuelle WordPress-Version herunter geladen und in das Verzeichnis entpackt werden. Wichtig dabei ist, dass es kein weiteres Unterverzeichnis “wordpress” gibt, d.h. das Document-Root ist das Verzeichnis namens “html“. Andernfalls wären die Verzeichnisse im docker-compose-File entsprechend anzupassen. 

Der Docker-Stack

Das docker-compose-File zur Service-Beschreibung sieht wie folgt aus:

Die meisten Abschnitte dürften bereits bekannt sein, daher möchte ich nur auf die Besonderheiten eingehen. 

Eineindeutigkeit der Service-Bezeichner

Die Laravel-Anwendung läuft auf demselben Host – die Services heißen nginx, phpbackend und mariadb. Beim Start des Docker Stacks werden die einzelnen Services kreiert. Der Name der Services setzt sich zusammen aus einem Prefix und dem Service-Bezeichner aus dem docker-compose-File. Im Beispiel:

Der Prefix ist dabei der Name des Stacks, der beim Start des docker-stack-Kommandos angegeben wird. Nun ist der Name Service-Name aus dem docker-compose-File jedoch gleichzeitig der Host-Name, unter dem im Swarm-Cluster der Service ansprechbar ist. 

Zwar können Services in einem Swarm-Cluster unter demselben Namen definiert werden, aber genau das führt zu nichtdeterministischem Verhalten: Es ist nicht vorhersagbar, welcher Service (welcher “Hostname”) auf einen Request antwortet. Wenn nun z.B. zwei Services “mariadb” existieren, ist nicht klar, welche Datenbank angesprochen wird – in Zweifelsfall ist es die falsche. Daher wurden die Service-Namen im docker-compose-File entsprechend modifiziert, so dass sie im Swarm-Cluster eineindeutig sind. 

Docker Secrets für MariaDB

Eine weitere Änderung ist bei der MariaDB-Umgebung zu finden – hier wurden alle vorab definierten Docker Secret Parameter verwendet. Damit wird beim Anlegen des MariaDB-Containers eine Datenbank erstellt, falls zuvor noch nicht vorhanden. 

Docker-Stack-Deployment

Alle übrigen Parameter sollten bereits aus den vorherigen Artikeln bekannt sein. Damit kann der Docker Stack gestartet werden:

Es kann einen Moment dauern, bis beim ersten Start die Datenbank angelegt ist, darüber hinaus muss Nginx-Proxy-Companion erst die Zertifikate von Let’s Encrypt anfordern und einrichten. Ggf. lohnt es sich somit, erst ein ein wenig zu warten, bevor die Fehlersuche gestartet wird. Falls alles erfolgreich eingerichtet wurde, sollte eine Prüfung ungefähr so aussehen:

WordPress läuft… nicht!

Nur um es zu erwähnen – als Voraussetzung für die Erstellung des Let’s-Encrypt-Zertifikates ist natürlich ein korrekter DNS-Eintrag für die entsprechende Domain notwendig. Nachdem alle Services erfolgreich gestartet wurden, sollte WordPress bzw. dessen Installations-Tool unter der soeben konfigurierten Domain erreichbar sein. Jedoch zeigte sich die Setup-Routine wie folgt:

Wie unschwer zu erkennen ist, fehlen hier sämtliche Stylesheet-Definitionen. Das hat einen einfachen Grund – die Seite wurde mit https aufgerufen, während die URLs für externe Stylesheets und JavaScript-Dateien von WordPress nur mit http eingebunden werden. Aus Sicherheitsgründen blockiert der Browser diese “unsicheren” Dateien, so dass die Seite ohne jegliche Stylesheets erscheint. 

Das wäre im Prinzip nicht weiter schlimm, denn das Formular zur Einrichtung der Datenbank-Verbindung, was durch den Klick auf “Los geht’s” erreichbar ist, funktioniert dennoch. Aber gerade diese nicht ganz unkritischen Daten müssten dann unverschlüsselt übertragen werden. 

Zudem wird die Situation danach nicht viel besser. Ohne weitere Konfiguration geht WordPress davon aus, dass das http-Protokoll benutzt werden soll – und generiert im Quelltext entsprechende URLs. Zwar beschäftigen sich einige Seiten mit dem Problem und geben Hinweise zur Umstellung von http auf https bei WordPress, doch meist wird davon ausgegangen, dass die Anwendung bereits erfolgreich installiert wurde und somit läuft, d.h. dass die Datei wp-config.php im Hauptverzeichnis verfügbar ist. Genau diese wird jedoch während des Setup-Prozesses erst generiert. Damit ergibt sich ein gewisses Henne-Ei-Problem. 

Lösungen für https vs. http bei WordPress

Letztlich soll WordPress ohne Änderung auf Seiten des Nginx-Proxy-Containers funktionieren. Da Nginx-Proxy alle notwendigen Server-Variablen durchschleift, besteht die Lösung im Hinzufügen einiger Code-Zeilen in der wp-config.php. Die Konfiguration ist somit dieselbe wie beim Betrieb eines Reverse-Proxys mit Nginx, der nach außen hin https anbietet, intern jedoch unverschlüsseltes http weitergibt. Zum Zeitpunkt der Installation fehlt jedoch genau die betreffende Datei wp-config.php

Zwar gibt es wie immer mehrere Lösungen für das Dilemma, aber so richtig empfehlen möchte ich an dieser Stelle keine. Eine mögliche Lösung wäre, die Generierung der Let’s-Encrypt-Zertifikate auf einen späteren Zeitpunkt zu verschieben und zunächst die grundlegende Einrichtung per http vorzunehmen. Dazu müssten die folgenden Zeilen aus der docker-compose-Datei entfernt (und später wieder eingefügt) werden:

Damit wäre sicher gestellt, dass die Admin-Seite von WordPress zunächst ohne weitere Code-Änderungen erreichbar ist. Anschließend können die bei WordPress notwendigen Umstellungen im Admin-Bereich vorgenommen werden, nach Hinzufügen der Let’s-Encrypt-Parameter kann der Service bzw. Docker Stack neu gestartet werden. 

Eine weitere Möglichkeit wäre, tatsächlich das “rohe” Formular zu nutzen und die Verbindungsparameter dort einzugeben. Es sieht zwar wenig ansprechend aus, funktioniert jedoch bis zu einem gewissen Punkt.

Nachdem die entsprechenden Verbindungsdaten eingegeben sind, wird die Datei wp-config.php im WordPress-Verzeichnis erstellt. Das funktioniert jedoch nur, sofern die Zugriffsrechte stimmen. Ggf. müssen daher die Rechte für den User www-data gesetzt werden:

Sobald die Konfigurationsdatei vorhanden ist, ist theoretisch WordPress verfügbar. Doch auch auf der Startseite zeigt sich das bekannte Problem – sämtliche CSS- und JavaScript-Dateien fehlen:

Zu allem Überfluss ist die Admin-Oberfläche aufgrund zu vieler Redirects gar nicht erreichbar, somit können dort keine weiteren Einstellungen vorgenommen werden. Die allgemeinen Hinweise zur Umstellung von http auf https helfen ebenfalls nicht, jedoch finden sich in der WordPress-Dokumentation Tipps, wie sich WordPress hinter einem Reverse-Proxy betreiben lässt. 

Dabei genügt es, die folgenden Zeilen in der Konfigurationsdatei wp-config.php hinzuzufügen (ein geeigneter Platz wäre oberhalb der “That’s all…”-Zeile:

Damit wird WordPress “auf die harte Tour” signalisiert, dass das https-Protokoll verwendet werden soll, somit werden auch die URLs entsprechend generiert. Nach dieser Änderung ist die Admin-Oberfläche wie gewohnt erreichbar, ebenfalls zeigt sich die Website bzw. das Blog nun im gewünschten (Standard-)Design:

 

In der Admin-Oberfläche kann anschließend unter “Einstellungen > Allgemein” die URL der Site für https eingerichtet werden:

Falls bereits Artikel vorhanden gewesen wären, müssten deren URLs ebenfalls geändert werden, dazu existieren Plugins wie “Better Search Replace”, doch da es sich hier um eine Neueinrichtung handelt und der erste Test-Beitrag in nahezu allen Fällen gelöscht oder deaktiviert werden dürfte, wird darauf nicht weiter eingegangen. 

Tatsächlich genügen die o.g. PHP-Zeilen, um WordPress SSL beizubringen. Rewrite-Rules oder ähnliches sind nicht notwendig, da sich Nginx-Proxy um alles Weitere kümmert und für den Redirect von http auf https sorgt. 

Nebenschauplatz: Austausch eines Docker-Images

Nachdem WordPress erst einmal lief, stellte ich fest, dass die GD-Library fehlte. Kein Problem – sie konnte schnell im php-fpm-swrm-Image hinzugefügt werden. Die aktuelle Fassung des Images ist somit bereits mit der GD-Library ausgestattet. Doch wie kann auf einfache Art und Weise das neue Image genutzt werden? Docker sieht dafür die Möglichkeit eines Service-Updates vor. Somit muss nicht der komplette Docker Stack mit allen Services neu gestartet werden, sondern es genügt, einen einzelnen Service zu aktualisieren. Dabei sind vielerlei Änderungen möglich, z.B. bzgl. Netzwerkkonfiguration, Hinzufügen von Environment-Variablen etc.. Der Austausch des Images war dabei noch eines der einfacheren Verfahren:

Da hierbei nur ein Service auf einem Node vorhanden war, wurde der Betrieb natürlich kurzzeitig unterbrochen. Die Service-Updates spielen ihren wahren Vorteil erst aus, sofern replizierte Services auf mehreren Nodes laufen, denn dann können Updates bzw. Änderungen sukzessive erfolgen, so dass die jeweilige Anwendung ohne Unterbrechung weiter läuft. 

Fazit

WordPress mit Docker im Swarm Mode auf einem Host – es funktioniert, und das sogar sehr gut, sofern man einige Kleinigkeiten beachtet. Die Installation von Grund auf war nicht ganz hürdenlos, aber letztlich hat es sich gelohnt. Nun fehlt nur noch die skalierbare und ausfallsichere Lösung für kleines Budget…

 

 

Ähnliche Beiträge

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Tags: