Zum Inhalt springen

Dies ist die mehrseitige Druckansicht dieses Abschnitts. .

Zur normalen Ansicht dieser Seite zurückkehren.

Interne Architektur

Discovery, Protokollierung und Go-Modulkonventionen für Mitwirkende an etcd.

1 - Protokoll des Discovery-Dienstes

Andere etcd-Mitglieder während der Clusterinitialisierung finden

Das Protokoll des Discovery-Dienstes hilft einem neuen etcd-Mitglied, während der Clusterinitialisierung alle anderen Mitglieder über ein gemeinsames Discovery-Token und eine Endpunktliste zu finden.

Das Protokoll des Discovery-Dienstes wird nur während der Clusterinitialisierung verwendet. Es kann nicht zur Neukonfiguration im laufenden Betrieb oder zur Clusterüberwachung genutzt werden.

Das Protokoll verwendet ein neues Discovery-Token, um einen eindeutigen etcd-Cluster zu initialisieren. Ein Discovery-Token kann nur einen etcd-Cluster repräsentieren. Sobald das Discovery-Protokoll mit diesem Token startet, darf es nicht zur Initialisierung eines anderen etcd-Clusters verwendet werden, auch wenn der Vorgang auf halbem Weg fehlschlägt.

Der Rest dieses Artikels erläutert den Discovery-Ablauf anhand von Beispielen für einen selbst betriebenen Discovery-Cluster.

Dieses Dokument behandelt ausschließlich v3-Discovery. Einzelheiten zu v2-Discovery enthält die frühere Dokumentation.

Protokollablauf

Das Discovery-Protokoll nutzt einen internen etcd-Cluster, um die Initialisierung eines neuen Clusters zu koordinieren. Zuerst kommunizieren alle neuen Mitglieder mit dem Discovery-Dienst und helfen dabei, die erwartete Mitgliederliste zu erstellen. Anschließend initialisiert jedes neue Mitglied seinen Server anhand dieser Liste. Sie erfüllt dieselbe Funktion wie die Option -initial-cluster.

Im folgenden Beispielablauf wird jeder Protokollschritt zur Veranschaulichung mit einem etcdctl-Befehl dargestellt. Dabei wird angenommen, dass unter http://example.com:2379 ein etcd-Cluster für den Discovery-Dienst läuft.

Das etcd-Discovery-Protokoll verwendet vereinbarungsgemäß das Schlüsselpräfix /_etcd/registry.

Ein neues Discovery-Token erstellen

Ein eindeutiges Token erzeugen, das den neuen Cluster identifiziert. In den folgenden Schritten dient es als eindeutiges Präfix im Discovery-Schlüsselraum. Mit uuidgen lässt es sich einfach erzeugen:

UUID=$(uuidgen)

Die erwartete Clustergröße angeben

Für das Discovery-Token muss eine Clustergröße angegeben werden. Anhand dieser Größe erkennt der Discovery-Dienst, wann er alle Mitglieder gefunden hat, die den Cluster anfänglich bilden sollen.

etcdctl --endpoints=http://example.com:2379 put /_etcd/registry/${UUID}/_config/size ${cluster_size}

Üblicherweise beträgt die Clustergröße 3, 5 oder 7. Weitere Einzelheiten enthält der Abschnitt zur optimalen Clustergröße .

etcd-Prozesse starten

Das Discovery-Token ${UUID} mit der Option --discovery-token und die Endpunkte des etcd-Clusters hinter dem Discovery-Dienst mit der Option --discovery-endpoints angeben. Damit wird v3-Discovery zur Initialisierung des etcd-Clusters aktiviert.

Wenn die Optionen --discovery-token und --discovery-endpoints angegeben sind, führt jeder etcd-Prozess intern die folgenden Schritte aus.

Wenn der Discovery-Dienst die Authentifizierung mit Client-Zertifikaten aktiviert hat, die folgenden Optionen konfigurieren. Ihre Verwendung entspricht genau der Verwendung von etcdctl zur Kommunikation mit einem etcd-Cluster.

--discovery-insecure-transport
--discovery-insecure-skip-tls-verify
--discovery-cert
--discovery-key
--discovery-cacert

Wenn der Discovery-Dienst rollenbasierte Authentifizierung aktiviert hat, die folgenden Optionen konfigurieren. Ihre Verwendung entspricht genau der Verwendung von etcdctl zur Kommunikation mit einem etcd-Cluster.

