Drupal Search API: Einen zweiten Solr-Core unter DDEV einrichten

Kürzlich hatte ich die Aufgabe, auf einer Drupal-Seite einen zweiten Solr-Core einzurichten. Da ich mich damit doch etwas schwer getan habe, wollte ich den Weg hier mal aufschreiben. Vielleicht hilft es ja mal jemandem.

Das Szenario

Die Drupal-Seite nutzt Drupal 9.3.14 und die Module „Search API“ und „Search API Solr“, um eine Suche über den Seiteninhalt anzubieten. Das ist alles nicht zu schwer einzurichten. Lokal arbeite ich mit der Entwicklungsumgebung DDEV. Hier kann man seit DDEV v1.19.0 Solr einfach so installieren:

ddev get drud/ddev-drupal9-solr

Voilá, ein Solr-Container läuft. Im Backend konfigurieren, Inhalte indexieren und eine Such-View einrichten. Die Details sollen hier heute nicht Thema sein.

Wozu braucht man nun einen zweiten Solr-Core (ein Core ist gewissermaßen die Datenbank, welche die indexierten Daten klammert). In diesem Fall gab es ein HTML-Archiv der alten Webseite, welches weiterhin durchsuchbar sein soll. Im Suchergebnis sollen die HTML-Seiten direkt über den Webserver aufgerufen werden. Die Seiten sind also nicht Teil der Drupal-Datenbank, sondern liegen einfach in einem Verzeichnis, welches über den Webserver zugänglich ist.

Hinweis für Windows-Benutzer: Ich arbeite mit WSL2, welches mir unter Windows eine Ubuntu-Umgebung ermöglicht. Das ist sehr cool und funktioniert generell sehr gut. Wichtig ist hier aus meiner Sicht, keine Windows-Abkürzungen zu nehmen, sondern lieber aus Ubuntu heraus mit der Kommandozeile zu arbeiten. Man kann z.B. auf den Inhalt eines Docker-Containers auch über eine Netzwerk-Adresse im Windows Explorer zugreifen. Dateien zu verschieben und anzulegen ist damit ggf. komfortabler, aber man kriegt dann sehr schnell Ärger mit falschen Datei-Berechtigungen. Also lieber den beschriebenen Weg so gehen, als würde man direkt unter Linux oder Mac arbeiten.

Der IST-Stand

Die Solr-Installation hat im “.ddev“-Projektordner den Unterordner „solr“ angelegt. Hier gibt es den „conf“-Ordner mit Dateien zur Konfiguration des Solr-Cores und den Ordner „docker-entrypoint-initdb.d“ mit dem Script „solr-configupdate.sh“.

Die Datei „docker-compose.solr.yaml“ regelt, wie der Solr-Container funktioniert. Hier sind drei Sachen wichtig:

  1. Unter „volumes“ wird das conf-Verzeichnis in den Container gemountet. Die Dateien stehen damit im Container zur Verfügung, tun für sich aber erst mal noch nichts.
  2. Der „docker-entrypoint-initdb.d“-Ordner wird ebenfalls so gemountet, dass das Script beim Start des Containers läuft. Es kopiert dann jeweils die Conf-Dateien an den richtigen Ort im Container. Änderungen der Konfiguration im laufenden Betrieb werden also beim Container-Start überschrieben.
  3. Es gibt einen Entrypoint, der beim Start des Containers läuft. Dieser legt einen Core namens „dev“ an, falls er nicht existiert, und startet dann Solr.

Der Entrypoint sieht so aus:

entrypoint: [ "sh", "-c", "docker-entrypoint.sh solr-precreate dev /solr-conf" ]

Der erste Core heißt standardmäßig „dev“. Wenn ihr ihn anders genannt habt, müsst ihr in allen folgenden Code-Beispielen „dev“ dagegen austauschen.

Das Vorgehen

