Blue/Green und Canary gelten als Themen für Systeme mit Container-Orchestrierung, und in fast jedem Text dazu steht Kubernetes im ersten Absatz. Das ist ein Missverständnis mit Folgen: Teams, die beides gut gebrauchen könnten, halten es für unerreichbar, weil sie erst eine Plattform einführen müssten.
Beide Verfahren sind älter als Container, und sie brauchen genau zwei Dinge. Zwei Versionen müssen gleichzeitig laufen können, und es muss eine Weiche geben, die den Verkehr zwischen ihnen verteilt. Ein PHP-Monolith auf zwei virtuellen Maschinen hinter nginx erfüllt beides.
Dieser Artikel zeigt, wie das konkret aussieht, und behandelt die Voraussetzung, an der es tatsächlich scheitert: nicht am Werkzeug, sondern an der Datenbank.
Die Bedingung, die niemand nennt
Vor jedem Verfahren steht eine Frage, und wird sie übergangen, ist der ganze Aufbau eine Beruhigung ohne Wirkung: Können die alte und die neue Fassung gleichzeitig auf derselben Datenbank arbeiten?
Bei Blue/Green laufen beide für Minuten parallel. Bei Canary für Stunden. Und beim Zurückschalten, dem eigentlichen Zweck der Übung, muss die alte Fassung mit den Daten arbeiten können, die die neue geschrieben hat.
Wer in derselben Auslieferung eine Spalte umbenennt und den Code darauf umstellt, hat kein Zurück. Die neue Fassung schreibt in die neue Spalte, die alte liest die alte, und nach dem Zurückschalten fehlen die Daten von zwanzig Minuten. Das ist kein Fehler im Verfahren, das ist ein Schemawechsel, der nicht dazu passt.
Die Bedingung lautet deshalb: Jede Schemaänderung ist abwärtsverträglich, nach dem Muster Expand und Contract. Erst die neue Spalte zusätzlich anlegen, beide schreiben, dann umstellen, und das Entfernen der alten erst in einer späteren Auslieferung. Das ist mehr Arbeit an drei Tagen im Jahr und die Voraussetzung dafür, dass an den anderen Tagen ein Rückweg existiert.
Wie eine abwärtsverträgliche Umstellung der Daten abläuft, steht in Dual-Write und Backfill.
Blue/Green mit zwei Zielen und einer Weiche
Die einfachste Umsetzung braucht keine neue Infrastruktur: zwei gleiche Umgebungen, ein Lastverteiler davor, und eine Datei, die sagt, welche gerade dran ist.
# /etc/nginx/conf.d/aktiv.conf: erzeugt, nicht gepflegt.
upstream anwendung {
server 10.0.1.10:8080; # blau
# server 10.0.1.11:8080; # gruen
}
# Die alte Fassung bleibt erreichbar, aber nur gezielt:
# darueber laeuft die Abnahme, bevor umgeschaltet wird.
server {
listen 8443 ssl;
server_name neu.intern.beispiel.de;
location / { proxy_pass http://10.0.1.11:8080; }
}Das Umschalten ist dann ein Skript, und sein wichtigster Teil sind nicht die zwei Zeilen, die die Datei ändern, sondern die Prüfung davor und der Weg zurück:
#!/usr/bin/env bash
set -euo pipefail
neu=$1 # 10.0.1.11
# Erst fragen, ob die neue Seite ueberhaupt antwortet. Ein
# Umschalten auf eine Instanz, die noch startet, ist ein
# selbstgemachter Ausfall.
curl -fsS --max-time 3 "http://$neu:8080/bereit" > /dev/null
cp /etc/nginx/conf.d/aktiv.conf /etc/nginx/conf.d/aktiv.vorher
printf 'upstream anwendung { server %s:8080; }\n' "$neu" \
> /etc/nginx/conf.d/aktiv.conf
# reload, nicht restart: bestehende Verbindungen laufen aus,
# neue gehen auf die neue Seite. Kein Verbindungsabbruch.
nginx -t && nginx -s reload
echo "umgeschaltet auf $neu. zurueck: mv aktiv.vorher aktiv.conf && nginx -s reload"Die letzte Zeile ist kein Komfort. Der Rückweg muss zum Zeitpunkt des Umschaltens bekannt und ausgeschrieben sein, weil er in dem Moment gebraucht wird, in dem niemand ruhig nachdenkt.
Was die neue Seite über sich sagen muss
Ein Punkt, der in beiden Verfahren vorkommt und deshalb einen eigenen Abschnitt verdient: Das Umschalten hängt an einer Auskunft der neuen Fassung über sich selbst, und die ist in den meisten Anwendungen zu optimistisch.
Ein Endpunkt, der schlicht „ok" antwortet, sobald der Webserver läuft, beantwortet die falsche Frage. Er sagt, dass der Prozess gestartet ist, nicht, dass die Anwendung arbeiten kann. Umgeschaltet wird dann auf eine Instanz, die die Datenbank noch nicht erreicht.
Brauchbar sind zwei getrennte Auskünfte. Die eine beantwortet „lebt der Prozess" und darf nichts prüfen, was von außen kommt, sonst wird ein kurzer Aussetzer der Datenbank zu einem Neustart aller Instanzen. Die andere beantwortet „kann diese Instanz Verkehr übernehmen" und prüft genau das, ohne was es nicht geht:
// GET /bereit: nur was ohne Ausnahme gebraucht wird.
// Jede weitere Pruefung hier macht das Umschalten unzuverlaessig,
// ohne das Risiko zu senken.
$pruefungen = [
'datenbank' => fn () => $this->db->fetchOne('SELECT 1') === 1,
'schema' => fn () => $this->migrationen->offene() === 0,
'speicher' => fn () => $this->cache->erreichbar(),
];
foreach ($pruefungen as $name => $pruefung) {
if (!$pruefung()) {
return new Response("nicht bereit: $name", 503);
}
}
return new Response('bereit', 200);Die zweite Zeile der Liste ist die, die am häufigsten fehlt und am meisten verhindert: Eine Instanz mit offenen Schemaänderungen ist nicht bereit, auch wenn sie antwortet.
Canary mit einem Anteil statt eines Schalters
Blue/Green beantwortet die Frage „funktioniert es" mit ja oder nein für alle gleichzeitig. Canary beantwortet sie für ein Prozent zuerst, und dafür genügt derselbe Lastverteiler.
# Ein stabiler Anteil: dieselbe Sitzung landet immer auf
# derselben Seite. Ohne diese Bindung springt ein Nutzer
# zwischen zwei Fassungen hin und her.
split_clients "${kennung}" $ziel {
5% neu;
* alt;
}
map $ziel $backend {
neu 10.0.1.11:8080;
alt 10.0.1.10:8080;
}Die Bindung an eine Kennung statt an die Adresse ist der Punkt, an dem viele erste Versuche schiefgehen. Ein Nutzer, der zwischen zwei Fassungen wechselt, sieht mal die eine und mal die andere Oberfläche, und in einem Formular über mehrere Schritte verliert er seine Eingaben. Als Kennung eignet sich die Sitzung, ersatzweise ein eigenes Merkmal, das beim ersten Aufruf gesetzt wird.
Ohne Abbruchregel ist Canary nur langsamer
Hier liegt der Unterschied zwischen einem Verfahren und einer Geste. Ein Anteil von fünf Prozent bringt nichts, wenn niemand hinsieht und niemand vorher festgelegt hat, wann abgebrochen wird.
Drei Angaben gehören vor den Start, und sie stehen im selben Dokument wie der Rückweg. Welche Kennzahl: die Fehlerquote der neuen Seite im Vergleich zur alten, nicht ihr absoluter Wert. Ein Vergleich ist robust gegen die Schwankungen des Tages, ein Grenzwert nicht.
Welches Fenster: lang genug, dass genug Anfragen zusammenkommen, kurz genug, dass ein Fehler nicht eine Stunde wirkt. Bei üblichem Verkehr sind zehn Minuten je Stufe brauchbar.
Wer abbricht: am besten niemand. Der Abbruch gehört in dieselbe Automatik, die die Stufen hochzählt, weil ein Mensch, der um 22 Uhr eine Kurve ansieht, zu lange zögert.
Die Stufenfolge ist dann eine Frage der Größenordnung, nicht der Diskussion: fünf Prozent, fünfundzwanzig, fünfzig, hundert. Und die Rückschaltung ist ein Wert in der Konfiguration, kein neues Ausrollen.
Die vergessene Hälfte: alles ohne Anfrage
Beide Verfahren verteilen den eingehenden Verkehr. Ein Teil eines Systems bekommt davon nichts mit, und genau dort entstehen die Überraschungen.
Hintergrundläufe und Warteschlangen. Während des Canary verarbeiten die Arbeiter Aufträge, ohne dass sie durch die Weiche laufen. Die brauchbare Regel ist, sie zunächst ganz auf der alten Fassung zu belassen und erst nach dem Umschalten mitzuziehen: Ein Auftrag, der von der neuen Fassung erzeugt und von der alten verarbeitet wird, ist der Fall, den niemand getestet hat.
Zeitgesteuerte Läufe. Wenn beide Fassungen aktiv sind, laufen sie doppelt. Sie gehören auf genau eine Seite, und das Einfachste ist: auf die alte, bis der Canary beendet ist.
Schemaänderungen. Sie laufen einmal, vor der Auslieferung, und zwar so, dass die alte Fassung danach weiterarbeitet. Das ist dieselbe Bedingung wie oben, an dieser Stelle nur zum zweiten Mal, weil sie in der Praxis zweimal übersehen wird.
Wer noch per FTP ausrollt, fängt eine Stufe früher an: Wenn das Deployment noch per FTP läuft.
Wann sich die Plattform doch lohnt
Zum Schluss die ehrliche Abgrenzung, damit dieser Artikel nicht das Gegenteil des Missverständnisses erzeugt, das er auflösen soll.
Zwei Maschinen und eine Weiche tragen erstaunlich weit: für ein bis zwei Anwendungen, einige Auslieferungen pro Woche und ein Team, das den Lastverteiler versteht. Ab etwa zehn Diensten mit eigenen Rhythmen wird das Verwalten der Weichen selbst zur Arbeit, und dann ist eine Plattform, die das mitbringt, die günstigere Wahl.
Die Reihenfolge bleibt trotzdem dieselbe, und sie ist der eigentliche Punkt: Erst die Abwärtsverträglichkeit der Datenbank, dann die Weiche, dann die Abbruchregel. Wer eine Plattform einführt, ohne die erste Bedingung zu erfüllen, hat ein Blue/Green, das beim Zurückschalten Daten verliert, nur mit mehr Werkzeugen drumherum. Wie ich Auslieferungswege baue, steht auf einer eigenen Seite.
Dieser Artikel gehört zu einer Reihe über Systeme, die es schon gibt. Der Rückblick ordnet alle Artikel der Reihe nach Anlass.

