Blue-Green-Deployment ohne Kubernetes: Ein Single-Server-Muster, das funktioniert

#Blue-Green-Deployment ohne Kubernetes
Sandor Farkas - Founder & Lead Developer at Wolf-Tech

Sandor Farkas

Gründer & Lead Developer

Experte für Softwareentwicklung und Legacy-Code-Optimierung

Die meisten Teams hören von Blue-Green-Deployment zum ersten Mal in einem Kubernetes-Vortrag, was eine stille Annahme in der Luft hängen lässt: dass Releases ohne Ausfallzeit einen Orchestrator, ein Service-Mesh und ein Plattform-Team zum Betrieb erfordern. Diese Annahme ist falsch, und sie kostet kleinere Teams mehr, als ihnen bewusst ist. Blue-Green-Deployment ohne Kubernetes ist nicht nur möglich, es ist auf einer einzelnen virtuellen Maschine unkompliziert, und es gibt einem zweiköpfigen SaaS-Team dasselbe Sicherheitsnetz mit sofortigem Rollback, für das große Plattform-Teams teuer bezahlen.

Die Kernidee ist Jahre älter als Kubernetes. Du betreibst zwei identische Kopien deiner Anwendung nebeneinander. Eine ist live und bedient Traffic (nenn sie Blue), die andere ist im Leerlauf und bereit, das nächste Release aufzunehmen (Green). Du deployst die neue Version auf die untätige Kopie, führst Health-Checks gegen sie aus, während kein Benutzer sie sehen kann, und legst erst dann einen Schalter um, sodass der Traffic auf die frisch deployte Kopie wechselt. Die vorherige Version läuft unangetastet weiter, so lange du willst. Wenn nach dem Umschalten etwas schiefgeht, ist der Rollback ein Zurückschalten des Schalters, kein Redeploy. Das ist das ganze Muster, und nichts davon braucht ein Cluster.

Warum ein einzelner Server für die meisten SaaS ausreicht

Der Instinkt, zu Kubernetes zu greifen, kommt meist aus einer echten Angst: dass ein Release die Seite vor zahlenden Kunden lahmlegt. Blue-Green-Deployment nimmt diese Angst direkt weg, und zwar in jedem Maßstab. Ein einzelner gut ausgestatteter VPS, der eine Symfony- oder Node-Anwendung betreibt, bedient bequem Tausende täglich aktiver Benutzer, und die überwältigende Mehrheit der B2B-SaaS-Produkte wächst nie über einen oder zwei Anwendungsserver hinter einer Managed Database hinaus. Einen Orchestrator einzuführen, um ein Problem der Deployment-Sicherheit zu lösen, heißt, das richtige Problem mit dem falschen Werkzeug zu lösen, und du bezahlst für die Fehlpassung jede Woche in operativem Aufwand.

Was du für sichere Releases tatsächlich brauchst, sind drei Dinge: zwei laufende Kopien der App, eine Möglichkeit zu prüfen, dass die neue Kopie gesund ist, bevor Benutzer sie berühren, und einen atomaren Wechsel zwischen ihnen. Ein Reverse-Proxy gibt dir den Wechsel. Ein Health-Endpunkt gibt dir die Prüfung. systemd oder ein Prozessmanager gibt dir die zwei Kopien. Alles andere, was Kubernetes bietet (Autoscaling, Multi-Node-Scheduling, Self-Healing über Maschinen hinweg), löst Probleme, die du noch nicht hast. Wenn du wirklich über eine einzelne Kiste hinauswächst, überträgt sich die Deployment-Disziplin, die du hier aufgebaut hast, sauber. Den richtigen Stack für deine Phase statt den modischsten zu wählen, ist genau die Art Entscheidung, für die unsere Arbeit zur Tech-Stack-Strategie existiert.

Die Anatomie des Musters

