Verteilte Systeme mit Etcd in der Praxis

Etcd ist ein in Go geschriebener, hierarchischer, verteilter Key-Value-Store. Als Bestandteil der CoreOs-Familie ist er Open Source. Anspruch der Entwickler war, die Verwaltung von verteilten Anwendungen überschaubar und gleichzeitig robust zu machen.

In Pocket speichern vorlesen Druckansicht 2 Kommentare lesen
Lesezeit: 18 Min.
Von
  • Jochen Mader
  • Conrad Pöpke
Inhaltsverzeichnis

Etcd ist ein in Go geschriebener, hierarchischer, verteilter Key-Value-Store. Als Bestandteil der CoreOs-Familie ist er Open Source. Anspruch der Entwickler war, die Verwaltung von verteilten Anwendungen überschaubar und gleichzeitig robust zu machen.

Verteilte Systeme sind heute eine Selbstverständlichkeit. Themen wie Big Data und Internet of Things (IoT) verstärken diesen Trend. Klassische Verteilungen, bei denen Frontend und Datenbank oft sogar auf derselben Maschine liefen, gehören der Vergangenheit an. An ihre Stelle sind Cloud-Systeme getreten, die das Frontend so nah wie möglich beim Kunden platzieren. Die einzelne Datenbank ist einem flexibel skalierenden Cluster gewichen. Hinzu kommen Erkenntnisse zum Thema Microservices, wie sie James Lewis und Martin Fowler beschrieben haben. Bei allen Vorzügen haben verteilte Systeme auch ihre Schattenseiten.

Verteilte Systeme bringen eine Reihe von Herausforderungen mit sich. Netzwerke können ausfallen und bringen stets Verzögerungen mit sich. Der Datendurchsatz ist endlich und kostet Geld. Durch den ständigen Austausch über das Netz entstehen zudem zusätzliche Herausforderungen an die Sicherheit beim Transport der Daten. Auch kann sich die Netzwerktopologie ohne Vorwarnung ändern.

Die Aufteilung von Anwendungen in immer kleinere Bestandteil stellt eine weitere Herausforderung dar. Der Betreiber muss im Blick behalten, wo sich die einzelnen Bestandteile des Systems befinden, wer sie koordiniert und woher sie ihre Konfiguration beziehen.

Das Ziel ist eine Möglichkeit, änderbare Informationen zentral an einem ausfallsicheren, fehlertoleranten und möglichst konsistenten Ort abzulegen. Ein vielversprechender Lösungsansatz für diese Probleme heißt Etcd.

Etcd erreichte im Januar dieses Jahres mit Version 2.0 ein Major Release. Laut den Entwicklern wurde besonderer Wert auf folgende Eigenschaften gelegt:

  • Einfachheit dank einer auf REST und JSON basierten API, die sich mit cURL bedienen lässt
  • Sicherheit durch Unterstützung von SSL-Zertifikat-Authentifizierung
  • Geschwindigkeit: Benchmarks zeigen einen Durchsatz von mehreren 1000 Schreiboperationen pro Sekunde.
  • Zuverlässigkeit: Clusterbetrieb setzt auf Raft als Konsens-Protokoll (siehe Exkurs am Ende des Artikels).

Um den praktischen Teil des Artikels nachzuvollziehen, braucht es eine Docker-Installation, cURL und eine Shell. Der einfachste Weg zu einem Etcd-Cluster führt über Docker. Das passende Image gibt es unter der folgenden URL:

docker pull quay.io/coreos/etcd:v2.0.12

Zur Vereinfachung der nächsten Schritte empfiehlt es sich, eine Umgebungsvariable mit der zu verwendenden IP-Adresse anzulegen. (Boot2Docker-Verwender finden sie in der Umgebungsvariable DOCKER_HOST oder erhalten es mit dem Befehl boot2docker ip):

export DOCKER_HOST_IP=192.168.59.103

Folgendes Kommando startet das erste Mitglied des Clusters, das dadurch zum Leader wird (siehe Kasten zum Thema Konsens):

