One place for hosting & domains

      Ansible

      Einrichten und Sichern eines etcd-Clusters mit Ansible unter Ubuntu 18.04


      Der Autor hat die Wikimedia Foundation dazu ausgewählt, im Rahmen des Programms Write for DOnations eine Spende zu erhalten.

      Einführung

      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.

      Voraussetzungen

      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.

      Schritt 1 — Konfigurieren von Ansible für den Steuerknoten

      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:

      • mkdir -p $HOME/playground/etcd-ansible

      Rufen Sie dann das gerade erstellte Verzeichnis auf:

      • cd $HOME/playground/etcd-ansible

      Erstellen und öffnen Sie im Verzeichnis mit Ihrem Editor eine leere Inventardatei namens hosts:

      • nano $HOME/playground/etcd-ansible/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:

      ~/playground/etcd-ansible/hosts

      [etcd]
      etcd1 ansible_host=etcd1_public_ip  ansible_user=root
      etcd2 ansible_host=etcd2_public_ip  ansible_user=root
      etcd3 ansible_host=etcd3_public_ip  ansible_user=root
      

      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:

      • ansible etcd -i hosts -m command -a hostname

      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:

      Output

      etcd2 | 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.

      Schritt 2 — Abrufen der Hostnamen von verwalteten Knoten mit Ansible Playbooks

      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:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Fügen Sie in playbook.yaml die folgenden Zeilen hinzu:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        tasks:
          - name: "Retrieve hostname"
            command: hostname
            register: output
          - name: "Print hostname"
            debug: var=output.stdout_lines
      

      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:

      • ansible-playbook -i hosts playbook.yaml

      Sie erhalten die folgende Ausgabe, was bedeutet, dass Ihr Playbook ordnungsgemäß funktioniert:

      Output

      PLAY [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.

      Schritt 3 — Installieren von etcd in den verwalteten Knoten

      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:

      • mkdir -p /opt/etcd/bin
      • cd /opt/etcd/bin
      • wget -qO- https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz | tar --extract --gzip --strip-components=1
      • echo 'export PATH="$PATH:/opt/etcd/bin"' >> ~/.profile
      • echo 'export ETCDCTL_API=3" >> ~/.profile

      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:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Ersetzen Sie die gesamte Datei playbook.yaml durch folgende Inhalte:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
            file:
              path: /opt/etcd/bin
              state: directory
              owner: root
              group: root
              mode: 0700
          - name: "Download the tarball into the /tmp directory"
            get_url:
              url: https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz
              dest: /tmp/etcd.tar.gz
              owner: root
              group: root
              mode: 0600
              force: True
          - name: "Extract the contents of the tarball"
            unarchive:
              src: /tmp/etcd.tar.gz
              dest: /opt/etcd/bin/
              owner: root
              group: root
              mode: 0600
              extra_opts:
                - --strip-components=1
              decrypt: True
              remote_src: True
          - name: "Set permissions for etcd"
            file:
              path: /opt/etcd/bin/etcd
              state: file
              owner: root
              group: root
              mode: 0700
          - name: "Set permissions for etcdctl"
            file:
              path: /opt/etcd/bin/etcdctl
              state: file
              owner: root
              group: root
              mode: 0700
          - name: "Add /opt/etcd/bin/ to the $PATH environment variable"
            lineinfile:
              path: /etc/profile
              line: export PATH="$PATH:/opt/etcd/bin"
              state: present
              create: True
              insertafter: EOF
          - name: "Set the ETCDCTL_API environment variable to 3"
            lineinfile:
              path: /etc/profile
              line: export ETCDCTL_API=3
              state: present
              create: True
              insertafter: EOF
      

      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:

      • ansible-playbook -i hosts playbook.yaml

      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:

      Output

      etcd 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:

      Output

      etcdctl 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.

      Schritt 4 — Erstellen einer Unit-Datei für etcd

      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:

      ~/playground/etcd-ansible/files/etcd.service

      [Unit]
      Description=etcd distributed reliable key-value store
      
      [Service]
      Type=notify
      ExecStart=/opt/etcd/bin/etcd
      Restart=always
      

      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:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Fügen Sie am Ende der bestehenden Aufgaben die folgende hervorgehobene Aufgabe an:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Set the ETCDCTL_API environment variable to 3"
            lineinfile:
              path: /etc/profile
              line: export ETCDCTL_API=3
              state: present
              create: True
              insertafter: EOF
          - name: "Create a etcd service"
            copy:
              src: files/etcd.service
              remote_src: False
              dest: /etc/systemd/system/etcd.service
              owner: root
              group: root
              mode: 0644
      

      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:

      • ansible-playbook -i hosts playbook.yaml

      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.

      Schritt 5 — Konfigurieren des Datenverzeichnisses

      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:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Fügen Sie am Ende der Liste der Aufgaben die folgende Aufgabe an:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Create a etcd service"
            copy:
              src: files/etcd.service
              remote_src: False
              dest: /etc/systemd/system/etcd.service
              owner: root
              group: root
              mode: 0644
          - name: "Create a data directory"
            file:
              path: /var/lib/etcd/{{ inventory_hostname }}.etcd
              state: directory
              owner: root
              group: root
              mode: 0755
      

      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:

      • nano templates/etcd.conf.yaml.j2

      Kopieren Sie als Nächstes die folgende Zeile und fügen Sie sie in die Datei ein:

      ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
      

      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:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Fügen Sie der Liste der Aufgaben die folgenden Aufgaben an, um ein Verzeichnis zu erstellen und die vorlagenbasierte Konfigurationsdatei darin hochzuladen:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Create a data directory"
            file:
              ...
              mode: 0755
          - name: "Create directory for etcd configuration"
            file:
              path: /etc/etcd
              state: directory
              owner: root
              group: root
              mode: 0755
          - name: "Create configuration file for etcd"
            template:
              src: templates/etcd.conf.yaml.j2
              dest: /etc/etcd/etcd.conf.yaml
              owner: root
              group: root
              mode: 0600
      

      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:

      ~/playground/etcd-ansible/files/etcd.service

      [Unit]
      Description=etcd distributed reliable key-value store
      
      [Service]
      Type=notify
      ExecStart=/opt/etcd/bin/etcd --config-file /etc/etcd/etcd.conf.yaml
      Restart=always
      

      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.

      Schritt 6 — Aktivieren und Starten des etcd-Diensts

      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:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Fügen Sie am Ende der Aufgabenliste die folgenden Aufgaben an:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Create configuration file for etcd"
            template:
              ...
              mode: 0600
          - name: "Enable the etcd service"
            command: systemctl enable etcd
          - name: "Start the etcd service"
            command: systemctl restart etcd
      

      Speichern und schließen Sie die Datei.

      Führen Sie ansible-playbook -i hosts playbook.yaml erneut aus:

      • ansible-playbook -i hosts playbook.yaml

      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.

      Schritt 7 — Testen von etcd

      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:

      Output

      OK

      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:

      Output

      foo 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:

      Output

      1

      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.

      Schritt 8 — Erstellen eines Clusters mit statischer Erkennung

      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:

      • die Anzahl der Memberknoten ist bekannt
      • die Endpunkte der einzelnen Memberknoten sind bekannt
      • die IP-Adressen für alle Endpunkte sind statisch

      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:

      • nano templates/etcd.conf.yaml.j2

      Fügen Sie dann die folgenden hervorgehobenen Zeilen hinzu:

      ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
      name: {{ inventory_hostname }}
      initial-advertise-peer-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380
      listen-peer-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380,http://127.0.0.1:2380
      advertise-client-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379
      listen-client-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379,http://127.0.0.1:2379
      initial-cluster-state: new
      initial-cluster: {% for host in groups['etcd'] %}{{ hostvars[host]['ansible_facts']['hostname'] }}=http://{{ hostvars[host]['ansible_facts']['eth1']['ipv4']['address'] }}:2380{% if not loop.last %},{% endif %}{% endfor %}
      

      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:

      • Eine Loopback-IP-Adresse – eine Adresse, die nur im gleichen Rechner gültig ist. Sie wird vom Rechner verwendet, um sich auf sich zu verweisen, z. B. 127.0.0.1.
      • Eine öffentliche IP-Adresse – eine Adresse, die über das öffentliche Internet routingfähig ist, z. B. 178.128.169.51.
      • Eine private IP-Adresse – eine Adresse, die nur im privaten Netzwerk routingfähig ist; im Fall von DigitalOcean Droplets gibt es in jedem Rechenzentrum ein privates Netzwerk, z. B. 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:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Fügen Sie dann vor der Aufgabe „Create a Data directory“ eine Aufgabe hinzu, um den etcd-Dienst anzuhalten:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
              group: root
              mode: 0644
          - name: "Stop the etcd service"
            command: systemctl stop etcd
          - name: "Create a data directory"
            file:
          ...
      

      Aktualisieren Sie als Nächstes die Aufgabe "Create a Data directory", um das Datenverzeichnis zunächst zu löschen und dann neu zu erstellen:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Stop the etcd service"
            command: systemctl stop etcd
          - name: "Create a data directory"
            file:
              path: /var/lib/etcd/{{ inventory_hostname }}.etcd
              state: "{{ item }}"
              owner: root
              group: root
              mode: 0755
            with_items:
              - absent
              - directory
          - name: "Create directory for etcd configuration"
            file:
          ...
      

      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:

      • ansible-playbook -i hosts playbook.yaml

      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:

      • etcdctl endpoint health --cluster

      Dadurch wird der Zustand der einzelnen Memberknoten im Cluster aufgelistet:

      Output

      http://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:

      Output

      foo 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.

      Schritt 9 — Abrufen der privaten IP-Adressen von verwalteten Knoten

      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:

      • ip -f inet addr show eth1

      Sie sehen eine Ausgabe, die den folgenden Zeilen ähnelt:

      Output

      3: 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.

      • ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/1/p'

      Die einzige Ausgabe ist nun die private IP-Adresse selbst:

      Output

      10.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:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Fügen Sie dann vor dem vorhandenen Play ein neues Play mit einer einzelnen Aufgabe hinzu:

      ~/playground/etcd-ansible/playbook.yaml

      ...
      - hosts: etcd
        tasks:
          - shell: ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/1/p'
            register: privateIP
      - hosts: etcd
        tasks:
      ...
      

      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.

      Schritt 10 — Erstellen der privaten Schlüssel und CSRs von etcd-Memberknoten

      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:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Verwenden Sie darin das file-Modul, um das Verzeichnis artifacts/ zu erstellen:

      ~/playground/etcd-ansible/playbook.yaml

      ...
          - shell: ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/1/p'
            register: privateIP
      - hosts: localhost
        gather_facts: False
        become: False
        tasks:
          - name: "Create ./artifacts directory to house keys and certificates"
            file:
              path: ./artifacts
              state: directory
      - hosts: etcd
        tasks:
      ...
      

      Fügen Sie als Nächstes am Ende des Plays eine weitere Aufgabe hinzu, um den privaten Schlüssel zu generieren:

      ~/playground/etcd-ansible/playbook.yaml

      ...
      - hosts: localhost
        gather_facts: False
        become: False
        tasks:
              ...
          - name: "Generate private key for each member"
            openssl_privatekey:
              path: ./artifacts/{{item}}.key
              type: RSA
              size: 4096
              state: present
              force: True
            with_items: "{{ groups['etcd'] }}"
      - hosts: etcd
        tasks:
      ...
      

      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:

      ~/playground/etcd-ansible/playbook.yaml

      ...
      - hosts: localhost
        gather_facts: False
        become: False
        tasks:
          ...
          - name: "Generate private key for each member"
            openssl_privatekey:
              ...
            with_items: "{{ groups['etcd'] }}"
          - name: "Generate CSR for each member"
            openssl_csr:
              path: ./artifacts/{{item}}.csr
              privatekey_path: ./artifacts/{{item}}.key
              common_name: "{{item}}"
              key_usage:
                - digitalSignature
              extended_key_usage:
                - serverAuth
              subject_alt_name:
                - IP:{{ hostvars[item]['privateIP']['stdout']}}
                - IP:127.0.0.1
              force: True
            with_items: "{{ groups['etcd'] }}"
      

      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:

      • ansible-playbook -i hosts playbook.yaml

      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:

      Output

      etcd1.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.

      Schritt 11 — Erstellen von CA-Zertifikaten

      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:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      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:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
        - name: "Generate CSR for each member"
          ...
          with_items: "{{ groups['etcd'] }}"
          - name: "Generate private key for CA"
            openssl_privatekey:
              path: ./artifacts/ca.key
              type: RSA
              size: 4096
              state: present
              force: True
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      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:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
          - name: "Generate private key for CA"
            openssl_privatekey:
              path: ./artifacts/ca.key
              type: RSA
              size: 4096
              state: present
              force: True
          - name: "Generate CSR for CA"
            openssl_csr:
              path: ./artifacts/ca.csr
              privatekey_path: ./artifacts/ca.key
              common_name: ca
              organization_name: "Etcd CA"
              basic_constraints:
                - CA:TRUE
                - pathlen:1
              basic_constraints_critical: True
              key_usage:
                - keyCertSign
                - digitalSignature
              force: True
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      Verwenden Sie schließlich das Modul openssl_certificate, um das CSR selbst zu signieren:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
          - name: "Generate CSR for CA"
            openssl_csr:
              path: ./artifacts/ca.csr
              privatekey_path: ./artifacts/ca.key
              common_name: ca
              organization_name: "Etcd CA"
              basic_constraints:
                - CA:TRUE
                - pathlen:1
              basic_constraints_critical: True
              key_usage:
                - keyCertSign
                - digitalSignature
              force: True
          - name: "Generate self-signed CA certificate"
            openssl_certificate:
              path: ./artifacts/ca.crt
              privatekey_path: ./artifacts/ca.key
              csr_path: ./artifacts/ca.csr
              provider: selfsigned
              force: True
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      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:

      • ansible-playbook -i hosts playbook.yaml

      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):

      Output

      ca.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.

      Schritt 12 — Signieren der CSRs von etcd-Memberknoten

      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:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Fügen Sie der Aufgabe "Generate self-signed CA certificate" die folgende hervorgehobene Aufgabe an:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
          - name: "Generate self-signed CA certificate"
            openssl_certificate:
              path: ./artifacts/ca.crt
              privatekey_path: ./artifacts/ca.key
              csr_path: ./artifacts/ca.csr
              provider: selfsigned
              force: True
          - name: "Generate an `etcd` member certificate signed with our own CA certificate"
            openssl_certificate:
              path: ./artifacts/{{item}}.crt
              csr_path: ./artifacts/{{item}}.csr
              ownca_path: ./artifacts/ca.crt
              ownca_privatekey_path: ./artifacts/ca.key
              provider: ownca
              force: True
            with_items: "{{ groups['etcd'] }}"
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      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:

      • ansible-playbook -i hosts playbook.yaml

      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:

      Output

      ca.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.

      Schritt 13 — Kopieren von privaten Schlüsseln und Zertifikaten

      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:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      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:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        ...
        tasks:
          ...
            with_items:
              - absent
              - directory
          - name: "Create directory for etcd configuration"
            file:
              path: "{{ item }}"
              state: directory
              owner: root
              group: root
              mode: 0755
            with_items:
              - /etc/etcd
              - /etc/etcd/ssl
          - name: "Create configuration file for etcd"
            template:
      ...
      

      Fügen Sie dann nach der modifizierten Aufgabe drei weitere Aufgaben hinzu, um die Dateien zu kopieren:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        ...
        tasks:
          ...
          - name: "Copy over the CA certificate"
            copy:
              src: ./artifacts/ca.crt
              remote_src: False
              dest: /etc/etcd/ssl/ca.crt
              owner: root
              group: root
              mode: 0644
          - name: "Copy over the `etcd` member certificate"
            copy:
              src: ./artifacts/{{inventory_hostname}}.crt
              remote_src: False
              dest: /etc/etcd/ssl/server.crt
              owner: root
              group: root
              mode: 0644
          - name: "Copy over the `etcd` member key"
            copy:
              src: ./artifacts/{{inventory_hostname}}.key
              remote_src: False
              dest: /etc/etcd/ssl/server.key
              owner: root
              group: root
              mode: 0600
          - name: "Create configuration file for etcd"
            template:
      ...
      

      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:

      • ansible-playbook -i hosts playbook.yaml

      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.

      Schritt 14 — Aktivieren von TLS in etcd

      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:

      • nano $HOME/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      Ä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:

      ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
      name: {{ inventory_hostname }}
      initial-advertise-peer-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380
      listen-peer-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380,https://127.0.0.1:2380
      advertise-client-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379
      listen-client-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379,https://127.0.0.1:2379
      initial-cluster-state: new
      initial-cluster: {% for host in groups['etcd'] %}{{ hostvars[host]['ansible_facts']['hostname'] }}=https://{{ hostvars[host]['ansible_facts']['eth1']['ipv4']['address'] }}:2380{% if not loop.last %},{% endif %}{% endfor %}
      
      client-transport-security:
        cert-file: /etc/etcd/ssl/server.crt
        key-file: /etc/etcd/ssl/server.key
        trusted-ca-file: /etc/etcd/ssl/ca.crt
      peer-transport-security:
        cert-file: /etc/etcd/ssl/server.crt
        key-file: /etc/etcd/ssl/server.key
        trusted-ca-file: /etc/etcd/ssl/ca.crt
      

      Schließen und speichern Sie die Datei templates/etcd.conf.yaml.j2.

      Führen Sie als Nächstes Ihr Ansible-Playbook aus:

      • ansible-playbook -i hosts playbook.yaml

      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:

      • etcdctl --cacert /etc/etcd/ssl/ca.crt endpoint health --cluster

      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:

      Output

      https://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:

      • etcdctl --cacert /etc/etcd/ssl/ca.crt put foo "bar"

      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:

      • etcdctl --cacert /etc/etcd/ssl/ca.crt get foo

      Dadurch wird der Eintrag zurückgegeben, der die folgende Ausgabe anzeigt:

      Output

      foo bar

      Sie können das Gleiche mit dem dritten Knoten tun, um zu prüfen, ob alle drei Memberknoten ausgeführt werden.

      Zusammenfassung

      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:



      Source link

      Cómo configurar y proteger un clúster etcd con Ansible en Ubuntu 18.04


      El autor seleccionó a Wikimedia Foundation para recibir una donación como parte del programa Write for DOnations.

      Introducción

      etcd es un almacén de clave-valor distribuido en el que se basan muchas plataformas y herramientas, como Kubernetes, Vulcand y Doorman. En Kubernetes, etcd se utiliza como una tienda de configuración global que almacena el estado del clúster. Saber administrar etcd es esencial para gestionar clústeres de Kubernetes. Si bien hay muchas ofertas de Kubernetes gestionados, conocidos como Kubernetes como servicio, que eliminan esta carga administrativa, muchas empresas siguen prefiriendo ejecutar clústeres autogestionados de Kubernetes en sus instalaciones debido a la flexibilidad que ofrecen.

      La primera mitad de este artículo lo guiará en el proceso de configuración de un clúster etcd de 3 nodos en servidores de Ubuntu 18.04. La segunda mitad se centrará en la protección del clúster usando Transport Layer Security, o TLS. Para ejecutar cada configuración de forma automatizada, utilizaremos Ansible en todo momento. Ansible es una herramienta de administración de configuración similar a Puppet, Chef y SaltStack que nos permite definir cada paso de configuración de manera declarativa dentro de archivos denominados cuadernos de estrategias.

      Al final de este tutorial, tendrá un clúster etcd de 3 nodos en ejecución en sus servidores. También tendrá un cuaderno de estrategias de Ansible que le permite recrear de manera repetida y consistente la misma configuración en conjuntos de servidores nuevos.

      Requisitos previos

      Para completar esta guía, necesitará lo siguiente:

      Advertencia: Como el propósito de este artículo es proporcionar una introducción a los ajustes de un clúster etcd en una red privada, los tres servidores de Ubuntu 18.04 de esta configuración no se probaron con un firewall y se accedió a ellos con un usuario root. En una configuración de producción, todos los nodos expuestos a una red pública de Internet requerirían un firewall y un usuario sudo para respetar las prácticas recomendadas de seguridad. Para obtener más información, consulte el tutorial Configuración inicial para servidores con Ubuntu 18.04.

      Paso 1: Configurar Ansible para el nodo de control

      Ansible es una herramienta que se utiliza para administrar servidores. Los servidores que administra Ansible se denominan nodos administrados y el equipo que ejecuta Ansible, nodo de control. Ansible utiliza las claves SSH del nodo de control para obtener acceso a los nodos administrados. Una vez que se establezca una sesión SSH, Ansible ejecutará un conjunto de secuencias de comandos para proporcionar y configurar los nodos administrados. En este paso, comprobaremos que podemos usar Ansible para establecer conexión con nodos administrados y ejecutar el comando hostname.

      Un día típico de un administrador de sistemas puede implicar administrar diferentes conjuntos de nodos. Por ejemplo, puede usar Ansible para proporcionar servidores nuevos, pero utilizarlo más adelante para volver a configurar otro conjunto de servidores. Ansible proporciona el concepto de inventario de host (o inventario para abreviar) para permitir a los administradores organizar mejor el conjunto de nodos administrados. Puede definir todos los nodos que desee administrar con Ansible en un archivo de inventario y organizarlos en grupos. Luego, al ejecutar los comandos ansible y ansible-playbook, puede especificar los hosts o los grupos a los que se aplica el comando.

      Ansible lee el archivo de inventario de /etc/ansible/hosts de forma predeterminada; sin embargo, podemos especificar un archivo de inventario diferente utilizando el indicador --inventory (o -i para abreviar).

      Para comenzar, cree un directorio nuevo en su equipo local (el nodo de control) para alojar todos los archivos de este tutorial:

      • mkdir -p $HOME/playground/etcd-ansible

      Luego, ingrese el directorio que acaba de crear:

      • cd $HOME/playground/etcd-ansible

      Dentro del directorio, cree y abra un archivo de inventario en blanco denominado hosts con su editor:

      • nano $HOME/playground/etcd-ansible/hosts

      Dentro del archivo hosts, enumere cada uno de sus nodos administrados con el siguiente formato, sustituyendo las direcciones IP públicas resaltadas por las de sus servidores:

      ~/playground/etcd-ansible/hosts

      [etcd]
      etcd1 ansible_host=etcd1_public_ip  ansible_user=root
      etcd2 ansible_host=etcd2_public_ip  ansible_user=root
      etcd3 ansible_host=etcd3_public_ip  ansible_user=root
      

      La línea [etcd] define un grupo denominado etcd. En la definición del grupo, enumeramos todos nuestros nodos administrados. Cada línea comienza con un alias (por ejemplo, etcd1), lo que nos permite referirnos a cada host usando un nombre fácil de recordar en vez de una dirección IP larga. ansible_host y ansible_user son variables de Ansible. En este caso, se utilizan para proporcionarle a Ansible las direcciones IP públicas y los nombres de usuario de SSH que se deben utilizar para la conexión a través de SSH.

      Para asegurarnos de que Ansible pueda conectarse con nuestros nodos administrados, podemos probar la conectividad al utilizar Ansible para ejecutar el comando hostname en cada uno de los hosts del grupo etcd:

      • ansible etcd -i hosts -m command -a hostname

      Desglosemos este comando para aprender lo que significa cada sección:

      • etcd: especifica el patrón de host que se utiliza para determinar qué hosts del inventario se administran con este comando. Aquí, utilizamos el nombre del grupo como patrón de host.
      • -i hosts: especifica el archivo de inventario que se debe utilizar.
      • -m command: la funcionalidad detrás de Ansible la proporcionan los módulos. El módulo command toma el argumento pasado y lo ejecuta como comando en cada uno de los nodos administrados. Presentaremos algunos módulos más de Ansible más adelante en este tutorial.
      • -a hostname: es el argumento que se pasa al módulo. La cantidad y los tipos de argumentos dependen del módulo.

      Después de ejecutar el comando, obtendrá el siguiente resultado, que indica que Ansible está configurado correctamente:

      Output

      etcd2 | CHANGED | rc=0 >> etcd2 etcd3 | CHANGED | rc=0 >> etcd3 etcd1 | CHANGED | rc=0 >> etcd1

      Los comandos que ejecuta Ansible se denominan tareas. El uso de ansible en la línea de comandos para ejecutar tareas se denomina comandos ad-hoc. La ventaja de los comandos ad-hoc es que son rápidos y requieren poca configuración; la desventaja es que se ejecutan manualmente y, por lo tanto, no pueden utilizarse con sistemas de control de versiones como Git.

      Una leve mejora sería escribir una secuencia de comandos de shell y ejecutar nuestros comandos usando el módulo script de Ansible. Esto nos permitiría registrar los pasos de configuración que tomamos en un control de versiones. Sin embargo, las secuencias de comandos de shell son imperativas, lo que significa que debemos asumir la tarea de determinar los comandos que se deben ejecutar para configurar el sistema de acuerdo al estado deseado (es decir, la manera de hacerlo). Ansible, por otro lado, promueve un enfoque declarativo en el que nosotros definimos el estado deseado que debería tener nuestro servidor en los archivos de configuración y Ansible se encarga de que lo alcance.

      El enfoque declarativo es preferible porque la intención del archivo de configuración se transmite de forma inmediata, por lo que es más fácil de entender y mantener. También atribuye la manipulación de casos de borde a Ansible en lugar de al administrador, lo que nos ahorra mucho trabajo.

      Ahora que configuró el nodo de control de Ansible para establecer conexión con los nodos administrados, en el siguiente paso, le presentaremos los cuadernos de estrategias de Ansible, que permiten especificar tareas de forma declarativa.

      Paso 2: Obtener nombres de los host de nodos administrados con cuadernos de estrategias de Ansible

      En este paso, vamos a replicar lo que hicimos en el Paso 1, imprimir los nombres de host de los nodos administrados, pero, en vez de ejecutar tareas ad-hoc, definiremos todas las tareas de forma declarativa como cuadernos de estrategias de Ansible y las ejecutaremos. El propósito de este paso es demostrar cómo funcionan los cuadernos de estrategias de Ansible; realizaremos tareas mucho más importantes con ellos en pasos posteriores.

      Cree un archivo nuevo denominado playbook.yaml en el directorio de su proyecto con su editor:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Añada las siguientes líneas en playbook.yaml:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        tasks:
          - name: "Retrieve hostname"
            command: hostname
            register: output
          - name: "Print hostname"
            debug: var=output.stdout_lines
      

      Cierra y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y.

      El playbook contiene una lista de estrategias; y cada una de ellas contiene una lista de tareas que se deben ejecutar en todos los hosts que coincidan con el patrón de host que especifica la clave hosts. En este cuaderno de estrategias, tenemos una estrategia que contiene dos tareas. La primera tarea ejecuta el comando hostname utilizando el módulo command y registra el resultado en una variable denominada output. En la segunda tarea, usamos el módulo debug para imprimir la propiedad stdout_lines de la variable output.

      Ahora, podemos ejecutar este cuaderno de estrategias utilizando el comando ansible-playbook:

      • ansible-playbook -i hosts playbook.yaml

      Obtendrá el siguiente resultado, que indica que su cuaderno de estrategias está funcionando correctamente:

      Output

      PLAY [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

      Nota: a veces, ansible-playbook utiliza cowsay como manera lúdica de imprimir los encabezamientos. Si encuentra muchos dibujos de una vaca en código ASCII en su terminal, ahora sabe el motivo. Para desactivar esta función, establezca la variable de entorno ANSIBLE_NOCOWS en 1 antes de ejecutar ansible-playbook ejecutando export ANSIBLE_NOCOWS=1 en su shell.

      En este paso, pasamos de ejecutar tareas ad-hoc imperativas a ejecutar cuadernos de estrategias declarativos. En el siguiente paso, sustituiremos estas dos tareas de demostración con tareas que configurarán nuestro clúster etcd.

      Paso 3: Instalar etcd en nodos administrados

      En este paso, le presentaremos los comandos necesarios para instalar etcd de forma manual y le demostraremos cómo convertir esos mismos comandos en tareas de nuestro cuaderno de estrategias de Ansible.

      etcd y su cliente etcdctl están disponibles como binarios, que descargaremos, extraeremos y colocaremos en un directorio que es parte de la variable de entorno PATH. Estos son los pasos que se deben llevar a cabo en cada uno de los nodos administrados al realizar la configuración de forma manual:

      • mkdir -p /opt/etcd/bin
      • cd /opt/etcd/bin
      • wget -qO- https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz | tar --extract --gzip --strip-components=1
      • echo 'export PATH="$PATH:/opt/etcd/bin"' >> ~/.profile
      • echo 'export ETCDCTL_API=3" >> ~/.profile

      Con los primeros cuatro comandos, se descargan los binarios y se extraen en el directorio /opt/etcd/bin/ . El cliente etcdctl usará API v2 para comunicarse con el servidor etcd de manera predeterminada. Como estamos ejecutando etcd v3.x, el último comando establece la variable de entorno ETCDCTL_API en 3.

      Nota: Aquí, utilizamos etcd v3.3.13 en un equipo con procesadores que utilizan el conjunto de instrucciones AMD64. Puede encontrar binarios de otros sistemas y otras versiones en la página oficial de Lanzamientos de GitHub.

      Para replicar los mismos pasos en un formato estandarizado, podemos agregar tareas a nuestro cuaderno de estrategias. Abra el archivo playbook.yaml en su editor:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Sustituya el archivo playbook.yaml en su totalidad por el siguiente contenido:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
            file:
              path: /opt/etcd/bin
              state: directory
              owner: root
              group: root
              mode: 0700
          - name: "Download the tarball into the /tmp directory"
            get_url:
              url: https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz
              dest: /tmp/etcd.tar.gz
              owner: root
              group: root
              mode: 0600
              force: True
          - name: "Extract the contents of the tarball"
            unarchive:
              src: /tmp/etcd.tar.gz
              dest: /opt/etcd/bin/
              owner: root
              group: root
              mode: 0600
              extra_opts:
                - --strip-components=1
              decrypt: True
              remote_src: True
          - name: "Set permissions for etcd"
            file:
              path: /opt/etcd/bin/etcd
              state: file
              owner: root
              group: root
              mode: 0700
          - name: "Set permissions for etcdctl"
            file:
              path: /opt/etcd/bin/etcdctl
              state: file
              owner: root
              group: root
              mode: 0700
          - name: "Add /opt/etcd/bin/ to the $PATH environment variable"
            lineinfile:
              path: /etc/profile
              line: export PATH="$PATH:/opt/etcd/bin"
              state: present
              create: True
              insertafter: EOF
          - name: "Set the ETCDCTL_API environment variable to 3"
            lineinfile:
              path: /etc/profile
              line: export ETCDCTL_API=3
              state: present
              create: True
              insertafter: EOF
      

      Cada tarea utiliza un módulo; en este conjunto de tareas, estamos utilizando los siguientes:

      • file: para crear el directorio /opt/etcd/bin y, más adelante, establecer los permisos del archivo para los binarios etcd y etcdctl.
      • get_url: para descargar el paquete tarball comprimido con gzip en los nodos administrados.
      • unarchive: para extraer y desempaquetar los binarios etcd y etcdctl del paquete tarball comprimido con gzip.
      • lineinfile: para agregar una entrada en el archivo .profile.

      Para aplicar estos cambios, cierre y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y. A continuación, vuelva a ejecutar el mismo comando ansible-playbook en la terminal:

      • ansible-playbook -i hosts playbook.yaml

      La sección PLAY RECAP del resultado solo mostrará ok y changed:

      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

      Para confirmar si etcd se instaló de forma correcta, ingrese con SSH de forma manual a uno de los nodos administrados y ejecute etcd y etcdctl:

      etcd1_public_ip es la dirección IP pública del servidor denominado etcd1. Una vez que haya obtenido acceso mediante SSH, ejecute etcd --version para imprimir la versión de etcd instalada:

      Obtendrá un resultado similar al siguiente, que indica que el binario etcd está instalado correctamente:

      Output

      etcd Version: 3.3.13 Git SHA: 98d3084 Go Version: go1.10.8 Go OS/Arch: linux/amd64

      Para confirmar que etcdctl se haya instalado correctamente, ejecute la versión etcdctl:

      Verá un resultado similar al siguiente:

      Output

      etcdctl version: 3.3.13 API version: 3.3

      Tenga en cuenta que el resultado dice API version: 3.3, lo que también confirma que nuestra variable de entorno ETCDCTL_API se estableció correctamente.

      Salga del servidor etcd1 para regresar a su entorno local.

      Con esto, instalamos etcd y etcdctl en todos nuestros nodos administrados correctamente. En el siguiente paso, añadiremos más tareas a nuestra estrategia para ejecutar etcd como servicio en segundo plano.

      Paso 4: Crear un archivo de unidad para etcd

      La manera más rápida de ejecutar etcd con Ansible parece ser ejecutar /opt/etcd/bin/etcd con el módulo command. Sin embargo, no funcionará, porque hará que etcd se ejecute como proceso en primer plano. El uso del módulo command hará que Ansible no responda mientras espera que se devuelva el comando etcd, lo que no sucederá en ningún momento. Por lo tanto, en este paso, vamos a actualizar nuestro cuaderno de estrategias para ejecutar nuestro binario etcd como servicio en segundo plano en su lugar.

      Ubuntu 18.04 utiliza systemd como sistema init, lo que significa que podemos crear servicios nuevos al escribir archivos de unidades y colocarlos en el directorio /etc/systemd/system/.

      Primero, cree un directorio nuevo denominado files/ en el directorio de nuestro proyecto:

      A continuación, cree un archivo nuevo denominado etcd.service en ese directorio con su editor:

      Luego, copie el siguiente bloque de código en el archivo files/etcd.service:

      ~/playground/etcd-ansible/files/etcd.service

      [Unit]
      Description=etcd distributed reliable key-value store
      
      [Service]
      Type=notify
      ExecStart=/opt/etcd/bin/etcd
      Restart=always
      

      Este archivo de unidad define un servicio que ejecuta el ejecutable en /opt/etcd/bin/etcd, notifica a systemd cuando termina de inicializarse y, si sale en algún momento, se reinicia.

      Nota: Si desea obtener más información sobre los archivos de unidades y systemd o adaptar el archivo de unidad a sus necesidades, consulte la guía Información sobre unidades y archivos de unidades de Systemd.

      Cierre y guarde el archivo files/etcd.service presionando CTRL+X y, luego, Y.

      A continuación, debemos agregar una tarea en nuestro cuaderno de estrategias para copiar el archivo local files/etcd.service al directorio /etc/systemd/system/etcd.service de cada nodo administrado. Podemos hacerlo usando el módulo copy.

      Abra su cuaderno de estrategias:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Añada la siguiente tarea resaltada al final de nuestras tareas existentes:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Set the ETCDCTL_API environment variable to 3"
            lineinfile:
              path: /etc/profile
              line: export ETCDCTL_API=3
              state: present
              create: True
              insertafter: EOF
          - name: "Create a etcd service"
            copy:
              src: files/etcd.service
              remote_src: False
              dest: /etc/systemd/system/etcd.service
              owner: root
              group: root
              mode: 0644
      

      Al copiar el archivo de unidad en /etc/systemd/system/etcd.service, definimos un servicio.

      Guarde y cierre el cuaderno de estrategias.

      Vuelva a ejecutar el mismo comando ansible-playbook para aplicar los cambios nuevos:

      • ansible-playbook -i hosts playbook.yaml

      Para confirmar que los cambios se hayan aplicado, primero, ingrese mediante SSH a uno de los nodos administrados:

      A continuación, ejecute systemctl status etcd para consultar a systemd el estado del servicio etcd:

      Obtendrá el siguiente resultado, que indica que el servicio está cargado:

      Output

      ● etcd.service - etcd distributed reliable key-value store Loaded: loaded (/etc/systemd/system/etcd.service; static; vendor preset: enabled) Active: inactive (dead) ...

      Nota: La última línea (Active: inactive (dead)) del resultado indica que el servicio está inactivo, lo que significa que no se ejecutará de forma automática cuando el sistema se inicie. Esto es de esperar, no es un error.

      Presione q para regresar al shell y, luego, ejecute exit para salir del nodo administrado y volver a su shell local:

      En este paso, actualizamos nuestro cuaderno de estrategias para ejecutar el binario etcd como servicio de systemd. En el siguiente paso, continuaremos configurando etcd al proporcionarle espacio para almacenar sus datos.

      Paso 5: Configurar el directorio de datos

      etcd es un almacén de datos de valor clave, lo que significa que debemos proporcionarle espacio para almacenar sus datos. En este paso, vamos a actualizar nuestro cuaderno de estrategias para definir un directorio de datos específico para etcd.

      Abra su cuaderno de estrategias:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Añada la siguiente tarea al final de la lista de tareas:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Create a etcd service"
            copy:
              src: files/etcd.service
              remote_src: False
              dest: /etc/systemd/system/etcd.service
              owner: root
              group: root
              mode: 0644
          - name: "Create a data directory"
            file:
              path: /var/lib/etcd/{{ inventory_hostname }}.etcd
              state: directory
              owner: root
              group: root
              mode: 0755
      

      En este caso, utilizamos /var/lib/etcd/hostname.etcd como directorio de datos, donde hostname es el nombre de host del nodo administrado actual. inventory_hostname es una variable que representa el nombre de host del nodo administrado actual; Ansible completa su valor de forma automática. La sintaxis de llaves (es decir, {{ inventory_hostname }}) se utiliza para sustituir variables con el motor de plantillas Jinja2, que es el motor predeterminado de Ansible.

      Cierre el editor de texto y guarde el archivo.

      A continuación, debemos indicarle a etcd que utilice este directorio de datos. Lo haremos pasando el parámetro data-dir a etcd. Para establecer los parámetros de etcd, podemos utilizar una combinación de variables de entorno, indicadores de la línea de comandos y archivos de configuración. En este tutorial, utilizaremos un archivo de configuración, dado que es mucho más fácil aislar todas las configuraciones en un archivo en lugar de tenerlas esparcidas por todo nuestro cuaderno de estrategias.

      Cree un directorio nuevo denominado templates/ en el directorio de su proyecto:

      A continuación, cree un archivo nuevo denominado etcd.conf.yaml.j2 en ese directorio con su editor:

      • nano templates/etcd.conf.yaml.j2

      Luego, copie la siguiente línea y péguela en el archivo:

      ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
      

      Este archivo utiliza la misma sintaxis de sustitución de variables de Jinja2 que nuestro cuaderno de estrategias. Podemos usar el módulo template para sustituir las variables y cargar el resultado en cada host administrado. Funciona de manera similar a copy, con la excepción de que realiza la sustitución de variables antes de la carga.

      Salga de etcd.conf.yaml.j2 y abra su cuaderno de estrategias:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Añada las siguientes tareas a la lista de tareas para crear un directorio y cargar el archivo de configuración basado en una plantilla en su interior:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Create a data directory"
            file:
              ...
              mode: 0755
          - name: "Create directory for etcd configuration"
            file:
              path: /etc/etcd
              state: directory
              owner: root
              group: root
              mode: 0755
          - name: "Create configuration file for etcd"
            template:
              src: templates/etcd.conf.yaml.j2
              dest: /etc/etcd/etcd.conf.yaml
              owner: root
              group: root
              mode: 0600
      

      Guarde y cierre este archivo.

      Como realizamos este cambio, debemos actualizar el archivo de unidad de nuestro servicio para que pase la ubicación de nuestro archivo de configuración (es decir, /etc/etcd/etcd.conf.yaml).

      Abra el archivo del servicio etcd en su equipo local:

      Actualice el archivo files/etcd.service añadiendo el indicador --config-file resaltado en lo siguiente:

      ~/playground/etcd-ansible/files/etcd.service

      [Unit]
      Description=etcd distributed reliable key-value store
      
      [Service]
      Type=notify
      ExecStart=/opt/etcd/bin/etcd --config-file /etc/etcd/etcd.conf.yaml
      Restart=always
      

      Guarde y cierre este archivo.

      En este paso, utilizamos nuestro cuaderno de estrategias para proporcionar un directorio de datos para que etcd almacene sus datos. En el siguiente paso, añadiremos algunas tareas adicionales para reiniciar el servicio etcd y hacer que se ejecute en el inicio.

      Paso 6: Habilitar e iniciar el servicio etcd

      Siempre que realizamos cambios en el archivo de unidad de un servicio, debemos reiniciarlo para que surtan efecto. Podemos hacerlo ejecutando el comando systemctl restart etcd. Además, para que el servicio etcd se inicie de forma automática en el inicio del sistema, debemos ejecutar systemctl enabled etcd. En este paso, ejecutaremos esos dos comandos utilizando el cuaderno de estrategias.

      Podemos usar el módulo command para ejecutar comandos:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Añada las siguientes tareas al final de la lista de tareas:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Create configuration file for etcd"
            template:
              ...
              mode: 0600
          - name: "Enable the etcd service"
            command: systemctl enable etcd
          - name: "Start the etcd service"
            command: systemctl restart etcd
      

      Guarde y cierre el archivo.

      Vuelva a ejecutar ansible-playbook -i hosts playbook.yaml una vez más:

      • ansible-playbook -i hosts playbook.yaml

      Para verificar que el servicio etcd se haya reiniciado y habilitado, ingrese mediante SSH a uno de los nodos administrados:

      A continuación, ejecute systemctl status etcd para consultar el estado del servicio etcd:

      Encontrará enabled y active (running), como indica lo resaltado en el siguiente ejemplo, lo que significa que se implementaron los cambios que hicimos en nuestro cuaderno de estrategias:

      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)

      En este paso, utilizamos el módulo command para ejecutar comandos systemctl para reiniciar y habilitar el servicio etcd en nuestros nodos administrados. Ahora que configuramos una instalación de etcd, en el siguiente paso, vamos a probar su funcionalidad al llevar a cabo algunas operaciones básicas de creación, lectura, actualización y eliminación (CRUD).

      Paso 7: Probar etcd

      Si bien tenemos una instalación de etcd en funcionamiento, no es segura ni está listo para usarse en producción. Pero antes de proteger nuestra configuración de etcd en pasos posteriores, primero, vamos comprender lo que puede hacer etcd en términos de funcionalidad. En este paso, vamos a enviar solicitudes a etcd de forma manual para añadir, recuperar, actualizar y eliminar sus datos.

      Por defecto, etcd expone una API que escucha en el puerto 2379 para la comunicación con el cliente. Esto significa que podemos enviar solicitudes de API sin procesar a etcd utilizando un cliente HTTP. Sin embargo, es más rápido usar el cliente oficial de etcd, etcdctl, que permite crear/actualizar, recuperar y eliminar pares clave-valor usando los subcomandos put, get y del respectivamente.

      Asegúrese de seguir estando en el nodo administrado etcd1 y ejecute los siguientes comandos de etcdctl para confirmar que su instalación de etcd esté funcionando.

      Primero, cree una nueva entrada utilizando el subcomando put.

      El subcomando put tiene la siguiente sintaxis:

      etcdctl put key value
      

      En etcd1, ejecute el siguiente comando:

      El comando que acabamos de ejecutar le indica a etcd que escriba el valor "bar" en la clave foo del almacén.

      A continuación, encontrará OK impreso en el resultado, lo que indica que los datos se mantuvieron:

      Output

      OK

      Luego, podemos recuperar esta entrada utilizando el subcomando get, que tiene la sintaxis etcdctl get key:

      Obtendrá este resultado, que muestra la clave en la primera línea y el valor que insertó anteriormente en la segunda:

      Output

      foo bar

      Podemos eliminar la entrada utilizando el subcomando del, que tiene la sintaxis etcdctl del key:

      Obtendrá el siguiente resultado, que indica la cantidad de entradas eliminadas:

      Output

      1

      Ahora, vamos a ejecutar el subcomando get una vez más para intentar recuperar un par clave-valor eliminado:

      No obtendrá ningún resultado, lo que significa que etcdctl no puede recuperar el par clave-valor. Esto confirma que las entradas eliminadas no pueden recuperarse.

      Ahora que probó las operaciones básicas de etcd y etcdctl, salga del nodo administrado y regrese a su entorno local:

      En este paso, utilizamos el cliente etcdctl para enviar solicitudes a etcd. En este punto, estamos ejecutando tres instancias separadas de etcd que actúan de forma independiente unas de otras. Sin embargo, etcd se diseñó como un almacén de clave-valor distribuido, lo que significa que se pueden agrupar varias instancias de etcd para formar un clúster único; luego, cada instancia se convierte en miembro del clúster. Después de crear un clúster, podrá recuperar pares clave-valor insertados desde distintos miembro del clúster. En el siguiente paso, utilizaremos nuestro playbook para transformar nuestros tres clústeres de un solo nodo en un clúster único de tres nodos.

      Paso 8: Crear un clúster utilizando descubrimiento estático

      Para crear un clúster de tres nodos en lugar de tres de un nodo, debemos configurar estas instalaciones de etcd para que se comuniquen entre sí. Esto significa que cada una de ellas debe conocer las direcciones IP de las demás. Este proceso se denomina descubrimiento. El descubrimiento se puede realizar utilizando una configuración estática o un descubrimiento de servicio dinámico. En este paso, analizaremos la diferencia entre ambas y, también, actualizaremos nuestro cuaderno de estrategias para configurar un clúster de etcd con descubrimiento estático.

      El descubrimiento mediante configuración estática es el método que requiere menos configuración; los puntos de conexión de cada miembro se pasan al comando etcd antes de su ejecución. Para usar la configuración estática, se deben cumplir las siguientes condiciones antes de la inicialización del clúster:

      • se debe conocer la cantidad de miembros
      • se deben conocer los puntos de conexión de cada miembro
      • las direcciones IP de todos los puntos de conexión deben ser estáticas

      Si no se pueden cumplir estas condiciones, puede usar un servicio de descubrimiento dinámico. Con el descubrimiento de servicios dinámicos, todas las instancias se registran en el servicio de descubrimiento, lo que permite a cada miembro recuperar información sobre la ubicación de los demás.

      Como sabemos que queremos tener un clúster de etcd de tres nodos y todos nuestros servidores tienen direcciones IP estáticas, utilizaremos el descubrimiento estático. Para iniciar nuestro clúster utilizando descubrimiento estático, debemos agregar varios parámetros a nuestro archivo de configuración. Utilice un editor para abrir el archivo de plantilla templates/etcd.conf.yaml.j2 :

      • nano templates/etcd.conf.yaml.j2

      A continuación, añada las siguientes líneas resaltadas:

      ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
      name: {{ inventory_hostname }}
      initial-advertise-peer-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380
      listen-peer-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380,http://127.0.0.1:2380
      advertise-client-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379
      listen-client-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379,http://127.0.0.1:2379
      initial-cluster-state: new
      initial-cluster: {% for host in groups['etcd'] %}{{ hostvars[host]['ansible_facts']['hostname'] }}=http://{{ hostvars[host]['ansible_facts']['eth1']['ipv4']['address'] }}:2380{% if not loop.last %},{% endif %}{% endfor %}
      

      Cierre y guarde el archivo templates/etcd.conf.yaml.j2  presionando CTRL+X y, luego, Y.

      A continuación, presentamos una breve explicación de cada parámetro:

      • name: el nombre del miembro en lenguaje natural. Por defecto, etcd utiliza un identificador único generado al azar para identificar cada miembro; sin embargo, un nombre en lenguaje natural nos permite referirnos a ellos con mayor facilidad tanto en los archivos de configuración como en la línea de comandos. Aquí, utilizaremos los nombres de host como nombres de los miembros (es decir, etcd1, etcd2 y etcd3).
      • initial-advertise-peer-urls: una lista de combinaciones de direcciones IP y puertos que pueden usar otros miembros para comunicarse con este. Además del puerto de la API (2379) de etcd también expone el puerto 2380 para la comunicación de punto a punto entre los miembros de etcd, lo que les permite enviar mensajes e intercambiar datos. Tenga en cuenta que estas URL deben poder ser accesibles para sus pares (y no deben ser una dirección IP local).
      • listen-peer-urls: una lista de combinaciones de direcciones IP y puertos que el miembro actual utilizará para escuchar comunicaciones de otros miembros. Debe incluir todas las URL del indicador --initial-advertise-peer-urls, así como las URL locales, como 127.0.0.1:2380. La dirección IP y el puerto de destino de los mensajes entrantes de pares deben coincidir con una de las URL que se enumeran aquí.
      • advertise-client-urls: una lista de combinaciones de direcciones IP y puertos que deben usar los clientes para comunicarse con este miembro. El cliente debe poder acceder a estas URL (y no deben ser una dirección local). Si el cliente está accediendo al clúster a través de una red pública de Internet, la dirección IP debe ser pública.
      • listen-client-urls: una lista de combinaciones de direcciones IP y puertos que el miembro actual utilizará para escuchar comunicaciones de los clientes. Debe incluir todas las URL del indicador --advertise-client-urls, así como las URL locales, como 127.0.0.1:2379. La dirección IP y el puerto de destino de los mensajes entrantes de clientes deben coincidir con una de las URL que se enumeran aquí.
      • initial-cluster: una lista de los puntos de conexión de cada miembro del clúster. Cada punto de conexión debe coincidir con una de las URL de initial-advertise-peer-urls del miembro correspondiente.
      • initial-cluster-state: puede ser new o existing.

      Para asegurar consistencia, etcd solo puede tomar decisiones cuando la mayoría de los nodos tienen un estado correcto. Esto se conoce como establecimiento de quorum. En otras palabras, en un clúster de tres miembros, hay quorum cuando dos o más de ellos tienen un estado correcto.

      Si el parámetro initial-cluster-state se establece en new, etcd sabrá que se trata de un clúster nuevo que se está iniciando y permitirá que los miembros se inicien en paralelo sin esperar a que se alcance el quorum. Más concretamente, una vez que se inicie el primer miembro, no habrá quorum porque un tercio (33,33 %) es menor o igual que el 50 %. Normalmente, etcd se detendría y se negaría a realizará más acciones, por lo que el clúster nunca se crearía. Sin embargo, con initial-cluster-state establecido en new, ignorará la falta de quorum inicial.

      Si se establece en existing, el miembro intentará unirse a un clúster existente, y esperará que el quorum ya esté establecido.

      Nota: Puede obtener más información sobre todos los indicadores de configuración compatibles en la sección Configuración de la documentación de etcd.

      En el archivo de plantilla templates/etcd.conf.yaml.j2 actualizado, hay algunas instancias de hostvars. Cuando Ansible se ejecute, recopilará variables de diversas fuentes. Ya hemos utilizado la variable inventory_hostname, pero hay mucho más disponible. Estas variables están disponibles en hostvars[inventory_hostname]['ansible_facts']. Aquí, estamos extrayendo las direcciones IP privadas de cada nodo y las estamos utilizando para crear el valor de nuestro parámetro.

      Nota: Como habilitamos la opción Redes privadas cuando creamos nuestros servidores, habrá tres direcciones IP asociadas a cada servidor:

      • Una dirección IP de bucle invertido: una dirección que solo es válida dentro del mismo equipo. Se utiliza para que el equipo haga referencia a sí mismo, por ejemplo, 127.0.0.1
      • Una dirección IP pública: una dirección que se puede enrutar mediante una red pública de Internet, por ejemplo, 178.128.169.51
      • Una dirección IP privada: una dirección que solo se puede enrutar mediante una red privada; en el caso de Droplets de DigitalOcean, hay una red privada en cada centro de datos, por ejemplo, 10.131.82.225

      Cada una de estas direcciones IP está asociada con una interfaz de red distinta: la dirección de bucle invertido se asocia con la interfaz lo, la dirección IP pública se asocia con la interfaz eth0 y la dirección IP privada con la interfaz eth1. Estamos utilizando la interfaz eth1 para que todo el tráfico se mantenga en la red privada, sin llegar a Internet.

      Para los fines de este artículo, no es necesario tener conocimientos sobre las interfaces de red, pero, si desea obtener más información, el artículo Introducción a terminología de redes, interfaces y protocolos es un buen punto de partida.

      La sintaxis {% %} de Jinja2 define la estructura de bucle for que se itera a través de cada nodo del grupo etcd para crear la cadena initial-cluster en un formato requerido por etcd.

      Para crear el clúster nuevo de tres miembros, debe detener el servicio etcd y borrar el directorio de datos antes de iniciar el clúster. Para hacerlo, utilice un editor para abrir el archivo playbook.yaml en su equipo local:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Luego, antes de la tarea "Create a data directory", añada una tarea para detener el servicio etcd:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
              group: root
              mode: 0644
          - name: "Stop the etcd service"
            command: systemctl stop etcd
          - name: "Create a data directory"
            file:
          ...
      

      A continuación, actualice la tarea "Create a data directory" para eliminar el directorio de datos y recrearlo:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Stop the etcd service"
            command: systemctl stop etcd
          - name: "Create a data directory"
            file:
              path: /var/lib/etcd/{{ inventory_hostname }}.etcd
              state: "{{ item }}"
              owner: root
              group: root
              mode: 0755
            with_items:
              - absent
              - directory
          - name: "Create directory for etcd configuration"
            file:
          ...
      

      La propiedad with_items define una lista de cadenas sobre la cual iterará en esta tarea. Es equivalente a repetir la misma tarea dos veces, pero con valores diferentes para la propiedad state. Aquí, estamos iterando sobre la lista con elementos absent y directory, lo que garantiza que el directorio de datos se elimine primero y, luego, se vuelva a crear.

      Cierra y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y. A continuación, vuelva a ejecutar ansible-playbook. Ahora, Ansible creará un clúster de etcd único, de 3 miembros:

      • ansible-playbook -i hosts playbook.yaml

      Puede verificarlo al ingresar mediante SSH en cualquier nodo miembro de etcd:

      A continuación, ejecute etcdctl endpoint health --cluster:

      • etcdctl endpoint health --cluster

      Esto enumerará el estado de cada miembro del clúster:

      Output

      http://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

      Ahora, hemos creado correctamente un clúster de etcd de tres nodos. Podemos confirmar esto añadiendo una entrada a etcd en un nodo miembro y recuperándola de otro. En uno de los nodos miembro, ejecute etcdctl put:

      A continuación, utilice una terminal nueva para ingresar mediante SSH en un nodo miembro diferente:

      Luego, intente recuperar la misma entrada usando la clave:

      Podrá recuperar la entrada, lo que demuestra que el clúster está funcionando:

      Output

      foo bar

      Por último, salga de todos los nodos administrados y regrese a su equipo local:

      En este paso, proporcionamos un clúster de tres nodos. En este momento, las comunicaciones entre los miembros de etcd y sus pares y clientes se llevan a cabo mediante HTTP. Esto significa que la comunicación no está cifrada y cualquier parte que pueda interceptar el tráfico puede leer los mensajes. Esto no es un problema importante si el clúster y los clientes de etcd están implementados en una red privada o una red privada virtual (VPN) que controle por completo. Sin embargo, si algún tráfico debe transmitirse a través de una red compartida (privada o pública), deberá asegurarse de que ese tráfico esté cifrado. Además, se debe establecer un mecanismo para que un cliente o par compruebe la autenticidad del servidor.

      En el siguiente paso, veremos cómo proteger las comunicaciones de cliente a servidor y las de punto a punto utilizando TLS.

      Paso 9: Obtener direcciones IP privadas de nodos administrados

      Para cifrar mensajes entre nodos miembros, etcd utiliza el Protocolo seguro de transferencia de hipertexto, o HTTPS, que es una capa por encima de la Seguridad de la capa de transporte, o TLS. TLS utiliza un sistema de claves privadas, certificados y entidades de confianza denominadas Entidades de certificación (CA) para autenticarse y enviar mensajes cifrados.

      En este tutorial, cada nodo miembro debe generar un certificado para identificarse y este debe estar firmado por una CA. Configuraremos todos los nodos miembros para que confíen en esta CA y, por lo tanto, también en cualquier certificado firmado por ella. Esto les permite a los nodos miembro autenticarse mutuamente.

      El certificado que genere cada nodo miembro debe permitir que los demás lo identifiquen. Todos los certificados incluyen el Nombre común (CN) de la entidad a la que están asociados. Esto se suele utilizar como la identidad de la entidad. Sin embargo, al verificar un certificado, las implementaciones de los clientes pueden comparar si la información recopilada sobre la entidad concuerda con la proporcionada en el certificado. Por ejemplo, si un cliente descarga un certificado TLS con firmante CN=foo.bar.com, pero, en realidad, se está conectando al servidor usando una dirección IP (por ejemplo, 167.71.129.110), hay una incongruencia y es posible que el cliente no confíe en el certificado. Al especificar un nombre alternativo del firmante (SAN) en el certificado, le informa que ambos nombres pertenecen a la misma entidad.

      Como nuestros miembros de etcd se emparejan utilizando sus direcciones IP privadas, al definir nuestros certificados, tendremos que proporcionar estas direcciones IP privadas como nombres alternativos del firmante.

      Para obtener la dirección IP privada de un nodo administrado, ingrese a él mediante SSH:

      Luego, ejecute el siguiente comando:

      • ip -f inet addr show eth1

      Verá un resultado similar a las siguientes líneas:

      Output

      3: 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

      En nuestro resultado ejemplo, 10.131.255.176 es la dirección IP privada del nodo administrado, y la única información que nos interesa. Para filtrar todo lo que no corresponde a la IP privada, podemos canalizar el resultado del comando ip a la utilidad sed, que se utiliza para filtrar y transformar texto.

      • ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/1/p'

      Ahora, el único resultado es la dirección IP privada:

      Output

      10.131.255.176

      Una vez que esté satisfecho con el funcionamiento del comando anterior, salga del nodo administrado:

      Para incorporar los comandos anteriores a nuestro playbook, abra el archivo playbook.yaml:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      A continuación, añada una nueva estrategia con una sola tarea antes de nuestra estrategia existente:

      ~/playground/etcd-ansible/playbook.yaml

      ...
      - hosts: etcd
        tasks:
          - shell: ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/1/p'
            register: privateIP
      - hosts: etcd
        tasks:
      ...
      

      La tarea utiliza el módulo shell para ejecutar los comandos ip y sed, con lo cual se obtiene la dirección IP privada del nodo administrado. A continuación, registra el valor que devolvió el comando shell dentro de una variable denominada privateIP, que utilizaremos más adelante.

      En este paso, añadimos una tarea al cuaderno de estrategias para obtener la dirección IP privada de los nodos administrados. En el siguiente paso, vamos a utilizar esta información para generar certificados para cada nodo miembro que firmaremos con una Entidad de certificación (CA).

      Paso 10: Generar claves privadas y CSR para los miembros de etcd

      Para que un nodo miembro pueda recibir tráfico cifrado, el remitente debe utilizar la clave pública del nodo miembro para cifrar los datos y el nodo miembro debe usar su clave privada para descifrar el texto cifrado y obtener los datos originales. La clave pública se empaqueta en un certificado y la firma una CA para garantizar que sea auténtica.

      Por lo tanto, debemos generar una clave privada y una solicitud de firma de certificado (CSR) para cada nodo miembro de etcd. Para facilitar la tarea, generaremos todos los pares de claves y firmaremos todos los certificados de forma local, en el nodo de control y, luego, copiaremos los archivos pertinentes a los hosts administrados.

      Primero, cree un directorio denominado artifacts/, donde colocaremos los archivos (claves y certificados) que se generarán durante el proceso. Abra el archivo playbook.yaml con un editor:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Utilice el módulo file para crear el directorio artifacts/ en su interior:

      ~/playground/etcd-ansible/playbook.yaml

      ...
          - shell: ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/1/p'
            register: privateIP
      - hosts: localhost
        gather_facts: False
        become: False
        tasks:
          - name: "Create ./artifacts directory to house keys and certificates"
            file:
              path: ./artifacts
              state: directory
      - hosts: etcd
        tasks:
      ...
      

      A continuación, añada otra tarea al final de la estrategia para generar la clave privada:

      ~/playground/etcd-ansible/playbook.yaml

      ...
      - hosts: localhost
        gather_facts: False
        become: False
        tasks:
              ...
          - name: "Generate private key for each member"
            openssl_privatekey:
              path: ./artifacts/{{item}}.key
              type: RSA
              size: 4096
              state: present
              force: True
            with_items: "{{ groups['etcd'] }}"
      - hosts: etcd
        tasks:
      ...
      

      La creación de claves privadas y CSR se puede llevar a cabo utilizando los módulos openssl_privatekey y openssl_csr respectivamente.

      El atributo force: True garantiza que siempre se genere una clave privada, incluso si ya existe.

      Del mismo modo, añada la siguiente tarea nueva al misma estrategia para generar las CSR para cada miembro utilizando el módulo openssl_csr:

      ~/playground/etcd-ansible/playbook.yaml

      ...
      - hosts: localhost
        gather_facts: False
        become: False
        tasks:
          ...
          - name: "Generate private key for each member"
            openssl_privatekey:
              ...
            with_items: "{{ groups['etcd'] }}"
          - name: "Generate CSR for each member"
            openssl_csr:
              path: ./artifacts/{{item}}.csr
              privatekey_path: ./artifacts/{{item}}.key
              common_name: "{{item}}"
              key_usage:
                - digitalSignature
              extended_key_usage:
                - serverAuth
              subject_alt_name:
                - IP:{{ hostvars[item]['privateIP']['stdout']}}
                - IP:127.0.0.1
              force: True
            with_items: "{{ groups['etcd'] }}"
      

      Estamos especificando que este certificado puede estar involucrado en un mecanismo de firma digital para la autenticación del servidor. Este certificado se asocia con el nombre de host (por ejemplo, etcd1), pero el verificador debe tratar las direcciones IP de bucle inverso privadas y locales de cada nodo como nombres alternativos. Tenga en cuenta que estamos utilizando la variable privateIP que registramos en la estrategia anterior.

      Cierre y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y. A continuación, vuelva a ejecutar el playbook:

      • ansible-playbook -i hosts playbook.yaml

      Ahora, encontraremos un nuevo directorio denominado artifacts en el directorio de nuestro proyecto; usaremos ls para enumerar su contenido:

      Encontrará las claves privadas y las CSR de cada miembro de etcd:

      Output

      etcd1.csr etcd1.key etcd2.csr etcd2.key etcd3.csr etcd3.key

      En este paso, usamos varios módulos de Ansible para generar claves privadas y certificados de clave pública para cada uno de los nodos miembro. En el siguiente paso, veremos cómo firmar una solicitud de firma de certificado (CSR).

      Paso 11: Generar certificados de CA

      Dentro de un clúster de etcd, los nodos miembro cifran los mensajes usando la clave pública del receptor. Para garantizar que la clave pública sea genuina, el receptor empaqueta la clave pública en una solicitud de firma de certificado (CSR) y hace que una entidad de confianza (es decir, la CA) lo firme. Como controlamos todos los nodos miembro y las CA en las que confían, no necesitamos usar una CA externa y podemos actuar como nuestra propia CA. En este paso, vamos a actuar como nuestra propia CA, lo que significa que debemos generar una clave privada y un certificado autofirmado para que funcione como CA.

      Primero, abra el archivo playbook.yaml con su editor:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      A continuación, al igual que en el paso anterior, añada una tarea a la estrategia localhost para generar una clave privada para la CA:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
        - name: "Generate CSR for each member"
          ...
          with_items: "{{ groups['etcd'] }}"
          - name: "Generate private key for CA"
            openssl_privatekey:
              path: ./artifacts/ca.key
              type: RSA
              size: 4096
              state: present
              force: True
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      A continuación, utilice el módulo openssl_csr para generar una CSR nueva. Esto es similar a lo que hicimos en el paso anterior, con la diferencia de que, en esta CSR, estamos añadiendo la extensión de uso de la clave y la restricción básica para indicar que este certificado puede utilizarse como certificado de CA:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
          - name: "Generate private key for CA"
            openssl_privatekey:
              path: ./artifacts/ca.key
              type: RSA
              size: 4096
              state: present
              force: True
          - name: "Generate CSR for CA"
            openssl_csr:
              path: ./artifacts/ca.csr
              privatekey_path: ./artifacts/ca.key
              common_name: ca
              organization_name: "Etcd CA"
              basic_constraints:
                - CA:TRUE
                - pathlen:1
              basic_constraints_critical: True
              key_usage:
                - keyCertSign
                - digitalSignature
              force: True
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      Por último, utilice el módulo openssl_certificate para autofirmar la CSR:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
          - name: "Generate CSR for CA"
            openssl_csr:
              path: ./artifacts/ca.csr
              privatekey_path: ./artifacts/ca.key
              common_name: ca
              organization_name: "Etcd CA"
              basic_constraints:
                - CA:TRUE
                - pathlen:1
              basic_constraints_critical: True
              key_usage:
                - keyCertSign
                - digitalSignature
              force: True
          - name: "Generate self-signed CA certificate"
            openssl_certificate:
              path: ./artifacts/ca.crt
              privatekey_path: ./artifacts/ca.key
              csr_path: ./artifacts/ca.csr
              provider: selfsigned
              force: True
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      Cierre y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y. A continuación, vuelva a ejecutar el playbook para aplicar los cambios:

      • ansible-playbook -i hosts playbook.yaml

      También puede ejecutar ls para verificar el contenido del directorio artifacts/ :

      Ahora, encontrará el certificado de CA generado recientemente (ca.crt):

      Output

      ca.crt ca.csr ca.key etcd1.csr etcd1.key etcd2.csr etcd2.key etcd3.csr etcd3.key

      En este paso, generamos una clave privada y un certificado autofirmado para la CA. En el siguiente paso, usaremos el certificado de CA para firmar la CSR de cada miembro.

      Paso 12: Firmar las CSR de los miembros de etcd

      En este paso, vamos a firmar la CSR de cada nodo miembro. Lo haremos de forma similar a la manera en que usamos el módulo openssl_certificate para firmar el certificado de CA, pero, en lugar de usar el proveedor self-signed, utilizaremos el proveedor ownca, que nos permite firmar con nuestro propio certificado de CA.

      Abra su cuaderno de estrategias:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Añada la siguiente tarea resaltada a la tarea "Generate self-signed CA certificate":

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
          - name: "Generate self-signed CA certificate"
            openssl_certificate:
              path: ./artifacts/ca.crt
              privatekey_path: ./artifacts/ca.key
              csr_path: ./artifacts/ca.csr
              provider: selfsigned
              force: True
          - name: "Generate an `etcd` member certificate signed with our own CA certificate"
            openssl_certificate:
              path: ./artifacts/{{item}}.crt
              csr_path: ./artifacts/{{item}}.csr
              ownca_path: ./artifacts/ca.crt
              ownca_privatekey_path: ./artifacts/ca.key
              provider: ownca
              force: True
            with_items: "{{ groups['etcd'] }}"
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      Cierre y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y. A continuación, vuelva a ejecutar el cuaderno de estrategias para aplicar los cambios:

      • ansible-playbook -i hosts playbook.yaml

      Ahora, enumere el contenido del directorio artifacts/:

      Encontrará la clave privada, la CSR y el certificado de cada miembro de etcd y la CA:

      Output

      ca.crt ca.csr ca.key etcd1.crt etcd1.csr etcd1.key etcd2.crt etcd2.csr etcd2.key etcd3.crt etcd3.csr etcd3.key

      En este paso, firmamos las CSR de cada nodo miembro usando la clave de la CA. En el siguiente paso, vamos a copiar los archivos pertinentes en cada nodo administrado, para que etcd tenga acceso a las claves y los certificados correspondientes para configurar las conexiones de TLS.

      Paso 13: Copiar claves privadas y certificados

      Cada nodo debe tener una copia del certificado de la CA autofirmado (ca.crt). Cada nodo de miembro de etcd también debe tener su clave privada y certificado propios. En este paso, vamos a cargar estos archivos y los colocaremos en un directorio nuevo /etc/etcd/ssl/.

      Para comenzar, abra el archivo playbook.yaml con su editor:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Para realizar estos cambios en nuestro playbook de Ansible, primero, actualice la propiedad path de la tarea Create directory for etcd configuration para crear el directorio /etc/etcd/ssl/:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        ...
        tasks:
          ...
            with_items:
              - absent
              - directory
          - name: "Create directory for etcd configuration"
            file:
              path: "{{ item }}"
              state: directory
              owner: root
              group: root
              mode: 0755
            with_items:
              - /etc/etcd
              - /etc/etcd/ssl
          - name: "Create configuration file for etcd"
            template:
      ...
      

      A continuación, detrás de la tarea modificada, añada tres tareas más para copiar los archivos:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        ...
        tasks:
          ...
          - name: "Copy over the CA certificate"
            copy:
              src: ./artifacts/ca.crt
              remote_src: False
              dest: /etc/etcd/ssl/ca.crt
              owner: root
              group: root
              mode: 0644
          - name: "Copy over the `etcd` member certificate"
            copy:
              src: ./artifacts/{{inventory_hostname}}.crt
              remote_src: False
              dest: /etc/etcd/ssl/server.crt
              owner: root
              group: root
              mode: 0644
          - name: "Copy over the `etcd` member key"
            copy:
              src: ./artifacts/{{inventory_hostname}}.key
              remote_src: False
              dest: /etc/etcd/ssl/server.key
              owner: root
              group: root
              mode: 0600
          - name: "Create configuration file for etcd"
            template:
      ...
      

      Cierre y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y.

      Vuelva a ejecutar ansible-playbook para realizar estos cambios:

      • ansible-playbook -i hosts playbook.yaml

      En este paso, cargamos correctamente las claves privadas y los certificados a los nodos administrados. Ahora que copiamos los archivos, debemos actualizar nuestro archivo de configuración para usarlos.

      Paso 14: Habilitar TLS en etcd

      En el último paso de este tutorial, vamos a actualizar algunas configuraciones de Ansible para habilitar TLS en un clúster de etcd.

      Primero, abra el archivo de plantillas templates/etcd.conf.yaml.j2 con su editor:

      • nano $HOME/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      En su interior, cambie todas las URL para usar https como protocolo en lugar de http. Adicionalmente, añada una sección al final de la plantilla para especificar la ubicación del certificado de CA, el certificado del servidor y la clave del servidor:

      ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
      name: {{ inventory_hostname }}
      initial-advertise-peer-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380
      listen-peer-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380,https://127.0.0.1:2380
      advertise-client-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379
      listen-client-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379,https://127.0.0.1:2379
      initial-cluster-state: new
      initial-cluster: {% for host in groups['etcd'] %}{{ hostvars[host]['ansible_facts']['hostname'] }}=https://{{ hostvars[host]['ansible_facts']['eth1']['ipv4']['address'] }}:2380{% if not loop.last %},{% endif %}{% endfor %}
      
      client-transport-security:
        cert-file: /etc/etcd/ssl/server.crt
        key-file: /etc/etcd/ssl/server.key
        trusted-ca-file: /etc/etcd/ssl/ca.crt
      peer-transport-security:
        cert-file: /etc/etcd/ssl/server.crt
        key-file: /etc/etcd/ssl/server.key
        trusted-ca-file: /etc/etcd/ssl/ca.crt
      

      Cierre y guarde el archivo templates/etcd.conf.yaml.j2.

      A continuación, ejecute su playbook de Ansible:

      • ansible-playbook -i hosts playbook.yaml

      Luego, ingrese mediante SSH a uno de los nodos administrados:

      Cuando haya ingresado, ejecute el comando etcdctl endpoint health para verificar si los puntos de conexión están usando HTTPS y si el estado de todos los miembros es correcto:

      • etcdctl --cacert /etc/etcd/ssl/ca.crt endpoint health --cluster

      Como nuestro certificado de CA no es, por defecto, un certificado root de CA de confianza instalado en el directorio /etc/ssl/certs/, debemos pasarlo a etcdctl usando el indicador --cacert.

      Esto generará el siguiente resultado:

      Output

      https://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

      Para confirmar que el clúster etcd efectivamente esté funcionando, podemos crear una entrada en un nodo miembro y recuperarla desde otro:

      • etcdctl --cacert /etc/etcd/ssl/ca.crt put foo "bar"

      A continuación, utilice una terminal nueva para ingresar mediante SSH en un nodo diferente:

      Ahora, recupere la misma entrada utilizando la clave foo:

      • etcdctl --cacert /etc/etcd/ssl/ca.crt get foo

      Esto devolverá la entrada y verá el siguiente resultado:

      Output

      foo bar

      Puede hacer lo mismo en el tercer nodo para garantizar que los tres miembros estén en funcionamiento.

      Conclusión

      Proporcionó correctamente un clúster de etcd de tres nodos, lo protegió con TLS y confirmó que esté funcionando.

      etcd es una herramienta creada originalmente por CoreOS. Para entender el uso de etcd en relación con CoreOS, puede consultar el artículo Cómo usar Etcdctl y Etcd, el almacén de clave-valor distribuido de CoreOS. El artículo también lo guía a través del proceso de configuración de un modelo de descubrimiento dinámico, algo que mencionamos, pero no demostramos en este tutorial.

      Como se mencionó al principio de este tutorial, etcd es un componente importante del ecosistema de Kubernetes. Para obtener más información sobre Kubernetes y la función que desempeña etcd en él, puede consultar el artículo Introducción a Kubernetes. Si está implementando etcd como parte de un clúster de Kubernetes, tenga en cuenta que puede utilizar otras herramientas, como kubespray y kubeadm. Para obtener más información al respecto, puede consultar el artículo Cómo crear un clúster de Kubernetes usando Kubeadm en Ubuntu 18.04.

      Por último, en este tutorial, se utilizaron muchas herramientas, pero no se pudo entrar en demasiado detalle en cada una de ellas. Encontrará enlaces que proporcionarán un análisis más detallado de cada herramienta aquí:



      Source link

      Como configurar e proteger um cluster etcd com o Ansible no Ubuntu 18.04


      O autor selecionou a Wikimedia Foundation para receber uma doação como parte do programa Write for DOnations.

      Introdução

      O etcd é um armazenamento distribuído de chaves-valores utilizado em muitas plataformas e ferramentas, incluindo o Kubernetes, Vulcand e o Doorman. Dentro do Kubernetes, o etcd é usado como um armazenamento de configuração global que armazena o estado do cluster. Saber administrar o etcd é essencial para administrar um cluster Kubernetes. Embora existam muitas opções gerenciadas do Kubernetes, também conhecidas como Kubernetes-as-a-Service, que removem esse peso administrativo de você, muitas empresas ainda escolhem utilizar clusters Kubernetes locais autogeridos devido à flexibilidade que eles trazem.

      A primeira metade deste artigo irá guiá-lo através da configuração de um cluster etcd de 3 nós em servidores Ubuntu 18.04. A segunda metade irá focar na proteção do cluster usando o Transport Layer Security, ou TLS. Para executar cada configuração de forma automatizada, usaremos o Ansible durante todo o processo. O Ansible é uma ferramenta de gerenciamento de configuração semelhante ao Puppet, Chef e ao SaltStack. Ele nos permite definir cada passo da configuração de maneira declarativa, dentro de arquivos chamados playbooks.

      No final deste tutorial, você terá um cluster etcd de 3 nós protegido em execução nos seus servidores. Você também terá um playbook do Ansible que lhe permite recriar repetidamente e consistentemente a mesma configuração em um conjunto de servidores novos.

      Pré-requisitos

      Antes de iniciar este guia, será necessário o seguinte:

      Aviso: como o objetivo deste artigo é fornecer uma introdução para configurar de um cluster etcd em uma rede privada, os três servidores Ubuntu 18.04 nesta configuração não foram testados com um firewall e são acessados com o usuário root. Em uma configuração de produção, qualquer nó exposto à internet pública exigiria um firewall e um usuário sudo para que ele se adequasse às práticas recomendadas de segurança. Para mais informações, confira o tutorial Configuração inicial de servidor com o Ubuntu 18.04.

      Passo 1 — Configurando o Ansible para o nó de controle

      O Ansible é uma ferramenta usada para gerenciar servidores. Os servidores que o Ansible está gerenciando são chamados de nós gerenciados, e a máquina que está executando o Ansible é chamada de nó de controle. O Ansible funciona usando as chaves SSH no nó de controle para obter acesso aos nós gerenciados. Depois que uma sessão SSH for estabelecida, o Ansible irá executar um conjunto de scripts para provisionar e configurar os nós gerenciados. Neste passo, vamos testar se somos capazes de usar o Ansible para nos conectar aos nós gerenciados e executar o comando hostname.

      Um dia típico para um administrador de sistemas pode envolver o gerenciamento de conjuntos diferentes de nós. Por exemplo, é possível usar o Ansible para provisionar alguns novos servidores, mas mais tarde usá-lo para reconfigurar outro conjunto de servidores. Para permitir que os administradores organizem melhor o conjunto de nós gerenciados, o Ansible oferece o conceito de inventário de host (ou somente inventário). Você pode definir todos os nós que deseja gerenciar com o Ansible dentro de um arquivo de inventário, e organizá-los em grupos. Dessa forma, ao executar os comandos ansible e ansible-playbook, é possível especificar a quais hosts ou grupos o comando se aplica.

      Por padrão, o Ansible lê o arquivo de inventário a partir de /etc/ansible/hosts. No entanto, podemos especificar um arquivo de inventário diferente usando o sinalizador --inventory (ou somente -i).

      Para iniciar, crie um novo diretório em sua máquina local (o nó de controle) para abrigar todos os arquivos para este tutorial:

      • mkdir -p $HOME/playground/etcd-ansible

      Em seguida, entre no diretório que acabou de criar:

      • cd $HOME/playground/etcd-ansible

      Dentro do diretório, crie e abra um arquivo de inventário em branco chamado hosts usando seu editor:

      • nano $HOME/playground/etcd-ansible/hosts

      Dentro do arquivo hosts, liste todos os seus seus nós gerenciados no seguinte formato, substituindo os endereços IP públicos destacados pelos endereços IP públicos reais dos seus servidores:

      ~/playground/etcd-ansible/hosts

      [etcd]
      etcd1 ansible_host=etcd1_public_ip  ansible_user=root
      etcd2 ansible_host=etcd2_public_ip  ansible_user=root
      etcd3 ansible_host=etcd3_public_ip  ansible_user=root
      

      A linha [etcd] define um grupo chamado etcd. Sob a definição de grupo, listamos todos os nossos nós gerenciados. Cada linha começa com um alias (por exemplo, etcd1), que nos permite fazer referência a cada host usando um nome fácil de lembrar, em vez de um endereço IP longo. O ansible_host e o ansible_user são variáveis do Ansible. Neste caso, são usados para fornecer ao Ansible os endereços IP públicos e os nomes de usuários SSH para usar ao conectar-se via SSH.

      Para garantir que o Ansible seja capaz de se conectar com nossos nós gerenciados, podemos testar a conectividade usando o Ansible para executar o comando hostname em cada um dos hosts dentro do grupo etcd:

      • ansible etcd -i hosts -m command -a hostname

      Vamos dividir esse comando para aprender o que cada parte significa:

      • etcd: especifica o padrão de host a ser usado para determinar quais hosts do inventário estão sendo gerenciados com este comando. Aqui, usamos o nome do grupo como padrão de host.
      • -i hosts: especifica o arquivo de inventário a ser usado.
      • -m command: as funcionalidades por trás do Ansible são fornecidas através de módulos. O módulo command recebe o argumento passado e executa-o como um comando em cada um dos nós gerenciados. Este tutorial irá introduzir alguns outros módulos do Ansible à medida que progredimos.
      • -a hostname: o argumento a ser passado para o módulo. A quantidade e os tipos de argumentos dependem do módulo.

      Depois de executar o comando, você verá o seguinte resultado, o que significa que o Ansible está configurado corretamente:

      Output

      etcd2 | CHANGED | rc=0 >> etcd2 etcd3 | CHANGED | rc=0 >> etcd3 etcd1 | CHANGED | rc=0 >> etcd1

      Cada comando que o Ansible executa é chamado de tarefa. Usar o ansible na linha de comando para executar tarefas é um processo conhecido como executar comandos ad-hoc. A vantagem dos comandos ad-hoc é que eles são rápidos e exigem pouca configuração. O lado negativo é que eles são executados manualmente, e assim não podem ser consignados a um sistema de controle de versão como o Git.

      Uma ligeira melhoria seria escrever o script do shell e executar nossos comandos usando o módulo script do Ansible. Isso permitiria que gravássemos os passos de configuração que fizemos no controle de versão. No entanto, os scripts do shell são imperativos, ou seja, somos responsáveis por arranjar os comandos a serem executados (os “como”s) para configurar o sistema no estado desejado. Por outro lado, o Ansible defende uma abordagem declarativa, onde definimos “qual” o estado desejado em que o nosso servidor deve estar dentro dos arquivos de configuração, e o Ansible é responsável por levar o servidor até esse estado desejado.

      A abordagem declarativa é mais interessante porque a intenção do arquivo de configuração é imediatamente transmitida, sendo assim mais fácil entendê-lo e mantê-lo. Ela também coloca a responsabilidade de casos extremos nas costas do Ansible ao invés do administrador, poupando-nos muito trabalho.

      Agora que o nó de controle do Ansible foi configurado para se comunicar com os nós gerenciados, no próximo passo, vamos apresentar os playbooks do Ansible, que permitem especificar tarefas de forma declarativa.

      Neste passo, vamos replicar o que foi feito no Passo 1 — impressão dos nomes de host dos nós gerenciados— mas em vez de executar tarefas ad-hoc, iremos definir cada tarefa declarativamente como um playbook do Ansible para então executá-lo. O objetivo deste passo é demonstrar como os playbooks do Ansible funcionam. Vamos realizar tarefas muito mais robustas com os playbooks em etapas posteriores.

      Dentro do seu diretório de projeto, crie um novo arquivo chamado playbook.yaml usando seu editor:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Dentro do playbook.yaml, adicione as seguintes linhas:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        tasks:
          - name: "Retrieve hostname"
            command: hostname
            register: output
          - name: "Print hostname"
            debug: var=output.stdout_lines
      

      Feche e salve o arquivo playbook.yaml pressionando CTRL+X e depois Y.

      O playbook contém uma lista de peças; cada peça contém uma lista de tarefas que devem ser executadas em todos os hosts que correspondem ao padrão de host especificado pela chave hosts. Neste playbook, temos uma peça que contém duas tarefas. A primeira tarefa executa o comando hostname usando o módulo command e registra o resultado em uma variável chamada output. Na segunda tarefa, usamos o módulo debug para imprimir a propriedade stdout_lines da variável output.

      Agora, podemos executar este playbook usando o comando ansible-playbook:

      • ansible-playbook -i hosts playbook.yaml

      Você verá o seguinte resultado, o que significa que seu playbook está funcionando corretamente:

      Output

      PLAY [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

      Nota: o ansible-playbook às vezes usa o cowsay como uma maneira brincalhona de imprimir os cabeçalhos. Caso encontre muitas vacas em arte ASCII impressas em seu terminal, agora sabe porquê. Para desativar esse recurso, defina a variável de ambiente ANSIBLE_NOCOWS como 1 antes de executar o ansible-playbook. Faça isso executando export ANSIBLE_NOCOWS=1 no seu shell

      Neste passo, passamos a executar playbooks declarativos ao invés de tarefas ad-hoc imperativas. No próximo passo, vamos substituir essas duas tarefas demonstrativas por tarefas que irão configurar nosso cluster do etcd.

      Passo 3 — Instalando o etcd nos nós gerenciados

      Neste passo, vamos mostrar os comandos para instalar o etcd manualmente e demonstrar como traduzir esses mesmos comandos para tarefas dentro do nosso playbook do Ansible.

      O etcd e seu cliente etcdctl estão disponíveis como binários, os quais vamos baixar, extrair e mover para um diretório que faz parte da variável de ambiente PATH. Quando configurados manualmente, esses são os passos que vamos seguir em cada um dos nós gerenciados:

      • mkdir -p /opt/etcd/bin
      • cd /opt/etcd/bin
      • wget -qO- https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz | tar --extract --gzip --strip-components=1
      • echo 'export PATH="$PATH:/opt/etcd/bin"' >> ~/.profile
      • echo 'export ETCDCTL_API=3" >> ~/.profile

      Os primeiros quatro comandos baixam e extraem os binários para o diretório /opt/etcd/bin/. Por padrão, o cliente etcdctl usará a API v2 para se comunicar com o servidor do etcd. Como estamos executando etcd v3.x, o último comando define a variável de ambiente ETCDCTL_API como 3.

      Nota: aqui, estamos usando o etcd v3.3.13 desenvolvido para uma máquina com processadores que usam o conjunto de instruções AMD64. Você pode encontrar binários para outros sistemas e outras versões na página oficial de Lançamentos no GitHub.

      Para replicar os mesmos passos em um formato padronizado, podemos adicionar tarefas ao nosso playbook. Abra o arquivo de playbook playbook.yaml no seu editor:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Substitua tudo no arquivo playbook.yaml pelo seguinte conteúdo:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
            file:
              path: /opt/etcd/bin
              state: directory
              owner: root
              group: root
              mode: 0700
          - name: "Download the tarball into the /tmp directory"
            get_url:
              url: https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz
              dest: /tmp/etcd.tar.gz
              owner: root
              group: root
              mode: 0600
              force: True
          - name: "Extract the contents of the tarball"
            unarchive:
              src: /tmp/etcd.tar.gz
              dest: /opt/etcd/bin/
              owner: root
              group: root
              mode: 0600
              extra_opts:
                - --strip-components=1
              decrypt: True
              remote_src: True
          - name: "Set permissions for etcd"
            file:
              path: /opt/etcd/bin/etcd
              state: file
              owner: root
              group: root
              mode: 0700
          - name: "Set permissions for etcdctl"
            file:
              path: /opt/etcd/bin/etcdctl
              state: file
              owner: root
              group: root
              mode: 0700
          - name: "Add /opt/etcd/bin/ to the $PATH environment variable"
            lineinfile:
              path: /etc/profile
              line: export PATH="$PATH:/opt/etcd/bin"
              state: present
              create: True
              insertafter: EOF
          - name: "Set the ETCDCTL_API environment variable to 3"
            lineinfile:
              path: /etc/profile
              line: export ETCDCTL_API=3
              state: present
              create: True
              insertafter: EOF
      

      Cada tarefa usa um módulo e para este conjunto de tarefas, estamos usando os seguintes módulos:

      • file: usado para criar o diretório /opt/etcd/bin, e para definir mais tarde as permissões de arquivo para os binários etcd e etcdctl.
      • get_url: usado para baixar o tarball gzipped nos nós gerenciados.
      • unarchive: usado para extrair e descompactar os binários etcd e etcdctl do tarball gzipped.
      • lineinfile: usado para adicionar uma entrada no arquivo .profile.

      Para aplicar essas alterações, feche e salve o arquivo playbook.yaml pressionando CTRL+X e depois Y. Então, no terminal, execute o mesmo comando ansible-playbook novamente:

      • ansible-playbook -i hosts playbook.yaml

      A seção PLAY RECAP da saída mostrará apenas ok e changed (alterado):

      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

      Para confirmar se houve uma instalação correta do etcd, entre manualmente via SSH em um dos nós gerenciados e execute o etcd e o etcdctl:

      etcd1_public_ip é o endereço IP público do servidor chamado etcd1. Depois que tiver obtido acesso ao SSH, execute etcd --version para imprimir a versão instalada do etcd:

      Você verá um resultado semelhante ao que será mostrado a seguir, que significa que o binário etcd foi instalado com sucesso:

      Output

      etcd Version: 3.3.13 Git SHA: 98d3084 Go Version: go1.10.8 Go OS/Arch: linux/amd64

      Para confirmar se o etcdctl foi instalado com sucesso, execute version etcdctl:

      Você irá obter um resultado similar ao seguinte:

      Output

      etcdctl version: 3.3.13 API version: 3.3

      Observe que o resultado diz API version: 3.3, o que também confirma que nossa variável de ambiente ETCDCTL_API foi definida corretamente.

      Saia do servidor etcd1 para voltar ao seu ambiente local.

      Agora, instalamos com sucesso o etcd e o etcdctl em todos os nossos nós gerenciados. No próximo passo, vamos adicionar mais tarefas à nossa peça para executar o etcd como um serviço em segundo plano.

      Passo 4 — Criando um arquivo de unidade para o etcd

      Aparentemente, a maneira mais rápida de executar o etcd com o Ansible é usando o módulo command para executar /opt/etcd/bin/etcd. No entanto, isso não funcionará porque fará com que o etcd seja executado como um processo em primeiro plano. Usar o módulo command fará com que o Ansible pare enquanto espera que o comando etcd gere um retorno, o que nunca acontecerá. Sendo assim, neste passo, vamos atualizar nosso playbook para que execute nosso binário etcd como um serviço em segundo plano.

      O Ubuntu 18.04 usa o systemd como seu sistema init, o que significa que podemos criar novos serviços criando arquivos de unidade e colocando-os dentro do diretório /etc/systemd/system/.

      Primeiro, dentro do nosso diretório de projeto, crie um novo diretório chamado files/:

      Em seguida, usando seu editor, crie um novo arquivo chamado etcd.service dentro desse diretório:

      Depois disso, copie o seguinte bloco de código no arquivo files/etcd.service:

      ~/playground/etcd-ansible/files/etcd.service

      [Unit]
      Description=etcd distributed reliable key-value store
      
      [Service]
      Type=notify
      ExecStart=/opt/etcd/bin/etcd
      Restart=always
      

      Este arquivo de unidade define um serviço que executa o arquivo executável /opt/etcd/bin/etcd, notifica o systemd assim que tiver terminado a inicialização e sempre reinicializa-se caso seja fechado.

      Nota: se quiser entender mais sobre o systemd e os arquivos de unidade, ou quiser adaptar o arquivo de unidade às suas necessidades, leia o guia Entendendo as unidades e arquivos de unidade do Systemd.

      Feche e salve o arquivo files/etcd.service pressionando CTRL+X e depois Y.

      Em seguida, é necessário adicionar uma tarefa dentro do nosso playbook que irá criar uma cópia do arquivo local files/etcd.service no diretório /etc/systemd/system/etcd.service para cada nó gerenciado. Podemos fazer isso usando o módulo copy.

      Abra seu playbook:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Adicione a seguinte tarefa destacada no final das tarefas já existentes:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Set the ETCDCTL_API environment variable to 3"
            lineinfile:
              path: /etc/profile
              line: export ETCDCTL_API=3
              state: present
              create: True
              insertafter: EOF
          - name: "Create a etcd service"
            copy:
              src: files/etcd.service
              remote_src: False
              dest: /etc/systemd/system/etcd.service
              owner: root
              group: root
              mode: 0644
      

      Ao copiar o arquivo de unidade em /etc/systemd/system/etcd.service, um serviço agora está definido.

      Salve e saia do playbook.

      Execute novamente o mesmo comando ansible-playbook para aplicar as novas alterações:

      • ansible-playbook -i hosts playbook.yaml

      Para confirmar se as alterações foram aplicadas, primeiro entre via SSH em um dos nós gerenciados:

      Em seguida, execute systemctl status etcd para consultar o systemd sobre o status do serviço etcd:

      Você receberá o seguinte resultado, que afirma que o serviço está carregado:

      Output

      ● etcd.service - etcd distributed reliable key-value store Loaded: loaded (/etc/systemd/system/etcd.service; static; vendor preset: enabled) Active: inactive (dead) ...

      Nota: a última linha (Active: inactive (dead)) do resultado afirma que o serviço está inativo, o que significa que ele não seria executado automaticamente quando o sistema iniciasse. Isso é esperado e não representa um erro.

      Pressione q para voltar ao shell e então execute exit para sair do nó gerenciado e voltar para seu shell local:

      Neste passo, atualizamos nosso playbook para executar o binário etcd como um serviço do systemd. No próximo passo, vamos continuar a configuração do etcd fornecendo espaço para armazenar seus dados.

      Passo 5 — Configurando o diretório de dados

      O etcd é um armazenamento de dados de chaves-valores, o que significa que devemos fornecer-lhe espaço para armazenar seus dados. Neste passo, vamos atualizar nosso playbook para definir um diretório de dados dedicado para o etcd usar.

      Abra seu playbook:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Acrescente a seguinte tarefa no final da lista de tarefas:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Create a etcd service"
            copy:
              src: files/etcd.service
              remote_src: False
              dest: /etc/systemd/system/etcd.service
              owner: root
              group: root
              mode: 0644
          - name: "Create a data directory"
            file:
              path: /var/lib/etcd/{{ inventory_hostname }}.etcd
              state: directory
              owner: root
              group: root
              mode: 0755
      

      Aqui, estamos usando o /var/lib/etcd/hostname.etcd como o diretório de dados, onde hostname é o nome de host do nó gerenciado em questão e inventory_hostname é uma variável que representa o nome de host do nó gerenciado em questão; seu valor é preenchido pelo Ansible automaticamente. A sintaxe com chaves (ou seja, {{ inventory_hostname }}) é usada para a substituição de variável, suportada pelo mecanismo de modelo padrão para o Ansible, Jinja2.

      Feche o editor de texto e salve o arquivo.

      Em seguida, é necessário instruir o etcd a usar este diretório de dados. Fazemos isso passando o parâmetro data-dir para o etcd. Para definir parâmetros do etcd, podemos usar uma combinação de variáveis de ambiente, sinalizadores de linha de comando e arquivos de configuração. Para este tutorial, usaremos um único arquivo de configuração, pois é muito mais simples isolar todas as configurações em apenas um arquivo, do que ter partes da configuração espalhadas em todo o nosso playbook.

      Em seu diretório de projeto, crie um novo diretório chamado templates/:

      Em seguida, usando seu editor, crie um novo arquivo chamado etcd.conf.yaml.j2 dentro do diretório:

      • nano templates/etcd.conf.yaml.j2

      Depois disso, copie a linha a seguir e cole no arquivo:

      ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
      

      Esse arquivo usa a mesma sintaxe de substituição de variável do Jinja2 que nosso playbook. Para substituir as variáveis e fazer upload do resultado para cada host gerenciado, podemos usar módulo template. Ele funciona de maneira semelhante ao copy, exceto que ele irá realizar a substituição de variáveis antes do upload.

      Saia do etcd.conf.yaml.j2 e abra seu playbook:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Acrescente as seguintes tarefas na lista de tarefas para criar um diretório e fazer upload do arquivo de configuração modelado nele:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Create a data directory"
            file:
              ...
              mode: 0755
          - name: "Create directory for etcd configuration"
            file:
              path: /etc/etcd
              state: directory
              owner: root
              group: root
              mode: 0755
          - name: "Create configuration file for etcd"
            template:
              src: templates/etcd.conf.yaml.j2
              dest: /etc/etcd/etcd.conf.yaml
              owner: root
              group: root
              mode: 0600
      

      Salve e feche esse arquivo.

      Como fizemos essa alteração, precisamos atualizar o arquivo de unidade do nosso serviço para passar-lhe a localização do nosso arquivo de configuração (ou seja, /etc/etcd/etcd.conf.yaml).

      Abra o arquivo do serviço etcd em sua máquina local:

      Atualize o arquivo files/etcd.service adicionando o sinalizador --config-file destacado a seguir:

      ~/playground/etcd-ansible/files/etcd.service

      [Unit]
      Description=etcd distributed reliable key-value store
      
      [Service]
      Type=notify
      ExecStart=/opt/etcd/bin/etcd --config-file /etc/etcd/etcd.conf.yaml
      Restart=always
      

      Salve e feche esse arquivo.

      Neste passo, usamos nosso playbook para fornecer um diretório de dados para o etcd armazenar seus dados. No próximo passo, iremos adicionar mais algumas tarefas para reiniciar o serviço etcd e fazê-lo ser executado na inicialização.

      Passo 6 — Habilitando e iniciando o serviço etcd

      Sempre que fazemos alterações no arquivo de unidade de um serviço, precisamos reiniciar o serviço para elas entrem em vigor. Podemos fazer isso executando o comando systemctl restart etcd. Além disso, para fazer com que o serviço etcd seja iniciado automaticamente na inicialização do sistema, precisamos executar systemctl enable etcd. Neste passo, vamos executar esses dois comandos usando o playbook.

      Para executar comandos, podemos usar o módulo command:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Acrescente as seguintes tarefas no final da lista de tarefas:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Create configuration file for etcd"
            template:
              ...
              mode: 0600
          - name: "Enable the etcd service"
            command: systemctl enable etcd
          - name: "Start the etcd service"
            command: systemctl restart etcd
      

      Salve e feche o arquivo.

      Execute ansible-playbook -i hosts playbook.yaml mais uma vez:

      • ansible-playbook -i hosts playbook.yaml

      Para verificar se o serviço etcd foi reiniciado e está habilitado, entre via SSH em um dos nós gerenciados:

      Em seguida, execute systemctl status etcd para verificar o status do serviço etcd:

      Você verá enabled e active (running) como destacado a seguir; isso significa que as alterações que fizemos em nosso playbook tiveram efeito:

      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)

      Neste passo, usamos o módulo command para executar comandos systemctl que reiniciam e habilitam o serviço etcd em nossos nós gerenciados. Agora que configuramos uma instalação etcd, vamos testar, no próximo passo, sua funcionalidade realizando algumas operações básicas de criação, leitura, atualização e exclusão (CRUD).

      Passo 7 — Testando o etcd

      Embora tenhamos uma instalação funcional do etcd, ela é insegura e ainda não está pronta para o uso na produção. Mas antes de adicionarmos segurança à nossa configuração do etcd em etapas posteriores, vamos primeiro entender o que o etcd pode fazer em termos de funcionalidade. Neste passo, vamos enviar manualmente solicitações ao etcd para adicionar, coletar, atualizar e excluir dados dele.

      Por padrão, o etcd expõe uma API que escuta na porta 2379 para a comunicação com o cliente. Isso significa que podemos enviar solicitações de API brutas para o etcd usando um cliente HTTP. No entanto, é mais rápido usar o cliente oficial do etcd, o etcdctl, que permite criar/atualizar, recuperar e excluir pares de chave-valor usando os subcomandos put, get e del, respectivamente.

      Certifique-se de ainda estar dentro do nó gerenciado etcd1 e execute os seguintes comandos do etcdctl para confirmar que sua instalação do etcd está funcionando.

      Primeiro, crie uma nova entrada usando o subcomando put.

      O subcomando put possui a seguinte sintaxe:

      etcdctl put key value
      

      No etcd1, execute o seguinte comando:

      O comando que acabamos de executar instrui o etcd a escrever o valor "bar" na chave foo no armazenamento.

      Em seguida, você verá um OK impresso no resultado, que indica que os dados persistiram:

      Output

      OK

      Em seguida, podemos recuperar essa entrada usando o subcomando get, que possui a sintaxe etcdctl get key:

      Você verá este resultado, que mostra a chave na primeira linha e o valor que você inseriu mais cedo na segunda linha:

      Output

      foo bar

      Podemos excluir a entrada usando o subcomando del, que possui a sintaxe etcdctl del key:

      Você verá o seguinte resultado, que indica o número de entradas excluídas:

      Output

      1

      Agora, vamos executar o subcomando get mais uma vez para tentar recuperar um par chave-valor excluído:

      Você não receberá um resultado, o que significa que o etcdctl não é capaz de recuperar o par chave-valor. Isso confirma que depois que a entrada é excluída, ela não pode mais ser recuperada.

      Agora que você testou as operações básicas do etcd e do etcdctl, vamos sair do nosso nó gerenciado e voltar para o ambiente local:

      Neste passo, usamos o cliente etcdctl para enviar solicitações ao etcd. Neste ponto, estamos executando três instâncias separadas do etcd sendo que cada uma age independentemente uma da outra. No entanto, o etcd é projetado como um armazenamento de chaves-valores distribuído, o que significa que várias instâncias do etcd podem se agrupar para formar um único cluster e cada instância torna-se então um membro do cluster. Depois de formar um cluster, seria possível recuperar um par chave-valor que foi inserido a partir de um membro diferente do cluster. No próximo passo, usaremos nosso playbook para transformar nossos 3 clusters de nó único em um único cluster de três nós.

      Passo 8 — Formando um cluster usando a descoberta estática

      Para criar um cluster de três nós ao invés de três clusters de somente 1 nó, devemos configurar essas instalações do etcd para se comunicar entre si. Isso significa que cada uma deve conhecer os endereços IP umas das outras. Este processo é chamado de descoberta. A descoberta pode ser feita usando a configuração estática ou a descoberta de serviço dinâmica. Neste passo, vamos discutir a diferença entre as duas, bem como atualizar nosso playbook para configurar um cluster do etcd usando a descoberta estática.

      A descoberta por configuração estática é o método cuja configuração é mais curta. É aqui que os pontos de extremidade de cada membro são passados no comando etcd antes que ele seja executado. Para usar a configuração estática, as seguintes condições devem ser atendidas antes da inicialização do cluster:

      • o número de membros é conhecido
      • os pontos de extremidade de cada membro são conhecidos
      • os endereços IP para todos os pontos de extremidade são estáticos

      Se essas condições não puderem ser atendidas, então será preciso usar um serviço de descoberta dinâmico. Com a descoberta de serviço dinâmica, todas as instâncias seriam registradas com o serviço de descoberta. Isso permite que cada membro obtenha informações sobre a localização de outros membros.

      Como sabemos que queremos um cluster do etcd de 3 nós e todos os nossos servidores têm endereços IP estáticos, usaremos a descoberta estática. Para iniciar nosso cluster usando a descoberta estática, devemos adicionar diversos parâmetros ao nosso arquivo de configuração. Use um editor para abrir o arquivo modelo templates/etcd.conf.yaml.j2:

      • nano templates/etcd.conf.yaml.j2

      Então, adicione as linhas destacadas a seguir:

      ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
      name: {{ inventory_hostname }}
      initial-advertise-peer-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380
      listen-peer-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380,http://127.0.0.1:2380
      advertise-client-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379
      listen-client-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379,http://127.0.0.1:2379
      initial-cluster-state: new
      initial-cluster: {% for host in groups['etcd'] %}{{ hostvars[host]['ansible_facts']['hostname'] }}=http://{{ hostvars[host]['ansible_facts']['eth1']['ipv4']['address'] }}:2380{% if not loop.last %},{% endif %}{% endfor %}
      

      Feche e salve o arquivo templates/etcd.conf.yaml.j2 pressionando CTRL+X e depois Y.

      Aqui está uma breve explicação de cada parâmetro:

      • name – um nome humanamente legível para o membro. Por padrão, o etcd usa uma ID único gerado aleatoriamente para identificar cada membro. No entanto, um nome humanamente legível nos permite fazer referência a ele com maior facilidade dentro dos arquivos de configuração e na linha de comando. Aqui, usaremos os nomes de host como os nomes de membro (ou seja, etcd1, etcd2 e etcd3).
      • initial-advertise-peer-urls – uma lista de combinações de endereço IP/porta que outros membros podem usar para se comunicar com este membro. Além da porta da API (2379), o etcd também expõe a porta 2380 para a comunicação ponto a ponto entre membros do etcd. Isso permite que eles enviem mensagens entre si e troquem dados. Observe que essas URLs devem ser acessíveis pelos seus pares (e não ser um endereço IP local).
      • listen-peer-urls – uma lista de combinações de endereço IP/porta onde o membro atual irá escutar a comunicação de outros membros. Isso deve incluir todas as URLs do sinalizador --initial-advertise-peer-urls, mas também as URLs locais como 127.0.0.1:2380. O endereço IP/ porta de destino das mensagens de pares recebidas deve corresponder a uma das URLs listadas aqui.
      • advertise-client-urls – uma lista de combinações de endereço IP/porta que os clientes devem usar para se comunicar com este membro. Essas URLs devem ser acessíveis ao cliente (e não ser um endereço local). Se o cliente estiver acessando o cluster através da internet pública, este deve ser um endereço IP público.
      • listen-peer-urls – uma lista de combinações de endereço IP/porta onde o membro atual irá escutar a comunicação de outros clientes. Isso deve incluir todas as URLs do sinalizador --advertise-client-urls, mas também as URLs locais como 127.0.0.1:2379. O endereço IP/ porta de destino das mensagens de clientes recebidas deve corresponder a uma das URLs listadas aqui.
      • initial-cluster – uma lista de pontos de extremidade para cada membro do cluster. Cada ponto de extremidade deve corresponder a uma das URLs do initial-advertise-peer-urls do membro correspondente.
      • initial-cluster-state – ou new (novo) ou existing (existente).

      Para garantir a consistência, o etcd só pode tomar decisões quando a maioria dos nós estiver em boas condições. Isso é conhecido como estabelecer o quorum. Em outras palavras, em um cluster de três membros, o quorum é alcançado se dois ou mais membros estiverem em boas condições.

      Se o parâmetro initial-cluster-state estiver definido como new, o etcd irá saber que este é um novo cluster sendo inicializado e permitirá que os membros sejam inicializados em paralelo, sem esperar que o quorum seja alcançado. De forma mais concreta, depois que o primeiro membro é iniciado, o quorum não será atingido porque um terço (33,33%) é menor ou igual a 50%. Normalmente, o etcd irá parar e recusar-se a realizar mais ações e o cluster nunca será formado. No entanto, com o initial-cluster-state definido para new, ele irá ignorar a falta inicial de quorum.

      Se for definido como existente, o membro irá tentar se juntar a um cluster existente e espera-se que o quorum já esteja estabelecido.

      Nota: é possível encontrar mais detalhes sobre todos os sinalizadoras de configuração suportados na seção Configuração da documentação do etcd.

      No arquivo modelo templates/etcd.conf.yaml.j2 atualizado, existem algumas instâncias de hostvars. Quando o Ansible for executado, ele irá coletar variáveis vindas de diversas de fontes. Já usamos a variável inventory_hostname anteriormente, mas há muitas outras disponíveis. Essas variáveis estão disponíveis em hostvars[inventory_hostname]['ansible_facts']. Aqui, estamos extraindo os endereços IP privados de cada nó e usamos isso para construir nosso valor de parâmetro.

      Nota: como ativamos a opção de Rede privada quando criamos nossos servidores, cada servidor têm três endereços IP associados a eles:

      • Um endereço IP de loopback – um endereço que é válido apenas dentro da mesma máquina. É usado para que a máquina faça referência a si mesma, por exemplo, 127.0.0.1
      • Um endereço endereço IP público – um endereço que é compartilhado através da internet pública, por exemplo, 178.128.169.51
      • Um endereço IP privado – um endereço que é compartilhável apenas dentro da rede privada; no caso das Droplets da DigitalOcean, existe uma rede privada dentro de cada datacenter, por exemplo, 10.131.82.225

      Cada um desses endereços endereço IP está associado a uma interface de rede diferente. O endereço loopback está associado à interface lo, o endereço IP público à interface eth0, e o endereço IP privado à interface eth1. Estamos usando a interface eth1 para que todo o tráfego fique dentro da rede privada, sem nunca chegar à internet.

      Não é necessário entender as interfaces de rede para este artigo, mas se você quiser aprender mais, Uma introdução à terminologia, interfaces e protocolos de rede é um ótimo lugar para começar.

      A sintaxe {% %} do Jinja2 define a estrutura de loop for que itera através de todos os nós no grupo do etcd para compilar a string initial-cluster em um formato exigido pelo etcd.

      Para formar o novo cluster de três membros, é necessário primeiro interromper o serviço etcd e limpar o diretório de dados antes de inicializar o cluster. Para fazer isso, use um editor para abrir o arquivo playbook.yaml em sua máquina local:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Em seguida, antes da tarefa "Create a data directory", adicione uma tarefa para interromper o serviço etcd:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
              group: root
              mode: 0644
          - name: "Stop the etcd service"
            command: systemctl stop etcd
          - name: "Create a data directory"
            file:
          ...
      

      Em seguida, atualize a tarefa "Create a data directory" para primeiro excluir o diretório de dados e depois recriá-lo:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Stop the etcd service"
            command: systemctl stop etcd
          - name: "Create a data directory"
            file:
              path: /var/lib/etcd/{{ inventory_hostname }}.etcd
              state: "{{ item }}"
              owner: root
              group: root
              mode: 0755
            with_items:
              - absent
              - directory
          - name: "Create directory for etcd configuration"
            file:
          ...
      

      A propriedade with_items define uma lista de strings através das quais esta tarefa irá iterar. Isso é o equivalente a repetir a mesma tarefa duas vezes, mas com valores diferentes para a propriedade state. Aqui, estamos iterando através da lista com os itens absent e directory, garantindo que o diretório de dados seja excluído primeiro e recriado em seguida.

      Feche e salve o arquivo playbook.yaml pressionando CTRL+X e depois Y. Em seguida, execute o ansible-playbook novamente. Agora, o Ansible irá criar um cluster do etcd único com 3 membros:

      • ansible-playbook -i hosts playbook.yaml

      Você pode verificar isso entrando via SSH em qualquer nó que seja membro do etcd:

      Em seguida, execute etcdctl endpoint health --cluster:

      • etcdctl endpoint health --cluster

      Isso irá listar a integridade de cada membro do cluster:

      Output

      http://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

      Agora, criamos com sucesso um cluster do etcd de 3 nós. Podemos verificar isso adicionando uma entrada ao etcd em um dos nós membros, e recuperando-a em outro nó membro. Em um dos nós membros, execute o etcdctl put:

      Em seguida, use um novo terminal para entrar via SSH em um nó membro diferente:

      Depois disso, tente recuperar a mesma entrada usando a chave:

      Você será capaz de recuperar essa entrada, o que prova que o cluster está funcionando:

      Output

      foo bar

      Por fim, saia de cada um dos nós gerenciados e volte para a sua máquina local:

      Neste passo, criamos um novo cluster de 3 nós. Neste momento, a comunicação entre os membros do etcd e seus pares e clientes é realizada através do HTTP. Isso significa que a comunicação é descriptografada e qualquer pessoa capaz de interceptar o tráfego pode ler as mensagens. Isso não representa um problema grande se o cluster e os clientes do etcd estiverem todos implantados dentro de uma rede privada, ou uma rede privada virtual (VPN) que você tem pleno controle. No entanto, se qualquer um dos sistemas de tráfego precisar viajar através de uma rede compartilhada (privada ou pública), então deve-se garantir que este tráfego seja criptografado. Além disso, um mecanismo precisa ser implantado para que um cliente ou um par verifique a autenticidade do servidor.

      No próximo passo, vamos analisar como proteger a comunicação cliente para servidor, bem como a comunicação por pares usando o TLS.

      Passo 9 — Obtendo os endereços IP privados dos nós gerenciados

      Para criptografar mensagens entre nós membros, o etcd usa o Hypertext Transfer Protocol Secure, ou HTTPS, que é uma camada além do protocolo Transport Layer Security, TLS. O TLS usa um sistema de chaves privadas, certificados e entidades confiáveis chamadas Autoridades de certificação (CAs) para autenticar-se e enviar mensagens criptografadas.

      Neste tutorial, cada nó membro precisa gerar um certificado para se identificar e ter este certificado assinado por uma CA. Vamos configurar todos os nós membros para confiar nesta CA e, dessa forma, também confiar em todos os certificados assinados por ela. Isso permite que os nós membros se autentiquem mutuamente entre si.

      O certificado gerado por um nó membro deve permitir que outros nós membros se identifiquem. Todos os certificados incluem um Nome comum (CN) da entidade com a qual estão associados. Isso é usado com frequência como a identidade da entidade. No entanto, ao verificar um certificado, pode ser que as implementações do cliente comparem se as informações coletadas sobre a entidade correspondem às fornecidas no certificado. Por exemplo, quando um cliente baixa o certificado TLS com a entidade CN=foo.bar.com, mas o cliente está na verdade se conectando ao servidor usando endereço IP (por exemplo, 167.71.129.110), então existe uma incompatibilidade e o cliente pode não confiar no certificado. Ao especificar um nome alternativo de entidade (SAN) no certificado, ele informa ao verificador que ambos os nomes pertencem à mesma entidade.

      Como nossos membros do etcd estão emparelhados uns com os outros usando seus endereços IP privados, quando definirmos nossos certificados, vamos precisar fornecer esses endereços IP privados como os nomes alternativos de entidade.

      Para descobrir o endereço IP privado de um nó gerenciado, entre via SSH nele:

      Em seguida, execute o seguinte comando:

      • ip -f inet addr show eth1

      Você verá um resultado parecido com as seguintes linhas:

      Output

      3: 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

      Em nosso exemplo de resultado, 10.131.255.176 é o endereço IP privado do nó gerenciado, e é a única informação na qual estamos interessados. Para filtrar tudo exceto o IP privado, podemos canalizar a saída do comando ip para o utilitário sed, que é usado para filtrar e transformar textos.

      • ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/1/p'

      Agora, a única saída é o próprio endereço IP privado:

      Output

      10.131.255.176

      Após verificar que o comando anterior funciona, saia do nó gerenciado:

      Para incorporar os comandos anteriores em nosso playbook, abra primeiro o arquivo playbook.yaml:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Em seguida, adicione uma nova peça com uma única tarefa antes da nossa peça existente:

      ~/playground/etcd-ansible/playbook.yaml

      ...
      - hosts: etcd
        tasks:
          - shell: ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/1/p'
            register: privateIP
      - hosts: etcd
        tasks:
      ...
      

      A tarefa usa o módulo o shell para executar os comandos ip e sed, os quais buscam o endereço IP privado do nó gerenciado. Em seguida, ela registra o valor de retorno do comando shell dentro de uma variável chamada privateIP, que usaremos mais tarde.

      Neste passo, adicionamos uma tarefa ao playbook para obter o endereço IP privado dos nós gerenciados. No próximo passo, vamos usar essas informações para gerar certificados para cada nó membro. Além disso, faremos eles serem assinados por uma Autoridade de Certificação (CA).

      Passo 10 — Gerando as chaves privadas e CSRs dos membros do etcd

      Para que um nó membro receba tráfego criptografado, o remetente deve usar a chave pública do nó membro criptografar os dados. Além disso, o nó membro deve usar sua chave privada para descriptografar o texto cifrado e recuperar os dados originais. A chave pública é empacotada em um certificado e assinada por uma CA para garantir que seja genuína.

      Portanto, vamos precisar gerar uma chave privada e uma solicitação de assinatura de certificado (CSR) para cada nó membro do etcd. Para facilitar o nosso trabalho, vamos gerar todos os pares de chaves e assinar todos os certificados localmente no nó de controle e então copiar os arquivos relevantes para os hosts gerenciados.

      Primeiro, crie um diretório chamado artifacts/, onde vamos colocar os arquivos (chaves e certificados) gerados durante o processo. Abra o arquivo playbook.yaml com um editor:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Nele, use o módulo file para criar o diretório artifacts/:

      ~/playground/etcd-ansible/playbook.yaml

      ...
          - shell: ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/1/p'
            register: privateIP
      - hosts: localhost
        gather_facts: False
        become: False
        tasks:
          - name: "Create ./artifacts directory to house keys and certificates"
            file:
              path: ./artifacts
              state: directory
      - hosts: etcd
        tasks:
      ...
      

      Em seguida, adicione uma outra tarefa no final da peça para gerar a chave privada:

      ~/playground/etcd-ansible/playbook.yaml

      ...
      - hosts: localhost
        gather_facts: False
        become: False
        tasks:
              ...
          - name: "Generate private key for each member"
            openssl_privatekey:
              path: ./artifacts/{{item}}.key
              type: RSA
              size: 4096
              state: present
              force: True
            with_items: "{{ groups['etcd'] }}"
      - hosts: etcd
        tasks:
      ...
      

      Criar chaves privadas e CSRs pode ser feito usando os módulos openssl_privatekey e openssl_csr respectivamente.

      O atributo force: True garante que a chave privada seja regenerada toda vez, mesmo que já exista.

      De maneira similar, adicione a nova tarefa a seguir na mesma peça gerar as CSRs para cada membro, usando o módulo openssl_csr:

      ~/playground/etcd-ansible/playbook.yaml

      ...
      - hosts: localhost
        gather_facts: False
        become: False
        tasks:
          ...
          - name: "Generate private key for each member"
            openssl_privatekey:
              ...
            with_items: "{{ groups['etcd'] }}"
          - name: "Generate CSR for each member"
            openssl_csr:
              path: ./artifacts/{{item}}.csr
              privatekey_path: ./artifacts/{{item}}.key
              common_name: "{{item}}"
              key_usage:
                - digitalSignature
              extended_key_usage:
                - serverAuth
              subject_alt_name:
                - IP:{{ hostvars[item]['privateIP']['stdout']}}
                - IP:127.0.0.1
              force: True
            with_items: "{{ groups['etcd'] }}"
      

      Estamos especificando que este certificado pode estar envolvido em um mecanismo de assinatura digital para fins da autenticação de servidor. Este certificado é associado ao nome do host (por exemplo, etcd1), mas o verificador também deve tratar os endereços IP privados, locais e de loopback de cada nó como nomes alternativos. Observe que estamos usando a variável privateIP que registramos na peça anterior.

      Feche e salve o arquivo playbook.yaml pressionando CTRL+X e depois Y. Em seguida, execute o playbook novamente:

      • ansible-playbook -i hosts playbook.yaml

      Agora, vamos encontrar um novo diretório chamado artifacts dentro do nosso diretório de projeto. Use ls para listar seu conteúdo:

      Você verá as chaves privadas e as CSRs para cada um dos membros do etcd:

      Output

      etcd1.csr etcd1.key etcd2.csr etcd2.key etcd3.csr etcd3.key

      Neste passo, usamos vários módulos do Ansible para gerar chaves privadas e certificados de chave pública para cada um dos nós membros. No próximo passo, vamos analisar como assinar uma solicitação de assinatura de certificado (CSR).

      Passo 11 — Gerando os certificados CA

      Dentro de um cluster do etcd, os nós membros criptografam mensagens usando a chave pública do receptor. Para garantir que a chave pública seja genuína, o receptor empacota a chave pública em uma solicitação de assinatura de certificado (CSR) e tem uma entidade confiável (ou seja, a CA) para assinar a CSR. Como controlamos todos os nós membros e as CA em que eles confiam, não precisamos usar uma CA externa e podemos atuar como nossa própria CA. Neste passo, atuaremos como nossa própria CA. Isso significa que vamos precisar gerar uma chave privada e um certificado auto-assinado que funcionarão como a CA.

      Primeiro, abra o arquivo playbook.yaml com seu editor:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Em seguida, de maneira similar ao passo anterior, adicione uma tarefa à peça localhost para gerar uma chave privada para a CA:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
        - name: "Generate CSR for each member"
          ...
          with_items: "{{ groups['etcd'] }}"
          - name: "Generate private key for CA"
            openssl_privatekey:
              path: ./artifacts/ca.key
              type: RSA
              size: 4096
              state: present
              force: True
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      Em seguida, use o módulo openssl_csr para gerar uma nova CSR. O processo é semelhante ao passo anterior, mas neste CSR, estamos adicionando a restrição básica e a extensão de uso de chave para indicar que este certificado pode ser usado como um certificado CA:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
          - name: "Generate private key for CA"
            openssl_privatekey:
              path: ./artifacts/ca.key
              type: RSA
              size: 4096
              state: present
              force: True
          - name: "Generate CSR for CA"
            openssl_csr:
              path: ./artifacts/ca.csr
              privatekey_path: ./artifacts/ca.key
              common_name: ca
              organization_name: "Etcd CA"
              basic_constraints:
                - CA:TRUE
                - pathlen:1
              basic_constraints_critical: True
              key_usage:
                - keyCertSign
                - digitalSignature
              force: True
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      Por fim, use o módulo openssl_certificate para auto-assinar a CSR:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
          - name: "Generate CSR for CA"
            openssl_csr:
              path: ./artifacts/ca.csr
              privatekey_path: ./artifacts/ca.key
              common_name: ca
              organization_name: "Etcd CA"
              basic_constraints:
                - CA:TRUE
                - pathlen:1
              basic_constraints_critical: True
              key_usage:
                - keyCertSign
                - digitalSignature
              force: True
          - name: "Generate self-signed CA certificate"
            openssl_certificate:
              path: ./artifacts/ca.crt
              privatekey_path: ./artifacts/ca.key
              csr_path: ./artifacts/ca.csr
              provider: selfsigned
              force: True
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      Feche e salve o arquivo playbook.yaml pressionando CTRL+X e depois Y. Em seguida, execute o playbook para aplicar as alterações:

      • ansible-playbook -i hosts playbook.yaml

      Você também pode executar o ls para verificar o conteúdo do diretório artifacts/:

      Agora, você verá o certificado CA recém-gerado (ca.crt):

      Output

      ca.crt ca.csr ca.key etcd1.csr etcd1.key etcd2.csr etcd2.key etcd3.csr etcd3.key

      Neste passo, geramos uma chave privada e um certificado auto-assinado para a CA. No próximo passo, usaremos o certificado CA para assinar a CSR de cada membro.

      Passo 12 — Assinando as CSRs dos membros do etcd

      Neste passo, vamos assinar a CSR de cada nó membro. Isso será feito de maneira semelhante a como usamos o módulo openssl_certificate para auto-assinar o certificado CA, mas em vez de usar o provedor selfsigned, usaremos o provedor ownca. Isso nos permite assinar usando nosso próprio certificado CA.

      Abra seu playbook:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Acrescente a tarefa destacada a seguir na tarefa "Generate self-signed CA certificate":

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
          - name: "Generate self-signed CA certificate"
            openssl_certificate:
              path: ./artifacts/ca.crt
              privatekey_path: ./artifacts/ca.key
              csr_path: ./artifacts/ca.csr
              provider: selfsigned
              force: True
          - name: "Generate an `etcd` member certificate signed with our own CA certificate"
            openssl_certificate:
              path: ./artifacts/{{item}}.crt
              csr_path: ./artifacts/{{item}}.csr
              ownca_path: ./artifacts/ca.crt
              ownca_privatekey_path: ./artifacts/ca.key
              provider: ownca
              force: True
            with_items: "{{ groups['etcd'] }}"
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      Feche e salve o arquivo playbook.yaml pressionando CTRL+X e depois Y. Em seguida, execute o playbook novamente para aplicar as alterações:

      • ansible-playbook -i hosts playbook.yaml

      Agora, liste o conteúdo do diretório artifacts/:

      Você verá a chave privada, a CSR e o certificado para cada membro do etcd, além da CA:

      Output

      ca.crt ca.csr ca.key etcd1.crt etcd1.csr etcd1.key etcd2.crt etcd2.csr etcd2.key etcd3.crt etcd3.csr etcd3.key

      Neste passo, assinamos as CSRs de cada nó membro usando a chave da CA. No próximo passo, vamos copiar os arquivos relevantes em cada nó gerenciado, para que o etcd tenha acesso às chaves e certificados relevantes para configurar as conexões TLS.

      Passo 13 — Copiando chaves privadas e certificados

      Todos os nós precisam ter uma cópia do certificado auto-assinado da CA (ca.crt). Cada nó membro do etcd também precisa ter sua própria chave privada e certificado. Neste passo, vamos fazer upload desses arquivos e colocá-los em um novo diretório /etc/etcd/ssl/.

      Para começar, abra o arquivo playbook.yaml com seu editor:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      A fim de fazer essas alterações em nosso playbook do Ansible, atualize primeiro a propriedade path da tarefa Create directory for etcd configuration para criar o diretório /etc/etcd/ssl/:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        ...
        tasks:
          ...
            with_items:
              - absent
              - directory
          - name: "Create directory for etcd configuration"
            file:
              path: "{{ item }}"
              state: directory
              owner: root
              group: root
              mode: 0755
            with_items:
              - /etc/etcd
              - /etc/etcd/ssl
          - name: "Create configuration file for etcd"
            template:
      ...
      

      Em seguida, adicione mais três tarefas após a tarefa modificada para copiar os arquivos:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        ...
        tasks:
          ...
          - name: "Copy over the CA certificate"
            copy:
              src: ./artifacts/ca.crt
              remote_src: False
              dest: /etc/etcd/ssl/ca.crt
              owner: root
              group: root
              mode: 0644
          - name: "Copy over the `etcd` member certificate"
            copy:
              src: ./artifacts/{{inventory_hostname}}.crt
              remote_src: False
              dest: /etc/etcd/ssl/server.crt
              owner: root
              group: root
              mode: 0644
          - name: "Copy over the `etcd` member key"
            copy:
              src: ./artifacts/{{inventory_hostname}}.key
              remote_src: False
              dest: /etc/etcd/ssl/server.key
              owner: root
              group: root
              mode: 0600
          - name: "Create configuration file for etcd"
            template:
      ...
      

      Feche e salve o arquivo playbook.yaml pressionando CTRL+X e depois Y.

      Execute o ansible-playbook novamente para fazer essas alterações:

      • ansible-playbook -i hosts playbook.yaml

      Neste passo, fizemos upload das chaves privadas e certificados para os nós gerenciados. Após termos copiado os arquivos, precisamos agora atualizar nosso arquivo de configuração do etcd para fazer uso deles.

      Passo 14 — Habilitando o TLS no etcd

      No último passo deste tutorial, vamos atualizar algumas configurações do Ansible para habilitar o TLS em um cluster do etcd.

      Primeiro, abra o arquivo modelo templates/etcd.conf.yaml.j2 usando seu editor:

      • nano $HOME/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      Uma vez dentro, mude todas as URLs para que usem o https como protocolo em vez de http. Além disso, adicione uma seção no final do modelo para especificar a localização do certificado CA, certificado do servidor e chave do servidor:

      ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
      name: {{ inventory_hostname }}
      initial-advertise-peer-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380
      listen-peer-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380,https://127.0.0.1:2380
      advertise-client-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379
      listen-client-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379,https://127.0.0.1:2379
      initial-cluster-state: new
      initial-cluster: {% for host in groups['etcd'] %}{{ hostvars[host]['ansible_facts']['hostname'] }}=https://{{ hostvars[host]['ansible_facts']['eth1']['ipv4']['address'] }}:2380{% if not loop.last %},{% endif %}{% endfor %}
      
      client-transport-security:
        cert-file: /etc/etcd/ssl/server.crt
        key-file: /etc/etcd/ssl/server.key
        trusted-ca-file: /etc/etcd/ssl/ca.crt
      peer-transport-security:
        cert-file: /etc/etcd/ssl/server.crt
        key-file: /etc/etcd/ssl/server.key
        trusted-ca-file: /etc/etcd/ssl/ca.crt
      

      Feche e salve o arquivo templates/etcd.conf.yaml.j2.

      Em seguida, execute seu playbook do Ansible:

      • ansible-playbook -i hosts playbook.yaml

      Depois disso, entre via SSH em um dos nós gerenciados:

      Uma vez dentro, execute o comando etcdctl endpoint health para verificar se os pontos de extremidade estão usando o HTTPS e se todos os membros estão íntegros:

      • etcdctl --cacert /etc/etcd/ssl/ca.crt endpoint health --cluster

      Como nosso certificado CA não é, por padrão, um certificado CA root confiável instalado no diretório /etc/ssl/certs/, precisamos passá-lo para o etcdctl usando o sinalizador --cacert.

      Isso dará o seguinte resultado:

      Output

      https://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

      Para confirmar se o cluster do etcd está realmente funcionando, podemos mais uma vez criar uma entrada em um nó membro, e recuperá-la em outro nó membro:

      • etcdctl --cacert /etc/etcd/ssl/ca.crt put foo "bar"

      Use um novo terminal para entrar via SSH em um nó membro diferente:

      Agora, recupere a mesma entrada usando a chave foo:

      • etcdctl --cacert /etc/etcd/ssl/ca.crt get foo

      Isso irá retornar a entrada, exibindo o resultado abaixo:

      Output

      foo bar

      Você pode fazer o mesmo no terceiro nó para garantir que todos os três membros estejam operacionais.

      Conclusão

      Agora, você provisionou com sucesso um cluster de 3 nós do etcd, protegeu ele com o TLS e confirmou que está funcionando.

      O etcd é uma ferramenta originalmente criada pelo CoreOS. Para entender o uso do etcd em relação ao CoreOS, consulte Como usar o etcdctl e o etcd, o armazenamento de chaves-valores distribuído do CoreOS. O artigo também dá orientação na configuração de um modelo de descoberta dinâmico, algo que foi discutido, mas não demonstrado neste tutorial.

      Como mencionado no início deste tutorial, o etcd é uma parte importante do ecossistema do Kubernetes. Para aprender mais sobre o papel do Kubernetes e do etcd dentro dele, leia Uma introdução ao Kubernetes. Se estiver implantando o etcd como parte de um cluster do Kubernetes, saiba que existem outras ferramentas disponíveis, como o kubespray e o kubeadm. Para mais detalhes sobre o kubeadm, leia Como criar um cluster do Kubernetes usando o Kubeadm no Ubuntu 18.04.

      Por fim, deve-se destacar que este tutorial fez uso de muitas ferramentas, mas não conseguiu se aprofundar em todos eles com muitos detalhes. A seguir, estão disponíveis links que irão fornecer uma análise mais detalhada sobre cada ferramenta:



      Source link