Drupal-Konfiguration managen und ausrollen

In diesem Beitrag will ich auf die Möglichkeiten des Konfigurations-Managements in Drupal 7 eingehen und zeigen, wie wir die Herausforderungen konkret in Drupal-Projekten handhaben.

Erstaunlicherweise widmen viele diesem Aspekt der Anwendungsentwicklung viel zu wenig Aufmerksamkeit und verlieren dadurch Zeit und Nerven.

Warum Konfigurations-Management?

In Applikationen unterscheiden wir zwischen "Inhalt" und "Konfiguration". Dabei ist der Inhalt meist das, was der Endanwender eigenständig anlegen kann - ohne dabei die Funktionalität der Seite grundlegend zu ändern. Die Konfiguration dagegen ist im Regelfall im Livebetrieb stets konstant und bildet die Grundlage der Anwendung.

Keine Konfiguration in der Datenbank

In Drupal 7 und vorherigen Versionen gibt es im Kernsystem keine wirkliche Möglichkeit, Einstellungen verlässlich zu verwalten und zu versionieren, da diese standardmäßig immer in der Datenbank gespeichert werden.

Konfiguration ausschließlich in der Datenbank zu speichern bedeutet, dass Änderungen an der Konfiguration nicht nachvollziehbar zu machen sind. Wer hat wann und warum die Einstellung geändert? Diese Frage müssen wir beantworten können. Die Konfiguration sollte also so abgespeichert werden, dass Änderungen nachvollziehbar werden und man im Problemfall auf eine vorherige Konfiguration zurückkehren kann.

Inhalte und Konfiguration in nur einem Medium, der großen Datenbank, abzulegen, macht es kaum möglich einen Überblick über die Änderungen zu erhalten, die im Laufe der Projektentwicklung anfallen. Versionierung von Datenbanken ist zwar möglich, aber für Konfigurationsänderungen viel zu unübersichtlich. Auch das Einspielen von Änderungen aus einer Versionsverwaltung ist praktisch unmöglich, da sich die Datenbank-Inhalte im Live-Betrieb ständig ändern und so das Einspielen einer lokalen Entwicklungsdatenbank ins Live-System für Datenverlust sorgen würde.

Aus diesem Grund versucht man Konfiguration stets im Code statt der Datenbank festzuhalten. Dieser Code stellt dann die Konfiguration für die jeweiligen Komponenten und Funktionen der Anwendung dar.

Durch die Ablage im Code kann Konfiguration besser (über ein Versionskontrollsystem) verteilt werden, was die Arbeit mit mehreren Entwicklern gleichzeitig im Projekt effektiv gestaltet.

Es gibt Möglichkeiten in Drupal 7

Auch wenn es im Drupal 7 Kernsystem keine Möglichkeit zum Konfigurations-Management gibt, hat die Drupal-Community ein paar Lösungen erarbeitet, die uns das Leben leichter machen.

Zusatzmodule wie Features und Strongarm ermöglichen es, Konfiguration einzelner Komponenten in Form von Pseudo-Modulen abzuspeichern. Drupal greift dann auf die Konfiguration im Code zu, statt in der Datenbank danach zu suchen. Diese Module sind Code und können deshalb einfach in der Versionsverwaltung abgelegt werden.

In unseren Deployment-Mechanismen müssen wir notgedrungen auf mehrere verschiedene Methoden setzen, um die komplette Konfiguration vom lokalen Entwicklungssystem auf die Testinstallation und das Livesystem auszurollen.

Im Nachfolgenden stelle ich die Komponenten im Einzelnen vor und erläutere am Schluss auch, wie wir diese nun bei undpaul im Einsatz haben.

Das Kernsystem an sich bietet eigentlich nur zwei sehr einfache Mechanismen, ein System über "Code" zu konfigurieren, die settings.php und die update-Hooks.

Drupals Konfigurations-Datei settings.php

In der Datei *settings.php* werden bereits bei der Installation Daten für relevante Teile des Systems, wie etwa die Datenbankverbindung, abgefragt. Bei manchen Modulen können hiermit bereits Einstellungen an der Konfiguration getätigt werden, indem hier Variablen gesetzt werden, die das Verhalten des Moduls bestimmen. Beispiele hierfür sind die Module Memcache, Environment Indicator oder Stage File Proxy.

settings.php für verschiedene Umgebungen

Man kann für verschiedene Entwicklungsumgebungen eine eigene settings.php vorhalten. Wir machen das konkret so, dass wir für jede Entwicklungsumgebung eine eigene settings.NAME.php erstellen und diese auf dem jeweiligen Server per Symlink als settings.php einsetzen. So arbeiten die Entwickler lokal mit einer settings.local.php, die Entwicklungsinstallation erhält die settings.develop.php, Staging settings.stage.php und die Live-Seite settings.live.php. Diese Dateien liegen alle im git-Repository, Einstellungen für die Live-Umgebung können also auch schon während der Entwicklung vorbereitet werden.

Um die lokalen Eigenheiten des Entwicklersystems noch berücksichtigen zu können, ohne dass der einzelne Entwickler seine Änderungen ins Git-Repository einspielt, halten wir noch die Möglichkeit über ein Snippet in der settings.local.php vor, dass der Entwickler lokal eine local.settings.php anlegt in der er z.B. seine lokalen Datenbankdaten oder Solr-Server-Settings einpflegt. Die settings.local.php ist also im Prinzip erst mal nur eine Kopie der default.settings.php, die noch diese Zeile enthält: [gist:5848755]