docker run -d -p 5001:5001 -p 7001:7001 --name node1 \
quay.io/coreos/etcd:v2.0.12 -name node1 \
-advertise-client-urls http://${DOCKER_HOST_IP}:7001 \
-listen-client-urls http://0.0.0.0:7001 \
-initial-advertise-peer-urls http://${DOCKER_HOST_IP}:5001 \
-listen-peer-urls http://0.0.0.0:5001 \
-initial-cluster-token etcd-cluster-1 \
-initial-cluster node1=http://${DOCKER_HOST_IP}:5001, \
node2=http://${DOCKER_HOST_IP}:5002, \
node3=http://${DOCKER_HOST_IP}:5003

Neben den üblichen Docker-Parametern gibt es hier auch ein paar Etcd-spezifische:

  • peer-addr: IP-Adresse und Port-Nummer für die RAFT-Kommunikation,
  • addr: IP-Adresse und Port-Nummer für Client-Verbindungen,
  • name: ein eindeutiger Name für das Cluster-Mitglied,
  • initial-cluster: enthält eine Liste der initialen Cluster-Mitglieder.

Den ersten Follower für den frischen Leader startet folgendes Kommando:

docker run -d -p 5002:5002 -p 7002:7002 --name node2 \
quay.io/coreos/etcd:v2.0.12 -name node2 \
-advertise-client-urls http://${DOCKER_HOST_IP}:7002 \
-listen-client-urls http://0.0.0.0:7002 \
-initial-advertise-peer-urls http://${DOCKER_HOST_IP}:5002 \
-listen-peer-urls http://0.0.0.0:5002 \
-initial-cluster-token etcd-cluster-1 \
-initial-cluster node1=http://${DOCKER_HOST_IP}:5001, \
node2=http://${DOCKER_HOST_IP}:5002, \
node3=http://${DOCKER_HOST_IP}:5003

Im Vergleich zum ersten Befehl ist der Parameter peers hinzugekommen. Er enthält eine kommaseparierte Liste, die mindestens eines der anderen Cluster-Mitglieder enthalten sollte. Zwei weitere Follower runden das Beispiel ab:

docker run -d -p 5003:5003 -p 7003:7003 --name node3 \
quay.io/coreos/etcd:v2.0.12 -name node3 \
-advertise-client-urls http://${DOCKER_HOST_IP}:7003 \
-listen-client-urls http://0.0.0.0:7003 \
-initial-advertise-peer-urls http://${DOCKER_HOST_IP}:5003 \
-listen-peer-urls http://0.0.0.0:5003 \
-initial-cluster-token etcd-cluster-1 \
-initial-cluster node1=http://${DOCKER_HOST_IP}:5001, \
node2=http://${DOCKER_HOST_IP}:5002, \
node3=http://${DOCKER_HOST_IP}:5003

Folgendes Kommando überprüft, ob alle Instanzen korrekt gestartet sind:

curl -L http://${DOCKER_HOST_IP}:7001/version

Die Ereignismeldung etcd 2.0.12 bedeutet, dass der erste 3-Node-Cluster steht.

Werte in Etcd können entweder einfache Key-Value- oder über einen Key identifizierte Verzeichnis-Knoten sein. Jeder Knoten kann somit eindeutig über seinen Pfad identifiziert werden, beispielsweise mit /Verzeichnis1/Verzeichnis2/Key1.

Für die weitere Arbeit stehen folgende Funktionen zur Verfügung:

  • beliebige Schachtelung von Verzeichnissen,
  • optionale Vergabe von automatisch erzeugten Schlüsseln zur Reihenfolgesicherung,
  • TTL (time to live) zur Begrenzung der Lebenszeit für Verzeichnis- und Key-Value-Knoten,
  • Check-and-Swap-/Check-and-Delete-Operation für atomare Schreiboperationen,
  • Wait-for-Change-Funktionalität, um Änderungen von Knoten zu beobachten.

Im Folgenden werden die wichtigsten Funktionen vorgestellt.