Einleitung: Warum haben Sie immer noch Angst, auf "Deploy" zu klicken?
\n\nSie haben Code geschrieben. Lokal getestet. Die Tests waren erfolgreich. Alles ist perfekt. Sie klicken auf die ersehnte Schaltfläche Deploy... und nach 5 Minuten sehen Sie, wie der 500er-Fehler auftritt und die Benutzer panisch den Support kontaktieren. Kommt Ihnen das bekannt vor?
\n\nTraditionelle Deployment-Strategien (Blue-Green, Rolling Update) sind gut, aber sie haben einen Nachteil: Sie aktivieren entweder den gesamten neuen Code auf einmal oder gar nicht. Was wäre, wenn Sie eine neue Funktion nur an 1% der Benutzer ausliefern, die Metriken beobachten und sie erst dann, wenn Sie sich von der Stabilität überzeugt haben, auf 100% ausrollen könnten?
\n\nGenau so funktionieren Canary Deployments (Kanarienvogel-Releases). Der Name leitet sich von einer alten Bergmannspraxis ab – einen Kanarienvogel mit in die Mine zu nehmen, um rechtzeitig ein Gasleck zu bemerken. Wenn der Vogel verstummte, wussten die Bergleute: Zeit, nach oben zu fliehen. In der IT-Welt ist der "Kanarienvogel" eine kleine Gruppe von Benutzern, an denen der neue Code getestet wird. Wenn alles gut läuft, wird das Release weiter ausgerollt. Wenn nicht, rollen Sie schneller zurück, als Sie "Hotfix" sagen können.
\n\nIn diesem Artikel werden wir mit TypeScript ein vollwertiges Canary-Deployment-System schreiben. Sie erfahren, wie Sie den Datenverkehr ausbalancieren, den Prozess mit CI/CD automatisieren und die "Gesundheit" Ihres Kanarienvogels überwachen. Los geht's!
\n\nWas ist ein Canary Deployment und warum wird es benötigt?
\n\nCanary Deployment ist eine Strategie zum Ausrollen von neuem Code, bei der die aktualisierte Version der Anwendung zunächst an einen kleinen Prozentsatz der Benutzer (normalerweise 1-10%) gesendet wird. Die anderen arbeiten weiterhin mit der alten, stabilen Version. Wenn bei der neuen Version keine Probleme auftreten (Fehler, Leistungseinbrüche, erhöhte Latenz), erhöhen Sie schrittweise den Traffic-Anteil für diese Version, bis sie die alte vollständig ersetzt.
\n\nWichtigste Vorteile:
\n- \n
- Minimierung von Risiken – Fehler betreffen nur einen kleinen Teil der Zielgruppe. \n
- Schneller Rollback – es reicht aus, den Datenverkehr zurück auf die stabile Version zu leiten. \n
- A/B-Tests unter realen Bedingungen – Sie können die Metriken der alten und der neuen Version vergleichen. \n
- Sanftes Ausrollen – Sie überlasten den Server nicht durch einen plötzlichen Lastanstieg. \n
Kommen wir nun von der Theorie zur Praxis. Wir werden ein System aufbauen, das aus drei Teilen besteht:
\n- \n
- Backend mit TypeScript (Express.js). \n
- Traffic-Lastverteiler mit Nginx. \n
- Automatisierung mit GitHub Actions. \n
Schritt 1. Schreiben einer TypeScript-Anwendung mit Versionsunterstützung
\n\nZunächst erstellen wir eine einfache Express-Anwendung, die in zwei Modi arbeiten kann: stable und canary. Der Unterschied besteht nur in der Antwort auf den Root-Endpunkt, aber in einem realen Projekt könnte dies jede neue Funktion sein.
\n\n// src/app.ts\nimport express, { Request, Response } from 'express';\nimport dotenv from 'dotenv';\n\ndotenv.config();\n\nconst app = express();\nconst PORT = process.env.PORT || 3000;\nconst VERSION = process.env.VERSION || 'stable'; // 'stable' oder 'canary'\n\napp.get('/', (req: Request, res: Response) => {\n res.json({\n message: 'Hello from API!',\n version: VERSION,\n timestamp: new Date().toISOString(),\n });\n});\n\n// Endpunkt für Health Check\napp.get('/health', (req: Request, res: Response) => {\n res.status(200).send('OK');\n});\n\napp.listen(PORT, () => {\n console.log(`Server running on port ${PORT} in ${VERSION} mode`);\n});\n\n\nJetzt erstellen wir ein Dockerfile, um die Anwendung in einen Container zu packen. Dies vereinfacht das Deployment und die Lastverteilung.
\n\n# Dockerfile\nFROM node:18-alpine AS builder\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci\nCOPY . .\nRUN npm run build\n\nFROM node:18-alpine AS runner\nWORK