Prinzipiell kann man auf zwei Arten vorgehen: Einen zweiten Solr-Container hochfahren oder in dem bestehenden Container einen zweiten Core ergänzen. Beides geht und ich habe nicht geprüft, was z.B. ressourcenschonender ist. Ich finde es aber sinnvoller, im vorhandenen Container einen zweiten Core zu ergänzen. Dafür muss man die Ordnerstruktur ein bisschen anpassen und außerdem dafür sorgen, dass DDEV beim Starten des Containers den neuen Core auch anlegt.

Schritt 1: Ordnerstruktur vorbereiten

In meinem Fall benötigt der zweite Core eine angepasste „schema.xml“-Datei. Deswegen braucht der neue Core sein eigenes Set aus Config-Dateien. Wir fügen also eine Ordnerebene ein:

  • Umbenennen von .ddev/solr/conf zu .ddev/solr/conf-core1
  • Neuen Unterordner conf darin anlegen und die Dateien hineinschieben (es muss ein Ordner namens conf vorhanden sein, sonst klappt eines der Scripte nicht, deswegen der eigentlich überflüssige Unterordner)
  • Neuen Ordner .ddev/solr/conf-core2/conf anlegen und die Conf-Dateien hinein kopieren

Schritt 2: Konfiguration für die neuen Verzeichnisse anpassen

Wir bearbeiten nun die Datei .ddev/docker-compose.solr.yaml und suchen darin diese Zeile in der „services > solr > volumes“-Sektion:

- ./solr:/solr-conf

Diese Zeile ersetzen wir durch:

# This mounts the conf in .ddev/solr/conf-core1 into the container where
# the solr-precreate command in the entrypoint uses it as a one-time
# configuration to copy config into the newly-created core. The script
# solr-configupdate.sh also uses it on startup to synch the conf files.
- ./solr/conf-core1:/solr-core1-conf

# The same for .ddev/solr/conf-core2.
- ./solr/conf-core2:/solr-core2-conf

Ein paar Zeilen darunter ist die oben schon zitierte Entrypoint-Zeile, welche dafür sorgt, dass beim Start des Containers ein Core angelegt wird, wenn er nicht schon existiert. Herauszufinden, wie hier die Syntax lautet, war für mich das Hauptproblem! Der Trick lag am Ende darin, den Unterschied zwischen „precreate-core“ und „solr-precreate“ zu sehen. Letzteres Script startet nämlich auch gleich Solr, und alles, was man dann noch danach ausführen möchte, passiert nicht mehr. Innen drin wird „precreate-core“ verwendet, und das können wir ganz einfach nutzen, um den ersten Core anzulegen. Der zweite Core wird dann mit dem normalen Script erzeugt und startet dann also auch Solr. Für drei Cores wäre es also „precreate-core && precreate-core && solr-precreate“.

Hier muss nun also stehen:

entrypoint: [ "sh", "-c", "docker-entrypoint.sh precreate-core dev /solr-core1-conf && solr-precreate core2 /solr-core2-conf" ]

Dann müssen wir noch die Datei .ddev/solr/docker-entrypoint-initdb.d/solr-configupdate.sh bearbeiten und den Inhalt wie folgt ändern. Dieses Script kopiert bei jedem Start des Containers die Conf-Dateien hinein und soll das jetzt für beide Cores machen.

#!/usr/bin/env bash
set -e

