Blue-Green-Deployment bedeutet: Sie betreiben zwei identische CAPTCHA-Worker-Flotten – „Blue“ und „Green“ – und schalten den Traffic per Router von einer auf die andere um. Nur eine Umgebung ist aktiv, die andere steht als geprüfter Ersatz bereit. Geht ein Update schief, wechseln Sie in Sekunden zurück, ohne dass eine einzige Anfrage ins Leere läuft.
Für eine Scraping- oder Automatisierungs-Pipeline, die rund um die Uhr CAPTCHAs über CaptchaAI löst, ist genau dieser Sicherheitsnetz-Charakter entscheidend. Ein fehlerhafter Rollout darf nicht dazu führen, dass Ihre Worker plötzlich Timeouts oder falsche Token liefern. Dieser Leitfaden zeigt Router, Canary-Test und automatisches Rollback in Python und Node.js – und wann sich der doppelte Ressourcenaufwand wirklich lohnt.
Was Blue-Green bei CAPTCHA-Workern anders macht
CAPTCHA-Worker teilen sich in der Praxis oft Sessions, Proxy-Pools und Retry-Logik. Genau diese Zustände machen In-Place-Updates riskant: Eine neue Version verändert womöglich das Session-Handling, und die Fehlerquote steigt erst unter Last – also dann, wenn ein Rollback am teuersten ist.
Blue-Green trennt Alt und Neu sauber. Die Standby-Umgebung erhält den neuen Code, wird isoliert getestet und übernimmt den Traffic erst, wenn sie einen Canary-Test bestanden hat. Der Router kennt immer nur eine aktive Flotte; das Umschalten ist ein einziger, atomarer Schritt und kein schrittweises Ausrollen über viele Instanzen. Das macht sowohl den Wechsel als auch den Rückweg vorhersehbar – die wichtigste Eigenschaft für eine Pipeline, deren Ausfall direkt Umsatz oder Datenqualität kostet.
Wann sich Blue-Green lohnt – und wann nicht
Der doppelte Aufbau kostet Ressourcen, deshalb ist Blue-Green nicht für jedes Setup die richtige Wahl. Die folgende Einordnung hilft bei der Entscheidung.
| Situation | Blue-Green passt gut | Oft reicht etwas Einfacheres |
|---|---|---|
| Kritische Solve-Pipeline mit klarer Rollback-Anforderung | Ja | – |
| Häufige Releases oder Konfigurationswechsel | Ja | – |
| Einzelner Worker oder kleines internes Tool | – | Rolling Update oder Neustart |
| Zusätzliche Infrastruktur wäre unverhältnismäßig teuer | – | Einfacheres Deployment-Modell |
Architektur: Router zwischen Blue und Green
Der Aufbau besteht aus drei Teilen: einem Traffic-Router, zwei identischen Worker-Flotten und der gemeinsamen CaptchaAI-API im Hintergrund. Der Router entscheidet pro Anfrage, welche Flotte antwortet – und lässt sich zur Laufzeit umschalten, ohne die Clients zu berühren.
┌─────────────────────┐
[Scraper Clients] → │ Traffic Router │
└──────┬──────┬───────┘
│ │
Active│ │Standby
▼ ▼
┌───────┐ ┌───────┐
│ BLUE │ │ GREEN │
│Workers│ │Workers│
└───┬───┘ └───┬───┘
│ │
└────┬─────┘
▼
[CaptchaAI API]
Umsetzung: Router und automatischer Deploy
Router in Python
Der folgende Router kapselt jede Umgebung in einem CaptchaWorkerPool. Jeder Pool zählt gelöste Aufgaben und Fehler mit, sodass der Router jederzeit eine Fehlerquote pro Flotte kennt. Die solve-Methode reicht die Aufgabe an CaptchaAI weiter: Sie übermittelt Sitekey und Page-URL an in.php und fragt das Ergebnis anschließend über res.php ab, bis ein Token vorliegt oder das Timeout greift. Der canary_test prüft die Standby-Flotte, bevor switch() den Traffic tatsächlich umlenkt.
import os
import time
import threading
import requests
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
class CaptchaWorkerPool:
"""Represents one environment (blue or green)."""
def __init__(self, name, config):
self.name = name
self.config = config
self.session = requests.Session()
self.tasks_solved = 0
self.errors = 0
self.healthy = True
def solve(self, task):
resp = self.session.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": task.get("method", "userrecaptcha"),
"googlekey": task["sitekey"],
"pageurl": task["pageurl"],
"json": 1
})
data = resp.json()
if data.get("status") != 1:
self.errors += 1
return {"error": data.get("request")}
captcha_id = data["request"]
for _ in range(60):
time.sleep(5)
result = self.session.get(
"https://ocr.captchaai.com/res.php",
params={
"key": API_KEY,
"action": "get",
"id": captcha_id,
"json": 1
}
).json()
if result.get("status") == 1:
self.tasks_solved += 1
return {"solution": result["request"]}
if result.get("request") != "CAPCHA_NOT_READY":
self.errors += 1
return {"error": result.get("request")}
self.errors += 1
return {"error": "TIMEOUT"}
@property
def error_rate(self):
total = self.tasks_solved + self.errors
return self.errors / total if total > 0 else 0.0
@property
def stats(self):
return {
"name": self.name,
"solved": self.tasks_solved,
"errors": self.errors,
"error_rate": round(self.error_rate, 4),
"healthy": self.healthy
}
class BlueGreenRouter:
def __init__(self, blue_config, green_config):
self.blue = CaptchaWorkerPool("blue", blue_config)
self.green = CaptchaWorkerPool("green", green_config)
self.active = self.blue
self.standby = self.green
self.lock = threading.Lock()
def solve(self, task):
"""Route task to the active environment."""
with self.lock:
pool = self.active
return pool.solve(task)
def switch(self):
"""Swap active and standby environments."""
with self.lock:
self.active, self.standby = self.standby, self.active
print(f"Switched: {self.active.name} is now ACTIVE")
return self.active.name
def rollback(self):
"""Switch back to the previous environment."""
return self.switch()
def canary_test(self, test_tasks, threshold=0.9):
"""Run test tasks on standby before switching."""
successes = 0
for task in test_tasks:
result = self.standby.solve(task)
if "solution" in result:
successes += 1
success_rate = successes / len(test_tasks) if test_tasks else 0
passed = success_rate >= threshold
print(
f"Canary test: {successes}/{len(test_tasks)} "
f"({success_rate:.0%}) — {'PASS' if passed else 'FAIL'}"
)
return passed
@property
def status(self):
return {
"active": self.active.stats,
"standby": self.standby.stats
}
# Usage
router = BlueGreenRouter(
blue_config={"version": "1.2.0", "workers": 4},
green_config={"version": "1.3.0", "workers": 4}
)
# Canary test before switching
test_tasks = [
{"sitekey": "6Le-wvkS...", "pageurl": "https://example.com/test"}
]
if router.canary_test(test_tasks, threshold=0.8):
router.switch()
print(f"Now active: {router.status['active']['name']}")
else:
print("Canary failed — staying on current environment")
Node.js – Deploy mit Canary und Monitoring
Die Node.js-Variante automatisiert den gesamten Ablauf: deploy() spielt die neue Version auf die Standby-Umgebung, führt einen Canary-Check aus, schaltet den Traffic um und überwacht die Fehlerquote danach noch eine Minute lang. Überschreitet sie den Schwellenwert, löst der Code selbstständig ein Rollback aus – ohne dass jemand nachts eingreifen muss.
const axios = require("axios");
const API_KEY = process.env.CAPTCHAAI_API_KEY;
class BlueGreenDeployment {
constructor() {
this.environments = {
blue: { name: "blue", version: null, solved: 0, errors: 0 },
green: { name: "green", version: null, solved: 0, errors: 0 },
};
this.activeEnv = "blue";
}
get active() {
return this.environments[this.activeEnv];
}
get standby() {
return this.environments[this.activeEnv === "blue" ? "green" : "blue"];
}
async deploy(version, config = {}) {
const target = this.standby;
target.version = version;
target.solved = 0;
target.errors = 0;
console.log(`Deployed v${version} to ${target.name} (standby)`);
// Run canary checks
const canaryPassed = await this.canaryCheck(config.canaryTasks || []);
if (!canaryPassed && config.canaryTasks?.length > 0) {
console.log("Canary check failed — aborting deployment");
return { success: false, reason: "canary_failed" };
}
// Switch traffic
this.activeEnv = target.name;
console.log(`Switched traffic to ${target.name} (v${version})`);
// Monitor for rollback
if (config.monitorDuration) {
const stable = await this.monitorAfterSwitch(config.monitorDuration);
if (!stable) {
this.rollback();
return { success: false, reason: "post_deploy_errors" };
}
}
return { success: true, active: this.activeEnv };
}
async canaryCheck(tasks) {
if (tasks.length === 0) return true;
let successes = 0;
for (const task of tasks) {
try {
await this.solveCaptcha(task);
successes++;
} catch (err) {
console.log(`Canary task failed: ${err.message}`);
}
}
const rate = successes / tasks.length;
console.log(`Canary: ${successes}/${tasks.length} (${(rate * 100).toFixed(0)}%)`);
return rate >= 0.8;
}
async monitorAfterSwitch(durationMs) {
const start = Date.now();
const checkInterval = 10000;
while (Date.now() - start < durationMs) {
await new Promise((r) => setTimeout(r, checkInterval));
const errorRate = this.active.errors /
Math.max(1, this.active.solved + this.active.errors);
if (errorRate > 0.2) {
console.log(`Error rate ${(errorRate * 100).toFixed(1)}% — triggering rollback`);
return false;
}
}
return true;
}
rollback() {
const previous = this.activeEnv === "blue" ? "green" : "blue";
console.log(`Rolling back: ${this.activeEnv} → ${previous}`);
this.activeEnv = previous === "blue" ? "blue" : "green";
}
async solveCaptcha(task) {
const submitResp = await axios.post("https://ocr.captchaai.com/in.php", null, {
params: {
key: API_KEY,
method: "userrecaptcha",
googlekey: task.sitekey,
pageurl: task.pageurl,
json: 1,
},
});
if (submitResp.data.status !== 1) {
this.active.errors++;
throw new Error(submitResp.data.request);
}
const captchaId = submitResp.data.request;
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const pollResp = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
});
if (pollResp.data.status === 1) {
this.active.solved++;
return pollResp.data.request;
}
if (pollResp.data.request !== "CAPCHA_NOT_READY") {
this.active.errors++;
throw new Error(pollResp.data.request);
}
}
this.active.errors++;
throw new Error("TIMEOUT");
}
}
// Deploy new version with canary and monitoring
const deployer = new BlueGreenDeployment();
deployer
.deploy("1.3.0", {
canaryTasks: [
{ sitekey: "6Le-wvkS...", pageurl: "https://example.com/test" },
],
monitorDuration: 60000, // Monitor for 1 minute after switch
})
.then((result) => console.log("Deploy result:", result));
Deployment-Workflow Schritt für Schritt
In der Praxis läuft jeder Release in fünf Schritten ab. Jeder Schritt hat einen klaren Rollback-Trigger – so ist im Vorfeld definiert, wann ein Deployment abgebrochen wird, statt im Ernstfall zu improvisieren.
| Schritt | Aktion | Rollback-Trigger |
|---|---|---|
| 1 | Stellen Sie neuen Code im Standby bereit | Build-Fehler |
| 2 | Führen Sie Canary-Tests im Standby-Modus aus | Erfolgsquote < 80 % |
| 3 | Traffic auf neue Version umstellen | – |
| 4 | Fehlerrate überwachen (5 Min.) | Fehlerquote > 20 % |
| 5 | Alte Umgebung außer Betrieb nehmen | – |
Threads und Kosten für zwei Umgebungen
Zwei parallele Worker-Flotten verdoppeln nicht automatisch Ihre CaptchaAI-Kosten. CaptchaAI rechnet Thread-basiert ab – ein Thread ist eine gleichzeitig laufende CAPTCHA-Anfrage, nicht eine einzelne Lösung, und jeder Tarif enthält unbegrenzt viele Lösungen pro Thread. Entscheidend ist also die gleichzeitige Last, nicht die Zahl der Umgebungen.
Während eines Wechsels verarbeitet fast immer nur eine Flotte produktiven Traffic; die Standby-Umgebung belegt nur für den Canary-Test kurz Threads. In der Praxis reicht dieselbe Thread-Zuteilung wie bei einem Single-Environment-Setup, plus etwas Reserve für die Überlappung beim Umschalten. Der BASIC-Tarif (15 $/Monat, 5 Threads) genügt für kleine Pipelines; wer dauerhaft hohe Parallelität fährt, plant mit ADVANCE (90 $/Monat, 50 Threads) oder PREMIUM (170 $/Monat, 100 Threads). Die Preise sind in US-Dollar; aktuelle Werte stehen auf der Preisseite.
Für den Betrieb selbst eignen sich in der DACH-Region VPS-Anbieter wie Hetzner, IONOS oder netcup: Blue und Green laufen als getrennte Container oder Server, ein vorgelagerter Reverse Proxy übernimmt das Umschalten. In GitLab-CI-Pipelines – in vielen deutschen Unternehmen Standard – lässt sich der Canary-Test als eigene Stage vor dem Traffic-Wechsel einhängen.
Fehlerbehebung
Wenn ein Blue-Green-Setup Probleme macht, liegt die Ursache meist im Unterschied zwischen Standby und Produktion – nicht im Umschalten selbst.
| Problem | Ursache | Lösung |
|---|---|---|
| Worker ist erreichbar, verarbeitet aber keine Aufgaben | Warteschlange, Credentials oder Eingabestrom stimmen nicht | Prüfen Sie Queue-Tiefe, API-Schlüssel, Health-Checks und Fehlerraten pro Worker gemeinsam |
| Fehlerrate steigt nach dem Rollout | Neue Version verändert Session-, Proxy- oder Retry-Verhalten | Vergleichen Sie erfolgreiche und fehlgeschlagene Runs zwischen alter und neuer Version und rollen Sie bei Bedarf zurück |
| Canary bleibt grün, Produktion schlägt fehl | Testaufgaben sind zu einfach und treffen die reale Last nicht | Verwenden Sie realistische Aufgaben aus der Produktions-Warteschlange für den Canary-Test |
| Canary oder Health-Check bleibt rot | Abhängigkeiten, Zeitlimits oder Secrets weichen von der Zielumgebung ab | Prüfen Sie Secrets, Netzwerkpfade und Schwellenwerte in exakt derselben Umgebung |
Häufige Fragen
Worin unterscheidet sich Blue-Green von einem Rolling Update?
Ein Rolling Update ersetzt Instanzen nach und nach, sodass alte und neue Version zeitweise gleichzeitig Traffic bekommen. Blue-Green hält beide Versionen getrennt und schaltet den gesamten Traffic in einem Schritt um – der Rückweg ist dadurch sofort und vollständig, statt Instanz für Instanz.
Was passiert mit laufenden CAPTCHA-Anfragen beim Umschalten?
In-Flight-Anfragen laufen auf der alten Flotte zu Ende. Nehmen Sie die alte Umgebung erst außer Betrieb, wenn ihre Warteschlange geleert ist – sonst brechen Sie noch offene Lösungen ab. Da CaptchaAI-Token ohnehin nur rund 120 Sekunden gültig sind, ist diese Übergangszeit kurz.
Wie viele Threads brauche ich für Blue und Green zusammen?
So viele, wie Ihre gleichzeitige Last erfordert – nicht doppelt so viele. Da im Normalbetrieb nur eine Flotte produktiv ist, genügt Ihre bisherige Thread-Zuteilung plus eine kleine Reserve für den Canary-Test und die Umschaltphase.
Funktioniert Blue-Green auf einem einzelnen Server?
Ja. Betreiben Sie Blue und Green als getrennte Prozesse oder Container auf demselben Host, etwa auf einem Hetzner-VPS, und schalten Sie den Traffic per Reverse Proxy wie NGINX zwischen zwei Ports um.
Verwandte Leitfäden
- CaptchaAI in wenigen Minuten einrichten
- API-Antwortformate und Fehlercodes verstehen
- reCAPTCHA v2 per API lösen