Die local.settings.php wird vom Entwickler angelegt und enthält lediglich Datenbankverbindungsdaten und eventuelle Eigenheiten, hier ein Beispiel: [gist:5848762]

.htaccess

Ähnlich verfahren wir auch mit der .htaccess-Datei. So kann die .htaccess.stage einen Passwortschutz enthalten. Wird der Passwortschutz in eine allgemeine .htaccess geschrieben, haben alle mit der Datei zu tun und müssen sie evtl. lokal bearbeiten, um den Schutz zu entfernen. Diese Vorgehensweise wäre zu fehleranfällig, genau wie der Ansatz, die .htaccess nicht zu versionieren.

Features macht vieles einfacher

Mit der Einführung des Features-Moduls wurde vieles einfacher. Das Modul ermöglicht es, Systemkomponenten (die auch von anderen Modulen definiert werden können) in Form von php-Code innerhalb eines Pseudo-Modules zu speichern.

Das Features-Modul kontrolliert hierbei das Einspielen des php-Codes in die Datenbank. Somit ist es über revert- und rebuild-Mechanismen möglich, die Konfiguration aus dem php-Code einzulesen und damit Datenbankeinträge zu überschreiben. Über die Export- oder Features-Update-Funktion können Konfigurationen, die in der Datenbank gespeichert sind, wieder in php-Code in die (vielleicht bereits vorhandenen) Pseudo-Module exportiert werden.

Über eine GUI lässt sich im Drupal-Backend dabei auch der Status der "Überschreibungen" einsehen und mit Hilfe des Moduls diff sogar zeilenweise die Konfiguration im Code mit der in der Datenbank vergleichen.

Jedes Modul kann Komponenten für Features bereitstellen, um bestimmte Konfigurationen zu exportieren. Strongarm bietet zum Beispiel die Möglichkeit Variablen (aus der {variable}-Tabelle) zu exportieren. Das deckt somit die Konfiguration vieler Module ab, die hauptsächlich auf diese Weise Konfiguration abspeichern. CTools und die Entity API bieten hingegen für Entwickler die Tools, eigene Konfigurationsobjekte mit integrierter Features-Implementierung zu kreieren.

Zusatzmodule bringen also oft schon Features-Implementierung mit. Wenn nicht, gibt es dazu meist ein eigenes Projekt oder ein Sandbox-Modul auf drupal.org. Dazu einfach mal auf drupal.org "[modulname] AND features" in die Suchmaske eingeben.

Bei den verfügbaren Komponenten ist man aber natürlich auf die Community oder die eigenen Programmierfähigkeiten beschränkt. Es gibt leider noch nicht für alle Zusatz-Module eine Features-Implementierung, aber die wichtigsten sind inzwischen gut abgedeckt.

Die Drupal install- und update-Hooks

Ein weiterer Core-Mechanismus sind die Drupal install- und update-Hooks. Diese Hooks werden einmalig beim Installieren oder Aktualisieren der Module ausgeführt und stellen somit eine Möglichkeit dar, einmalig Änderungen am System vorzunehmen.

Das Problem hierbei besteht allerdings darin, dass während des Update-Prozesses nicht der komplette Drupal-Bootstrap zur Verfügung steht und somit die API der meisten Module nicht oder nicht vollständig zur Verfügung steht.

Der hook_update_N() eignet sich daher nur für Low-Level-Aktionen, wie das direkte Ändern von Datenbankeinträgen. Ein Zugriff auf node_load() oder anderen umfangreichen API-Mechanismen ist mit Vorsicht zu genießen und verursacht meist Probleme oder benötigt ein umständliches Einbinden diverser Moduldateien.

Drush-Skript als Lösung

Die wenigen Mechanismen, die das Drupal-Kernsystem bietet, reichen also nicht komplett aus. Die Möglichkeiten, die Drupal-Zusatzmodule und zusätzlicher Code eröffnen, lassen allerdings in Verbindung mit Drush doch ein gutes Konfigurations-Management zu. Mit der Drupal-Shell können nicht nur Befehle wie "drush update-db" oder "drush feature-revert-all" ausgeführt werden, auch können über den "hook_drush_command()" eigene Drush-Befehle erstellt werden, die im Projekt nützlich zum Ausrollen von Konfiguration eingesetzt werden können. Der Einsatz von Drush hat hierbei einen großen Vorteil gegenüber Update-Scripts, die nur den "update.php"-Mechanismus des Kerns verwenden. In Drush-Skripten steht immer der komplette Drupal-Bootstrap zur Vefügung und damit die komplette Drupal API. Auf diese Weise werden Fehler in der Interaktion mit anderen Modulen oder Drittanbieter-Applikationen vermieden, das Ausrollen einer Drupal-Anwendung ist wesentlich robuster. Ein Beispiel zur Benutzung der Update-Skripte sind in unserem Blog-Beitrag "Drupal-Entwicklung mit Drush- und Shell-Skripten vereinfachen" zu finden.

Ausblick 

Für Drupal 8 wird mit einem zentralen Ansatz im Kern (Configuration Management Initiative) einiges verbessert, was uns nach Erscheinen von Drupal 8 in den nächsten Jahren hoffentlich viel Zeit und Nerven erspart.

In den nächsten Wochen werde ich hier im Blog noch ein paar Beispiele und Hilfsmittel präsentieren, wie wir sie in Projekten einsetzen, dazu zählen unter anderem unser shellwrapper und das Master-Modul.