Ziel ist es, WordPress mittels Docker auf einem eigenen Server zu installieren.
Wir werden zuerst auf einem bestehenden Linux-Computer (Host mit z.B. Ubuntu) Docker installieren. Dann erstellen wir ein eigenes Dockerfile, welches ein offizielles WordPress-Image um mSMTP erweitert, damit WordPress E-Mails versenden kann. Als Datenbank verwenden wir die bereits existierende MySQL-Datenbank auf dem Host.
Docker auf dem Host installieren
~ $ sudo apt-get update ~ $ sudo apt install docker.io ~ $ sudo systemctl enable --now docker ~ $ sudo apt upgrade ~ $ sudo usermod -aG docker markus ~ $ sudo systemctl reboot ~ $ groups markus adm cdrom sudo dip plugdev users docker
Zeile 5 ist optional und gibt dem Linux-User markus administrative Privilegien für Docker.
Zeile 8 gibt die Linux-Gruppen des aktuellen Benutzers (markus) aus. Hier ist es wichtig, dass der User nun in der Gruppe docker ist.
Docker ausprobieren (Hello World)
Wir starten nun ein Hello World, um zu sehen, ob Docker grundsätzlich funktioniert.
~ $ docker run hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 0e03bdcc26d7: Pull complete Digest: sha256:1a523af650137b8accdaed439c17d684df61ee4d74feac151b5b337bd29e7eec Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. ...
Bei Docker müssen wir zwischen Image und Container unterscheiden. Ein Image enthält praktisch alle Dateien, um daraus einen Container zu erstellen. Ein Container wird dann, vereinfacht gesagt, wie ein eigener Computer gestartet.
Der Befehl docker run erstellt aus dem Image „hello-world“ einen Container und startet ihn dann. Docker führt dann in dem Container einen Befehl aus. Sobald der Befehl abgeschlossen ist, wird der Container beendet. Später werden wir im WordPress-Container einen Apache WebServer starten, der sich nicht beendet, sondern dauerhaft läuft.
Docker kann das Image hello-world auf dem Host nicht finden und lädt ihn automatisch aus dem Internet (siehe https://hub.docker.com/search?q=&type=image ).
Alle vorhandenen Container auflisten:
~/dockerbuilds/wordpress_lina $ docker ps --all CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 174d8abe9628 hello-world "/hello" 14 seconds ago Exited (0) 13 seconds ago goofy_blackwell
Hier sehen wir, dass aus dem Image hello-world ein Container mit dem automatisch generierten Namen goofy_blackwell erstellt wurde. Docker führt beim Starten des Containers den Befehl /hello innerhalb des Containers aus. Der Befehl (und damit auch der Container) wurde vor 13 Sekungen beendet (Exited 13 seconds ago). Wir werden dem WordPress-Container später einen Namen geben, damit er nicht automatisch generiert wird.
Alle lokal vorhandene Images auflisten:
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest d1165f221234 2 months ago 13.3kB
Datenbank vorbereiten (MySQL)
WordPress braucht eine Datenbank. Wir haben bereits eine MySQL-Datenbank auf dem Host installiert. In meinem Fall ist es keine MariaDB, sondern eine MySQL. Damit später der Zugriff aus dem Container auf die MySQL-DB funktioniert, müssen wir noch eine Änderung daran konfigurieren, denn seit MySQL Version 8 ist die Default-Authentication-Method nicht mehr mysql_native_password, sondern caching_sha2_password. Das führt beim Zugriff auf die Datenbank zur Fehlermeldung „Plugin caching_sha2_password could not be loaded“.
Wir stellen in der mysqld.cnf den Default auf mysql_native_password:
~ $ sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf ~ $ cat /etc/mysql/mysql.conf.d/mysqld.cnf|grep default-authentication-plugin default-authentication-plugin=mysql_native_password ~ $ sudo systemctl restart mysql.service
Nun können wir in der MySQL-Datenbank eine leere Datenbank (Schema) sowie einen Datenbank-Benutzer erstellen. „passwort“ muss natürlich durch ein ausreichend starkes Passwort ersetzt werden! Gut sind hier die Passwort-Generatoren von Passwort-Managern wie insb. KeepassXC.
~ $ sudo mysql
mysql> CREATE DATABASE my_wordpress; mysql> CREATE USER 'wordpress_user'@'172.17.0.0/255.255.0.0' IDENTIFIED BY 'passwort'; mysql> GRANT ALL ON my_wordpress.* TO 'wordpress_user'@'172.17.0.0/255.255.0.0'; mysql> use mysql mysql> select Host, User, plugin from user; +---------------------------+------------------+-----------------------+ | Host | User | plugin | +---------------------------+------------------+-----------------------+ | 172.17.0.0/255.255.0.0 | wordpress_user | mysql_native_password | +---------------------------+------------------+-----------------------+ mysql> exit
Die letzten Befehle müssen ergeben, dass mysql_native_password verwendet wird. Der WordPress-Container wird später in einem Netzwerk-Segement 172.17.x.x laufen. Daher schränken wir den Datenbankbenutzer darauf ein. Man kann sich nur aus diesem Netz mit dem Benutzer an der Datenbank anmelden.
Eigenes Image erstellen (Dockerfile)
Im offiziellen WordPress-Image fehlt ein SMTP-Client, mit dem WordPress E-Mail versenden kann. Nur mit einem SMTP-Client kann ein WordPress-Benutzer sein Passwort zurücksenden, denn dafür muss WordPress dem Benutzer eine E-Mail senden, in dem ein Link enthalten ist, mit dem ein neues Passwort gesetzt werden kann.
Dockerfile und weitere Dateien erstellen:
~ $ nano Dockerfile ~ $ mkdir mSMTP ~ $ nano mSMTP/aliases ~ $ nano mSMTP/mail.rc ~ $ nano mSMTP/msmtprc
Dockerfile:
FROM wordpress:php7.4-apache RUN apt-get update && apt-get install -y lsb-release nano msmtp msmtp-mta mailutils RUN echo "export LS_OPTIONS='--color=auto'" >> /root/.bashrc RUN echo "alias ll='ls --color=auto -al'" >> /root/.bashrc COPY --chown=root:msmtp ./mSMTP/msmtprc /etc/msmtprc RUN chmod 640 /etc/msmtprc COPY --chown=root:root ./mSMTP/aliases /etc/aliases COPY --chown=root:root ./mSMTP/mail.rc /etc/mail.rc
Zeile 1: Das eigene Image, welches wir anhand des Dockerfiles erstellen, basiert auf dem Image, welches mit FROM angegeben ist. Es ist in diesem Fall die WordPress-Variante mit PHP in Version 7.4 und Apache WebServer. Hier könnte man auch andere angeben. Siehe auch https://hub.docker.com/_/wordpress.
Zeile 2: Hier installieren wir weitere Linux-Pakete. lsb-release, nano und mailutils werden eigentlich von WordPress nicht benötigt und können entfallen. Es vereinfacht den Umgang mit dem Container später etwas. lsb-release zeigt einem die genaue Linux-Distribution des Containers. Nano ist ein einfacher Editor zum Ändern von Konfigurationsdateien. Mailutils enthält den Mail-Client „mail“, mit dem man aus dem Container zum Testen E-Mails versenden kann. msmtp und msmtp-mta sind die eigentlichen Pakete unseres SMTP-Clients, den wir zum Versenden von E-Mails benötigen.
Zeilen 3 und 4 aktivieren die farbliche Anzeige in der Console und setzen einen Alias. Dadurch hat man den Befehl „ll“, der dann den Befehl „ls -al“ ausführt. Das wird von WordPress ebenfalls nicht verwendet.
Zeilen 6 bis 8 kopieren Dateien in unser Image (und damit auch in den Container). Das sind die Konfigurationsdateien für mSMTP.
aliases:
root: wordpress@example.de default: wordpress@example.de
E-Mails an Linux-User sollen an die oben angegebenen externen E-Mail-Adressen weitergeleitet werden.
mail.rc:
set sendmail="/usr/bin/msmtp -t"
Sendmail soll den mSMTP-Client zum Versenden nutzen.
msmtprc:
defaults logfile ~/.msmtp.log auth on tls on account strato host smtp.strato.de port 587 from wordpress@example.de user wordpress@example.de password geheimespasswort account default: strato aliases /etc/aliases
In msmtprc müssen die Werte des externen SMTP-Servers eingetragen werden. mSMTP sendet ausgehende E-Mails dann an diesen Server. In meinem Beispiel ist es ein SMTP-Server bei Strato.
Zeile 2: Hier schreibt mSMTP das Logfile. Damit finden sich Fehler, falls das Versenden von E-Mails nicht funktioniert.
Zeile 6: strato ist der Name der Account-Definition. Er kann selber vergeben werden und muss mit dem Namen in Zeile 13 übereinstimmen.
Zeilen 7 bis 11: Hier müssen die individuellen Daten des SMTP-Server angegeben werden. User und Passwort sind die Zugangsdaten beim jeweiligen Provider.
Nun erstellen wir unser eigenes WordPress-Image:
~ $ docker build -t my_wordpress_img . ... Successfully built b365b8fe4a51 Successfully tagged my_wordpress_img:latest ~ $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE my_wordpress_img latest b365b8fe4a51 3 hours ago 660MB
Als Tag verwendet Docker automatisch latest. Hier könnten wir auch selber ein Tag angeben.
Docker-Container erstellen und starten
Mit docker run erstellen wir aus unserem selbst erstellen Image einen Container und starten ihn.
$ docker run -v my_wordpress-vol:/var/www/html --restart unless-stopped --name my_wordpress_container -p 8380:80 -d -e WORDPRESS_DB_HOST=172.17.0.1 -e WORDPRESS_DB_NAME=my_wordpress -e WORDPRESS_DB_USER=wordpress_user -e WORDPRESS_DB_PASSWORD=passwort my_wordpress_img
my_wordpress-vol ist ein Docker-Volume. Das ist ein Bereich auf der Festplatte des Hosts, der im Cointainer bei /var/www/html eingehangen wird. Beim ersten Start des Containers legt das WordPress-Image die eigentlichen WordPress-Dateien in den Ordner /var/www/html ab. Durch das Volume werden sie dann tatsächlich außerhalb des Container gespeichert. Das bewirkt, dass man den Container löschen und wieder neu erstellen kann, ohne seine WordPress-Installation zu verlieren. Nachträglich über die WordPress-Oberfläche installierte Plugins werden ebenfalls unterhalb dieses Ordners abgespeichert.
–restart unless-stopped bewirkt, dass der Container automatisch gestartet wird, außer man stoppt ihn manuell selber. Das hat den Vorteil, dass nach einem Neustart des Hosts der Container von Docker automatisch wieder gestartet wird.
–name my_wordpress_container definiert den Namen des erstellten Containers
-p 8380:80 bewirkt, dass WordPress auf dem Host über den Port 8380 erreichbar ist. Im Container wird ein Apache WebServer gestartet, der auf Port 80 läuft. Man kann selber den äußeren Port ändern. Der Port darf noch nicht von etwas anderem belegt sein.
-d startet den Container im Hintergrund (detached).
-e WORDPRESS_DB_HOST=172.17.0.1 gibt die IP-Adresse der MySQL-Datenbank an. Es ist die IP-Adresse das Netzwerk-Gateways zum Host.
-e WORDPRESS_DB_NAME=wordpress ist der Name der Datenbank (Schema). Den haben wir oben beim CREATE der Datenbank vergeben.
-e WORDPRESS_DB_USER=wordpress Ist der Benutzername für die Anmeldung an der Datenbank.
-e WORDPRESS_DB_PASSWORD=passwort ist das Passwort für die Anmeldung an der Datenbank und muss noch durch das oben zufällig generierte Passwort ersetzt werden (siehe Passwort-Manager / KeepassXC).
my_wordpress_img ist der Name des von uns selbst erstellten Images.
Jetzt können wir bereits WordPress mit einem Browser aufrufen: http://host:8380
host muss noch durch die IP-Adresse oder den Namen des Host-Computers ersetzt werden, auf dem Docker installiert wurde.
Falls WordPress nicht auf die Datenbank zugreifen kann, dann wird direkt die Fehlermeldung „Error establishing a database connection“ im Browser angezeigt. Dann bitte prüfen, ob beim Anlegen der Datenbank und dem Erstellen des Datenbank-Benutzers etwas anderes als beim docker run angegeben wurde. Wenn es übereinstimmt, kann WordPress auf die Datenbank zugreifen, die anfangs noch leer ist.
Wenn der Zugriff auf die Datenbank funktioniert, dann startet die Erstkonfiguration von WordPress. Zuerst wird die Sprache abgefragt.
Auf der nächsten Seite gibt man den Namen der WordPress-WebSite an. Hier gibt man zudem Name und Kennwort des ersten Benutzers an, der dann der Administrator der WordPress-WebSite ist.
Danach wird WordPress eingerichtet und die Datenbank befüllt.
Nützliche Befehle
# Logausgaben des Container kontinuierlich ausgeben docker logs -f my_wordpress_container # Details zum Container anzeigen docker inspect my_wordpress_container|less # Container auflisten docker ps --all # Container stoppen docker stop my_wordpress_container # Container starten docker start my_wordpress_container # Container löschen (sollte man einfach wieder neu erstellen # können, wenn man keine manuellen Änderungen am Container # vorgenommen und anderes auf ein Volume ausgelagert hat.) docker rm my_wordpress_container # oder docker container rm my_wordpress_container # Details zum Images anzeigen docker inspect my_wordpress_img|less # Images auflisten docker images # Image löschen (kann einfach mittels Dockerfile neu erstellt werden) docker rmi my_wordpress_img # Volumes auflisten docker volume ls # Volume löschen (VORSICHT, lässt sich ohne Backup nicht rekonstuieren) docker volume rm my_wordpress-vol # Image aus Container erstellen (sollte nur im Ausnahmefall benötigt werden, # weil in Containern keine Daten liegen sollten, die nicht wiederherstellbar # sind, denn Nutzdaten sollten in Volumes oder einer Datenbank liegen) docker commit <container-id> <image-name>:tag # Beispiel: docker commit 1d68e432aa06 my_wordpress-container:copy01
Weiteres
Weitere Themen rund um WordPress werde ich hier nur anschneiden.
Reverse Proxy
Aus Sicherheitsgründen sollte man WordPress nicht direkt aus dem Internet erreichbar machen. Es ist besser einen WebServer (z.B. Apache2) mit Reverse Proxy davor zu schalten. Ein weitere Vorteil ist, dass man mehrere Apaches (z.B. mehrere WordPress oder noch andere WebSites) hinter dem Reverse Proxy betreiben kann und alle nach außen den gleichen SSL-Port 443 verwenden können. Es ist möglich, mehrere Sub-Domains zu verwenden.
Let’s Encrypt SSL-Zertifikat
Bei der Anmeldung werden Benutzername und Kennwort über das Internet gesendet. Deshalb muss die Verbindung unbedingt verschlüsselt werden (https). Heutzutage bekommt man von Let’s Encrypt kostenlos SSL-Zertifikate, die dann für eine Verschlüsselung verwendet werden. Man kann auf dem vorgeschalteten WebServer, auf dem ein Reverse Proxy läuft, SSL mit Let’s Encrypt konfigurieren und dort SSL terminieren.
Siehe Blog-Eintrag: Apache2 mit Let’s Encrypt SSL-Zertifikat und ReverseProxy installieren
Backup
- Volumes
- Datenbankinhalte
- Dockerfile mit benötigten Dateien zum Generieren der eigenen Images
Eine Datensicherung muss von der Datenbank, dem Volume und den Dateien für das Build von Images (Dockerfile,…) gemacht werden. Für die Datenbank könnte man auf dem Host z.B. automysqlbackup nutzen. Es sichert regelmäßig die Inhalte der MySQL-Datenbank ins Dateisystem. Davon muss man dann nur noch die Dateien auf einem anderen Computer sichern. Das lässt sich automatisieren.
Die Volumes sind auf dem Host im Ordner /var/lib/docker/volumes gespeichert. Diesen Ordner sichert man am besten regelmäßig zusammen mit den von automysqlbackup erstellten SQL-Dumps.
Weitere Details zum Einrichten eines automatisierten Backups sind jetzt nicht mehr hier im Scope. Das gute an Docker ist, dass die Images und Container schnell wieder erzeugt werden können. Wer mehr Sicherheit möchte, exportiert zumindest noch die Basis-Images, also die Images, aus denen wir mit Dockerfiles unsere Images erstellt haben. Ich persönlich mache das nicht. Falls man sich nicht sicher ist, ob sich Container rekonstruieren lassen, weil dort evtl. doch außerhalb der Datenbank oder Volumes nicht wieder herstellbares liegt, der kann sich von den Containern Images erstellen und diese Images exportieren, um sie dann zu sichern. Ich jedenfalls lösche mir Container und Images beim Aufsetzen immer wieder, um genau das Problem auszuschließen.