--discovery-user
--discovery-password

Die Standardwerte für Zeitintervalle und Zeitüberschreitungen können ebenfalls über die folgenden Optionen geändert werden. Ihre Verwendung entspricht genau der Verwendung von etcdctl zur Kommunikation mit einem etcd-Cluster.

--discovery-dial-timeout
--discovery-request-timeout
--discovery-keepalive-time
--discovery-keepalive-timeout

Selbstregistrierung

Zuerst registriert sich jeder etcd-Prozess als Mitglied des angegebenen neuen Clusters. Dazu legt er seine Mitglieds-ID als Schlüssel unter dem vollständigen Registrierungsschlüssel an.

etcdctl --endpoints=http://example.com:2379 put /_etcd/registry/${UUID}/members/${member_id} ${member_name}=${member_peer_url_1}&${member_name}=${member_peer_url_2}

Status prüfen

Er prüft die erwartete Clustergröße und den Registrierungsstatus und entscheidet dann über den nächsten Schritt.

etcdctl --endpoints=http://example.com:2379 get /_etcd/registry/${UUID}/_config/size
etcdctl --endpoints=http://example.com:2379 get /_etcd/registry/${UUID}/members

Wenn noch nicht genügend Mitglieder registriert sind, wartet er auf weitere Mitglieder.

Wenn die Anzahl registrierter Mitglieder die erwartete Größe N übersteigt, gelten die ersten N registrierten Mitglieder als Mitgliederliste des Clusters. Ist das Mitglied selbst in dieser Liste enthalten, ist der Discovery-Vorgang erfolgreich, und es ermittelt alle Peers anhand der Mitgliederliste. Ist es nicht enthalten, endet der Discovery-Vorgang mit der Fehlermeldung, dass der Cluster bereits voll ist.

Das Mitglied kann den Clusterstatus bereits vor seiner Registrierung prüfen. So kann es frühzeitig mit einem Fehler abbrechen, wenn der Cluster bereits voll ist.

Auf alle Mitglieder warten

Der Wartevorgang beobachtet das Schlüsselpräfix /_etcd/registry/${UUID}/members, bis alle Mitglieder gefunden sind.

etcdctl --endpoints=http://example.com:2379 watch /_etcd/registry/${UUID}/members --prefix

2 - Konventionen für die Protokollierung

Kategorien der Protokollierungsstufen

etcd verwendet die Bibliothek zap , um Anwendungsausgaben nach Stufen gegliedert zu protokollieren. Die Stufe einer Protokollmeldung richtet sich nach diesen Konventionen:

  • DebugLevel-Meldungen sind meist umfangreich und im Produktivbetrieb üblicherweise deaktiviert.

    • Beispiele:
      • Eine normale Nachricht an einen entfernten Peer senden
      • Einen Protokolleintrag auf den Datenträger schreiben
  • InfoLevel ist die standardmäßige Protokollierungspriorität.

    • Beispiele:
      • Startkonfiguration
      • Einen Snapshot beginnen
      • Einen neuen Knoten zum Cluster hinzufügen
      • Einen neuen Benutzer zum Authentifizierungssubsystem hinzufügen
  • WarnLevel-Meldungen sind wichtiger als Info-Meldungen, müssen aber nicht einzeln von einer Person geprüft werden.

    • Beispiele:
      • Fehler beim Senden einer Raft-Nachricht an einen entfernten Peer
      • Keine Heartbeat-Nachricht innerhalb des konfigurierten Wahlzeitlimits empfangen
  • ErrorLevel-Meldungen haben hohe Priorität. Wenn eine Anwendung störungsfrei läuft, sollte sie keine Meldungen dieser Stufe erzeugen.

    • Beispiele:
      • Fehler beim Zuweisen von Datenträgerspeicher für das WAL
  • PanicLevel protokolliert eine Meldung und löst anschließend eine Panic aus.

    • Beispiele:
      • Fehler beim Kodieren von Raft-Nachrichten
  • FatalLevel protokolliert eine Meldung und ruft anschließend os.Exit(1) auf.

    • Beispiele:
      • Fehler beim Speichern eines Raft-Snapshots

3 - Go-Module

Aufbau der Go-Module des etcd-Projekts

Das etcd-Projekt ist seit Version 3.5 in mehrere Go-Module gegliedert, die in einem gemeinsamen Repository liegen.

Moduldiagramm

