WordPress Multi-Container-Installation mit Docker und Nginx
WordPress ist ein kostenloses und quelloffenes Content-Management-System (CMS), das auf einer MySQL-Datenbank mit PHP-Verarbeitung basiert. Dank seiner erweiterbaren Plugin-Architektur und seines Template-Systems kann ein Großteil der Verwaltung über die Weboberfläche erfolgen. Dies ist ein Grund, warum WordPress eine beliebte Wahl für die Erstellung verschiedener Arten von Websites ist, von Blogs über Produktseiten bis hin zu E-Commerce-Websites.
Verwendung von Docker und Docker Compose für die WordPress-Installation
Die Ausführung von WordPress erfordert in der Regel die Installation eines LAMP- (Linux, Apache, MySQL und PHP) oder LEMP-Stacks (Linux, Nginx, MySQL und PHP), was zeitaufwändig sein kann. Mit Tools wie Docker und Docker Compose kann der Prozess der Einrichtung des bevorzugten Stacks und der Installation von WordPress jedoch vereinfacht werden. Anstatt einzelne Komponenten manuell zu installieren, können Sie Images verwenden, die Bibliotheken, Konfigurationsdateien und Umgebungsvariablen standardisieren. Diese Images werden dann in Containern ausgeführt – isolierte Prozesse, die auf einem gemeinsamen Betriebssystem laufen. Darüber hinaus ermöglicht die Verwendung von Compose die Koordination mehrerer Container – beispielsweise einer Anwendung und einer Datenbank –, damit diese miteinander kommunizieren können.
Überblick über den Installationsprozess
In diesem Tutorial erstellen Sie eine Multi-Container-WordPress-Installation. Ihre Container umfassen eine MySQL-Datenbank, einen Nginx-Webserver und WordPress selbst. Zudem sichern Sie Ihre Installation, indem Sie TLS/SSL-Zertifikate mit Let’s Encrypt für die Domain abrufen, die Sie mit Ihrer Website verknüpfen möchten. Abschließend richten Sie einen Cron-Job ein, um Ihre Zertifikate automatisch zu erneuern, sodass Ihre Domain stets sicher bleibt.
Voraussetzungen
Falls Sie Ubuntu in der Version 16.04 oder älter verwenden, empfehlen wir Ihnen, auf eine aktuellere Version zu aktualisieren, da Ubuntu für diese Versionen keinen Support mehr anbietet. Diese Sammlung von Anleitungen hilft Ihnen dabei, Ihre Ubuntu-Version zu aktualisieren.
Schritt 1 – Definieren der Webserver-Konfiguration
Bevor Sie Container ausführen, besteht Ihr erster Schritt darin, die Konfiguration für Ihren Nginx-Webserver festzulegen. Ihre Konfigurationsdatei enthält einige WordPress-spezifische Location-Blöcke sowie einen Location-Block, um Let’s Encrypt-Verifizierungsanfragen an den Certbot-Client zur automatischen Zertifikatsverlängerung weiterzuleiten.
Erstellen des Projektverzeichnisses
Zunächst erstellen Sie ein Projektverzeichnis für Ihre WordPress-Installation. In diesem Beispiel wird es wordpress
genannt. Sie können diesem Verzeichnis jedoch auch einen anderen Namen geben, wenn Sie möchten:
mkdir wordpress
Wechseln Sie dann in das Verzeichnis:
cd wordpress
Erstellen der Konfigurationsdatei
Als Nächstes erstellen Sie ein Verzeichnis für die Konfigurationsdatei:
mkdir nginx-conf
Öffnen Sie die Datei mit Nano oder Ihrem bevorzugten Editor:
nano nginx-conf/nginx.conf
Fügen Sie in dieser Datei einen Server-Block mit Direktiven für Ihren Servernamen und das Dokumenten-Root hinzu sowie Location-Blöcke, um Anfragen des Certbot-Clients für Zertifikate, PHP-Verarbeitung und statische Asset-Anfragen weiterzuleiten.
Hinzufügen der Server-Block-Konfiguration
Fügen Sie den folgenden Code in die Datei ein. Ersetzen Sie dabei your_domain
durch Ihren eigenen Domainnamen:
server {
listen 80;
listen [::]:80;
server_name your_domain www.your_domain;
index index.php index.html index.htm;
root /var/www/html;
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/html;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location ~ /\.ht {
deny all;
}
location = /favicon.ico {
log_not_found off; access_log off;
}
location = /robots.txt {
log_not_found off; access_log off; allow all;
}
location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
expires max;
log_not_found off;
}
}
Verständnis der Server-Block-Konfiguration
Unser Server-Block enthält die folgende Konfiguration:
Direktiven
- listen: Diese Direktive weist Nginx an, auf Port 80 zu lauschen. Dadurch können Sie das Webroot-Plugin von Certbot für Ihre Zertifikatsanforderungen verwenden. Beachten Sie, dass Port 443 noch nicht enthalten ist – Sie werden Ihre Konfiguration später aktualisieren, um SSL einzubinden, sobald Sie Ihre Zertifikate erfolgreich erhalten haben.
- server_name: Diese Direktive definiert Ihren Servernamen und bestimmt, welcher Server-Block für Anfragen an Ihren Server verwendet werden soll. Ersetzen Sie
your_domain
in dieser Zeile durch Ihren eigenen Domainnamen. - index: Diese Direktive legt fest, welche Dateien als Index verwendet werden, wenn Anfragen an Ihren Server gestellt werden. Die Standard-Priorität wurde hier geändert, indem
index.php
vorindex.html
gesetzt wurde, sodass Nginx, wenn möglich,index.php
-Dateien priorisiert. - root: Diese Direktive gibt das Stammverzeichnis für Anfragen an Ihren Server an. Dieses Verzeichnis,
/var/www/html
, wird zur Build-Zeit als Mount-Punkt durch Anweisungen in Ihrer WordPress-Dockerfile erstellt. Diese Dockerfile-Anweisungen stellen zudem sicher, dass die Dateien der WordPress-Version in dieses Volume eingebunden werden.
Location-Blöcke
- location ~ /.well-known/acme-challenge: Dieser Location-Block verarbeitet Anfragen an das Verzeichnis
.well-known
, in dem Certbot eine temporäre Datei ablegt, um zu überprüfen, dass die DNS-Einträge Ihrer Domain auf Ihren Server verweisen. Mit dieser Konfiguration können Sie das Webroot-Plugin von Certbot verwenden, um Zertifikate für Ihre Domain zu erhalten. - location /: In diesem Location-Block wird die Direktive
try_files
verwendet, um nach Dateien zu suchen, die den individuellen URI-Anfragen entsprechen. Anstatt standardmäßig den Status „404 Not Found“ zurückzugeben, wird die Kontrolle an dieindex.php
-Datei von WordPress mit den Anfrageargumenten übergeben. - location ~ \.php$: Dieser Location-Block verarbeitet PHP-Anfragen und leitet diese an Ihren
wordpress
-Container weiter. Da Ihr WordPress-Docker-Image auf demphp:fpm
-Image basiert, werden in diesem Block auch Konfigurationsoptionen enthalten sein, die speziell für das FastCGI-Protokoll erforderlich sind. Nginx benötigt einen separaten PHP-Prozessor für PHP-Anfragen. In diesem Fall werden diese Anfragen vomphp-fpm
-Prozessor verarbeitet, der imphp:fpm
-Image enthalten ist. Zusätzlich enthält dieser Location-Block FastCGI-spezifische Direktiven, Variablen und Optionen, um Anfragen an die in Ihremwordpress
-Container laufende WordPress-Anwendung weiterzuleiten, den bevorzugten Index für die analysierte Anfrage-URI festzulegen und URI-Anfragen zu verarbeiten. - location ~ /\.ht: Dieser Block verarbeitet
.htaccess
-Dateien, da Nginx diese nicht ausliefert. Die Direktivedeny all
stellt sicher, dass.htaccess
-Dateien niemals für Benutzer zugänglich sind. - location = /favicon.ico, location = /robots.txt: Diese Blöcke sorgen dafür, dass Anfragen an
/favicon.ico
und/robots.txt
nicht protokolliert werden. - location ~* \.(css|gif|ico|jpeg|jpg|js|png)$: Dieser Block deaktiviert das Logging für statische Asset-Anfragen und stellt sicher, dass diese Assets stark zwischengespeichert werden, da ihre Bereitstellung in der Regel ressourcenintensiv ist.
Weitere Informationen zum FastCGI-Proxying finden Sie unter Verständnis und Implementierung von FastCGI-Proxying in Nginx. Informationen zu Server- und Location-Blöcken erhalten Sie unter Verständnis der Nginx-Server- und Location-Block-Auswahlalgorithmen.
Speichern und Schließen der Datei
Speichern und schließen Sie die Datei, wenn Sie die Bearbeitung abgeschlossen haben. Falls Sie nano
verwendet haben, drücken Sie CTRL+X, dann Y und anschließend ENTER.
Nachdem Ihre Nginx-Konfiguration abgeschlossen ist, können Sie nun Umgebungsvariablen erstellen, die zur Laufzeit an Ihre Anwendungs- und Datenbankcontainer übergeben werden.
Schritt 2 – Definieren von Umgebungsvariablen
Ihre Datenbank- und WordPress-Anwendungscontainer benötigen zur Laufzeit Zugriff auf bestimmte Umgebungsvariablen, damit Ihre Anwendungsdaten erhalten bleiben und für Ihre Anwendung zugänglich sind. Diese Variablen umfassen sowohl sensible als auch nicht sensible Informationen: Sensible Werte sind Ihr MySQL-Root-Passwort sowie Benutzername und Passwort für die Anwendungsdatenbank. Nicht sensible Werte betreffen den Namen und Host Ihrer Anwendungsdatenbank.
Statt alle diese Werte in Ihrer Docker-Compose-Datei zu definieren – der Hauptdatei, die Informationen darüber enthält, wie Ihre Container ausgeführt werden –, sollten Sie die sensiblen Werte in einer .env
-Datei speichern und deren Verbreitung einschränken. Dadurch wird verhindert, dass diese Werte in Ihre Projekt-Repositories kopiert und öffentlich zugänglich gemacht werden.
Erstellen der .env-Datei
Öffnen Sie im Hauptprojektverzeichnis ~/wordpress
eine Datei mit dem Namen .env
:
nano .env
Die vertraulichen Werte, die Sie in dieser Datei festlegen, umfassen ein Passwort für den MySQL-Root-Benutzer sowie einen Benutzernamen und ein Passwort, die WordPress für den Zugriff auf die Datenbank verwenden wird.
Fügen Sie die folgenden Variablennamen und Werte in die Datei ein. Denken Sie daran, für jede Variable Ihre eigenen Werte anzugeben:
MYSQL_ROOT_PASSWORD=your_root_password
MYSQL_USER=your_wordpress_database_user
MYSQL_PASSWORD=your_wordpress_database_password
Die Datei enthält ein Passwort für das Root-Administratorkonto sowie Ihren bevorzugten Benutzernamen und Ihr Passwort für die Anwendungsdatenbank.
Speichern und schließen Sie die Datei, wenn Sie die Bearbeitung abgeschlossen haben.
Schützen sensibler Informationen
Da Ihre .env
-Datei sensible Informationen enthält, sollten Sie sicherstellen, dass sie in den .gitignore
– und .dockerignore
-Dateien Ihres Projekts aufgeführt ist. Dadurch wird verhindert, dass diese Datei in Ihre Git-Repositories oder Docker-Images kopiert wird.
Falls Sie Git für die Versionskontrolle verwenden möchten, initialisieren Sie Ihr aktuelles Arbeitsverzeichnis als Repository mit:
git init
git init
Erstellen und öffnen Sie anschließend die Datei .gitignore
:
nano .gitignore
Fügen Sie .env
in die Datei ein:
.env
Speichern und schließen Sie die Datei, wenn Sie die Bearbeitung abgeschlossen haben.
Hinzufügen von .env zu .dockerignore
Ebenfalls ist es eine gute Vorsichtsmaßnahme, .env
in die Datei .dockerignore
aufzunehmen, damit sie nicht in Ihre Container gelangt, wenn Sie dieses Verzeichnis als Build-Kontext verwenden.
Öffnen Sie die Datei:
nano .dockerignore
Fügen Sie .env
in die Datei ein:
.env
Unterhalb davon können Sie optional Dateien und Verzeichnisse hinzufügen, die mit der Entwicklung Ihrer Anwendung verbunden sind:
.env
.git
docker-compose.yml
.dockerignore
Speichern und schließen Sie die Datei, wenn Sie die Bearbeitung abgeschlossen haben.
Nachdem Ihre sensiblen Informationen gesichert sind, können Sie nun Ihre Dienste in einer docker-compose.yml
-Datei definieren.
Schritt 3 – Definieren von Diensten mit Docker Compose
Ihre docker-compose.yml
-Datei enthält die Dienstdefinitionen für Ihr Setup. Ein Dienst in Compose ist ein laufender Container, und die Dienstdefinitionen legen fest, wie jeder Container ausgeführt wird.
Mit Compose können Sie verschiedene Dienste definieren, um Multi-Container-Anwendungen auszuführen, da Compose es ermöglicht, diese Dienste mit gemeinsamen Netzwerken und Volumes zu verknüpfen. Dies ist für Ihr aktuelles Setup besonders hilfreich, da Sie separate Container für Ihre Datenbank, die WordPress-Anwendung und den Webserver erstellen. Außerdem werden Sie einen Container hinzufügen, der den Certbot-Client ausführt, um Zertifikate für Ihren Webserver zu erhalten.
Erstellen der Docker-Compose-Datei
Erstellen und öffnen Sie zunächst die Datei docker-compose.yml
:
nano docker-compose.yml
Fügen Sie den folgenden Code hinzu, um die Version Ihrer Compose-Datei und den db
-Datenbankdienst zu definieren:
version: '3'
services:
db:
image: mysql:8.0
container_name: db
restart: unless-stopped
env_file: .env
environment:
- MYSQL_DATABASE=wordpress
volumes:
- dbdata:/var/lib/mysql
command: '--default-authentication-plugin=mysql_native_password'
networks:
- app-network
Verständnis der db-Dienstdefinition
- image: Diese Direktive gibt an, welches Image Compose verwenden soll, um den Container zu erstellen. Hier wird das
mysql:8.0
-Image festgelegt, um zukünftige Konflikte zu vermeiden, da sich dasmysql:latest
-Image mit Updates ändern kann. Weitere Informationen zum Festlegen von Versionen und zur Vermeidung von Abhängigkeitskonflikten finden Sie in der Docker-Dokumentation zu Best Practices für Dockerfiles. - container_name: Diese Direktive gibt den Namen des Containers an.
- restart: Hier wird die Neustart-Richtlinie für den Container definiert. Standardmäßig ist sie auf
no
gesetzt, aber in diesem Fall wird der Container so konfiguriert, dass er neu gestartet wird, es sei denn, er wird manuell gestoppt. - env_file: Diese Option weist Compose an, Umgebungsvariablen aus einer Datei namens
.env
zu laden, die sich im Build-Kontext befindet. In diesem Fall ist der Build-Kontext Ihr aktuelles Verzeichnis. - environment: Mit dieser Option können Sie zusätzliche Umgebungsvariablen hinzufügen, die über die in der
.env
-Datei definierten hinausgehen. Sie setzen die VariableMYSQL_DATABASE
aufwordpress
, um einen Namen für Ihre Anwendungsdatenbank festzulegen. Da es sich hierbei um nicht sensible Informationen handelt, können Sie sie direkt in derdocker-compose.yml
-Datei einfügen. - volumes: Hier wird ein benanntes Volume namens
dbdata
in das Verzeichnis/var/lib/mysql
des Containers eingebunden. Dies ist das Standard-Datenverzeichnis für MySQL in den meisten Distributionen. - command: Diese Option ermöglicht es, den Standard-
CMD
-Befehl des Images zu überschreiben. In diesem Fall wird eine zusätzliche Option zum Standardbefehlmysqld
des Docker-Images hinzugefügt, der den MySQL-Server im Container startet. Die Option--default-authentication-plugin=mysql_native_password
setzt die Systemvariable--default-authentication-plugin
aufmysql_native_password
. Dadurch wird festgelegt, welches Authentifizierungsverfahren für neue Anfragen an den Server verwendet werden soll. Da PHP und damit Ihr WordPress-Image die neuere MySQL-Authentifizierungsmethode nicht unterstützen, muss diese Anpassung vorgenommen werden, damit sich Ihr Anwendungsdatenbankbenutzer erfolgreich authentifizieren kann. - networks: Diese Option gibt an, dass Ihr Anwendungsdienst dem Netzwerk
app-network
beitritt, das Sie am Ende der Datei definieren werden.
Hinzufügen des WordPress-Anwendungsdienstes
Fügen Sie als Nächstes unterhalb der db
-Dienstdefinition die Definition für den WordPress-Anwendungsdienst hinzu:
...
wordpress:
depends_on:
- db
image: wordpress:5.1.1-fpm-alpine
container_name: wordpress
restart: unless-stopped
env_file: .env
environment:
- WORDPRESS_DB_HOST=db:3306
- WORDPRESS_DB_USER=$MYSQL_USER
- WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
- WORDPRESS_DB_NAME=wordpress
volumes:
- wordpress:/var/www/html
networks:
- app-network
Verständnis der WordPress-Dienstdefinition
- depends_on: Diese Option stellt sicher, dass Ihre Container in der richtigen Abhängigkeitsreihenfolge gestartet werden, wobei der
wordpress
-Container erst nach demdb
-Container startet. Ihre WordPress-Anwendung ist auf die Existenz der Anwendungsdatenbank und des Benutzers angewiesen. Durch das Festlegen dieser Abhängigkeit wird sichergestellt, dass Ihre Anwendung ordnungsgemäß gestartet wird. - image: Für dieses Setup wird das
5.1.1-fpm-alpine
-WordPress-Image verwendet. Wie in Schritt 1 erläutert, stellt dieses Image sicher, dass Ihre Anwendung denphp-fpm
-Prozessor enthält, den Nginx für die Verarbeitung von PHP-Anfragen benötigt. Es handelt sich zudem um einalpine
-Image, das vom Alpine-Linux-Projekt abgeleitet ist. Dies trägt dazu bei, die Gesamtgröße Ihres Images klein zu halten. Weitere Informationen zu den Vor- und Nachteilen der Verwendung von Alpine-Images und ob dies für Ihre Anwendung sinnvoll ist, finden Sie im Abschnitt Image Variants auf der Docker Hub WordPress-Image-Seite. - env_file: Hier wird erneut festgelegt, dass Werte aus der
.env
-Datei geladen werden sollen, da dort der Benutzername und das Passwort für die Anwendungsdatenbank definiert wurden. - environment: Die in der
.env
-Datei definierten Werte werden hier den Variablennamen zugewiesen, die das WordPress-Image erwartet:WORDPRESS_DB_USER
undWORDPRESS_DB_PASSWORD
. Zudem wird einWORDPRESS_DB_HOST
festgelegt, der auf den MySQL-Server verweist, der imdb
-Container läuft und über den Standardport von MySQL,3306
, erreichbar ist. Die VariableWORDPRESS_DB_NAME
entspricht dem in der MySQL-Dienstdefinition angegebenenMYSQL_DATABASE
-Wert:wordpress
. - volumes: Ein benanntes Volume namens
wordpress
wird als Mountpoint/var/www/html
eingebunden, der vom WordPress-Image erstellt wurde. Die Verwendung eines benannten Volumes ermöglicht es, den Anwendungscode mit anderen Containern zu teilen. - networks: Der
wordpress
-Container wird ebenfalls dem Netzwerkapp-network
hinzugefügt.
Hinzufügen des Webserver-Dienstes (Nginx)
Fügen Sie als Nächstes unterhalb der wordpress
-Anwendungsdienstdefinition die folgende Definition für Ihren Nginx-Webserver-Dienst hinzu:
...
webserver:
depends_on:
- wordpress
image: nginx:1.15.12-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
Verständnis der Webserver-Dienstdefinition
- depends_on: Stellt sicher, dass der
webserver
-Container erst startet, nachdem derwordpress
-Container ausgeführt wird. - image: Verwendet das
nginx:1.15.12-alpine
-Image, um die Containergröße klein zu halten. - ports: Exponiert Port
80
, um den HTTP-Datenverkehr basierend auf dernginx.conf
-Konfiguration zu ermöglichen. - volumes: Definiert eine Kombination aus benannten Volumes und Bind-Mounts:
wordpress:/var/www/html
: Bindet den Anwendungscode von WordPress ein../nginx-conf:/etc/nginx/conf.d
: Bindet das Nginx-Konfigurationsverzeichnis vom Host in den Container.certbot-etc:/etc/letsencrypt
: Bindet die Let’s Encrypt-Zertifikate und Schlüssel für Ihre Domain ein.
- networks: Fügt diesen Container dem Netzwerk
app-network
hinzu.
Hinzufügen des Certbot-Dienstes
Fügen Sie abschließend unterhalb der webserver
-Definition die letzte Dienstdefinition für den certbot
-Dienst hinzu. Ersetzen Sie dabei die hier aufgeführte E-Mail-Adresse und die Domainnamen durch Ihre eigenen Informationen:
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --staging -d your_domain -d www.your_domain
Verständnis der Certbot-Dienstdefinition
- depends_on: Stellt sicher, dass der
certbot
-Container erst gestartet wird, nachdem derwebserver
-Container läuft. - image: Verwendet das
certbot/certbot
-Image von Docker Hub. - volumes: Teilt Ressourcen mit dem Nginx-Container:
certbot-etc:/etc/letsencrypt
: Speichert SSL-Zertifikate und Schlüssel.wordpress:/var/www/html
: Teilt die Anwendungsdateien.
- command: Verwendet Certbot zum Abrufen eines SSL-Zertifikats mit den folgenden Optionen:
--webroot
: Nutzt das Webroot-Plugin zur Authentifizierung.--webroot-path=/var/www/html
: Legt das Webroot-Verzeichnis fest.--email sammy@your_domain
: E-Mail-Adresse für Registrierung und Wiederherstellung.--agree-tos
: Akzeptiert die ACME-Teilnehmervereinbarung.--no-eff-email
: Verhindert das Teilen der E-Mail-Adresse mit der EFF.--staging
: Nutzt die Testumgebung von Let’s Encrypt.-d your_domain -d www.your_domain
: Gibt die Domainnamen für das Zertifikat an.
Hinzufügen von Netzwerk- und Volume-Definitionen
Fügen Sie unterhalb der certbot
-Dienstdefinition die Netzwerk- und Volume-Definitionen hinzu:
...
volumes:
certbot-etc:
wordpress:
dbdata:
networks:
app-network:
driver: bridge
Verständnis der Netzwerk- und Volume-Definitionen
- volumes: Definiert drei benannte Volumes:
certbot-etc:
Speichert Let’s Encrypt-Zertifikate und -Schlüssel.wordpress:
Speichert die WordPress-Anwendungsdateien.dbdata:
Speichert die MySQL-Datenbankdateien.
- networks: Das benutzerdefinierte Bridge-Netzwerk
app-network
ermöglicht die Kommunikation zwischen den Containern, da sie sich auf demselben Docker-Daemon-Host befinden. Dies optimiert den Datenverkehr und die Kommunikation innerhalb der Anwendung, da alle Ports zwischen Containern im selben Bridge-Netzwerk geöffnet sind, ohne Ports nach außen freizugeben. Somit können diedb
-,wordpress
– undwebserver
-Container miteinander kommunizieren, während nur Port80
für den externen Zugriff auf die Anwendung freigegeben wird.
Vollständige Docker-Compose-Datei
Die folgende Datei enthält die vollständige docker-compose.yml
-Konfiguration:
version: '3'
services:
db:
image: mysql:8.0
container_name: db
restart: unless-stopped
env_file: .env
environment:
- MYSQL_DATABASE=wordpress
volumes:
- dbdata:/var/lib/mysql
command: '--default-authentication-plugin=mysql_native_password'
networks:
- app-network
wordpress:
depends_on:
- db
image: wordpress:5.1.1-fpm-alpine
container_name: wordpress
restart: unless-stopped
env_file: .env
environment:
- WORDPRESS_DB_HOST=db:3306
- WORDPRESS_DB_USER=$MYSQL_USER
- WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
- WORDPRESS_DB_NAME=wordpress
volumes:
- wordpress:/var/www/html
networks:
- app-network
webserver:
depends_on:
- wordpress
image: nginx:1.15.12-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --staging -d your_domain -d www.your_domain
volumes:
certbot-etc:
wordpress:
dbdata:
networks:
app-network:
driver: bridge
Speichern und schließen Sie die Datei, wenn Sie die Bearbeitung abgeschlossen haben.
Nachdem Ihre Dienstdefinitionen eingerichtet sind, können Sie nun die Container starten und Ihre Zertifikatsanforderungen testen.
Schritt 4 – Abrufen von SSL-Zertifikaten und Zugangsdaten
Starten Sie Ihre Container mit dem Befehl docker-compose up
, der die Container in der von Ihnen festgelegten Reihenfolge erstellt und ausführt. Durch Hinzufügen der -d
-Option werden die db
-, wordpress
– und webserver
-Container im Hintergrund ausgeführt:
docker-compose up -d
Die folgende Ausgabe bestätigt, dass Ihre Dienste erfolgreich erstellt wurden:
Output
Creating db ... done
Creating wordpress ... done
Creating webserver ... done
Creating certbot ... done
Überprüfen Sie den Status Ihrer Dienste mit docker-compose ps
:
docker-compose ps
Nach Abschluss sollten die Dienste db
, wordpress
und webserver
den Status Up anzeigen, während der certbot
-Container mit der Statusmeldung 0
beendet wurde:
Output
Name Command State Ports
-------------------------------------------------------------------------
certbot certbot certonly --webroot ... Exit 0
db docker-entrypoint.sh --def ... Up 3306/tcp, 33060/tcp
webserver nginx -g daemon off; Up 0.0.0.0:80->80/tcp
wordpress docker-entrypoint.sh php-fpm Up 9000/tcp
Falls in der State-Spalte für die Dienste db
, wordpress
oder webserver
ein anderer Status als Up angezeigt wird oder der certbot
-Container mit einem anderen Exit-Status als 0
beendet wurde, sollten Sie die Dienstprotokolle mit dem Befehl docker-compose logs
überprüfen:
docker-compose logs service_name
Sie können nun überprüfen, ob Ihre Zertifikate im webserver
-Container eingebunden wurden, indem Sie den Befehl docker-compose exec
ausführen:
docker-compose exec webserver ls -la /etc/letsencrypt/live
Sobald Ihre Zertifikatsanforderungen erfolgreich waren, wird die folgende Ausgabe angezeigt:
Output
total 16
drwx------ 3 root root 4096 May 10 15:45 .
drwxr-xr-x 9 root root 4096 May 10 15:45 ..
-rw-r--r-- 1 root root 740 May 10 15:45 README
drwxr-xr-x 2 root root 4096 May 10 15:45 your_domain
Da Ihre Zertifikatsanforderung erfolgreich war, können Sie nun die certbot
-Dienstdefinition bearbeiten, um die --staging
-Option zu entfernen.
Aktualisieren von Certbot zum Abrufen eines gültigen SSL-Zertifikats
Öffnen Sie die Datei docker-compose.yml
:
nano docker-compose.yml
Suchen Sie den Abschnitt der Datei mit der certbot
-Dienstdefinition und ersetzen Sie die --staging
-Option in der command
-Direktive durch die Option --force-renewal
. Diese Option weist Certbot an, ein neues Zertifikat für dieselben Domains wie ein bestehendes Zertifikat zu beantragen.
Die aktualisierte certbot
-Dienstdefinition lautet wie folgt:
...
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- certbot-var:/var/lib/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --force-renewal -d your_domain -d www.your_domain
...
Neuerstellen des Certbot-Containers
Führen Sie nun den Befehl docker-compose up
aus, um den certbot
-Container neu zu erstellen. Verwenden Sie die Option --no-deps
, um Compose anzuweisen, den webserver
-Dienst nicht neu zu starten, da dieser bereits läuft:
docker-compose up --force-recreate --no-deps certbot
Überprüfen der Ausgabe
Die folgende Ausgabe zeigt an, dass Ihre Zertifikatsanforderung erfolgreich war:
Output
Recreating certbot ... done
Attaching to certbot
certbot | Saving debug log to /var/log/letsencrypt/letsencrypt.log
certbot | Plugins selected: Authenticator webroot, Installer None
certbot | Renewing an existing certificate
certbot | Performing the following challenges:
certbot | http-01 challenge for your_domain
certbot | http-01 challenge for www.your_domain
certbot | Using the webroot path /var/www/html for all unmatched domains.
certbot | Waiting for verification...
certbot | Cleaning up challenges
certbot | IMPORTANT NOTES:
certbot | - Congratulations! Your certificate and chain have been saved at:
certbot | /etc/letsencrypt/live/your_domain/fullchain.pem
certbot | Your key file has been saved at:
certbot | /etc/letsencrypt/live/your_domain/privkey.pem
certbot | Your cert will expire on 2019-08-08. To obtain a new or tweaked
certbot | version of this certificate in the future, simply run certbot
certbot | again. To non-interactively renew *all* of your certificates, run
certbot | "certbot renew"
certbot | - Your account credentials have been saved in your Certbot
certbot | configuration directory at /etc/letsencrypt. You should make a
certbot | secure backup of this folder now. This configuration directory will
certbot | also contain certificates and private keys obtained by Certbot so
certbot | making regular backups of this folder is ideal.
certbot | - If you like Certbot, please consider supporting our work by:
certbot |
certbot | Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
certbot | Donating to EFF: https://eff.org/donate-le
certbot |
certbot exited with code 0
Nachdem Ihre Zertifikate erfolgreich eingebunden wurden, können Sie nun Ihre Nginx-Konfiguration anpassen, um SSL zu aktivieren.
Schritt 5 – Anpassen der Webserver-Konfiguration und Dienstdefinition
Die Aktivierung von SSL in Ihrer Nginx-Konfiguration umfasst das Hinzufügen einer HTTP-Weiterleitung zu HTTPS, das Festlegen der Speicherorte für Ihr SSL-Zertifikat und Ihren Schlüssel sowie das Hinzufügen von Sicherheitsparametern und Headern.
Stoppen des Webservers
Da Sie den webserver
-Dienst neu erstellen müssen, um diese Änderungen zu übernehmen, können Sie ihn jetzt stoppen:
docker-compose stop webserver
Hinzufügen empfohlener Sicherheitsparameter
Bevor Sie die Konfigurationsdatei ändern, rufen Sie die empfohlenen Nginx-Sicherheitsparameter von Certbot mit curl
ab:
curl -sSLo nginx-conf/options-ssl-nginx.conf https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf
Dieser Befehl speichert die Parameter in einer Datei namens options-ssl-nginx.conf
, die im Verzeichnis nginx-conf
abgelegt wird.
Ersetzen der Nginx-Konfigurationsdatei
Entfernen Sie als Nächstes die zuvor erstellte Nginx-Konfigurationsdatei:
rm nginx-conf/nginx.conf
Erstellen und öffnen Sie eine neue Version der Datei:
nano nginx-conf/nginx.conf
Aktualisieren der Konfigurationsdatei
Fügen Sie den folgenden Code in die Datei ein, um HTTP-Anfragen auf HTTPS umzuleiten und SSL-Zertifikate, Protokolle sowie Sicherheitsheader hinzuzufügen. Ersetzen Sie dabei your_domain
durch Ihre eigene Domain:
server {
listen 80;
listen [::]:80;
server_name your_domain www.your_domain;
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/html;
}
location / {
rewrite ^ https://$host$request_uri? permanent;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name your_domain www.your_domain;
index index.php index.html index.htm;
root /var/www/html;
server_tokens off;
ssl_certificate /etc/letsencrypt/live/your_domain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem;
include /etc/nginx/conf.d/options-ssl-nginx.conf;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# enable strict transport security only if you understand the implications
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location ~ /\.ht {
deny all;
}
location = /favicon.ico {
log_not_found off; access_log off;
}
location = /robots.txt {
log_not_found off; access_log off; allow all;
}
location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
expires max;
log_not_found off;
}
}
Nachdem Ihre aktualisierte Nginx-Konfiguration implementiert wurde, können Sie nun Ihren Webserver neu starten.
Abschließen der Webserver-Konfiguration und Aktualisieren von Docker Compose
Der HTTP-Server-Block legt das Webroot-Verzeichnis für Certbot-Erneuerungsanfragen im .well-known/acme-challenge
-Verzeichnis fest. Zusätzlich enthält er eine rewrite
-Direktive, die HTTP-Anfragen an das Stammverzeichnis auf HTTPS umleitet.
Der HTTPS-Server-Block aktiviert ssl
und http2
. Weitere Informationen darüber, wie HTTP/2 die bestehenden HTTP-Protokolle verbessert und welche Vorteile dies für die Website-Performance hat, finden Sie in der Einführung zu How To Set Up Nginx with HTTP/2 Support on Ubuntu 18.04.
Dieser Block enthält außerdem die Speicherorte für Ihr SSL-Zertifikat und Ihren Schlüssel sowie die empfohlenen Certbot-Sicherheitsparameter, die Sie in nginx-conf/options-ssl-nginx.conf
gespeichert haben.
Zusätzlich sind einige Sicherheitsheader enthalten, die Ihnen helfen, A-Bewertungen bei Tests wie SSL Labs und Security Headers zu erhalten. Diese Header umfassen:
- X-Frame-Options: Verhindert Clickjacking-Angriffe, indem das Einbetten von iframes eingeschränkt wird.
- X-Content-Type-Options: Verhindert MIME-Type-Sniffing.
- Referrer-Policy: Steuert die Menge an Referrer-Informationen, die gesendet werden.
- Content-Security-Policy: Definiert, welche Inhaltsquellen als vertrauenswürdig gelten.
- X-XSS-Protection: Schützt vor Cross-Site-Scripting (XSS)-Angriffen.
Der HTTP Strict Transport Security (HSTS)-Header ist auskommentiert – aktivieren Sie diesen nur, wenn Sie die Auswirkungen verstehen und die preload
-Funktionalität sorgfältig bewertet haben.
Ihre root
– und index
-Direktiven befinden sich ebenfalls in diesem Block, ebenso wie die WordPress-spezifischen Location-Blöcke, die in Schritt 1 besprochen wurden.
Sobald Sie die Bearbeitung abgeschlossen haben, speichern und schließen Sie die Datei.
Aktualisieren der Docker-Compose-Konfiguration
Bevor Sie den webserver
-Dienst neu erstellen, müssen Sie eine 443
-Port-Zuordnung zur Webserver-Dienstdefinition hinzufügen.
Öffnen Sie Ihre docker-compose.yml
-Datei:
nano docker-compose.yml
Fügen Sie in der webserver
-Dienstdefinition die folgende Port-Zuordnung hinzu:
...
webserver:
depends_on:
- wordpress
image: nginx:1.15.12-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
Vollständige Docker-Compose-Datei
Hier ist die vollständige docker-compose.yml
-Datei nach den Änderungen:
version: '3'
services:
db:
image: mysql:8.0
container_name: db
restart: unless-stopped
env_file: .env
environment:
- MYSQL_DATABASE=wordpress
volumes:
- dbdata:/var/lib/mysql
command: '--default-authentication-plugin=mysql_native_password'
networks:
- app-network
wordpress:
depends_on:
- db
image: wordpress:5.1.1-fpm-alpine
container_name: wordpress
restart: unless-stopped
env_file: .env
environment:
- WORDPRESS_DB_HOST=db:3306
- WORDPRESS_DB_USER=$MYSQL_USER
- WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
- WORDPRESS_DB_NAME=wordpress
volumes:
- wordpress:/var/www/html
networks:
- app-network
webserver:
depends_on:
- wordpress
image: nginx:1.15.12-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --force-renewal -d your_domain -d www.your_domain
volumes:
certbot-etc:
wordpress:
dbdata:
networks:
app-network:
driver: bridge
Nachdem diese Änderungen vorgenommen wurden, können Sie nun den webserver
-Dienst mit aktivierter SSL-Unterstützung neu starten.
Neustart des Webserver-Dienstes
Speichern und schließen Sie die Datei, wenn Sie die Bearbeitung abgeschlossen haben.
Neuerstellen des Webserver-Dienstes
Erstellen Sie den webserver
-Dienst mit folgendem Befehl neu:
docker-compose up -d --force-recreate --no-deps webserver
Überprüfen des Dienststatus
Überprüfen Sie Ihre Dienste mit docker-compose ps
:
docker-compose ps
Die Ausgabe sollte anzeigen, dass die Dienste db
, wordpress
und webserver
ausgeführt werden:
Output
Name Command State Ports
----------------------------------------------------------------------------------------------
certbot certbot certonly --webroot ... Exit 0
db docker-entrypoint.sh --def ... Up 3306/tcp, 33060/tcp
webserver nginx -g daemon off; Up 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
wordpress docker-entrypoint.sh php-fpm Up 9000/tcp
Abschließen der WordPress-Installation
Nachdem Ihre Container ausgeführt werden, können Sie die WordPress-Installation über die Weboberfläche abschließen.
Besuchen Sie https://your_domain
in Ihrem Webbrowser und folgen Sie den Anweisungen auf dem Bildschirm, um WordPress einzurichten.
Schritt 6 – Abschließen der Installation über die Weboberfläche
Nachdem Ihre Container laufen, beenden Sie die Installation über die WordPress-Weboberfläche.
Zugriff auf die Installationsseite
Öffnen Sie in Ihrem Webbrowser die Domain Ihres Servers. Ersetzen Sie your_domain
durch Ihren eigenen Domainnamen:
https://your_domain
Wähle eine Sprache, die Sie verwenden möchten:
Einrichten von WordPress
Nachdem Sie auf Weiter geklickt haben, gelangen Sie zur Hauptseite der Einrichtung, auf der Sie einen Namen für Ihre Website und einen Benutzernamen festlegen müssen.
- Wählen Sie einen einprägsamen Benutzernamen (anstelle von „admin“).
- Verwenden Sie ein sicheres Passwort (WordPress generiert automatisch eines, oder Sie können Ihr eigenes erstellen).
- Geben Sie Ihre E-Mail-Adresse ein.
- Entscheiden Sie, ob Sie Suchmaschinen daran hindern möchten, Ihre Website zu indexieren.
Abschließen der Installation
Wenn Sie unten auf der Seite auf WordPress installieren klicken, gelangen Sie zur Anmeldeseite:
Nach der Anmeldung haben Sie Zugriff auf das WordPress-Administrations-Dashboard:
Nachdem Ihre WordPress-Installation abgeschlossen ist, können Sie Maßnahmen ergreifen, um sicherzustellen, dass Ihre SSL-Zertifikate automatisch erneuert werden.
Schritt 7 – Erneuern der Zertifikate
Let’s Encrypt-Zertifikate sind 90 Tage lang gültig. Um sicherzustellen, dass sie nicht ablaufen, können Sie einen automatisierten Erneuerungsprozess einrichten. Eine Möglichkeit besteht darin, eine Aufgabe mit dem cron
-Planungsdienst zu erstellen. Im folgenden Beispiel richten Sie einen Cron-Job ein, der regelmäßig ein Skript ausführt, um Ihre Zertifikate zu erneuern und die Nginx-Konfiguration neu zu laden.
Erstellen des SSL-Erneuerungsskripts
Öffnen Sie zunächst ein Skript mit dem Namen ssl_renew.sh
:
nano ssl_renew.sh
Fügen Sie dem Skript den folgenden Code hinzu, um Ihre Zertifikate zu erneuern und die Webserver-Konfiguration neu zu laden. Ersetzen Sie dabei sammy
durch Ihren eigenen nicht-root Benutzer:
#!/bin/bash
COMPOSE="/usr/local/bin/docker-compose --no-ansi"
DOCKER="/usr/bin/docker"
cd /home/sammy/wordpress/
$COMPOSE run certbot renew --dry-run && $COMPOSE kill -s SIGHUP webserver
$DOCKER system prune -af
Dieses Skript führt die folgenden Aktionen aus:
- Weist die
docker-compose
-Binärdatei einer Variablen namensCOMPOSE
zu und verwendet die Option--no-ansi
, um ANSI-Steuerzeichen zu deaktivieren. - Weist die
docker
-Binärdatei einer Variablen namensDOCKER
zu. - Wechselt in das Projektverzeichnis
~/wordpress
. - Führt die folgenden Docker-Befehle aus:
docker-compose run
: Startet einencertbot
-Container und überschreibt dessen Standardbefehl mitrenew --dry-run
, um eine Erneuerung zu simulieren.docker-compose kill
: Sendet einSIGHUP
-Signal an denwebserver
-Container, um die Nginx-Konfiguration neu zu laden.docker system prune
: Bereinigt nicht verwendete Container und Images.
Schließen Sie die Datei nach der Bearbeitung. Machen Sie sie mit folgendem Befehl ausführbar:
chmod +x ssl_renew.sh
Einrichten des Cron-Jobs
Öffnen Sie als Nächstes die crontab
-Datei des Root-Benutzers, um das Erneuerungsskript in einem festgelegten Intervall auszuführen:
sudo crontab -e
Falls Sie diese Datei zum ersten Mal bearbeiten, werden Sie aufgefordert, einen Editor auszuwählen:
Output
no crontab for root - using an empty one
Select an editor. To change later, run 'select-editor'.
1. /bin/nano <---- easiest
2. /usr/bin/vim.basic
3. /usr/bin/vim.tiny
4. /bin/ed
Choose 1-4 [1]:
Fügen Sie am Ende der Datei die folgende Zeile hinzu:
*/5 * * * * /home/sammy/wordpress/ssl_renew.sh >> /var/log/cron.log 2>&1
Dies setzt das Job-Intervall auf alle fünf Minuten für Testzwecke. Eine Protokolldatei cron.log
wird erstellt, um die Ausgaben des Jobs aufzuzeichnen.
Überprüfen des Cron-Jobs
Nach fünf Minuten können Sie die Datei cron.log
überprüfen, um festzustellen, ob die Erneuerungsanforderung erfolgreich war:
tail -f /var/log/cron.log
Die folgende Ausgabe bestätigt eine erfolgreiche Erneuerung:
Output
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
** DRY RUN: simulating 'certbot renew' close to cert expiry
** (The test certificates below have not been saved.)
Congratulations, all renewals succeeded. The following certs have been renewed:
/etc/letsencrypt/live/your_domain/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
** (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Beenden Sie die Ansicht, indem Sie CTRL+C in Ihrem Terminal eingeben.
Einrichten täglicher Erneuerungen
Sie können die crontab
-Datei so anpassen, dass das Skript täglich ausgeführt wird. Um das Skript jeden Tag um 12:00 Uhr mittags auszuführen, ändern Sie die letzte Zeile der Datei wie folgt:
0 12 * * * /home/sammy/wordpress/ssl_renew.sh >> /var/log/cron.log 2>&1
Entfernen Sie außerdem die Option --dry-run
aus Ihrem ssl_renew.sh
-Skript:
#!/bin/bash
COMPOSE="/usr/local/bin/docker-compose --no-ansi"
DOCKER="/usr/bin/docker"
cd /home/sammy/wordpress/
$COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver
$DOCKER system prune -af
Ihr Cron-Job stellt sicher, dass Ihre Let’s Encrypt-Zertifikate nicht ablaufen, indem er sie erneuert, sobald sie dafür berechtigt sind. Sie können außerdem die Protokollrotation mit dem Logrotate-Dienst auf Ubuntu 22.04 / 20.04 einrichten, um Ihre Protokolldateien zu rotieren und zu komprimieren.
Fazit
In diesem Tutorial haben Sie Docker Compose verwendet, um eine WordPress-Installation mit einem Nginx-Webserver zu erstellen. Im Rahmen dieses Workflows haben Sie:
- Dienste in
docker-compose.yml
definiert und eingerichtet. - Nginx als Webserver konfiguriert und TLS/SSL-Zertifikate mit Certbot erhalten.
- Eine automatisierte SSL-Zertifikatserneuerung mit einem Cron-Job eingerichtet.
Mit diesem Setup ist Ihre WordPress-Website über HTTPS gesichert, und Ihre SSL-Zertifikate werden automatisch erneuert, sodass die Sicherheit und Verfügbarkeit kontinuierlich gewährleistet ist.