Artikel

Ein Laden als einzelner Dienst statt drei Container

Ein gekauftes Shop-System kam als Verbund aus Anwendung, Datenbank und Zwischenspeicher. Wie daraus ein einzelner Dienst wurde — und die vier Dinge, die dabei geklemmt haben.

BlackZackBlackzack

1016 Wörter · 5 Min. Lesezeit

  • shop
  • pelican
  • infrastruktur

Für den Verkauf von Serverrängen und Zusatzinhalten habe ich ein fertiges Shop-System gekauft. Es kommt als Verbund aus drei Containern: die Anwendung, eine Datenbank, ein Zwischenspeicher.

Ich habe daraus einen einzelnen Dienst gemacht. Der Weg dorthin ist ein gutes Beispiel dafür, wie man fremde Software in den eigenen Betrieb holt, statt ihren mitzunehmen.

Warum das überhaupt geht

Der erste Schritt war nicht Technik, sondern Lesen. In der Standardkonfiguration des Herstellers — nicht in der Container-Fassung, sondern in der Anwendung selbst — stehen Werte, die etwas anderes sagen, als der Verbund vermuten lässt: eine Datei-basierte Datenbank, ein Zwischenspeicher über die Datenbank, eine Warteschlange über die Datenbank.

Mit anderen Worten: Die zusätzlichen Dienste sind eine Zugabe der Container-Fassung, keine Voraussetzung. Der Hersteller liefert außerdem ein Aktualisierungsskript mit, das nur die Sprachumgebung und den Paketmanager verlangt.

Diese fünf Minuten Lesen haben den ganzen Umbau ermöglicht. Wer nur die Container-Beschreibung liest, hält drei Dienste für zwingend.

Der Umbau

Ein eigenes Abbild, aufgesetzt auf das Anwendungsabbild des Herstellers — dort stecken die Sprachumgebung, alle Erweiterungen und die Abhängigkeiten bereits drin. Ergänzt wurden nur:

  • ein Benutzer mit den Nummern, die mein Panel erwartet
  • ein Startskript nach dem Vorbild der Standardabbilder
  • ein paar Werkzeuge, die im Betrieb hilfreich sind
  • eine Einstellung, die Fehlerausgaben im Webbereich abschaltet

Die vier Dinge, die geklemmt haben

Erstens: ohne Startskript tut das Panel nichts. Der Container lief, aber es passierte nichts. Das Panel übergibt den Startbefehl in einer Variablen und erwartet ein Skript im Abbild, das Platzhalter ersetzt und den Befehl ausführt. Ohne dieses Skript startet die Anwendung nie. Die Vorlage dafür holt man sich aus einem der Standardabbilder — der Inhalt ist überschaubar.

Zweitens: eine Migration verträgt sich nicht mit der Datei-Datenbank. Eine Schemaänderung entfernt ein Feld, ohne den zugehörigen eindeutigen Index zu lösen. Die große Datenbank räumt ihn stillschweigend mit weg; die Datei-Datenbank baut die Tabelle neu auf und bricht ab, weil der Index auf ein Feld zeigt, das es nicht mehr gibt. Behoben, indem der Index vorher gelöst wird — nur für diesen Datenbanktyp. Das Original liegt daneben.

Drittens: ein interaktives Werkzeug scheitert ohne Heimatverzeichnis. Eine Meldung über ein Verzeichnis, in das nicht geschrieben werden darf. Die Ursache ist eine fehlende Umgebungsvariable.

Viertens: eine Korrektur an der Sprachumgebung war im alten Verbund eingehängt und fehlte im neuen Container. Sofortige Folge: Fehlerseiten, die mit dem Statuscode für Erfolg ausgeliefert werden. Jetzt liegt sie im Abbild.

Der vierte Punkt ist der lehrreichste: Was in einem Verbund als Einhängung konfiguriert ist, verschwindet beim Umbau lautlos. Solche Kleinigkeiten stehen in keiner Dokumentation, weil sie zur Umgebung gehören und nicht zur Anwendung.

Der Datenumzug, geprüft

Der heikelste Teil, und ich habe ihn bewusst langsam gemacht: Jede Tabelle einzeln, vorher und nachher gezählt und verglichen. Zwanzig Tabellen mit Inhalt, gut hundertfünfzig Zeilen insgesamt.