Es gibt folgende Module:

  • go.etcd.io/etcd/api/v3 - enthält API-Definitionen (wie Protos und daraus generierte Bibliotheken), die das Kommunikationsprotokoll zwischen etcd-Clients und -Servern festlegen.

  • go.etcd.io/etcd/pkg/v3 - Sammlung von Hilfspaketen, die etcd verwendet, die jedoch nicht spezifisch für etcd sind. Ein Paket gehört nur dann hierher, wenn es künftig in ein eigenes Repository ausgelagert werden könnte. Hier sollte kein Code mit vielen eigenen Abhängigkeiten hinzugefügt werden, da diese automatisch zu Abhängigkeiten der Client-Bibliothek werden, die wir schlank halten möchten.

  • go.etcd.io/etcd/client/v3 - Client-Bibliothek für den Zugriff auf etcd über das Netzwerk (grpc). Für alle neuen Anwendungen von etcd empfohlen.

  • go.etcd.io/etcd/client/v2 - alte Client-Bibliothek für den Zugriff auf etcd über das HTTP-Protokoll. Veraltet. Alle neuen Anwendungen sollten die Bibliothek /v3 verwenden.

  • go.etcd.io/etcd/raft/v3 - Implementierung des verteilten Konsensprotokolls. Sollte keinen etcd-spezifischen Code enthalten.

  • go.etcd.io/etcd/server/v3 - etcd-Implementierung. Der Code in diesem Paket ist für den internen Gebrauch in etcd bestimmt und sollte von externen Projekten nicht verwendet werden. Der Paketaufbau und die API können sich innerhalb von Minor-Versionen ändern.

  • go.etcd.io/etcd/etcdctl/v3 - ein Befehlszeilenwerkzeug für den Zugriff auf und die Verwaltung von etcd.

  • go.etcd.io/etcd/tests/v3 - ein Modul mit allen Integrationstests von etcd. Hinweis: Alle Unit-Tests, die schnell sind und keine modulübergreifenden Abhängigkeiten benötigen, sollten in den jeweiligen Modulen beim getesteten Code liegen.

  • go.etcd.io/bbolt - Implementierung eines persistenten B-Baums. Liegt in einem separaten Repository: https://github.com/etcd-io/bbolt .

Abläufe

  1. Alle etcd-Module sollten mit denselben Versionen veröffentlicht werden, z. B. muss go.etcd.io/etcd/client/v3@v3.5.10 von go.etcd.io/etcd/api/v3@v3.5.10 abhängen.

    Die Versionen lassen sich mit folgendem Befehl einheitlich aktualisieren:

    % DRY_RUN=false TARGET_VERSION="v3.5.10" ./scripts/release_mod.sh update_versions
  2. Veröffentlichte Module sollten gemäß den Regeln unter https://golang.org/ref/mod#vcs-version mit Tags versehen werden. Jedes Modul sollte also einen eigenen Tag erhalten. Die Tags lassen sich mit folgendem Befehl setzen:

    % DRY_RUN=false REMOTE_REPO="origin" ./scripts/release_mod.sh push_mod_tags
  3. Alle etcd-Module sollten dieselben Versionen ihrer zugrunde liegenden Abhängigkeiten verwenden. Dies lässt sich folgendermaßen überprüfen:

    % PASSES="dep" ./test.sh
  4. Die go.mod-Dateien dürfen keine ungenutzten Abhängigkeiten enthalten und müssen dem Format von go mod tidy entsprechen. Dies wird folgendermaßen überprüft:

    % PASSES="mod_tidy" ./test.sh
  5. Um Aktionen über alle Module hinweg auszuführen, etwa alle Dateien automatisch zu formatieren, das folgende Skript verwenden oder erweitern:

    % ./scripts/fix.sh

Zukunft

Als langfristiges Ziel möchten wir die Entwicklung der etcd-Module hin zum folgenden Modell prüfen:

Moduldiagramm

Dies setzt Folgendes voraus:

  • etcdmigrate/etcdadm aus der etcdctl-Binärdatei ausgliedern. Dadurch würde etcdctl klar zu einer Befehlszeilenhülle für die Netzwerk-Client-API, während etcdmigrate/etcdadm direkte physische Operationen auf den Speicherdateien von etcd unterstützen würden.
  • etcd-proxy aus der Binärdatei ./etcd ausgliedern, da er mehr experimentellen Code und somit zusätzliche Risiken und Abhängigkeiten enthält.
  • Die Unterstützung des v2-Protokolls als veraltet einstufen.