Adrian RomoAdrian Romo
Alle Texte
Projekt-Log 3 Min. Lesezeit

Produktives DNS upgraden, ohne den Live-Knoten jemals zu berühren

Zwei Pi-holes, eine Floating-IP und ein Major-Upgrade auf der Box, die im ganzen Haus alles auflöst. Der Trick ist, dass das sichere Verfahren und das riskante Verfahren von außen identisch aussehen.

DNS ist der Punkt, an dem ein Homelab aufhört, nur ein Hobby zu sein. Ohne DNS funktioniert im Haus nichts, niemand im Haus interessiert sich dafür, warum, und ein Ausfall ist total, nicht nur partiell.

Bei mir läuft es als Paar: zwei Pi-hole-Instanzen, jede mit eigenem rekursivem Resolver, die sich eine Floating-IP teilen, verwaltet von VRRP. Ein Node ist Master mit höherer Priorität und preemptet; der andere ist ein warmer Backup. Clients sprechen nur mit der Floating-IP.

Kürzlich musste der Backup-Node ein Major-Upgrade bekommen — ein neues Basis-Image, das auch den eingebetteten rekursiven Resolver fallen ließ, der jetzt ebenfalls als eigener Container laufen muss. Genug bewegliche Teile, um den Dienst, von dem alles abhängt, wirklich riskant zu machen.

Das Vorgehen hängt davon ab, welchen Node du anfasst

Die gesamte Sicherheit dieser Operation beruht auf einer Vorbedingung, die vor allem anderen geprüft wird:

Bestätige, dass der Master die Floating-IP hält und dass der Node, an dem du arbeitest, der Backup ist.

Wenn das stimmt, kann der DNS des Backups blinken, neu starten, kaputtgehen oder komplett neu aufgebaut werden — mit null Auswirkungen auf die Clients, weil kein Client mit ihm spricht. Wenn das nicht stimmt — wenn der Backup still und heimlich übernommen hat und du es nicht bemerkt hast — dann führt dieselbe Befehlsfolge zu einem kompletten Ausfall.

Gleiche Befehle. Gleicher Host. Alles gleich. Der Unterschied zwischen Routinewartung und Incident ist eine Abfrage, die du zuerst machst. Ich schreibe diese Prüfung jetzt explizit als Gate ganz oben ins Runbook, mit einem „nicht fortfahren, wenn“ dahinter, weil die Version in meinem Kopf die ist, die ich überspringe, wenn ich zu selbstsicher bin.

Der Health Check ist ein Vertrag

Der Failover-Health-Check war eine einzige Zeile: kann jemand eine Verbindung zum Webinterface-Port aufbauen.

Das verwandelt ein Implementierungsdetail — den Port, auf dem das Admin-Interface lauscht — in einen tragenden Vertrag zwischen zwei Containern, die nichts voneinander wissen. Das neue Image verwendet standardmäßig einen anderen Port. Hätte ich das zugelassen, wäre der Health Check fehlgeschlagen, der Backup hätte sich als ungesund markiert, und ich hätte den Abend mit VRRP-Debugging statt DNS verbracht.

Deshalb pinnt die neue Deployment-Konfiguration den Webserver explizit auf den alten Port. Die Konfigurationszeile sieht willkürlich und überflüssig aus. Sie ist der einzige Grund, warum Failover funktioniert, und ich habe einen Absatz daneben geschrieben, der das erklärt — denn die nächste Person, die eine „willkürliche“ Portbindung aufräumt, bin ich in acht Monaten.

Zwei Nodes sind nicht zwei Konfigurationen

Ein redundantes Paar bringt ein neues Problem mit sich: Sie müssen sich einig sein. Blocklisten, lokale Records und Einstellungen driften auseinander, wenn beide editiert werden, und Drift im DNS erzeugt die schlimmste Art von Bug — Verhalten, das davon abhängt, welcher Node geantwortet hat.

Die Lösung war, sie nicht mehr als gleichberechtigte Peers für die Konfiguration zu behandeln. Ein Node ist die Quelle der Wahrheit, und ein Sync-Service schiebt eine ausgewählte Teilmenge auf den anderen. Nicht alles: Gravity und spezifische Einstellungen, bewusst eingeschränkt, denn eine vollständige Replikation würde auch die Node-Identität kopieren und damit das zunichte machen, was sie zu separaten Maschinen macht.

Redundanz ist eine Eigenschaft der Datenebene. Die Steuerungsebene sollte genau einen Schreiber haben. Zwei Pi-holes, die ich beide editieren kann, sind keine Hochverfügbarkeit, sondern zwei Pi-holes.

Was das sicher gemacht hat, war langweilig

Ein Backup vor allem anderen, in ein Verzeichnis auf dem Host, mit der alten Konfiguration daneben. Images per Digest gepinnt statt mit einem schwimmenden Tag, damit ein Rollback exakt dieselben Bytes zurückbringt. Auto-Update auf den DNS-Containern explizit deaktiviert, weil ein Dienst, von dem das ganze Haus abhängt, sich nicht ändern sollte, während ich schlafe, nur weil jemand upstream einen Tag gepusht hat.

Nichts davon ist clever. Alles davon ist der Grund, warum ein Major-Upgrade der produktiven DNS ein normaler Abend war.

Geschrieben von

Adrian Romo

Senior Backend Engineer für skalierbare Python-APIs, AWS-Lambda-Architekturen, Voice-Systeme und Enterprise-Integrationen.

Weiter geht's

Wohin als Nächstes?

Stöbere durch weitere technische Texte, sieh dir die Engineering Case Studies an oder melde dich direkt.