Stell dir zwei Anwendungsinstanzen auf demselben Host vor. Blue lauscht auf Port 8001, Green auf Port 8002. Davor sitzt ein Reverse-Proxy (Nginx, Caddy oder HAProxy), der Anfragen an ein Upstream weiterleitet, das in einer kleinen Include-Datei definiert ist. Diese Include-Datei ist der Schalter. Sie enthält eine einzige Zeile, die entweder auf 8001 oder 8002 zeigt. Zu ändern, welchen Port sie nennt, und dann den Proxy neu zu laden, verschiebt allen neuen Traffic von einer Instanz zur anderen, ohne eine einzige in Bearbeitung befindliche Anfrage zu verlieren, weil ein Proxy-Reload bestehende Verbindungen zu Ende bedient, bevor er die neue Konfiguration anwendet.

Beide Instanzen teilen sich dieselbe Datenbank, dasselbe Redis und denselben Speicher für hochgeladene Dateien. Das ist die eine architektonische Einschränkung, die zählt: Weil Blue und Green während eines Umschaltens im selben Moment gegen dieselben Daten laufen, müssen deine Schemaänderungen für die Dauer des Wechsels über die beiden Anwendungsversionen hinweg abwärtskompatibel sein. Das ist eine gesunde Disziplin, unabhängig davon, wie du deployst, und wir kommen darauf zurück.

Ein Deploy-Skript verbindet alles. Es erkennt, welche Instanz aktuell live ist, deployt das neue Release auf die untätige, startet sie, pollt ihren Health-Endpunkt, bis er bereit meldet, schreibt das Upstream-Include so um, dass es auf die neu gesunde Instanz zeigt, und lädt den Proxy neu. Wenn der Health-Check nie besteht, beendet sich das Skript, ohne den Proxy anzufassen, und der Live-Traffic fließt weiter zur alten Version, die nie gestört wurde.

Ein konkreter Durchlauf

Beginne mit dem Proxy. In deinem Server-Block wird das Upstream aus einer eingebundenen Datei gelesen, sodass der Schalter an genau einer Stelle lebt.

upstream app {
    include /etc/nginx/conf.d/active-upstream.inc;
}