Eine Beobachtung dabei, die zunächst nach Datenverlust aussah: Eine Zählung über das Anwendungsmodell lieferte einen anderen Wert als eine direkte Zählung auf der Tabelle. Ursache ist ein Filter im Modell, der bestimmte Einträge ausblendet — auf dem alten Stand galt derselbe Unterschied.

Genau deshalb zählt man vorher und nachher mit derselben Methode. Ein Unterschied zwischen zwei Zählweisen sagt nichts über einen Verlust aus.

Infobox

Der Dienst blieb im Panel trotzdem auf „startet" stehen. Das Panel erkennt einen erfolgreichen Start an bestimmten Ausgaben, und diese Anwendung meldet ihren Start mit einem anderen Wortlaut. Statt die gemeinsame Vorlage zu ändern, meldet der Startbefehl seine Bereitschaft jetzt selbst: eine Schleife fragt den eigenen Port ab und gibt erst nach der ersten erfolgreichen Antwort die erwartete Zeile aus. Damit steht der Zustand nie zu früh auf „läuft" — und ist ehrlicher als vorher, weil er tatsächlich die Erreichbarkeit prüft.

Das Update-Verfahren, das sich daraus ergeben hat

Nach zwei Aktualisierungen steht ein Ablauf, der sich bewährt hat. Er ist die eigentliche Antwort auf die Frage, wie man fremde Software langfristig betreibt.

Erst prüfen, ob das Abbild neu gebaut werden muss. Haben sich die Abhängigkeitsdateien geändert? Wenn nein, bleibt das Abbild — das spart den größten Teil der Arbeit.

Anwendungscode tauschen, Daten stehen lassen. Mit einer Ausnahmeliste, die alles schützt, was nicht aus dem Paket kommt: Konfiguration, Erweiterungen, Speicherbereiche, Datenbank, eigene Vergleichskopien.

Die Oberfläche neu bauen. Der Teil, den viele Pakete nicht mitliefern — er entsteht erst beim Bauen. Ohne diesen Schritt bleibt das Aussehen auf dem alten Stand, obwohl der Code neu ist.

Eigene Eingriffe wieder einarbeiten. Bei mir sind das eine Handvoll Stellen, die jedes Update im Auslieferungszustand zurückbringt. Sie liegen einzeln gesichert daneben, damit das Zurückspielen ein Kopiervorgang ist.

Danach prüfen — zweimal. Der zweite Aufruf ist wichtiger als der erste, weil manche Fehler erst beim zweiten Mal auftreten.

Der Fehler, den man nur beim zweiten Aufruf sieht

Genau dieser letzte Punkt hat mir einen Fehler gezeigt, den ich sonst nie gefunden hätte.

Eine Funktion zählte Aufrufe pro Artikel und Tag. Sie suchte den Eintrag für heute und legte ihn an, wenn er fehlte. Der Haken: Gesucht wurde nach einem Datum, geschrieben wurde ein Datum mit Uhrzeit — und die Datei-Datenbank speichert wörtlich, während das größere System das stillschweigend normalisiert hätte.

Ergebnis: Der Eintrag wurde nie wiedergefunden. Der erste Aufruf legte einen an, der zweite lief in eine Eindeutigkeitsbedingung und beantwortete die Seite mit einem Fehler.

Der erste Aufruf je Artikel und Tag ging durch. Wer eine Seite einmal prüft, sieht Erfolg und hält alles für in Ordnung.

Seitdem gilt bei mir: Nach jeder Änderung an einer Anwendung, die Zähler oder Protokolle schreibt, wird jede Prüfung zweimal ausgeführt. Der Aufwand ist ein zweiter Klick.

Was übrig bleibt

Der alte Verbund ist entfernt — nach vollständiger Abnahme des neuen Dienstes, mit einer Sicherung aus Datenbankabzug, Anwendungsarchiv und Prüfsummen.

Was stehen bleibt, ist der Quellbaum, aus dem das Anwendungsabbild entsteht. Ohne ihn ließe sich mein Abbild nicht neu bauen. Daneben liegt eine Warnung, dass ein Start des alten Verbunds an dieser Stelle einen zweiten, leeren Laden starten würde.

Solche Warnungen für mein zukünftiges Ich halte ich inzwischen für einen wesentlichen Teil der Arbeit. In sechs Monaten sehe ich ein Verzeichnis mit einer Verbundbeschreibung darin und denke, ich hätte es vergessen aufzuräumen.