Zum Inhalt springen

Blog ·

IPv4-Adresspools für ein VPS-Produkt planen

Adressen sind die eine Ressource, die Sie nicht nachkaufen können. Wie Pools zu schneiden sind, warum die Datenbank entscheidet und was bei Erschöpfung folgt.

Von Provibr

Alles andere an einem VPS-Produkt ist elastisch. Mehr CPU nötig, kaufen Sie einen Knoten. Mehr Platte nötig, kaufen Sie Platte. Mehr Adressen nötig, und Sie stehen auf einer Warteliste, zahlen einen Makler oder füllen ein Begründungsformular für eine regionale Registrierungsstelle aus.

IPv4 ist die eine Ressource, die sich wie ein physisches Gut verhält, und sie bekommt meist die geringste Aufmerksamkeit im Entwurf, denn am Anfang haben Sie ein /24 und 254 fühlt sich nach reichlich an. Es hört genau an dem Punkt auf, sich nach reichlich anzufühlen, an dem eine Neugestaltung teuer ist.

Fangen Sie bei dem an, was ein Block wirklich hergibt

Die nutzbare Anzahl ist nie die Größe des Blocks. Zwei Adressen gehen an das Netz und die Broadcast-Adresse, eine weitere an das Gateway. Bei kleinen Größen ist das kein Rundungsfehler, es ist der größte Teil des Blocks:

BlockAdressenNetz + BroadcastGatewayVerkäuflichVerlust
/24256212531 %
/273221299 %
/2816211319 %
/29821538 %
/30421175 %

Ein /29 klingt genau so lange nach acht Adressen, bis Sie fünf davon verkauft haben. Kaufen Sie Adressraum in kleinen Blöcken, planen Sie den Verlust als eigene Position ein; bei /29 werfen Sie drei Achtel dessen weg, wofür Sie bezahlt haben.

Zwei Schranken gehören in das, was Sie bauen. Wir akzeptieren höchstens ein /8, denn bei größeren Blöcken bedeutet ein Auslastungsbalken nichts mehr, und mindestens ein /30, denn /31 und /32 haben unter normalen Hostregeln null nutzbare Adressen, und sie anzunehmen erzeugt nur einen Pool, der nie zuteilen kann. Sie ausdrücklich abzulehnen ist besser, als sie anzunehmen und leer zu sein.

Noch eine Feinheit: Die Gateway-Adresse darf innerhalb Ihres Zuteilungsbereichs liegen und wird einfach übersprungen. Das ist Absicht, denn für einen Kunden ist diese Anordnung am leichtesten zu durchschauen, aber es bedeutet, dass die Kapazität eines Pools eine Berechnung und nie eine gespeicherte Zahl ist. Alles, was einen Zähler freier Adressen führt, wird an dem Tag mit der Wirklichkeit uneins sein, an dem jemand ein Gateway verschiebt.

Schneiden Sie Pools nach dem, was Sie verkaufen, nicht nach dem, was Sie gekauft haben

Die Einheit, die ein Kunde sieht, ist ein Tarif. Die Einheit, die Sie beim Makler gekauft haben, ist ein Block. Diese beiden haben fast nie dieselbe Form, und sie aneinanderzubinden geht schief, sobald Sie einen zweiten Block kaufen.

Die Anordnung, die sich gehalten hat: Ein Tarif zeigt auf einen Pool, und ein Pool ist entweder ein Präfix oder eine benannte Gruppe von Präfixen. Kapazität zu einem Produkt hinzuzufügen heißt dann, einer Gruppe ein Präfix hinzuzufügen, und das ist eine Zeile. Der Zuteiler läuft die Gruppe der Reihe nach ab und nimmt die erste Adresse, die er bekommen kann, eine Gruppe ist also zugleich eine Verbrauchsreihenfolge: Der Block, den Sie zuerst aufgebraucht haben wollen, steht vorn.

Unterblöcke, und der Fehler, der still doppelt zuteilt

Kunden nutzen ein /24 nicht als ein flaches Netz. Sie zerschneiden es: ein Stück hinter dem einen Router, ein Stück auf einem anderen VLAN, ein Stück mit eigenem Gateway. Ein Adressplan braucht also Kindblöcke.

Hier wohnt der interessante Fehlerfall, und er gehört genau ausgesprochen, denn er scheitert still.

Der Mechanismus, der zwei gleichzeitige Bestellungen daran hindert, dieselbe Adresse zu bekommen, ist ein partieller eindeutiger Index auf (Präfix, Adresse), begrenzt auf Zeilen, die nicht freigegeben sind. Dieser Index gilt je Präfix. Wenn zwei Präfixzeilen jeweils dieselbe Adresse vergeben können, dann sind das für den Index zwei verschiedene Adressen, und die Plattform gibt dieselbe IP an zwei Maschinen, ohne dass in der Software irgendetwas schiefgeht.

