Der Autor hat die Wikimedia Foundation dazu ausgewählt, im Rahmen des Programms Write for DOnations eine Spende zu erhalten.
etcd ist ein verteilter Schlüsselwertspeicher, auf den sich viele Plattformen und Tools verlassen, darunter Kubernetes, Vulcand und Doorman. Innerhalb von Kubernetes dient etcd als globaler Konfigurationsspeicher, der den Status des Clusters speichert. Kenntnisse zur Verwaltung von etcd sind unerlässlich für die Verwaltung eines Kubernetes-Clusters. Zwar gibt es viele verwaltete Kubernetes-Produkte (auch als Kubernetes-as-a-Service bekannt), die diese Administrationsaufgaben für Sie übernehmen, doch entscheiden sich viele Unternehmen wegen der damit verbundenen Flexibilität immer noch für selbstverwaltete Kubernetes-Cluster.
Die erste Hälfte dieses Artikels führt Sie durch die Einrichtung eines etcd-Clusters mit drei Knoten auf Ubuntu 18.04-Servern. In der zweiten Hälfte geht es um das Sichern des Clusters mit Transport Layer Security oder TLS. Um jede Einrichtung automatisiert auszuführen, verwenden wir durchgehend Ansible. Ansible ist ein Konfigurationsmanagement-Tool ähnlich wie Puppet, Chef, und SaltStack; damit können wir die einzelnen Einrichtungsschritte auf deklarative Weise definieren, und zwar in Dateien namens Playbooks.
Am Ende dieses Tutorials verfügen Sie über einen sicheren etcd-Cluster mit drei Knoten, der auf Ihren Servern ausgeführt wird. Außerdem werden Sie über ein Ansible-Playbook verfügen, mit dem Sie die gleiche Einrichtung auf einem neuen Satz von Servern wiederholt und konsequent nachbilden können.
Bevor Sie diese Anleitung beginnen, benötigen Sie Folgendes:
Python, pip
und das auf Ihrem lokalen Computer installierte pyOpenSSL
-Paket. Um zu erfahren, wie Sie Python3, pip und Python-Pakete installieren können, lesen Sie Installieren von Python 3 und Einrichten einer lokalen Programmierumgebung unter Ubuntu 18.04.
Drei Ubuntu 18.04-Server im gleichen lokalen Netzwerk mit mindestens 2 GB RAM und root SSH-Zugriff. Außerdem sollten Sie die Server so konfigurieren, dass sie die Hostnamen etcd1, etcd2 und etcd3 tragen. Die in diesem Artikel beschriebenen Schritte würden auf jedem generischen Server funktionieren, nicht nur bei DigitalOcean Droplets. Wenn Sie Ihre Server aber in DigitalOcean hosten möchten, können Sie dem Leitfaden Erstellen eines Droplets über das DigitalOcean Control Panel folgen, um diese Anforderung zu erfüllen. Beachten Sie, dass Sie bei der Erstellung Ihres Droplets die Option Private Networking aktivieren müssen. Um für vorhandene Droplets private Netzwerke zu aktivieren, lesen Sie Aktivieren von Private Networking in Droplets.
Warnung: Da der Zweck dieses Artikels darin besteht, eine Einführung in das Einrichten eines etcd-Clusters in einem privaten Netzwerk zu liefern, wurden die drei Ubuntu 18.04-Server in dieser Einrichtung nicht mit einer Firewall getestet und als root user aufgerufen. In einer Produktionsumgebung würde jeder dem öffentlichen Internet ausgesetzte Knoten eine Firewall und einen Sudo-Benutzer erfordern, damit sich bewährte Sicherheitspraktiken einhalten lassen. Weitere Informationen finden Sie im Tutorial Ersteinrichtung des Servers mit Ubuntu 18.04.
Ein SSH-Schlüsselpaar, das Ihrem lokalen Rechner Zugriff auf die Server etcd1, etcd2 und etcd3 erlaubt. Wenn Sie nicht wissen, was SSH ist oder über kein SSH-Schlüsselpaar verfügen, können Sie hier mehr darüber erfahren: SSH Essentials: Working with SSH Servers, Clients, and Keys (SSH-Grundlagen: Arbeiten mit SSH-Servern, -Clients und -Schlüsseln).
Auf Ihrem lokalen Rechner installiertes Ansible. Wenn Sie beispielsweise Ubuntu 18.04 ausführen, können Sie Ansible installieren, indem Sie Schritt 1 des Artikels Installieren und Konfigurieren von Ansible unter Ubuntu 18.04 befolgen. Dadurch werden die Befehle ansible
und ansible-playbook
auf Ihrem Computer verfügbar. Vielleicht möchten Sie auch How to Use Ansible: A Reference Guide (Verwenden von Ansible: Ein Referenzhandbuch) parat halten. Die Befehle in diesem Tutorial sollten mit Ansible v2.x funktionieren; wir haben sie in Ansible v2.9.7 unter Ausführung von Python v3.8.2 getestet.
Ansible ist ein Tool, das zum Verwalten von Servern dient. Die Server, die Ansible verwaltet, werden verwaltete Knoten genannt. Das Gerät, auf dem Ansible ausgeführt wird, wird als Steuerknoten bezeichnet. Ansible arbeitet mit SSH-Schlüsseln im Steuerknoten, um Zugriff auf die verwalteten Knoten zu erhalten. Sobald eine SSH-Sitzung eingerichtet ist, führt Ansible eine Reihe von Skripten aus, um die verwalteten Knoten bereitzustellen und zu konfigurieren. In diesem Schritt testen wir, ob wir Ansible zur Verbindungsherstellung mit den verwalteten Knoten verwenden und den Befehl hostname
ausführen können.
Ein typischer Tag für einen Systemadministrator kann das Verwalten verschiedener Sätze von Knoten beinhalten. Beispielsweise können Sie Ansible verwenden, um neue Server bereitzustellen; später aber verwenden Sie es, um einen anderen Satz von Servern neu zu konfigurieren. Um Administratoren eine bessere Organisation des Satzes von verwalteten Knoten zu ermöglichen, verfügt Ansible über das Konzept des Hostinventars (oder kurz Inventar). Sie können jeden Knoten, den Sie mit Ansible verwalten möchten, in einer Inventardatei definieren und in Gruppen anordnen. Wenn Sie dann die Befehle ansible
und ansible-playbook
ausführen, können Sie angeben, für welche Hosts oder Gruppen der Befehl gelten soll.
Standardmäßig liest Ansible die Inventardatei von /etc/ansible/hosts
; wir können jedoch eine andere Inventardatei angeben, indem wir das Flag --inventory
(oder kurz -i
) verwenden.
Erstellen Sie zunächst ein neues Verzeichnis auf Ihrem lokalen Rechner (dem Steuerknoten), in dem alle Dateien für dieses Tutorial installiert werden:
Rufen Sie dann das gerade erstellte Verzeichnis auf:
Erstellen und öffnen Sie im Verzeichnis mit Ihrem Editor eine leere Inventardatei namens hosts
:
Listen Sie in der Datei hosts
alle Ihre verwalteten Knoten im folgenden Format auf und ersetzen Sie die markierten öffentlichen IP-Adressen durch die wahren öffentlichen IP-Adressen Ihrer Server:
Die Zeile [etcd]
definiert eine Gruppe namens etcd
. Unter der Gruppendefinition listen wir alle unsere verwalteten Knoten auf. Jede Zeile beginnt mit einem Alias (z. B. etcd1
), mit dem wir unter Verwendung eines leicht zu merkenden Namens anstelle einer langen IP-Adresse auf jeden einzelnen Host verweisen können. Die Variablen ansible_host
und ansible_user
sind Ansible-Variablen. In diesem Fall dienen sie zur Bereitstellung von Ansible mit den öffentlichen IP-Adressen und SSH-Benutzernamen, die beim Herstellen einer Verbindung über SSH verwendet werden.
Um zu prüfen, ob Ansible eine Verbindung mit unseren verwalteten Knoten herstellen kann, testen wir mithilfe von Ansible durch Ausführung des Befehls hostname
auf den einzelnen Hosts in der Gruppe etcd
die Konnektivität:
Lassen Sie uns diesen Befehl genauer ansehen, um zu erfahren, was die einzelnen Teile bedeuten:
etcd
: gibt das Hostmuster an, mit dem ermittelt wird, welche Hosts aus dem Inventar mit diesem Befehl verwaltet werden. Hier verwenden wir den Gruppennamen als Hostmuster.-i hosts
: gibt die zu verwendende Inventardatei an.-m command
: Die Funktionalität hinter Ansible wird von Modulen bereitgestellt. Das command
-Modul nimmt das übergebene Argument und führt es als Befehl auf den einzelnen verwalteten Knoten aus. Im Verlauf dieses Tutorials werden noch einige weitere Ansible-Module eingeführt.-a hostname
: das Argument, das an das Modul übergeben wird. Die Zahl und Arten von Argumenten hängen vom Modul ab.Nach Ausführung des Befehls sehen Sie die folgende Ausgabe, was bedeutet, dass Ansible richtig konfiguriert wurde:
Outputetcd2 | CHANGED | rc=0 >>
etcd2
etcd3 | CHANGED | rc=0 >>
etcd3
etcd1 | CHANGED | rc=0 >>
etcd1
Jeder Befehl, den Ansible ausführt, wird als Aufgabe bezeichnet. Die Verwendung von ansible
in der Befehlszeile zum Ausführen von Aufgaben wird Ausführung von ad-hoc-Befehlen genannt. Der Vorteil von Ad-hoc-Befehlen besteht darin, dass sie schnell sind und wenig Einrichtung benötigen; der Nachteil ist, dass sie manuell ausgeführt werden und sich somit nicht für ein Versionskontrollsystem wie Git verwenden lassen.
Eine kleine Verbesserung wäre es, ein Shell-Skript zu schreiben und unsere Befehle mit dem script
-Modul von Ansible auszuführen. So könnten wir die Konfigurationsschritte, die wir ergriffen haben, in die Versionskontrolle aufnehmen. Allerdings sind Shell-Skripte imperativ. Das bedeutet, dass wir die auszuführenden Befehle (die „wie“s) ermitteln müssen, um das System mit Blick auf den gewünschten Zustand zu konfigurieren. Ansible hingegen setzt auf einen deklarativen Ansatz, bei dem wir definieren, „was“ der gewünschte Zustand unseres Servers in Konfigurationsdateien sein sollte. Ansible ist dafür verantwortlich, den Server in den gewünschten Zustand zu bringen.
Der deklarative Ansatz wird bevorzugt, da die Absicht der Konfigurationsdatei sofort übermittelt wird, was bedeutet, dass er leichter zu verstehen und zu verwalten ist. Außerdem wird dabei die Verantwortung für die Bearbeitung von Edge-Fällen vom Administrator auf Ansible übertragen, was uns eine Menge Arbeit spart.
Nachdem Sie nun den Ansible-Steuerknoten zur Kommunikation mit den verwalteten Knoten konfiguriert haben, stellen wir Ihnen im nächsten Schritt Ansible-Playbooks vor, mit denen Sie Aufgaben deklarativ angeben können.
In diesem Schritt werden wir replizieren, was wir in Schritt 1 getan haben: das Ausdrucken der Hostnamen der verwalteten Knoten. Anstatt Ad-hoc-Aufgaben auszuführen, werden wir die einzelnen Aufgaben jedoch deklarativ als Ansible-Playbook definieren und ausführen. Ziel dieses Schritt ist es, zu zeigen, wie Ansible Playbooks funktionieren. Wir werden mit Playbooks in späteren Schritten noch deutlich umfangreichere Aufgaben ausführen.
Erstellen Sie in Ihrem Projektverzeichnis mit Ihrem Editor eine neue Datei namens playbook.yaml
:
Fügen Sie in playbook.yaml
die folgenden Zeilen hinzu:
Schließen und speichern Sie die Datei playbook.yaml
, indem Sie Strg+X
drücken, gefolgt von J
.
Das Playbook enthält eine Liste von Plays; jedes Play enthält eine Liste von Aufgaben, die auf allen Hosts ausgeführt werden sollen, die mit dem vom Schlüssel hosts
angegebenen Hostmuster übereinstimmen. In diesem Playbook verfügen wir über ein Play, das zwei Aufgaben enthält. Die erste Aufgabe führt den Befehl hostname
mit dem command
-Modul aus und registriert die Ausgabe in einer Variable namens output
. In der zweiten Aufgabe verwenden wir das debug
-Modul, um die Eigenschaft stdout_lines
der output
-Variablen auszugeben.
Wir können dieses Playbook nun mit dem Befehl ansible-playbook
ausführen:
Sie erhalten die folgende Ausgabe, was bedeutet, dass Ihr Playbook ordnungsgemäß funktioniert:
OutputPLAY [etcd] ***********************************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************************
ok: [etcd2]
ok: [etcd3]
ok: [etcd1]
TASK [Retrieve hostname] **********************************************************************************************************
changed: [etcd2]
changed: [etcd3]
changed: [etcd1]
TASK [Print hostname] *************************************************************************************************************
ok: [etcd1] => {
"output.stdout_lines": [
"etcd1"
]
}
ok: [etcd2] => {
"output.stdout_lines": [
"etcd2"
]
}
ok: [etcd3] => {
"output.stdout_lines": [
"etcd3"
]
}
PLAY RECAP ************************************************************************************************************************
etcd1 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
etcd2 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
etcd3 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Anmerkung: ansible-playbook
verwendet zum Teil cowsay
als verspielte Methode zur Ausgabe der Überschriften. Wenn Sie in Ihrem Terminal viele ASCII-artige Kühe sehen, wissen Sie jetzt warum. Um diese Funktion zu deaktivieren, setzen Sie die Umgebungsvariable ANSIBLE_NOCOWS
vor dem Ausführen von ansible-playbook
auf 1
, indem Sie in Ihrer Shell export ANSIBLE_NOCOWS=1
ausführen.
In diesem Schritt sind wir von der Ausführung von imperativen Ad-hoc-Aufgaben zum Ausführen von deklarativen Playbooks übergegangen. Im nächsten Schritt ersetzen wir diese beiden Vorführaufgaben durch Aufgaben, die für die Einrichtung unseres etcd-Clusters sorgen werden.
In diesem Schritt zeigen wir Ihnen die Befehle zur manuellen Installation von etcd
und demonstrieren, wie Sie die gleichen Befehle in unserem Ansible-Playbook in Aufgaben übersetzen können.
etcd
und dessen Client etcdctl
sind als Binärdateien verfügbar, die wir herunterladen, extrahieren und in einem Verzeichnis platzieren werden, das Teil der PATH
-Umgebungsvariablen ist. Bei manueller Konfiguration sind dies die Schritte, die wir auf jedem der verwalteten Knoten ausführen würden:
Die ersten vier Befehle sorgen dafür, dass die Binärdateien heruntergeladen und im Verzeichnis /opt/etcd/bin/
extrahiert werden. Standardmäßig nutzt der etcdctl
-Client API v2 zur Kommunikation mit dem etcd
-Server. Da wir etcd v3.x ausführen, setzt der letzte Befehl die Umgebungsvariable ETCDCTL_API
auf 3
.
Anmerkung: Hier verwenden wir etcd v3.3.13, was für Rechner mit Prozessoren entwickelt wurde, die das AMD64-Anweisungsset verwenden. Auf der offiziellen GitHub Release-Seite finden Sie Binärdateien für andere Systeme und Versionen.
Um die gleichen Schritte in einer standardisierten Form zu replizieren, können wir unserem Playbook Aufgaben hinzufügen. Öffnen Sie die Playbook-Datei playbook.yaml
in Ihrem Editor:
Ersetzen Sie die gesamte Datei playbook.yaml
durch folgende Inhalte:
Jede Aufgabe nutzt ein Modul; für diesen Satz von Aufgaben verwenden wir folgende Module:
file
: zum Erstellen des Verzeichnisses /opt/etcd/bin
und zum späteren Festlegen der Dateiberechtigungen für die Binärdateien etcd
und etcdctl
.get_url
: zum Herunterladen des gzip-ten Tarball auf die verwalteten Knoten.unarchive
: zum Extrahieren und Entpacken der Binärdateien etcd
und etcdctl
aus dem gzip-ten Tarball.lineinfile
: zum Hinzufügen eines Eintrags in die Datei .profile
.Um diese Änderungen anzuwenden, schließen und speichern Sie die Datei playbook.yaml
, indem Sie Strg+X
drücken, gefolgt von J
. Führen Sie dann im Terminal den gleichen Befehl ansible-playbook
erneut aus:
Der Abschnitt PLAY RECAP
der Ausgabe wird nur ok
und changed
anzeigen:
Output...
PLAY RECAP ************************************************************************************************************************
etcd1 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
etcd2 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
etcd3 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Um die ordnungsgemäße Installation von etcd zu prüfen, stellen Sie manuell eine SSH-Verbindung zu einem der verwalteten Knoten her und führen Sie etcd
und etcdctl
aus:
etcd1_public_ip
ist die öffentliche IP-Adresse des Servers namens etcd1. Sobald Sie sich SSH-Zugriff verschafft haben, führen Sie etcd --version
aus, um die installierte Version von etcd auszudrucken:
Sie werden eine Ausgabe erhalten, die in etwa der folgenden ähnelt. Das bedeutet, dass die Binärdatei etcd
erfolgreich installiert wurde:
Outputetcd Version: 3.3.13
Git SHA: 98d3084
Go Version: go1.10.8
Go OS/Arch: linux/amd64
Um sich zu vergewissern, dass etcdctl
erfolgreich installiert wurde, führen Sie etcdctl version
aus:
Sie werden eine Ausgabe sehen, die etwa folgendermaßen aussieht:
Outputetcdctl version: 3.3.13
API version: 3.3
Beachten Sie, dass die Ausgabe API version: 3.3
lautet, wodurch bestätigt wird, dass unsere Umgebungsvariable ETCDCTL_API
richtig festgelegt wurde.
Beenden Sie den etcd1-Server, um zu Ihrer lokalen Umgebung zurückzukehren.
Wir haben etcd
und etcdctl
nun erfolgreich auf allen unseren verwalteten Knoten installiert. Im nächsten Schritt fügen wir unserem Play zusätzliche Aufgaben hinzu, sodass etcd als Hintergrunddienst ausgeführt wird.
Die schnellste Methode zur Ausführung von etcd mit Ansible scheint die Verwendung des command
-Moduls zur Ausführung von /opt/etcd/bin/etcd
zu sein. Das funktioniert jedoch nicht, da etcd
dadurch als Vordergrundprozess ausgeführt wird. Durch die Verwendung des command
-Moduls wird Ansible hängenbleiben, da es auf die Rückgabe des Befehl etcd
wartet, was nie geschehen wird. In diesem Schritt werden wir unser Playbook also so aktualisieren, dass stattdessen unsere Binärdatei etcd
als Hintergrunddienst ausgeführt wird.
Ubuntu 18.04 verwendet systemd als sein init-System. Das bedeutet, dass wir neue Dienste erstellen können, indem wir Unit-Dateien schreiben und im Verzeichnis /etc/systemd/system/
platzieren.
Erstellen Sie zunächst in Ihrem Projektverzeichnis ein neues Verzeichnis namens files/
:
Erstellen Sie dann in diesem Verzeichnis mit Ihrem Editor eine neue Datei namens etcd.service
:
Kopieren Sie als Nächstes den folgenden Codeblock in die Datei files/etcd.service
:
Diese Unit-Datei definiert einen Dienst, der die ausführbare Datei unter /opt/etcd/bin/etcd
ausführt, systemd benachrichtigt, sobald die Initialisierung beendet ist, und immer neu startet, sollte sie je beendet werden.
Anmerkung: Wenn Sie mehr über systemd und Unit-Dateien erfahren möchten oder die Unit-Datei an Ihre Bedürfnisse anpassen möchten, lesen Sie den Leitfaden Understanding Systemd Units and Unit Files (systemd-Units und Unit-Dateien verstehen).
Schließen und speichern Sie die Datei files/etcd.service
, indem Sie Strg+X
drücken, gefolgt von Y
.
Als Nächstes müssen wir eine Aufgabe in unserem Playbook hinzufügen, die die lokale Datei files/etcd.service
für die einzelnen verwalteten Knoten in das Verzeichnis /etc/systemd/system/etcd.service
kopiert. Wir können dies mit dem copy
-Modul tun.
Öffnen Sie Ihr Playbook:
Fügen Sie am Ende der bestehenden Aufgaben die folgende hervorgehobene Aufgabe an:
Durch Kopieren der Unit-Datei in /etc/systemd/system/etcd.service
wird nun ein Dienst definiert.
Speichern und schließen Sie das Playbook.
Führen Sie den gleichen Befehl ansible-playbook
erneut aus, um die neuen Änderungen anzuwenden:
Um zu prüfen, ob die Änderungen angewendet wurden, stellen Sie zunächst eine SSH-Verbindung mit einem der verwalteten Knoten her:
Führen Sie dann systemctl status etcd
aus, um systemd über den Status des Diensts etcd
abzufragen:
Sie erhalten die folgende Ausgabe, in der angegeben wird, dass der Dienst geladen wurde:
Output● etcd.service - etcd distributed reliable key-value store
Loaded: loaded (/etc/systemd/system/etcd.service; static; vendor preset: enabled)
Active: inactive (dead)
...
Anmerkung: Die letzte Zeile (Active: inactive (dead)
) der Ausgabestatus gibt an, dass der Dienst inaktiv ist. Das bedeutet, dass er beim Starten des Systems nicht automatisch ausgeführt würde. Dies ist zu erwarten und kein Fehler.
Drücken Sie q
, um zur Shell zurückzukehren, und führen Sie dann exit
aus, um den verwalteten Knoten zu verlassen und zu Ihrer lokalen Shell zurückzukehren:
In diesem Schritt haben wir unser Playbook so aktualisiert, das es die Binärdatei etcd
als systemd-Dienst ausführt. Im nächsten Schritt werden wir etcd weiter einrichten, indem wir Platz zur Speicherung seiner Daten zur Verfügung stellen.
etcd ist ein Datenspeicher für Schlüsselwerte. Das bedeutet, dass wir ihm Platz zur Speicherung seiner Daten zur Verfügung stellen müssen. In diesem Schritt werden wir unser Playbook so aktualisieren, dass ein dediziertes Datenverzeichnis zur Verwendung durch etcd definiert wird.
Öffnen Sie Ihr Playbook:
Fügen Sie am Ende der Liste der Aufgaben die folgende Aufgabe an:
Hier verwenden wir /var/lib/etcd/hostname.etcd
als Datenverzeichnis, wobei hostname
der Hostname des aktuellen verwalteten Knotens ist. inventory_hostname
ist eine Variable, die den Hostnamen des aktuellen verwalteten Knoten darstellt; ihr Wert wird automatisch von Ansible ausgefüllt. Die Syntax mit geschweiften Klammern (d. h. {{ inventory_hostname }}
) wird zur Variablenersetzung genutzt, unterstützt durch die Jinja2-Vorlagen-Engine, die die standardmäßige Vorlagen-Engine für Ansible ist.
Schließen Sie den Texteditor und speichern Sie die Datei.
Als Nächstes müssen wir etcd anweisen, dieses Datenverzeichnis zu verwenden. Dazu übergeben wir den Parameter data-dir
an etcd. Zum Festlegen von etcd-Parametern können wir eine Kombination aus Umgebungsvariablen, Befehlszeilen-Flags und Konfigurationsdateien verwenden. In diesem Tutorial verwenden wir eine Konfigurationsdatei, da es deutlich eleganter ist, alle Konfigurationen in einer Datei zu isolieren, anstatt die Konfiguration über unser ganzes Playbook zu verteilen.
Erstellen Sie in Ihrem Projektverzeichnis ein neues Verzeichnis namens templates/
:
Erstellen Sie dann in dem Verzeichnis mit Ihrem Editor eine neue Datei namens etcd.conf.yaml.j2
:
Kopieren Sie als Nächstes die folgende Zeile und fügen Sie sie in die Datei ein:
Diese Datei verwendet die gleiche Jinja2-Variablenersetzungssyntax wie unser Playbook. Um die Variablen zu ersetzen und das Ergebnis in die einzelnen verwalteten Hosts hochzuladen, können wir das template
-Modul verwenden. Es funktioniert auf ähnliche Weise wie copy
, nimmt vor dem Upload jedoch eine Variablenersetzung vor.
Beenden Sie etcd.conf.yaml.j2
und öffnen Sie dann Ihr Playbook:
Fügen Sie der Liste der Aufgaben die folgenden Aufgaben an, um ein Verzeichnis zu erstellen und die vorlagenbasierte Konfigurationsdatei darin hochzuladen:
Speichern und schließen Sie diese Datei.
Da wir diese Änderung vorgenommen haben, müssen wir nun die Unit-Datei unseres Diensts aktualisieren, damit ihr der Speicherort unserer Konfigurationsdatei übergeben wird (d. h. /etc/etcd/etcd.conf.yaml
).
Öffnen Sie die Datei etcd.service auf Ihrem lokalen Rechner:
Aktualisieren Sie die Datei files/etcd.service
, indem Sie das im Folgenden hervorgehobene Flag --config-file
hinzufügen:
Speichern und schließen Sie die Datei.
In diesem Schritt haben wir unser Playbook zur Bereitstellung eines Datenverzeichnisses für etcd zum Speichern seiner Daten verwendet. Im nächsten Schritt werden wir noch einige Aufgaben hinzufügen, um den etcd
-Dienst neu zu starten und für ein Ausführen beim Systemstart zu sorgen.
Jedes Mal wenn wir Änderungen an der Unit-Datei eines Diensts vornehmen, müssen wir diesen Dienst neu starten, damit die Änderungen wirksam werden. Wir können dazu den Befehl systemctl restart etcd
ausführen. Damit der etcd
-Dienst beim Systemstart automatisch gestartet wird, müssen wir systemctl enable etcd
ausführen. In diesem Schritt werden wir mit dem Playbook diese beiden Befehle ausführen.
Um Befehle auszuführen, können wir das command
-Modul verwenden:
Fügen Sie am Ende der Aufgabenliste die folgenden Aufgaben an:
Speichern und schließen Sie die Datei.
Führen Sie ansible-playbook -i hosts playbook.yaml
erneut aus:
Um zu überprüfen, ob der Dienst etcd
neu gestartet und aktiviert wurde, stellen Sie eine SSH-Verbindung zu einem der verwalteten Knoten her:
Führen Sie dann systemctl status etcd
aus, um den Status des etcd
-Diensts zu überprüfen:
Sie werden im Folgenden enabled
und active (running)
als hervorgehoben sehen; das bedeutet, dass die Änderungen, die wir in unserem Playbook vorgenommen haben, wirksam geworden sind:
Output● etcd.service - etcd distributed reliable key-value store
Loaded: loaded (/etc/systemd/system/etcd.service; static; vendor preset: enabled)
Active: active (running)
Main PID: 19085 (etcd)
Tasks: 11 (limit: 2362)
In diesem Schritt haben wir das command
-Modul zur Ausführung von systemctl
-Befehlen verwendet, die den Dienst etcd
neu starten und auf unseren verwalteten Knoten aktivieren. Nachdem wir eine etcd-Installation eingerichtet haben, testen wir nun im nächsten Schritt ihre Funktionalität durch Ausführung von CRUD-Operationen zum Erstellen, Lesen, Aktualisieren und Löschen.
Zwar verfügen wir über eine funktionierende etcd-Installation, doch ist diese unsicher und noch nicht bereit für den Produktionseinsatz. Bevor wir aber unsere etcd-Installation in späteren Schritten sichern, sollten wir zunächst wissen, was etcd für Funktionen bietet. In diesem Schritt werden wir manuell Anfragen an etcd senden, um Daten hinzuzufügen, abzurufen, zu aktualisieren und zu löschen.
Standardmäßig macht etcd eine API verfügbar, die an Port 2379
auf Client-Kommunikation lauscht. Das bedeutet, dass wir mit einem HTTP-Client rohe API-Anfragen an etcd senden können. Es ist jedoch schneller, den offiziellen etcd-Client etcdctl
zu verwenden. Damit können Sie Schlüsselwertpaare mit den Unterbefehlen put
, get
bzw. del
erstellen/aktualisieren, abrufen und löschen.
Stellen Sie sicher, dass Sie sich noch im verwalteten Knoten etcd1 befinden, und führen Sie die folgenden etcdctl
-Befehle aus, um sich zu vergewissern, dass Ihre etcd-Installation richtig funktioniert.
Erstellen Sie zunächst mit dem Unterbefehl put
einen neuen Eintrag.
Der Unterbefehl put
weist die folgende Syntax auf:
etcdctl put key value
Führen Sie für etcd1 folgenden Befehl aus:
Der Befehl, den wir gerade ausgeführt haben, weist etcd an, den Wert "bar"
im Speicher in den Schlüssel foo
zu schreiben.
Dann werden Sie OK
in der Ausgabe sehen, was angibt, dass die Daten persistiert wurden:
OutputOK
Wir können diesen Eintrag dann mit dem Unterbefehl get
abrufen, der die Syntax etcdctl get key
hat:
Sie werden diese Ausgabe finden, die den Schlüssel in der ersten Zeile und den Wert anzeigt, den Sie zuvor in der zweiten Zeile eingefügt haben:
Outputfoo
bar
Wir können den Eintrag mit dem Unterbefehl del
löschen, der die Syntax etcdctl del key
hat:
Sie werden die folgende Ausgabe sehen, die die Anzahl der gelöschten Einträge angibt:
Output1
Lassen Sie uns nun den Unterbefehl get
erneut ausführen, um zu versuchen, ein gelöschtes Schlüssel-Wert-Paar abzurufen:
Sie erhalten keine Ausgabe, was bedeutet, dass etcdctl
das Schlüssel-Wert-Paar nicht abrufen kann. Dadurch wird bestätigt, dass der Eintrag nach dem Löschen nicht mehr abgerufen werden kann.
Nachdem Sie die grundlegenden Operationen von etcd und etcdctl
getestet haben, verlassen wir nun unseren verwalteten Knoten und kehren zurück zu der lokalen Umgebung:
In diesem Schritt haben wir den etcdctl
-Client zum Senden von Anfragen an etcd verwendet. An diesem Punkt führen wir drei separate Instanzen von etcd aus, die jeweils unabhängig voneinander agieren. Jedoch ist etcd als verteilter Schlüssel-Wert-Speicher konzipiert; das bedeutet, dass sich mehrere etcd-Instanzen gruppieren können, um einen einzelnen Cluster zu bilden; jede Instanz wird dann ein Member (Mitglied) des Clusters. Nach der Einrichtung eines Clusters könnten Sie ein Schlüssel-Wert-Paar abrufen, das von einem anderen Memberknoten des Clusters eingefügt wurde. Im nächsten Schritt werden wir unser Playbook verwenden, um unsere drei 1-Node-Cluster in einen einzigen 3-Node-Cluster zu verwandeln.
Um anstelle von drei 1-Node-Clustern einen 3-Node-Cluster zu erstellen, müssen wir die etcd-Installationen so konfigurieren, dass sie miteinander kommunizieren. Das bedeutet, dass alle die IP-Adressen der anderen kennen müssen. Dieser Prozess wird Erkennung genannt. Erkennung kann entweder mit statischer Konfiguration oder mit einer dynamischen Diensterkennung erfolgen. In diesem Schritt werden wir den Unterschied zwischen den beiden erörtern sowie unser Playbook aktualisieren, um einen etcd-Cluster mit statischer Erkennung einzurichten.
Erkennung mit statischer Konfiguration ist die Methode, die die geringste Einrichtung erfordert; hier werden die Endpunkte der einzelnen Memberknoten in den Befehl etcd
übergeben, bevor er ausgeführt wird. Um statische Konfiguration zu verwenden, müssen vor der Initialisierung des Clusters folgende Bedingungen erfüllt sein:
Wenn sich diese Bedingungen nicht erfüllen lassen, können Sie einen dynamischen Erkennungsdienst verwenden. Bei der dynamischen Diensterkennung würden sich alle Instanzen beim Erkennungsdienst registrieren, damit jeder Memberknoten Informationen über den Ort anderer Memberknoten abrufen kann.
Da wir wissen, dass wir einen 3-Node-etcd-Cluster nutzen möchten und alle unsere Server über statische IP-Adressen verfügen, werden wir statische Erkennung verwenden. Um unseren Cluster mit statischer Erkennung zu initiieren, müssen wir unserer Konfigurationsdatei mehrere Parameter hinzufügen. Verwenden Sie einen Editor, um die Vorlagendatei templates/etcd.conf.yaml.j2
zu öffnen:
Fügen Sie dann die folgenden hervorgehobenen Zeilen hinzu:
Schließen und speichern Sie die Datei templates/etcd.conf.yaml.j2
, indem Sie Strg+X
drücken, gefolgt von J
.
Hier ist eine kurze Erläuterung der einzelnen Parameter:
name
- ein menschenlesbarer Name für den Memberknoten. Standardmäßig verwendet etcd eine eindeutige, zufällig generierte ID zur Identifizierung der einzelnen Memberknoten; ein menschlich lesbarer Name erleichtert jedoch in Konfigurationsdateien und in der Befehlszeile das Verweisen darauf. Hier werden wir die Hostnamen als Membernamen (d. h. etcd1
, etcd2
und etcd3
) verwenden.initial-advertise-peer-urls
- eine Liste mit IP-Adress-/Port-Kombinationen, die andere Memberknoten zur Kommunikation mit diesem Memberknoten verwenden können. Neben dem API-Port (2379
) macht etcd auch Port 2380
für Peer-Kommunikation zwischen etcd-Memberknoten verfügbar, sodass sie Nachrichten aneinander senden und Daten austauschen können. Beachten Sie, dass diese URLs von ihren Peers erreichbar sein müssen (und keine lokalen IP-Adressen sein dürfen).listen-peer-urls
- eine Liste mit IP-Adress-/Port-Kombinationen, bei denen der aktuelle Memberknoten auf Kommunikation von anderen Memberknoten lauscht. Sie muss alle URLs aus dem Flag --initial-advertise-peer-urls
enthalten, aber auch lokale URLs wie 127.0.0.1:2380
. Die Ziel-IP-Adresse/der Port eingehender Peer-Nachrichten müssen mit einer der hier aufgeführten URLs übereinstimmen.advertise-client-urls
- eine Liste mit IP-Adress-/Port-Kombinationen, die Clients zur Kommunikation mit diesem Memberknoten verwenden sollen. Diese URLs müssen vom Client erreichbar sein (und dürfen keine lokalen Adressen sein). Wenn der Client über das öffentliche Internet auf den Cluster zugreift, muss dies eine öffentliche IP-Adresse sein.listen-client-urls
- eine Liste mit IP-Adress-/Port-Kombinationen, bei denen der aktuelle Memberknoten auf Kommunikation von Clients lauscht. Sie muss alle URLs aus dem Flag --advertise-client-urls
enthalten, aber auch lokale URLs wie 127.0.0.1:2379
. Die Ziel-IP-Adresse/der Port eingehender Client-Nachrichten müssen mit einer der hier aufgeführten URLs übereinstimmen.initial-cluster
- eine Liste mit Endpunkten für jeden Memberknoten des Clusters. Jeder Endpunkt muss mit einer der initial-advertise-peer-urls
-URLs des entsprechenden Memberknotens übereinstimmen.initial-cluster-state
- entweder new
oder existing
.Zur Gewährleistung der Konsistenz kann etcd nur Entscheidungen treffen, wenn eine Mehrheit der Knoten integer ist. Dies wird als Einrichten eines Quorum bezeichnet. Mit anderen Worten,: In einem Cluster mit drei Memberknoten wird das Quorum erreicht, wenn zwei oder mehr der Mitglieder integer sind.
Wenn der Parameter initial-cluster-state
auf new
gesetzt ist, weiß etcd
, dass dies ein neuer Cluster ist, für den Bootstrapping ausgeführt wird; so können Memberknoten parallel gestartet werden, ohne dass auf das Erreichen des Quorums gewartet werden muss. Konkret: Nachdem das erste Mitglied gestartet wurde, wird kein Quorum erreicht, da ein Drittel (33,33 %) nicht mehr als 50 % ist. Normalerweise wird etcd anhalten und sich weigern, weitere Aktionen zu übergeben; der Cluster wird nie erstellt. Wenn der initial-cluster-state
jedoch auf new
gesetzt ist, wird das anfängliche Fehlen des Quorums ignoriert.
Wenn der Wert auf existing
gesetzt ist, wird der Memberknoten versuchen, einem vorhandenen Cluster beizutreten, und erwarten, dass das Quorum bereits eingerichtet wurde.
Anmerkung: Weitere Details zu allen unterstützten Konfigurations-Flags finden Sie im Abschnitt Konfiguration der etcd-Dokumentation.
In der aktualisierten Vorlagendatei templates/etcd.conf.yaml.j2
gibt es einige Instanzen von hostvars
. Wenn Ansible ausgeführt wird, erfassen sie Variablen aus verschiedenen Quellen. Wir haben bereits die Variable inventory_hostname
verwendet, aber es gibt noch viel mehr. Diese Variablen sind verfügbar unter hostvars[inventory_hostname]['ansible_facts']
. Hier extrahieren wir die privaten IP-Adressen der einzelnen Knoten und nutzen Sie zur Erstellung unseres Parameterwerts.
Anmerkung: Da wir bei der Erstellung unserer Server die Option Private Networking aktiviert haben, würde jeder Server über drei IP-Adressen verfügen, die mit ihm verknüpft sind:
127.0.0.1
.178.128.169.51
.10.131.82.225
.Jede dieser IP-Adressen ist mit einer anderen Netzwerkschnittstelle verbunden: Die Loopback-Adresse ist mit der lo
-Schnittstelle verbunden, die öffentliche IP-Adresse mit der eth0
-Schnittstelle und die private IP-Adresse mit der eth1
-Schnittstelle. Wir verwenden die eth1
-Schnittstelle, damit der gesamte Datenverkehr im privaten Netzwerk bleibt, ohne je das Internet zu erreichen.
Für diesen Artikel ist kein Verständnis von Netzwerkschnittstellen erforderlich; wenn Sie jedoch mehr erfahren möchten, ist An Introduction to Networking Terminology, Interfaces, and Protocols( Eine Einführung in Netzwerkbegriffe, Schnittstellen und Protokolle) ein guter Ausgangspunkt.
Die Jinja2-Syntax {% %}
definiert die for
-Schleifenstruktur, die über jeden Knoten in der Gruppe etcd
iteriert, um die Zeichenfolge initial-cluster
in einem Format zu erstellen, das etcd benötigt.
Um den neuen Cluster mit drei Memberknoten zu erstellen, müssen Sie zunächst den etcd
-Dienst stoppen und das Datenverzeichnis löschen, bevor Sie den Cluster starten. Verwenden Sie einen Editor, um die Datei playbook.yaml
auf Ihrem lokalen Rechner zu öffnen:
Fügen Sie dann vor der Aufgabe „Create a Data directory“
eine Aufgabe hinzu, um den etcd
-Dienst anzuhalten:
Aktualisieren Sie als Nächstes die Aufgabe "Create a Data directory"
, um das Datenverzeichnis zunächst zu löschen und dann neu zu erstellen:
Die Eigenschaft with_items
definiert eine Liste von Zeichenfolgen, über die diese Aufgabe iterieren wird. Es ist genauso, als würden Sie die gleiche Aufgabe zweimal wiederholen, aber mit verschiedenen Werten für die Eigenschaft state
. Hier iterieren wir über die Liste mit Elementen absent
und directory
, was gewährleistet, dass das Datenverzeichnis zunächst gelöscht und danach neu erstellt wird.
Schließen und speichern Sie die Datei playbook.yaml
, indem Sie Strg+X
drücken, gefolgt von J
. Führen Sie dann ansible-playbook
erneut aus. Ansible erstellt nun einen einzelnen etcd
-Cluster mit drei Memberknoten:
Sie können dies überprüfen, indem Sie eine SSH-Verbindung zu einem beliebigen etcd-Memberknoten herstellen:
Führen Sie dann etcdctl endpoint health --cluster
aus:
Dadurch wird der Zustand der einzelnen Memberknoten im Cluster aufgelistet:
Outputhttp://etcd2_private_ip:2379 is healthy: successfully committed proposal: took = 2.517267ms
http://etcd1_private_ip:2379 is healthy: successfully committed proposal: took = 2.153612ms
http://etcd3_private_ip:2379 is healthy: successfully committed proposal: took = 2.639277ms
Wir haben nun erfolgreich einen etcd-Cluster mit drei Knoten erstellt. Wir können dies prüfen, indem wir auf einem Memberknoten einen Eintrag zu etcd hinzufügen und diesen dann auf einem anderen Memberknoten abrufen. Führen Sie auf einem der Memberknoten etcdctl put
aus:
Verwenden Sie dann ein neues Terminal, um eine SSH-Verbindung zu einem anderen Memberknoten herzustellen:
Versuchen Sie als Nächstes, mit folgendem Schlüssel den gleichen Eintrag abzurufen:
Sie können den Eintrag abrufen, was beweist, dass der Cluster funktioniert:
Outputfoo
bar
Verlassen Sie abschließend die einzelnen verwalteten Knoten und kehren Sie zurück zu Ihrem lokalen Rechner:
In diesem Schritt haben wir einen neuen Cluster mit drei Knoten bereitgestellt. Derzeit erfolgt die Kommunikation zwischen etcd
-Memberknoten sowie ihren Peers und Clients über HTTP. Das bedeutet, dass die Kommunikation unverschlüsselt ist und jede Person, die den Verkehr abfangen kann, auch die entsprechenden Nachrichten lesen kann. Dies ist kein großes Problem, wenn der etcd
-Cluster und die Clients alle in einem privaten Netzwerk oder einem virtuellen privaten Netzwerk (VPN) bereitgestellt werden, das Sie vollständig kontrollieren. Wenn jedoch Teile des Datenverkehrs über ein freigegebenes Netzwerk (privat oder öffentlich) übertragen werden, sollten Sie sicherstellen, dass dieser Datenverkehr verschlüsselt ist. Außerdem muss für Clients oder Peers ein Mechanismus eingerichtet werden, der die Authentizität des Servers überprüft.
Im nächsten Schritt werden wir uns ansehen, wie wir Client-to-Server- sowie Peer-Kommunikation mit TLS sichern können.
Um Nachrichten zwischen Memberknoten zu verschlüsseln, verwendet etcd Hypertext Transfer Protocol Secure oder HTTPS, was eine Ebene über der Transport Layer Security oder dem TLS-Protokoll ist. TLS nutzt ein System aus privaten Schlüsseln, Zertifikaten und vertrauenswürdigen Entitäten namens Zertifizierungsstellen (CAs) zum Authentifizieren und gegenseitigen Senden verschlüsselter Nachrichten.
In diesem Tutorial muss jeder Memberknoten ein Zertifikat generieren, um sich selbst zu identifizieren, und von einer Zertifizierungsstelle signieren lassen. Wir werden konfigurieren alle Memberknoten so, dass sie dieser Zertifizierungsstelle vertrauen und somit auch Zertifikaten vertrauen, die von ihr signiert wurden. Dadurch können sich Mitgliedsknoten gegenseitig authentifizieren.
Das Zertifikat, das ein Memberknoten generiert, muss es anderen Memberknoten erlauben, sich selbst zu identifizieren. Alle Zertifikate umfassen den Common Name (CN) der Entität, mit der sie verknüpft sind. Dies wird oft als Identität der Entität verwendet. Bei der Prüfung eines Zertifikats können Clientimplementierungen jedoch vergleichen, ob die von ihnen erfassten Informationen über die Entität mit dem übereinstimmen, was im Zertifikat angegeben wurde. Wenn beispielsweise ein Client das TLS-Zertifikat mit dem Betreff CN=foo.bar.com
herunterlädt, der Client in Wahrheit aber mit einer IP-Adresse (z. B. 167.71.129.110
) verbunden ist, gibt es einen Konflikt und der Client vertraut dem Zertifikat ggf. nicht. Indem Sie im Zertifikat einen Subject Alternative Name (SAN) angeben, erfährt der Überprüfer, dass beide Namen zur gleichen Entität gehören.
Da unsere etcd-Memberknoten über ihre privaten IP-Adressen Peering betreiben, müssen wir diese privaten IP-Adressen, wenn wir unsere Zertifikate definieren, als alternative Antragstellernamen (SANs) angeben.
Um die private IP-Adresse eines verwalteten Knoten zu erfahren, stellen Sie eine SSH-Verbindung damit her:
Führen Sie dann den folgenden Befehl aus:
Sie sehen eine Ausgabe, die den folgenden Zeilen ähnelt:
Output3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
inet 10.131.255.176/16 brd 10.131.255.255 scope global eth1
valid_lft forever preferred_lft forever
In unserer Beispielausgabe ist 10.131.255.176
die private IP-Adresse des verwalteten Knotens und die einzige Information, an der wir interessiert sind. Um alles mit Ausnahme der privaten IP-Adresse herauszufiltern, können wir die Ausgabe des Befehls ip
an das Dienstprogramm
sed übergeben, das zum Filtern und Umformen von Text dient.
Die einzige Ausgabe ist nun die private IP-Adresse selbst:
Output10.131.255.176
Sobald Sie damit zufrieden sind, dass der vorhergehende Befehl funktioniert, verlassen Sie den verwalteten Knoten:
Um die vorherigen Befehle in Ihr Playbook aufzunehmen, öffnen Sie zunächst die Datei playbook.yaml
:
Fügen Sie dann vor dem vorhandenen Play ein neues Play mit einer einzelnen Aufgabe hinzu:
Die Aufgabe verwendet das shell
-Modul, um die Befehle ip
und sed
auszuführen, wodurch die private IP-Adresse des verwalteten Knoten abgerufen wird. Dann registriert sie den Rückgabewert des Shell-Befehls in einer Variable namens privateIP
, die wir später verwenden werden.
In diesem Schritt haben wir dem Playbook eine Aufgabe hinzugefügt, um die private IP-Adresse der verwalteten Knoten zu erhalten. Im nächsten Schritt werden wir diese Informationen verwenden, um für jeden der Knoten ein Zertifikat zu generieren und die Zertifikate von einer Zertifizierungsstelle (CA) signieren zu lassen.
Damit ein Memberknoten verschlüsselten Datenverkehr erhält, muss der Absender den öffentlichen Schlüssel des Memberknotens verwenden, um die Daten zu verschlüsseln. Der Memberknoten muss den privaten Schlüssel nutzen, um den verschlüsselten Text zu entschlüsseln und die Originaldaten abzurufen. Der öffentliche Schlüssel ist in einem Zertifikat verpackt und wurde von einer CA signiert, um sicherzustellen, dass er echt ist.
Daher müssen wir für jeden etcd-Memberknoten einen privaten Schlüssel und eine Zertifikatsignaturanforderung (CSR) generieren. Um es einfacher zu machen, erstellen wir alle Schlüsselpaare und signieren alle Zertifikate lokal auf dem Steuerknoten und kopieren die entsprechenden Dateien dann auf die verwalteten Hosts.
Erstellen Sie zunächst ein Verzeichnis namens artifacts/
, in dem Sie die in dem Prozess generierten Dateien (Schlüssel und Zertifikate) platzieren werden. Öffnen Sie die Datei playbook.yaml
mit einem Editor:
Verwenden Sie darin das file
-Modul, um das Verzeichnis artifacts/
zu erstellen:
Fügen Sie als Nächstes am Ende des Plays eine weitere Aufgabe hinzu, um den privaten Schlüssel zu generieren:
Das Erstellen von privaten Schlüsseln und CSRs kann mit den Modulen openssl_privatekey
bzw. openssl_csr
erfolgen.
Das Attribut force: True
stellt sicher, dass der private Schlüssel jedes Mal neu generiert wird, auch wenn er bereits existiert.
Fügen Sie nun die folgende neue Aufgabe demselben Play an, um die CSRs für die einzelnen Memberknoten zu generieren; nutzen Sie dazu das Modul openssl_csr
:
Wir geben an, dass sich dieses Zertifikat für den Zweck der Serverauthentifizierung an einem digitalen Signaturmechanismus beteiligen kann. Das Zertifikat ist mit dem Hostnamen (z. B. etcd1
) verknüpft; der Überprüfer soll jedoch auch die privaten und lokalen Loopback-IP-Adressen der einzelnen Knoten als alternative Namen behandeln. Beachten Sie, dass wir die Variable privateIP
verwenden, die wir im vorherigen Play registriert haben.
Schließen und speichern Sie die Datei playbook.yaml
, indem Sie Strg+X
drücken, gefolgt von J
. Führen Sie das Playbook dann erneut aus:
Wir werden in unserem Projektverzeichnis nun ein neues Verzeichnis namens Artefakte
sehen; verwenden Sie ls
, um den Inhalt aufzulisten:
Sie sehen die privaten Schlüssel und CSRs für die einzelnen etcd-Memberknoten:
Outputetcd1.csr etcd1.key etcd2.csr etcd2.key etcd3.csr etcd3.key
In diesem Schritt haben wir verschiedene Ansible-Module verwendet, um für die einzelnen Memberknoten private Schlüssel und öffentliche Schlüsselzertifikate zu generieren. Im nächsten Schritt werden wir uns ansehen, wie eine Zertifikatsignaturanforderung (CSR) signiert wird.
Innerhalb eines etcd-Clusters verschlüsseln Memberknoten Nachrichten mit dem öffentlichen Schlüssel des Empfängers. Um sicherzustellen, dass der öffentliche Schlüssel echt ist, verpackt der Empfänger den öffentlichen Schlüssel in eine Zertifikatsignaturanforderung (CSR) und lässt diese von einer vertrauenswürdigen Entität (d. h. der CA) signieren. Da wir alle Memberknoten und die Zertifizierungstellen, denen sie vertrauen, kontrollieren, müssen wir keine externe CA nutzen und können als eigene CA fungieren. In diesem Schritt werden wir als eigene CA agieren; das bedeutet, dass wir einen privaten Schlüssel und ein selbstsigniertes Zertifikat erstellen müssen, um als CA zu fungieren.
Öffnen Sie die Datei playbook.yaml
mit Ihrem Editor:
Fügen Sie dann ähnlich wie im vorherigen Schritt eine Aufgabe im Play localhost
an, um einen privaten Schlüssel für die CA zu generieren:
Als Nächstes verwenden Sie das Modul openssl_csr
, um eine neue CSR zu generieren. Dies ähnelt dem vorherigen Schritt; in dieser CSR fügen wir jedoch die Basiseinschränkung und Schlüsselverwendungserweiterung hinzu, um anzugeben, dass dieses Zertifikat als CA-Zertifikat verwendet werden kann:
Verwenden Sie schließlich das Modul openssl_certificate
, um das CSR selbst zu signieren:
Schließen und speichern Sie die Datei playbook.yaml
, indem Sie Strg+X
drücken, gefolgt von J
. Führen Sie dann das Playbook erneut aus, um die Änderungen anzuwenden:
Außerdem können Sie ls
ausführen, um den Inhalt des Verzeichnisses artifacts/
zu überprüfen:
Sie sehen nun das neu generierte CA-Zertifikat (ca.crt
):
Outputca.crt ca.csr ca.key etcd1.csr etcd1.key etcd2.csr etcd2.key etcd3.csr etcd3.key
In diesem Schritt haben wir einen privaten Schlüssel und ein selbstsigniertes Zertifikat für die CA generiert. Im nächsten Schritt verwenden wir das CA-Zertifikat nutzen, um die CSRs der einzelnen Memberknoten zu signieren.
In diesem Schritt signieren wir die CSRs der einzelnen Memberknoten. Dies ähnelt der Methode, mit der wir das Modul openssl_certificate
verwendet haben, um das CA-Zertifikat selbst zu signieren. Aber anstelle des Anbieters selfsigned
nutzen wir den Anbieter ownca
, damit wir unsere eigenen CA-Zertifikate signieren können.
Öffnen Sie Ihr Playbook:
Fügen Sie der Aufgabe "Generate self-signed CA certificate"
die folgende hervorgehobene Aufgabe an:
Schließen und speichern Sie die Datei playbook.yaml
, indem Sie Strg+X
drücken, gefolgt von ``Y. Führen Sie dann das Playbook erneut aus, um die Änderungen anzuwenden:
Listen Sie nun den Inhalt des Verzeichnisses artifacts/
auf:
Sie finden den privaten Schlüssel, die CSR und das Zertifikat für jeden einzelnen etcd-Memberknoten und die CA:
Outputca.crt ca.csr ca.key etcd1.crt etcd1.csr etcd1.key etcd2.crt etcd2.csr etcd2.key etcd3.crt etcd3.csr etcd3.key
In diesem Schritt haben wir die CSRs der einzelnen Memberknoten mithilfe des Schlüssels der CA signiert. Im nächsten Schritt kopieren wir die relevanten Dateien in die einzelnen verwalteten Knoten, damit etcd zum Einrichten von TLS-Verbindungen Zugriff auf die entsprechenden Schlüssel und Zertifikate hat.
Jeder Knoten muss eine Kopie des selbstsignierten Zertifikats der CA (ca.crt
) haben. Jeder etcd
-Memberknoten muss auch über einen eigenen privaten Schlüssel und ein Zertifikat verfügen. In diesem Schritt laden wir die Dateien hoch und platzieren sie in einem neuen Verzeichnis namens /etc/etcd/ssl/
.
Öffnen Sie zunächst die Datei playbook.yaml
mit Ihrem Editor:
Um diese Änderungen an unserem Ansible-Playbook vorzunehmen, aktualisieren Sie zunächst die Eigenschaft path
der Aufgabe Create directory for etcd configuration
, um das Verzeichnis /etc/etcd/ssl/
zu erstellen:
Fügen Sie dann nach der modifizierten Aufgabe drei weitere Aufgaben hinzu, um die Dateien zu kopieren:
Schließen und speichern Sie die Datei playbook.yaml
, indem Sie Strg+X
drücken, gefolgt von J
.
Führen Sie ansible-playbook
erneut aus, um diese Änderungen vorzunehmen:
In diesem Schritt haben wir die privaten Schlüssel und Zertifikate erfolgreich in die verwalteten Knoten hochgeladen. Nachdem wir die Dateien kopiert haben, müssen wir nun unsere etcd-Konfigurationsdatei so aktualisieren, dass sie sie nutzt.
Im letzten Schritt dieses Tutorials werden wir einige Ansible-Konfigurationen aktualisieren, um TLS in einem etcd-Cluster zu aktivieren.
Öffnen Sie zunächst die Vorlagendatei templates/etcd.conf.yaml.j2
mit Ihrem Editor:
Ändern Sie darin alle URLs so, dass sie https
als Protokoll anstelle von http
verwenden. Fügen Sie außerdem am Ende der Vorlage einen Abschnitt hinzu, um den Speicherort des CA-Zertifikats, des Serverzertifikats und des Serverschlüssels anzugeben:
Schließen und speichern Sie die Datei templates/etcd.conf.yaml.j2
.
Führen Sie als Nächstes Ihr Ansible-Playbook aus:
Stellen Sie dann eine SSH-Verbindung zu einem der verwalteten Knoten her:
Führen Sie darauf den Befehl etcdctl endpoint health
aus, um zu überprüfen, ob die Endpunkte HTTPS verwenden und ob alle Memberknoten integer sind:
Da unser CA-Zertifikat standardmäßig kein vertrauenswürdiges CA-Stammzertifikat ist, das im Verzeichnis /etc/ssl/certs/
installiert ist, müssen wir es mit dem Flag --cacert
an etcdctl
übergeben.
Dadurch erhalten Sie folgende Ausgabe:
Outputhttps://etcd3_private_ip:2379 is healthy: successfully committed proposal: took = 19.237262ms
https://etcd1_private_ip:2379 is healthy: successfully committed proposal: took = 4.769088ms
https://etcd2_private_ip:2379 is healthy: successfully committed proposal: took = 5.953599ms
Um zu bestätigen, dass der etcd
-Cluster tatsächlich funktioniert, können wir erneut einen Eintrag auf einem etcd-Memberknoten erstellen und ihn dann von einem anderen etcd-Memberknoten abrufen:
Verwenden Sie ein neues Terminal, um eine SSH-Verbindung zu einem anderen Knoten herzustellen:
Rufen Sie nun mit dem Schlüssel foo
den gleichen Eintrag ab:
Dadurch wird der Eintrag zurückgegeben, der die folgende Ausgabe anzeigt:
Outputfoo
bar
Sie können das Gleiche mit dem dritten Knoten tun, um zu prüfen, ob alle drei Memberknoten ausgeführt werden.
Sie haben nun erfolgreich einen etcd-Cluster mit drei Knoten bereitgestellt, mit TLS gesichert und sich vergewissert, dass er funktioniert.
etcd ist eine ursprünglich von CoreOS entwickelte Software. Um die Verwendung von etcd in Bezug auf CoreOS zu verstehen, können Sie Folgendes lesen: How To Use Etcdctl and Etcd, CoreOS’s Distributed Key-Value Store. Der Artikel führt Sie außerdem durch die Einrichtung eines dynamischen Erfassungsmodells, das in diesem Tutorial diskutiert, aber nicht vorgeführt wurde.
Wie am Anfang dieses Tutorials erwähnt, ist etcd ein wichtiger Teil des Kubernetes-Ökosystems. Um mehr über Kubernetes und die Rolle von etcd darin zu erfahren, können Sie An Introduction to Kubernetes (Eine Einführung in Kubernetes) lesen. Wenn Sie etcd als Teil eines Kubernetes-Clusters bereitstellen, sollten Sie wissen, dass es andere Tools gibt, wie z. B. kubespray und kubeadm
. Weitere Details dazu finden Sie unter Erstellen eines Kubernetes-Clusters unter Ubuntu 18.04.
Schließlich wurden in diesem Tutorial auch viele Tools verwendet, die einzeln nicht genau besprochen werden konnten. Im Folgenden finden Sie Links, die Ihnen genauere Informationen zu den einzelnen Tools liefern:
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!