Dockerize Drupal 9
Der heutige Blogartikel wird etwas technischer als gewöhnlich, denn heute geht’s darum, Drupal 9 als Container-Anwendung bereitzustellen. Der Artikel gilt für Drupal 9 wie auch für Drupal 8.
Wozu das Ganze?
Es gibt mehrere Vorteile, wenn Anwendungen in Docker-Containern laufen. Hier mal die 3, die mich überzeugt haben:
- Auf einem System können gleichzeitig mehrere Systeme mit unterschiedlichen Dependencies laufen. Ich kann also gleichzeitig eine PHP5 und eine PHP7 Applikation laufen lassen!
- Entwicklungsumgebungen können automatisiert konfiguriert und hochgefahren werden.
- Wir sind dem Hosting auf Kubernetes einen Schritt näher.
Im folgenden Beitrag erkläre ich, wie du eine Drupal 9 Applikation als Docker Image bereitstellen kannst. Auf die Basics zu Docker, und was das eigentlich ist, werde ich in diesem Beitrag nicht näher eingehen. Sollten dir noch jegliche Grundlagen zu Docker fehlen, empfehle ich zuerst den Artikel “Let’s dockerize everything” zu lesen, und natürlich auch die offizielle Docker Website zu durchforsten.
Auch zu Kubernetes werde ich in diesem Beitrag nichts erklären. Falls du dennoch schon neugierig bist, kannst du schon mal durch die Kubernetes Dokumentation surfen, um dir einen Überblick zu verschaffen! 🙂
Docker-Image erstellen
Ein Docker-Image zu erstellen ist recht simple. Alles was man dafür benötigt ist ein Dockerfile. Meistens wird dies im Projekt-Root abgelegt.
Wie auf der Dockerfile reference erwähnt wird, muss jedes Dockerfile ein FROM Statement enthalten. Dieses Statement gibt das Parent-Image an, welches als Basis verwendet wird. Das kann beispielsweise ein blankes Debian Betriebssystem sein, oder eines mit diversen vorinstallierten Programmen. Auf Docker Hub stehen zig Images zur Auswahl (heute: 4.394.740) und man kann jedes einzelne erweitern.
Wir verwenden das offizielle PHP Image für unsere PHP Projekte. Dieses bietet eine Vielzahl an verschiedenen Tags für unterschiedliche Anwendungsbereiche an. Für unsere Drupal Projekte verwenden wir php:7.4-apache, da in diesem Image bereits ein Apache2 Webserver vorinstalliert ist.
Ein minimales, funktionales Dockerfile würde also folgendermaßen aussehen:
FROM php:7.4-apache
In der Praxis sieht man das so allerdings nicht, weil das Image dem parent Image exakt gleicht — das wäre natürlich sinnlos 😉
Hier fehlen noch die projektspezifischen Dependencies! Für Drupal wird man beispielsweise weitere PHP Extensions installieren müssen.
Drupal Base-Image
Wenn es darum geht, mehrere Drupal Projekte gleichzeitig zu verwalten, ergibt es definitiv Sinn, ein eigenes Base-Image dafür zu erstellen. Das Drupal Base-Image erweitert php:7.4-apache um weitere Dependencies, benutzerdefinierte Scripte, und alles weitere, was in Drupal-Projekten so benötigt wird. In meinem Fall ist das Folgendes:
- Programme (zip, unzip, zsh, oh-my-zsh, memcached, …)
- PHP-Extensions (pdo_mysql, gd, opcache, memcached, …)
- Custom Scripts
Hier findest du ein simplifiziertes Dockerfile (und ein paar Dateien, welche darin referenziert werden) auf Github: https://gist.github.com/dsteindl/bc7295c9cc6612813a538c026efa0e14
Jeder Schritt ist in einem Kommentar erklärt.
Wie bereits erwähnt, handelt es sich dabei um eine vereinfachte Version eines Base-Images. Das Beispiel soll lediglich veranschaulichen, wie einfach es ist, Dependencies in ein Docker-Image zu laden. In einem tatsächlichen Projekt sollte es so nicht verwendet werden, da auf wichtige Komponenten einer Drupal Umgebung nicht eingegangen wurde: Filesystem Security, Konfigurationsmanagement, Multi-Environment Setup, etc.
Sobald du dein eigenes Base-Image erstellt hast, muss es in eine Docker-Registry deiner Wahl gepusht werden. Dockerhub (Docker’s hauseigene Registry) ist die wohl bekannteste Alternative.
Achtung: zum Erstellen privater Repositories benötigt man auf Dockerhub einen Pro Account! Sollte dein Projekt heikle Business-Logik enthalten, bitte unbedingt darauf achten.
Der Einfachheit halber werde ich in diesem Artikel auch weiterhin von Dockerhub sprechen – du kannst das aber natürlich wie gesagt durch die Docker-Registry deiner Wahl ersetzen 🙂
Projektspezifisches Image
Da nun ein Base-Image auf Dockerhub liegt, kann pro Projekt ganz einfach ein Image erstellt werden.
Das projektspezifische Image erbt nun vom vorher erstellten Drupal Base-Image, wodurch alle Drupal Dependencies automatisch im Image geladen sind.
Hier ein Beispiel Dockerfile:
# FROM <your-repo>/<your-image>:<your-tag>
# example:
FROM myrepo/drupal:php74-apache
# Copy source code
COPY . /var/www/html
# install composer dependencies dependencies
RUN composer install --optimize-autoloader --no-dev
Dieses Dockerfile lädt nun alle Drupal Dependencies vom Base-Image, kopiert das komplette Drupal Projekt in den Ordner /var/www/html und führt ein composer install aus, um die projektspezifischen Composer-Dependencies zu laden.
Das war’s auch schon. Das Drupal-Image ist vorbereitet und kann gestartet werden!
Uploads
Ein wichtiger Punkt, um den man sich bei der Dockerisierung von Drupal kümmern muss (und das trifft auch auf viele andere Systeme zu), sind alle Daten, die permanent gespeichert bleiben sollen. Im Falle eines CMS: die Uploads.
Docker Container – und somit auch alle Daten im Container – sind grundsätzlich nicht permanent gespeichert. Sie existieren nur solange der Container läuft!
Daher müssen wir uns darum kümmern, dass Uploads, und jegliche anderen Files, die permanent gespeichert werden sollen, nicht nur im Container, sondern auch auf dem Host-System (oder einem anderen permanenten Speichermedium) gespeichert werden.
Natürlich hat Docker auch dafür eine Lösung parat: Persistent Volumes!
Persistent Volumes werden beim Starten eines Docker-Containers in den Container gemountet. Man kann beispielsweise einen Ordner des Host-Systems als Volume bereitstellen und dieses dann in den gewünschten Pfad im Container mounten. Wenn nun Dateien innerhalb des Containers in dem gemounteten Ordner erstellt werden, werden diese auch in den Ordner am Host-System gespiegelt!
Bei Drupal wäre das z.B. der public files Ordner:
./web/sites/default/files
Hier werden alle Uploads gespeichert.
Die Datenbank
Wie wir wissen benötigt Drupal eine Datenbank. Dafür kann ebenfalls ein Docker Container gestartet werden. Das ist auch ganz einfach – es gibt für beinahe alle gängigen Datenbanken ein offizielles Docker-Image. Einfach auf Dockerhub nach der gewünschten Datenbank suchen (Bsp.: mariadb, postgres, mysql, …) und die offiziellen Docker-Images verwenden.
Wir verwenden für unsere lokalen Entwicklungsumgebungen meist das Bitnami MariaDB Image und sind damit sehr zufrieden.
Compose
Wenn man einen Docker Container auf seinem Rechner starten möchte, muss man je nach Image Folgendes bedenken:
- Ports freigeben
- Environment Variablen konfigurieren
- Volumes mounten,
- … weitere projektspezifische Konfigurationen
Es ist natürlich möglich, all diese Parameter dem klassischen docker run Befehl zu übergeben, jedoch kann der Command schnell sehr lang werden. Sobald Kubernetes ins Spiel kommt, werden alle konfigurierbaren Werte als Environment Variablen übergeben.
Das sieht dann schnell mal so aus:
docker run \
-p 80:80 \
-v ./web/sites/default/files:/var/www/html/web/sites/default/files:delegated \
-v ./files/private:/var/www/html/files/private \
-e DATABASE_HOST=db-host \
-e DATABASE_USERNAME=db-username \
-e DATABASE_PASSWORD=db-password \
-e DATABASE_NAME=drupal-example \
-t drupal-example \
- ... \
.
Und hier kommt docker-compose zum Einsatz!
Durch docker-compose können Docker Services ganz einfach als YAML Files konfiguriert werden. Dadurch ist die Konfiguration der Services schön strukturiert in einem File. Yeah!
Ich werde hier nicht auf die Syntax der docker-compose Files eingehen. Bei Interesse findest du in der Docker Dokumentation mehr dazu.
Beim Drupal Beispiel werden 2 Services, also 2 Container benötigt:
- MariaDB
- Drupal 9
Hier ein Beispiel docker-compose.yml:
Das compose-File ist um einiges schöner zu lesen und kann deutlich einfacher konfiguriert werden im Vergleich zur Übergabe der Options im Terminal.
Will man die konfigurierten Container starten, muss man lediglich Folgendes ausführen:
docker-compose up
Wenn man die Images neu builden möchte, geht das auch ganz einfach:
docker-compose build
Fazit
Das wars auch schon! Eigentlich ist’s gar nicht so kompliziert, ein Drupal 9 Docker Image zu erstellen, oder?
Interessiert an weiteren technischen Artikeln zu Drupal 8/9?
Es gäbe noch eine Menge zu erzählen – unter anderem:
- Filesystem Security
- Must-have Drupal Module
- Konfigurationsmanagement
- Kubernetes Deployment
- Kubernetes Deployment automatisieren
- Multi-Environment System (dev, staging, fqa, production)
Du willst mit jemanden über das Thema plaudern?
Einen kostenlosen Termin mit CEO Susanne vereinbaren!AI-Driven UX - Möglichkeiten, Design-Prinzipien und Pflichten für UX-Designer - 2024 Update
UPDATE 2024: Ausgegraben aus 2019 dieses schmucke Fundstück über AI und UX. Irgendwie drehen sich die Trend-Themen doch alle Jahre im Kreis und man könnte glauben man findet sich diesbezüglich als Bill Murray in "Täglich grüßt das Murmeltier [...]
Jetzt lesenFolge #62 mit Susanne Liechtenecker
In Folge 62 besinnt sich Susanne auf die Anfänge dieses Podcasts und begrüßt keinen Gast, sondern erzählt über das Buch "Jäger, Hirten, Kritiker" von Richard David Precht und warum es sie inspiriert hat.
Jetzt anhören