Der Index kann das nicht beheben. Der Aufbau muss es. Der Adressraum ist deshalb eine strikte Partition, und die Regeln, die ihn dazu machen, sind:

  1. Ein Kind liegt vollständig in seinem Elternteil und ist echt kleiner.
  2. Geschwister dürfen sich nicht überlappen.
  3. Der Elternteil vergibt nichts, was in einen Kindblock fällt. Der ganze Block wird aus dem Fenster des Elternteils herausgeschnitten, nicht bloß der eigene Zuteilungsbereich des Kindes, denn das würde die Antwort von einem Bereich abhängig machen, den irgendein Administrator nächste Woche verschiebt.
  4. Die Verschachtelung ist eine Ebene tief.

Mit diesen vieren hat „zu welchem Präfix gehört 192.0.2.70“ immer genau eine Antwort, und der vorhandene Index reicht.

Was ein Kind erbt, ist eine eigene Reihe von Entscheidungen, und die Antworten sind nicht einheitlich. Gateway: nie (ein Kind ohne eigenes Gateway hat keinen Grund zu existieren). DNS: ja, wenn das eigene Feld leer ist, denn Resolver gehören zum Netz und nicht zum Block. Zuteilungsbereich: nur dort, wo der Bereich des Elternteils das Kind tatsächlich berührt, denn der Bereich des Elternteils ist in dessen Adressen ausgedrückt und überlappt das Kind meist überhaupt nicht; ihn blind zu erben erzeugt ein leeres Fenster.

Lassen Sie die Datenbank entscheiden

Die Versuchung, wenn zwei Bestellungen gleichzeitig ankommen, ist, etwas zu sperren oder einen Zähler zu führen. Beides ist schlechter als die langweilige Antwort.

Beide Bestellungen berechnen dieselbe „niedrigste freie Adresse“ und beide versuchen einzufügen. Ein Insert gewinnt, das andere läuft in die Eindeutigkeitsverletzung, und der Verlierer geht zum nächsten Kandidaten. Die Datenbank ist die einzige Partei, die jeden Schreiber sieht, auch den in einem anderen Prozess, einem anderen Worker oder einem anderen Browsertab.

Die Folgerung zählt beim Schreiben des Codes: nicht vorher prüfen. „Ist diese Adresse frei“ zu lesen und dann einzufügen öffnet genau das Fenster, das die Bedingung schließen soll. Das fehlgeschlagene Insert ist die Antwort auf „war sie frei“, und es ist die einzige verlässliche.

Ein Umsetzungsdetail, das viele erwischt: Viele ORMs verpacken Treiberfehler in einen eigenen Fehlertyp; der SQLSTATE, den Sie brauchen, steht also nicht auf der Ausnahme, die Sie fangen, sondern auf deren Ursache. Prüfen Sie die ganze Kette, und entscheiden Sie anhand des Bedingungsnamens, welche Meldung Sie zeigen. Eine einzelne Tabelle hat oft vier verschiedene Eindeutigkeitsregeln, und sie alle dasselbe sagen zu lassen schickt den Kunden zum falschen Feld.

Was passiert, wenn ein Pool leer läuft

Hinter „keine Adresse verfügbar“ verstecken sich drei verschiedene Fehler, und sie in einen Topf zu werfen erzeugt jedes Mal ein Supportticket:

LageWas es bedeutetWas der Kunde lesen sollte
Der Tarif zeigt auf keinen PoolKonfiguration, nicht KapazitätDer Tarif hat keinen Adresspool
Ein Präfix hat unbrauchbare DatenEine kaputte Zeile, andere in OrdnungDiese Präfixe konnten nicht gelesen werden
Jeder Kandidat ist belegtEchte ErschöpfungDieser Pool ist voll, und hier steht welcher

Der mittlere ist den zusätzlichen Code wert. Ein einzelnes Präfix mit unlesbaren Daten sollte die anderen Präfixe seiner Gruppe nicht blockieren; vermerken Sie es, versuchen Sie den Rest, und melden Sie es nur, wenn überhaupt nichts funktioniert hat. Sonst legt eine schlechte Zeile die Bestellung einer ganzen Produktlinie lahm.

Und die Erschöpfungsmeldung sollte das Präfix und sein CIDR nennen. „Keine IP verfügbar“ schickt jemanden zum Logs-Lesen. „Pool voll: customer-block-2 (198.51.100.0/28)“ schickt ihn Adressen kaufen.

Geben Sie Adressen zurück, und achten Sie auf den Zeitpunkt

Die Freigabe ist die Hälfte, die still Kapazität verliert.