# Ensure "core1" core config is always up to date even after the
# core has been created. This does not execute the first time,
# when solr-precreate has not yet run.
CORENAME=dev
if [ -d /var/solr/data/${CORENAME}/conf ]; then
    cp /solr-core1-conf/conf/* /var/solr/data/${CORENAME}/conf
fi

# Ensure "core2" core config is always up to date even after the
# core has been created. This does not execute the first time,
# when solr-precreate has not yet run.
CORENAME=core2
if [ -d /var/solr/data/${CORENAME}/conf ]; then
    cp /solr-core2-conf/conf/* /var/solr/data/${CORENAME}/conf
fi

Schritt 3: Die Dateien indexieren

Nach einem ddev restart kann man nun die Solr-Adminseite im Browser aufrufen. Hier sollten nun zwei Cores zur Auswahl stehen (links über das „Core Selector“-Auswahlfeld). Der zweite Core ist zwar vorhanden, aber leer. Da die Dateien des HTML-Archivs nicht in Drupal vorliegen, können wir sie auch nicht über Drupal indexieren. Da das HTML-Archiv sich nicht mehr ändert, reicht es, die Indexierung einmal manuell auszuführen.

  • Wir legen einen neuen Ordner an und packen die HTML-Dateien hinein. Zum Testen kann man z.B. ein paar gespeicherte Wikipedia-Seiten benutzen.
  • Im Produktivsystem müssen die Dateien so abgelegt werden, dass sie über den Webserver ausgeliefert werden können. Zum reinen Testen der Suche ist das aber nicht unbedingt nötig.
  • Für die Indexierung müssen die Dateien aber im Solr-Container zur Verfügung stehen. In Schritt 2 hatten wir in .ddev/docker-compose.solr.yaml die neuen Conf-Verzeichnisse gemounted. Darunter ergänzen wir nun auch das neue Verzeichnis für die HTML-Dateien:
      # This mounts the HTML directory into the container, so that the files can be indexed.
      - ./[path to your new directory, relative to the location of this file]:/var/data/html_archive
  • DDEV noch einmal neustarten mit ddev restart, damit sich die Konfigurationsänderungen auswirken.
  • Dann verbinden wir uns mit dem Solr-Container. Über docker ps finden wir den Hash des Containers heraus (am Anfang der Zeile, z.B. „d1aed526c94e“) und rufen dann auf: docker exec -it -u root d1aed526c94e bash
  • Jetzt sind wir im Innern des Containers. Wir können ins Verzeichnis /var/data/html_archive wechseln und sicherstellen, dass die HTML-Dateien dort sichtbar sind.
  • Die Indexierung geht dann einfach über das folgende Kommando. Danach mit „exit“ den Container verlassen.
post -c html -filetypes html /var/data/html_archive

Schritt 4: Die Suche testen

Jetzt bleibt noch, die Suche zu testen. Als erstes geht das auf der Solr-Adminseite. Dort müsste man in dem zweiten Core unter „Query“ nun Ergebnisse sehen, wenn man die Suche nach „*:*“ ausführt.

Danach bleibt noch, in Drupal auf der „Search-API“-Seite einen zweiten Solr-Server mit dem zweiten Core-Namen anzulegen. Der Server sollte dann als Status „Aktiviert“ anzeigen. Dazu dann noch einen zweiten Index anlegen und diesen in den Optionen auf „Nur Lesen“ stellen. Nicht irritieren lassen: Drupal kann hier den Status der Indexierung nicht korrekt anzeigen, da Drupal ja gar nicht weiß, was hier indexiert werden soll. Egal ob hier 0% oder 100% indexiert angezeigt wird, die Info stimmt im Fall des Falles nicht, und man kann über die Indexseite auch keine Änderungen am Index anstoßen. Aber wie gesagt, die zu indexierenden Archivdaten ändern sich ja nicht.

Letzter Schritt: Eine eigene Seite für die Archivsuche einrichten, analog zur Haupt-Suchseite. In der eigentlichen Seitensuche werden die Archivdaten nicht mit angezeigt und in der Archivsuche werden nur Ergebnisse aus dem HTMK-Archiv angezeigt.

Fazit

Das Ergänzen eines zweiten Cores war eigentlich nicht so schwer, und prinzipiell war auch klar, wie es gehen muss. Die meiste Zeit ging dafür drauf, herauszufinden wie genau die Syntax für das Entrypoint-Script lautet, damit beim Starten des Containers automatisch der zweite Core angelegt wird. Wenn man nur eine Installation hat, kann man das ggf. auch manuell machen, aber schöner ist es wenn es automatisch passiert (lokal, auf Staging, in Produktion…).

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Bitte beachte die Kommentarregeln: 1) Kein Spam, und bitte höflich bleiben. 2) Ins Namensfeld gehört ein Name. Gerne ein Pseudonym, aber bitte keine Keywords. 3) Keine kommerziellen Links, außer es hat Bezug zum Beitrag. mehr Details...

So, noch mal kurz drüber schauen und dann nichts wie ab damit. Vielen Dank fürs Kommentieren! :-)