server {
    listen 443 ssl;
    server_name app.example.com;
    location / {
        proxy_pass http://app;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Die active-upstream.inc-Datei enthält eine Zeile, zum Beispiel server 127.0.0.1:8001;. Betreibe Blue und Green als zwei systemd-Dienste, identisch bis auf den Port, den sie binden, und das Release-Verzeichnis, aus dem sie laufen. Gib der Anwendung einen echten Health-Endpunkt, der die Dinge prüft, die ein Release kaputtmachen kann: dass sich die Datenbankverbindung öffnet, dass erforderliche Umgebungsvariablen vorhanden sind und dass jede kritische Abhängigkeit antwortet. Ein Health-Check, der nur 200 aus der Web-Schicht zurückgibt, sagt dir, dass der Prozess gestartet ist, nicht dass das Release funktioniert.

Das Deploy-Skript ist das Herzstück:

#!/usr/bin/env bash
set -euo pipefail

ACTIVE=$(grep -oE '800[12]' /etc/nginx/conf.d/active-upstream.inc)
if [ "$ACTIVE" = "8001" ]; then
  IDLE_PORT=8002; IDLE=green
else
  IDLE_PORT=8001; IDLE=blue
fi

echo "Live is on $ACTIVE, deploying to $IDLE ($IDLE_PORT)"
deploy_release_to "$IDLE"          # pull code, install deps, build assets
systemctl restart "app@${IDLE}"

for i in $(seq 1 30); do
  if curl -fsS "http://127.0.0.1:${IDLE_PORT}/health" >/dev/null; then
    echo "$IDLE healthy"; break
  fi
  [ "$i" = "30" ] && { echo "health check failed, aborting"; exit 1; }
  sleep 2
done

echo "server 127.0.0.1:${IDLE_PORT};" > /etc/nginx/conf.d/active-upstream.inc
nginx -t && systemctl reload nginx
echo "Traffic switched to $IDLE"

Der Rollback ist derselbe Schalter in umgekehrter Richtung. Weil die vorherige Instanz noch ihr vorheriges Release betreibt, zeigst du das Include zurück auf den alten Port und lädst neu. Kein Rebuild, keine Datenbankwiederherstellung, kein Warten auf eine Deploy-Pipeline. Die Wiederherstellungszeit misst sich in den Sekunden, die ein Proxy-Reload braucht, und das macht, dass sich dieses Muster wie das Sicherheitsnetz auf Orchestrator-Niveau anfühlt, das es tatsächlich ist.

Die Datenbank-Disziplin, die niemand erwähnt

Die eine scharfe Kante des Musters ist die Schemamigration, und es ist dieselbe Kante, auf die Kubernetes-Deployments treffen. Während eines Umschaltens sprechen zwei Anwendungsversionen mit einer Datenbank. Wenn dein neues Release eine Spalte umbenennt und du die Migration vor dem Wechsel ausführst, bricht die alte Version in dem Moment, in dem die Migration landet. Führst du sie danach aus, bricht die neue Version, bis sie es tut. Der Ausweg ist das Expand-and-Contract-Muster: Zerlege jede brechende Änderung in abwärtskompatible Schritte. Um eine Spalte umzubenennen, fügst du die neue Spalte hinzu, deployst Code, der in beide schreibt und die neue liest, füllst zurück, schaltest den Traffic um und löschst erst in einem späteren Release die alte Spalte, sobald nichts mehr auf sie verweist.

Das ist mehr Disziplin, als eine destruktive Migration während des Deploys loszufeuern, aber es ist die Disziplin, die jedes Deployment ohne Ausfallzeit sicher macht, ob Blue-Green oder Rolling, Kubernetes oder nicht. Teams, die sie überspringen, entdecken auf die harte Tour, dass ihr Setup ohne Ausfallzeit dennoch ein Ausfallfenster hat, das in jeder Schemaänderung steckt. Migrationen zu entwirren, die ohne diese Einschränkung geschrieben wurden, ist ein wiederkehrendes Thema in unseren Engagements zur Legacy-Code-Optimierung, und es lohnt sich, die Gewohnheit richtig hinzubekommen, bevor sie zu einem Produktionsvorfall wird.

Wann dies das falsche Werkzeug ist

Sei ehrlich über die Grenzen. Dieses Single-Server-Muster verdoppelt den Speicherbedarf deiner App auf einem Host, weil zwei Kopien gleichzeitig laufen, also dimensioniere die Maschine entsprechend. Es gibt dir keine Hochverfügbarkeit gegen Hardware-Ausfall, denn ein VPS ist ein einzelner Ausfallpunkt; wenn diese Maschine stirbt, sterben Blue und Green mit ihr. Wenn deine Uptime-Anforderungen wirklich verlangen, einen Node-Verlust zu überstehen, brauchst du mindestens zwei Maschinen und einen Load-Balancer davor, und an diesem Punkt gilt dieselbe Blue-Green-Logik über Hosts hinweg. Und wenn du Dutzende Dienste betreibst, die unabhängiges Scaling und Scheduling brauchen, ist das der Punkt, an dem ein Orchestrator anfängt, sich seine Komplexität zu verdienen.

Für das Team, das ein fokussiertes SaaS-Produkt auf einem oder zwei Servern ausliefert, liefert dieses Muster jedoch das meiste von dem, wofür Leute Kubernetes installieren, zu einem Bruchteil der operativen Kosten. Die richtige Deployment-Architektur ist die, die zu deinem tatsächlichen Maßstab und deiner Ausfalltoleranz passt, und sie bewusst zu wählen ist Teil dessen, wie wir Custom Software Development vom ersten Tag an angehen.

Erste Schritte

Du kannst dies schrittweise einführen. Füge zuerst einen echten Health-Endpunkt hinzu, stelle dann eine zweite Instanz und ein Deploy-Skript auf und behalte deinen bestehenden Release-Prozess als Rückfalloption, bis du dem Schalter vertraust. Innerhalb einer Woche haben die meisten Teams ein sofortiges Rollback, das sie nie zuvor hatten, ohne ein Cluster zu warten. Wenn du ein zweites Paar Augen auf dein Deployment-Setup oder deine Migrationsstrategie haben möchtest, bevor du den ersten Schalter in der Produktion umlegst, helfen wir gerne. Erreiche uns unter hello@wolf-tech.io oder unter wolf-tech.io, und wir gehen deinen Stack mit dir durch.