Continuous Delivery mit Drupal und Docker

Continuous Delivery (CD) innerhalb eines Drupal-Projekts realisieren: in diesem Blogartikel beschreibe ich ein Setup, bei dem eine Drupal-Anwendungen als Docker-Container ausgeführt wird. Die Motivation hier hinter besteht darin, die Anwendung in verschiedenen Umgebungen laufen lassen zu können. Das wiederum ermöglicht, dass ein oder mehrere Entwickler unabhängig von der Produktivumgebung an der Anwendung arbeiten können. Somit kann außerdem eine Testumgebung bereitgestellt werden damit Stakeholder einen Eindruck von dem gewinnen können, was gerade entwickelt wird. Ziel des Setups ist es, dass die Anwendung in der Testumgebung immer aktuell ist, um Ergebnisse schnell präsentieren und Feedback einholen zu können.

Bei der Umsetzung CD für Drupal-Projekte zu implementieren, hat sich herausgestellt, dass dieses Vorhaben nicht ganz trivial ist. Deswegen möchte einige Ansätze teilen, die dem einen oder anderen von Nutzen sein könnten, der ähnliches vor hat. Außerdem möchte ich Herausforderungen beschreiben, auf die ich während des Aufsetzens gestoßen bin. Ich möchte jedoch nicht darauf eingehen, ob es generell eine gute Idee ist Drupal in einem Docker-Setup zu betreiben, da Drupal vielleicht nicht optimal hierfür geeignet ist. Es ist aber dennoch machbar und ich bin davon überzeugt, dass sich der Aufwand für viele Projekte rentiert.

Das Docker-Image

Der erste Schritt ist das Verpacken der Drupal-Anwendung in ein Docker-Image. Dieses Image wird in einem Dockerfile beschrieben, auf welches ich gleich noch genauer eingehen werde. Auf einem Event der Drupal-Usergroup in Köln hat Jens Schulze einen Vortrag zum Thema gehalten. Von ihm stammt auch die ein oder andere Zeile.

Als Ausgangsbasis für das Dockerfile dient das offizielle PHP-Image vom Dockerhub. Dieses bietet u.a. den Vorteil, dass sich recht komfortabel PHP-Extensions installieren und konfigurieren lassen. Hierfür stehen die Befehle docker-php-ext-install und docker-php-ext-configure bereit.

FROM php:7.3.2-apache 

# Install tooling, php and PECL extensions
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
    libpng-dev \
    libjpeg-dev \
    git \
    && rm -r /var/lib/apt/lists/* && \
    apt-get clean
RUN docker-php-ext-install \
    gd \
    pdo_mysql \
    mysqli \
    bcmath \
    zip \
    opcache
RUN docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr;

# Install app dependencies
ENV APP_ROOT="/var/www/html"
ENV COMPOSER_ALLOW_SUPERUSER=1
WORKDIR $APP_ROOT
COPY composer.json composer.lock load.environment.php .
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
RUN composer global require hirak/prestissimo && \
    composer install -vvv --no-autoloader && \
    composer clearcache \
    composer dump-autoload --optimize

# Copy app and configurations
COPY . .
COPY ./config/apache/000-default.conf /etc/apache2/sites-enabled
COPY ./config/apache/20-httpd.conf /etc/httpd/conf.d/20-httpd.conf
COPY ./config/php/php.ini /usr/local/etc/php/php.ini
COPY ./config/drupal/sync /my/config/sync
COPY ./scripts/docker /my/scripts

# Permissions for public user
RUN chown -R www-data:www-data ./web/sites/default/files

WORKDIR /my/scripts

Über Composer werden die Dependencies installiert. Dazu wird zunächst die composer.json in das Verzeichnis kopiert, in der die Anwendung später im Container landen soll: /var/ww/html. Dieses Verzeichnis ist das Root-Verzeichnis unserer Anwendung.

Über den Befehl COPY --from erspart man sich die Installation von Composer. Die Binaries werden vom offiziellen Composer Docker-Image einfach in unser Drupal-Image kopiert und wir können Composer verwenden. Um das Auflösen der Dependencies etwas zu beschleunigen, verwenden wir das Composer Plugin prestissimo. Dieser Schritt ist nämlich recht zeitintensiv und das Downloaden wird hierdurch parallelisiert.

Anschließend werden alle Dateien aus dem Build-Context in das App-Root Verzeichnis kopiert. Wenn man das Projekt auch lokal installiert hat, würde auch das vendor Verzeichnis mit kopiert werden. Um dies zu verhindern, kann das Verzeichnis in der .dockerignore vom Build-Kontext ausgeschlossen werden. Außerdem kann landen auch Abhängigkeiten in web/modules/contrib, die wir ignorieren möchten, damit die Installation via Composer in den vorherigen Schritten im Dockerfile nicht überschrieben wird.

vendor
web/modules/contrib

Zusätzlich sollte man auch .git oder andere Version-Control-Verzeichnisse hier hinzufügen, um den Build-Prozess des Docker-Images nicht unnötig zu verlangsamen.

Zusammenfassung

In diesem Teil haben wir detailliert beschrieben, wie ein Dockerfile für Drupal eigens aufgesetzt werden kann. Weiter geht es mit dem Thema Konfigurationssynchronisation und Automatisierung mit Gitlab CI.