Zwei Regeln, die sich als wichtiger erwiesen haben als erwartet. Erstens: Freigeben ist nicht das Löschen der Zeile, es setzt einen Freigabezeitpunkt. Der partielle eindeutige Index betrachtet nur lebende Zeilen, die Adresse ist also sofort wieder nutzbar, während „wer hatte 192.0.2.10 letztes Jahr“ beantwortbar bleibt. Das ist der Hauptgrund, überhaupt eine Adressverwaltung zu betreiben, und es ist die Frage, die eine Missbrauchsmeldung stellt.

Zweitens: Die Freigabe geschieht, wenn die Maschine bestätigt weg ist, nicht wenn die Löschung angefordert wurde. Nur wer neben dem Hypervisor steht, weiß, dass die VM tatsächlich verschwunden ist; wer beim ausgehenden Befehl freigibt, übergibt die Adresse an den nächsten Kunden, während die alte Maschine noch läuft und darauf noch antwortet. Der Spiegelfall ist eine Bestellung, die auf halbem Weg scheitert: Dort wird die Zuteilung verworfen statt freigegeben, denn sie war nie auf irgendetwas konfiguriert.

Es gibt hier eine bewusste Asymmetrie, die genannt gehört, denn eine Ebene höher treffen wir die entgegengesetzte Wahl. Adressen werden wiederverwendet, die niedrigste freie zuerst. Maschinen-IDs nie. Eine späte Statusmeldung über eine gelöschte VM, die auf einer neuen Zeile mit derselben ID landet, verdirbt die Geschichte zweier Maschinen; eine Adresse trägt auf diese Weise keine Geschichte, und sie nie wiederzuverwenden würde ein /24 nach 254 Maschinen dauerhaft erschöpfen. Dasselbe Produkt, entgegengesetzte Regeln, verschiedene Gründe.

Reservierungen gehören in dieselbe Tabelle wie Zuteilungen

Sie werden Adressen zurückhalten müssen: einen Loadbalancer, ein Gateway auf einem Block, den Sie noch nicht ausgerollt haben, die fünf Adressen, die jemand einem Kunden fürs nächste Quartal versprochen hat.

Legen Sie sie in dieselbe Tabelle wie echte Zuteilungen. Jeder Leser, der fragt „was ist belegt“, also der Zuteiler, der Auslastungsbalken, die Vorschlagsliste in einem Adressfeld, zählt sie dann automatisch mit, und es gibt keine zweite Verwaltung, die aus dem Tritt geraten kann. Eine getrennte Reservierungstabelle ist eine zweite Quelle der Wahrheit, die binnen eines Monats mit der ersten uneins sein wird.

Mit einer Ausnahme, die man leicht falsch macht: Eine reservierte Adresse darf nie in der Firewall-Allowlist einer Maschine landen. Diese Liste wird aus den Zuteilungen eines Servers gebaut, und eine Reservierung gehört zu keinem Server. Machen Sie das falsch, und die VM eines Kunden nimmt Verkehr an, der an den Loadbalancer von jemand anderem gerichtet ist.

Und IPv6

Wir erkennen IPv6-Eingaben und lehnen sie höflich ab, statt sie anzunehmen und etwas Plausibles zu berechnen: Ein halb funktionierender IPv6-Pfad ist gefährlicher als keiner, denn die Zahlen, die er erzeugt, sehen richtig aus. Die billige Entscheidung, die sich bezahlt macht, ist, Adressen, Gateways und DNS-Server als Text zu speichern statt als 32-Bit-Ganzzahl, damit IPv6 später Code ist und keine Migration jeder Zeile, die Sie besitzen.

Die Kurzfassung

  • Rechnen Sie den Verlust ein. Ein /29 ergibt fünf verkäufliche Adressen, nicht acht.
  • Lassen Sie Tarife auf Pools zeigen und Pools auf Präfixe. Mehr Raum zu kaufen sollte eine Zeile sein.
  • Wenn Sie Unterblöcke erlauben, machen Sie den Adressraum zu einer strikten Partition, sonst gilt die Eindeutigkeitsregel, die Sie schützt, still je Präfix und schützt nichts.
  • Lassen Sie die Eindeutigkeitsbedingung das Rennen entscheiden. Nie vorher prüfen.
  • Geben Sie bei Bestätigung frei, nicht auf Anforderung, und bewahren Sie die Historie.
  • Nennen Sie den Pool in der Erschöpfungsmeldung. Das ist der Unterschied zwischen einem Ticket und einer Bestellung.

Das entstand beim Einbau der Adressverwaltung in Provibr, wo Adressen bei der Bestellung ausgegeben und zurückgegeben werden, sobald der Agent bestätigt, dass die Maschine tatsächlich weg ist.

Fangen Sie mit einem Host und einem Agenten an

Konto erstellen, den Agenten mit einem einzigen Befehl installieren und Ihren ersten Proxmox-Host verbinden. Die Testphase läuft 14 Tage und fragt nicht nach Zahlungsdaten.