One place for hosting & domains

      Einrichten

      Lesen und Einrichten von Umgebungs- und Shell-Variablen unter Linux


      Einführung

      Wenn Sie mit Ihrem Server über eine Shell-Sitzung interagieren, gibt es viele Informationen, die Ihre Shell kompiliert, um ihr Verhalten und den Zugriff auf Ressourcen zu bestimmen. Einige dieser Einstellungen sind innerhalb der Konfigurationseinstellungen enthalten und andere werden durch Benutzereingabe bestimmt.

      Eine Möglichkeit, wie die Shell alle diese Einstellungen und Details verfolgt, ist ein Bereich, den sie als Umgebung bezeichnet. Die Umgebung ist ein Bereich, den die Shell jedes Mal, wenn sie eine Sitzung startet, erstellt und der Variablen enthält, die Systemeigenschaften definieren.

      In diesem Leitfaden besprechen wir, wie Sie mit der Umgebung interagieren und Umgebungs- und Shell-Variablen interaktiv und über Konfigurationsdateien lesen oder einrichten.

      Funktionsweise der Umgebung und Umgebungsvariablen

      Bei jedem Starten einer Shell-Sitzung findet ein Prozess statt, der Informationen sammelt und kompiliert, die dem Shell-Prozess und seinen Unterprozessen verfügbar sein sollen. Die Daten für diese Einstellungen bezieht er aus einer Vielzahl verschiedener Dateien und Einstellungen auf dem System.

      Die Umgebung stellt ein Medium bereit, über das der Shell-Prozess Einstellungen erhalten oder einrichten kann und diese wiederum an seine Unterprozesse weitergibt.

      Die Umgebung ist als Zeichenketten implementiert, die Schlüsselwertpaare darstellen. Werden mehrere Werte übergeben, werden sie typischerweise durch Doppelpunkte (:) getrennt. Jedes Paar sieht allgemein etwa so aus:

      KEY=value1:value2:...
      

      Wenn der Wert signifikante Leerzeichen enthält, werden Anführungszeichen verwendet:

      KEY="value with spaces"
      

      Die Schlüssel in diesen Szenarien sind Variablen. Sie können einer von zwei Typen sein, Umgebungsvariablen oder Shell-Variablen.

      Umgebungsvariablen sind Variablen, die für die aktuelle Shell definiert sind und an alle untergeordneten Shells oder Prozesse vererbt werden. Umgebungsvariablen werden verwendet, um Informationen an Prozesse zu übergeben, die von der Shell gestartet werden.

      Shell-Variablen sind Variablen, die ausschließlich innerhalb der Shell enthalten sind, in der sie festgelegt oder definiert wurden. Sie werden oft verwendet, um kurzlebige Daten wie das aktuelle Arbeitsverzeichnis zu verfolgen.

      Gemäß der Konvention werden diese Arten von Variablen normalerweise mit Großbuchstaben definiert. Dies hilft dem Benutzer, Umgebungsvariablen innerhalb anderer Kontexte zu unterscheiden.

      Ausgeben von Shell- und Umgebungsvariablen

      Jede Shell-Sitzung verfolgt ihre eigenen Shell- und Umgebungsvariablen. Wir können auf diese auf verschiedene Weise zugreifen.

      Wir können eine Liste aller unsere Umgebungsvariablen sehen, indem wir die Befehle env oder printenv verwenden. In ihrem Standardzustand sollten sie genau gleich funktionieren:

      Output

      SHELL=/bin/bash TERM=xterm USER=demouser LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca:... MAIL=/var/mail/demouser PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games PWD=/home/demouser LANG=en_US.UTF-8 SHLVL=1 HOME=/home/demouser LOGNAME=demouser LESSOPEN=| /usr/bin/lesspipe %s LESSCLOSE=/usr/bin/lesspipe %s %s _=/usr/bin/printenv

      Dies ist ziemlich typisch für die Ausgabe sowohl von printenv als auch env. Der Unterschied zwischen den beiden Befehlen ist nur in ihrer spezifischeren Funktionalität sichtbar. Beispielsweise können Sie mit printenv die Werte einzelner Variablen anfordern:

      Output

      /bin/bash

      Mit env hingegen können Sie die Umgebung, in der Programme ausgeführt werden, ändern, indem Sie einen Satz von Variablendefinitionen wie folgt an einen Befehl übergeben:

      • env VAR1="value" command_to_run command_options

      Da, wie wir vorstehend gelernt haben, untergeordnete Prozesse typischerweise die Umgebungsvariablen des übergeordneten Prozesses erben, bietet Ihnen dies die Möglichkeit, Werte zu überschreiben oder zusätzliche Variablen für den untergeordneten Prozess hinzuzufügen.

      Wie Sie in der Ausgabe unseres Befehls printenv sehen können, gibt es eine ganze Reihe von Umgebungsvariablen, die, ohne unsere Eingabe, durch unsere Systemdateien und Prozesse eingerichtet wurden.

      Diese zeigen die Umgebungsvariablen, aber wie sehen wir Shell-Variablen?

      Hierfür kann der Befehl set verwendet werden. Wenn wir set ohne zusätzliche Parameter eingeben, erhalten wir eine Liste aller Shell-Variablen, Umgebungsvariablen, lokalen Variablen und Shell-Funktionen:

      Output

      BASH=/bin/bash BASHOPTS=checkwinsize:cmdhist:expand_aliases:extglob:extquote:force_fignore:histappend:interactive_comments:login_shell:progcomp:promptvars:sourcepath BASH_ALIASES=() BASH_ARGC=() BASH_ARGV=() BASH_CMDS=() . . .

      Dies ist normalerweise eine riesige Liste. Wahrscheinlich möchten Sie sie in ein Pager-Programm leiten, um die Menge der Ausgabe leichter bewältigen zu können:

      Die Menge zusätzlicher Informationen, die wir zurückerhalten, ist etwas überwältigend. Wahrscheinlich müssen wir nicht alle Bash-Funktionen kennen, die beispielsweise definiert sind.

      Wir können die Ausgabe bereinigen, indem wir angeben, dass set im POSIX-Modus arbeiten soll, wodurch die Shell-Funktionen nicht ausgegeben werden. Wir können dies in einer Unter-Schell ausführen, sodass es unsere aktuelle Umgebung nicht verändert:

      Dies listet alle Umgebungs- und Shell-Variablen auf, die definiert sind.

      Wir können versuchen, diese Ausgabe mit der Ausgabe der Befehle env oder printenv zu vergleichen, um zu versuchen, nur eine Liste der Shell-Variablen zu erhalten, aber dies wird aufgrund der unterschiedlichen Art und Weise, wie diese Befehle Informationen ausgeben, nicht perfekt sein:

      • comm -23 <(set -o posix; set | sort) <(env | sort)

      Es werden wahrscheinlich immer noch einige Umgebungsvariablen enthalten sein, da der Befehl set Werte in Anführungszeichen ausgibt, währen die Befehle printenv und env die Werte von Zeichenketten nicht in Anführungszeichen setzen.

      Dennoch sollte dies Ihnen einen guten Überblick über die Umgebungs- und Shell-Variablen geben, die in Ihrer Sitzung festgelegt sind.

      Diese Variablen werden für alle möglichen Dinge verwendet. Sie bieten einen alternativen Weg, um zwischen Prozessen dauerhafte Werte für die Sitzung zu setzen, ohne Änderungen in eine Datei zu schreiben.

      Häufige Umgebungs- und Shell-Variablen

      Einige Umgebungs- und Shell-Variablen sind sehr nützlich und werden ziemlich oft referenziert. Hier sind einige häufige Umgebungsvariablen, denen Sie begegnen werden:

      • SHELL: Dies beschreibt die Shell, die alle von Ihnen eingegebenen Befehle interpretiert. In den meisten Fällen wird dies standardmäßig Bash sein, jedoch können auch andere Werte festgelegt werden, wenn Sie andere Optionen bevorzugen.
      • TERM: Dies gibt die Art des Terminals an, das bei der Ausführung der Shell emuliert werden soll. Es können verschiedene Hardware-Terminals für unterschiedliche Betriebsanforderungen emuliert werden. Normalerweise müssen Sie sich darüber aber keine Gedanken machen.
      • USER: Der aktuelle angemeldete Benutzer.
      • PWD: Das aktuelle Arbeitsverzeichnis.
      • OLDPWD: Das vorherige Arbeitsverzeichnis. Dieses wird von der Shell beibehalten, um durch Ausführen von cd - wieder in Ihr vorheriges Verzeichnis zu wechseln.
      • LS_COLORS: Dies definiert Farbcodes, die verwendet werden, um dem Befehl Is optional eine farbige Ausgabe hinzuzufügen. Dies wird verwendet, um verschiedene Dateitypen zu unterscheiden und dem Benutzer auf einen Blick mehr Informationen zu bieten.
      • MAIL: Der Pfad zur Mailbox des aktuellen Benutzers.
      • PATH: Eine Liste von Verzeichnissen, die das System bei der Suche nach Befehlen überprüfen wird. Wenn ein Benutzer einen Befehl eingibt, überprüft das System die Verzeichnisse in dieser Reihenfolge auf die ausführbare Datei.
      • LANG: Die aktuelle Sprach- und Lokalisierungseinstellungen, einschließlich der Zeichencodierung.
      • HOME: Das Heimatverzeichnis des aktuellen Benutzers.
      • _: Der letzte zuvor ausgeführte Befehl.

      Zusätzlich zu diesen Umgebungsvariablen gibt es noch einige Shell-Variablen, die Sie oft sehen werden:

      • BASHOPTS: Die Liste der Optionen, die bei der Ausführung von Bash verwendet wurden. Dies kann nützlich sein, um herauszufinden, ob die Shell-Umgebung so funktioniert, wie Sie es wünschen.
      • BASH_VERSION: Die Version von Bash, die ausgeführt wird, in menschenlesbarer Form.
      • BASH_VERSINFO: Die Version von Bash, in maschinenlesbarer Ausgabe.
      • COLUMNS: Die Anzahl der Spaltenbreite, die zur Ausgabe auf dem Bildschirm verwendet wird.
      • DIRSTACK: Der Stack von Verzeichnissen, die mit den Befehlen pushd und popd verfügbar sind.
      • HISTFILESIZE: Anzahl der Zeilen der Befehlshistorie, die in einer Datei gespeichert werden.
      • HISTSIZE: Anzahl der Zeilen der Befehlshistorie, die im Speicher erlaubt sind.
      • HOSTNAME: Der Hostname des Computers zu diesem Zeitpunkt.
      • IFS: Das interne Feldtrennzeichen zur Trennung der Eingabe in der Befehlszeile. Standardmäßig ist dies ein Leerzeichen.
      • PS1: Die primäre Definition der Eingabeaufforderung. Damit wird definiert, wie Ihre Eingabeaufforderung beim Starten einer Shell-Sitzung aussieht. Der PS2 wird verwendet, um sekundäre Eingabeaufforderungen zu deklarieren, wenn sich ein Befehl über mehrere Zeilen erstreckt.
      • SHELLOPTS: Shell-Optionen, die mit der Option set festgelegt werden können.
      • UID: Die UID des aktuellen Benutzers.

      Einstellen von Shell- und Umgebungsvariablen

      Um den Unterschied zwischen Shell- und Umgebungsvariablen besser zu verstehen und die Syntax für die Einstellung dieser Variablen einzuführen, werden wir eine kleine Demonstration durchführen.

      Erstellen von Shell-Variablen

      Wir beginnen mit der Definition einer Shell-Variable innerhalb unserer aktuellen Sitzung. Dies ist einfach zu erreichen; wir müssen nur einen Namen und einen Wert angeben. Wir halten uns an die Konvention, den Variablennamen in Großbuchstaben zu schreiben, und setzen ihn auf eine einfache Zeichenkette.

      Hier haben wir Anführungszeichen verwendet, da der Wert unserer Variable ein Leerzeichen enthält. Außerdem haben wir einfache Anführungszeichen verwendet, weil das Ausrufezeichen ein Sonderzeichen in der Bash-Shell ist, das sich normalerweise in den Bash-Verlauf ausdehnt, wenn es nicht mit einem Escape-Zeichen versehen oder in einfache Anführungszeichen gesetzt wird.

      Wir haben nun eine Shell-Variable. Diese Variable ist in unserer aktuellen Sitzung verfügbar, wird aber nicht an untergeordnete Prozesse übergeben.

      Wir können dies sehen, indem wir mit dem Befehl „grep“ in der Ausgabe set nach unserer neuen Variable suchen:

      Output

      TEST_VAR='Hello World!'

      Wir können überprüfen, dass es sich nicht um eine Umgebungsvariable handelt, indem wir das Gleiche mit printenv versuchen:

      Es sollte keine Ausgabe zurückgegeben werden.

      Nehmen wir dies zum Anlass, eine Möglichkeit zu demonstrieren, wie auf den Wert einer beliebigen Shell- oder Umgebungsvariablen zugegriffen werden kann.

      Output

      Hello World!

      Wie Sie sehen können, referenzieren Sie den Wert einer Variable, indem Sie ihr ein $-Zeichen voranstellen. Die Shell bezeichnet damit, dass sie den Wert der Variable ersetzen soll, wenn sie auf diese stößt.

      Jetzt haben wir eine Shell-Variable. Sie sollte nicht an untergeordnete Prozesse übergeben werden. Wir können zur Demonstration eine neue Bash-Shell aus unserer aktuellen heraus starten:

      Wenn wir bash eingeben, um eine untergeordnete Shell zu starten, und dann versuchen, auf den Inhalt der Variable zuzugreifen, wird nichts zurückgegeben. Das haben wir erwartet.

      Gehen Sie zurück zu unserer ursprünglichen Shell, indem Sie exit eingeben:

      Erstellen von Umgebungsvariablen

      Lassen Sie uns nun unsere Shell-Variable in eine Umgebungsvariable verwandeln. Wir können dies tun, indem wir die Variable exportieren. Der Befehl dazu heiß passenderweise:

      Dadurch wird unsere Variable in eine Umgebungsvariable umgewandelt. Wir können dies überprüfen, indem wir unsere Umgebungsliste erneut überprüfen:

      Output

      TEST_VAR=Hello World!

      Diesmal wird unsere Variable angezeigt. Versuchen wir unser Experiment mit unserer untergeordneten Shell:

      Output

      Hello World!

      Klasse! Unsere untergeordnete Shell hat die Variable, die von ihrer übergeordneten Shell gesetzt wurde, erhalten. Bevor wir diese untergeordnete Shell verlassen, versuchen wir, eine andere Variable zu exportieren. Wir können Umgebungsvariablen in einem einzigen Schritt wie folgt setzen:

      • export NEW_VAR="Testing export"

      Testen Sie, dass sie als Umgebungsvariable exportiert wird:

      Output

      NEW_VAR=Testing export

      Lassen Sie uns nun wieder in unsere ursprüngliche Shell gehen:zurückkehren:

      Sehen wir, ob unsere neue Variable verfügbar ist:

      Es wird nichts zurückgegeben.

      Das liegt daran, dass Umgebungsvariablen nur an untergeordnete Prozesse übergeben werden. Es gibt keine integrierte Möglichkeit, Umgebungsvariablen der übergeordneten Shell zu setzen. Dies ist in den meisten Fällen gut und verhindert, dass Programme die Betriebsumgebung beeinträchtigen, aus der sie aufgerufen wurden.

      Die Variable NEW_VAR wurde in unserer untergeordneten Shell als Umgebungsvariable gesetzt. Diese Variable würde für sie selbst und alle untergeordneten Shells und Prozesse verfügbar sein. Beim Verlassen zurück in unsere Haupt-Shell wurde diese Umgebung zerstört.

      Herunterstufen und Zurücksetzen von Variablen

      Wir haben noch immer unsere Variable TEST_VAR als Umgebungsvariable definiert. Wir können sie wieder in eine Shell-Variable umwandeln, indem wir Folgendes eingeben:

      Sie ist keine Umgebungsvariable mehr:

      Sie ist jedoch noch immer eine Shell-Variable:

      Output

      TEST_VAR='Hello World!'

      Wenn wir eine Variable, egal ob Shell- oder Umgebungsvariable, komplett zurücksetzen möchten, können wir dies mit dem Befehl unset tun:

      Wir können überprüfen, dass sie nicht mehr gesetzt ist:

      Es wird nichts zurückgegeben, da die Variable zurückgesetzt wurde.

      Setzen von Umgebungsvariablen bei der Anmeldung

      Wir haben bereits erwähnt, dass viele Programme Umgebungsvariablen verwenden, um die Besonderheiten der Funktionsweise zu entscheiden. Wir wollen nicht jedes Mal, wenn wir eine neue Shell-Sitzung starten, wichtige Variablen setzen müssen, und wir haben bereits gesehen, wie viele Variablen beim Anmelden bereits gesetzt sind, also wie können wir Variablen automatisch erstellen und definieren?

      Dies ist tatsächlich ein komplexeres Problem, als es auf den ersten Blick scheint, aufgrund der zahlreichen Konfigurationsdateien, die die Bash-Shell liest, je nachdem, wie sie gestartet wird.

      Der Unterschied zwischen Anmelde-, Nicht-Anmelde-, interaktiven und nicht-interaktiven Shell-Sitzungen

      Die Bash-Shell liest verschiedene Konfigurationsdateien, je nachdem, wie die Sitzung gestartet wird.

      Ein Unterschied zwischen verschiedenen Sitzungen ist, ob die Shell als Anmelde- oder Nicht-Anmelde-Sitzung gestartet wird.

      Eine Anmelde-Shell ist eine Shell-Sitzung, die mit der Authentifizierung des Benutzers beginnt. Wenn Sie sich in einer Terminal-Sitzung oder über SSH anmelden und sich authentifizieren, wird Ihre Shell-Sitzung als Anmelde-Shell gesetzt.

      Wenn Sie aus Ihrer authentifizierten Sitzung eine neue Shell-Sitzung starten, wie wir es mit dem Aufruf des Befehls bash über das Terminal getan haben, wird eine Nicht-Anmelde-Shell-Sitzung gestartet. Sie wurden beim Start Ihrer untergeordneten Shell nicht nach Ihren Authentifizierungsdetails gefragt.

      Eine weitere Unterscheidung, die vorgenommen werden kann, ist, ob eine Shell-Sitzung interaktiv oder nicht interaktiv ist.

      Eine interaktive Shell-Sitzung ist eine Shell-Sitzung, die an ein Terminal gebunden ist. Eine nicht interaktive Shell-Sitzung ist eine Shell-Sitzung, die nicht an eine Terminal-Sitzung gebunden ist.

      Jede Shell-Sitzung wird also entweder als Anmelde oder Nicht-Anmelde und interaktiv oder nicht interaktiv klassifiziert.

      Eine normale Sitzung, die mit SSH beginnt, ist normalerweise eine interaktive Anmelde-Shell. Ein Skript, das von der Befehlszeile ausgeführt wird, wird normalerweise in einer nicht interaktiven, Nicht-Anmelde-Shell ausgeführt. Eine Terminal-Sitzung kann eine beliebige Kombination dieser beiden Eigenschaften sein.

      Ob eine Shell-Sitzung als Anmelde- oder Nicht-Anmelde-Shell klassifiziert wird, hat Auswirkungen darauf, welche Dateien zur Initialisierung der Shell-Sitzung gelesen werden.

      Eine als Anmelde-Sitzung gestartete Sitzung liest zuerst die Konfigurationsdetails aus der Datei /etc/profile. Sie sucht nach der ersten Anmelde-Shell-Konfigurationsdatei im Heimatverzeichnis des Benutzers, um benutzerspezifische Konfigurationsdetails zu erhalten.

      Sie liest die erste Datei, die sie aus ~/.bash_profile, ~/.bash_login und ~/.profile finden kann, und liest keine weiteren Dateien.

      Im Gegensatz dazu liest eine als Nicht-Anmelde-Shell definierte Sitzung /etc/bash.bashrc und dann die benutzerspezifische Datei ~/.bashrc, um ihre Umgebung zu erstellen.

      Nicht-interaktive Shells lesen die Umgebungsvariable namens BASH_ENV und lesen die angegebene Datei, um die neue Umgebung zu definieren.

      Implementieren von Umgebungsvariablen

      Wie Sie sehen können, gibt es eine Vielzahl von verschiedenen Dateien, die wir normalerweise für die Platzierung unserer Einstellungen ansehen müssten.

      Dies bietet eine Menge Flexibilität, die in bestimmten Situationen hilfreich sein kann, in denen wir bestimmte Einstellungen in einer Anmelde-Shell und andere Einstellungen in einer Nicht-Anmelde-Shell wünschen. Die meiste Zeit werden jedoch die gleichen Einstellungen in beiden Situationen wünschen.

      Glücklicherweise konfigurieren die meisten Linux-Distributionen die Anmelde-Konfigurationsdateien als Quelle für die Nicht-Anmelde-Konfigurationsdateien. Das bedeutet, dass Sie Umgebungsvariablen, die Sie in beiden Fällen benötigen, in den Nicht-Anmelde-Konfigurationsdateien definieren können. Sie werden dann in beiden Szenarien gelesen.

      Normalerweise werden wir benutzerspezifische Umgebungsvariablen setzen, und wir werden normalerweise wünschen, dass unsere Einstellungen sowohl in Anmelde- als auch Nicht-Anmelde-Shells verfügbar sind. Das bedeutet, dass sich der Ort zur Definition dieser Variablen in der Datei ~/.bashrc befindet.

      Öffnen Sie nun diese Datei:

      Diese wird wahrscheinlich bereits eine ganze Menge Daten enthalten. Die meisten Definitionen hier sind für das Setzen von Bash-Optionen, wobei diese nichts mit Umgebungsvariablen zu tun haben. Sie können Umgebungsvariablen genauso setzen wie Sie es über die Befehlszeile tun würden:

      Neue Umgebungsvariablen können an beliebiger Stelle in der Datei ~/.bashrc eingefügt werden, solange sie nicht in der Mitte eines anderen Befehls oder einer for-Schleife stehen. Anschließend können wir die Datei speichern und schließen. Wenn Sie das nächste Mal eine Shell-Sitzung starten, wird Ihre Umgebungsvariablendeklaration gelesen und an die Shell-Umgebung übergeben. Sie können Ihre aktuelle Sitzung zwingen, die Datei nun zu lesen, indem Sie Folgendes eingeben:

      Wenn Sie systemweite Variablen setzen müssen, sollten Sie darüber nachdenken, diese in /etc/profile, /etc/bash.bashrc oder /etc/environment hinzuzufügen.

      Zusammenfassung

      Umgebungs- und Shell-Variablen sind in Ihren Shell-Sitzungen immer vorhanden und können sehr nützlich sein. Sie sind eine interessante Möglichkeit für einen übergeordneten Prozess, die Konfigurationsdetails für seinen untergeordneten Prozess festzulegen und bieten eine Möglichkeit, Optionen außerhalb von Dateien zu setzen.

      Dies hat in bestimmten Situationen viele Vorteile. Einige Bereitstellungsmechanismen verlassen sich beispielsweise auf Umgebungsvariablen, um Authentifizierungsinformationen zu konfigurieren. Dies ist nützlich, da es nicht erforderlich ist, diese in Dateien zu halten, die von Außenstehenden gesehen werden können.

      Es gibt viele andere, alltäglichere, aber häufigere Szenarien, in denen Sie die Umgebung Ihres Systems auslesen oder ändern müssen. Diese Tools und Techniken sollten Ihnen eine gute Grundlage für die Durchführung dieser Änderungen und die korrekte Verwendung bieten.



      Source link

      Einrichten eines Node-Projekts mit TypeScript


      Einführung

      Node ist eine Laufzeitumgebung, die es ermöglicht, serverseitiges JavaScript zu schreiben. Seit ihrer Veröffentlichung im Jahr 2011 hat sie eine weite Verbreitung gefunden. Das Schreiben von serverseitigem JavaScript kann eine Herausforderung sein, da die Codebasis aufgrund der Art der JavaScript-Sprache – dynamisch und schwach typisiert – wächst.

      Entwickler, die aus anderen Sprachen zu JavaScript kommen, beklagen sich oft über den Mangel an starker statischer Typisierung, aber genau hier kommt TypeScript ins Spiel, um diese Lücke zu schließen.

      TypeScript ist eine typisierte (optionale) Obermenge von JavaScript, die bei der Erstellung und Verwaltung großer JavaScript-Projekte helfen kann. Man kann es sich als JavaScript mit zusätzlichen Funktionen wie starker statischer Typisierung, Kompilierung und objektorientierter Programmierung vorstellen.

      Anmerkung: TypeScript ist technisch gesehen eine Obermenge von JavaScript, wobei der gesamte JavaScript-Code gültiger TypeScript-Code ist.

      Hier sind einige Vorteile der Verwendung von TypeScript:

      1. Optionale statische Typisierung.
      2. Typinferenz.
      3. Fähigkeit zur Verwendung von Schnittstellen.

      In diesem Tutorial richten Sie ein Node-Projekt mit TypeScript ein. Sie werden eine Express-Anwendung mit TypeScript erstellen und diese in einen sauberen und zuverlässigen JavaScript-Code transpilieren.

      Voraussetzungen

      Bevor Sie mit diesem Leitfaden beginnen, muss Node.js auf Ihrem Computer installiert sein. Sie können dies erreichen, indem Sie dem Leitfaden Installieren von Node.js und Erstellen einer lokalen Entwicklungsumgebung für Ihr Betriebssystem folgen.

      Schritt 1 — Initialisieren eines npm-Projekts

      Erstellen Sie zunächst einen neuen Ordner namens node_project und wechseln Sie in dieses Verzeichnis.

      • mkdir node_project
      • cd node_project

      Initialisieren Sie es als npm-Projekt:

      Nach der Ausführung von npm init müssen Sie npm Informationen über Ihr Projekt bereitstellen. Wenn Sie es vorziehen, npm sinnvolle Standardeinstellungen annehmen zu lassen, können Sie das Flag y hinzufügen, um die Eingabeaufforderungen für zusätzliche Informationen zu überspringen:

      Nachdem Ihr Projektraum nun eingerichtet ist, können Sie mit der Installation der erforderlichen Abhängigkeiten fortfahren.

      Schritt 2 — Installieren der Abhängigkeiten

      Wenn ein blankes npm-Projekt initialisiert wurde, besteht der nächste Schritt darin, die Abhängigkeiten zu installieren, die zur Ausführung von TypeScript erforderlich sind.

      Führen Sie die folgenden Befehle aus Ihrem Projektverzeichnis aus, um die Abhängigkeiten zu installieren:

      • npm install -D typescript@3.3.3
      • npm install -D tslint@5.12.1

      Das Flag -D ist die Kurzform für: -save-dev. In der Dokumentation zu npmjs können Sie mehr über dieses Flag erfahren.

      Jetzt ist es an der Zeit, das Express-Framework zu installieren:

      • npm install -S express@4.16.4
      • npm install -D @types/express@4.16.1

      Der zweite Befehl installiert die Express-Typen für die TypeScript-Unterstützung. Typen in TypeScript sind Dateien, normalerweise mit einer Erweiterung von .d.ts. Die Dateien werden verwendet, um Typinformationen über eine API, in diesem Fall das Express-Framework, bereitzustellen.

      Dieses Paket ist erforderlich, da TypeScript und Express unabhängige Pakete sind. Ohne das Paket @types/express gibt es für TypeScript keine Möglichkeit, Informationen über die Typen von Express-Klassen zu erhalten.

      Schritt 3 — Konfigurieren von TypeScript

      In diesem Abschnitt richten Sie TypeScript ein und konfigurieren Linting für TypeScript. TypeScript verwendet eine Datei namens tsconfig.json, um die Compiler-Optionen für ein Projekt zu konfigurieren. Erstellen Sie eine Datei tsconfig.json im Stammverzeichnis des Projektverzeichnisses und fügen Sie das folgende Snippet ein:

      tsconfig.json

      {
        "compilerOptions": {
          "module": "commonjs",
          "esModuleInterop": true,
          "target": "es6",
          "moduleResolution": "node",
          "sourceMap": true,
          "outDir": "dist"
        },
        "lib": ["es2015"]
      }
      

      Sehen wir uns einige der Schlüssel im obigen JSON-Snippet an:

      • module: Gibt die Methode zur Erzeugung des Modulcodes an. Node verwendet commonjs.
      • target: Gibt das Ausgabe-Sprachniveau an.
      • moduleResolution: Dies hilft dem Compiler herauszufinden, worauf sich ein Import bezieht. Der Wert node imitiert den Mechanismus zur Auflösung des Node-Moduls.
      • outDir: Dies ist der Speicherort für die Ausgabe von .js-Dateien nach der Transpilation. In diesem Tutorial speichern Sie sie als dist.

      Eine Alternative zur manuellen Erstellung und Population der Datei tsconfig.json ist die Ausführung des folgenden Befehls:

      Dieser Befehl generiert eine gut kommentierte tsconfig.json-Datei.

      Um mehr über die verfügbaren Schlüsselwertoptionen zu erfahren, bietet die offizielle TypeScript-Dokumentation Erklärungen zu jeder Option.

      Jetzt können Sie TypeScript-Linting für das Projekt konfigurieren. Führen Sie den folgenden Befehl in einem Terminal im Stammverzeichnis Ihres Projektverzeichnisses aus, das in diesem Tutorial als node_project eingerichtet wurde, um eine tslint.json-Datei zu erzeugen:

      • ./node_modules/.bin/tslint --init

      Öffnen Sie die neu erzeugte tslint.json-Datei und fügen Sie die Regel no-console entsprechend hinzu:

      tslint.json

      {
        "defaultSeverity": "error",
        "extends": ["tslint:recommended"],
        "jsRules": {},
        "rules": {
          "no-console": false
        },
        "rulesDirectory": []
      }
      

      Standardmäßig verhindert der TypeScript-Linter die Verwendung von Debugging mittels der Anweisungen console, weshalb der Linter explizit angewiesen werden muss, die standardmäßige Regel no-console zu widerrufen.

      Schritt 4 — Aktualisieren der Datei package.json

      An diesem Punkt im Tutorial können Sie entweder Funktionen im Terminal einzeln ausführen oder ein npm-Skript erstellen, um sie auszuführen.

      In diesem Schritt erstellen Sie ein Skript start, das den TypeScript-Code kompiliert und transpiliert und dann die resultierende Anwendung .js ausführt.

      Öffnen Sie die Datei package.json und aktualisieren Sie sie entsprechend:

      package.json

      {
        "name": "node-with-ts",
        "version": "1.0.0",
        "description": "",
        "main": "dist/app.js",
        "scripts": {
          "start": "tsc && node dist/app.js",
          "test": "echo "Error: no test specified" && exit 1"
        },
        "author": "",
        "license": "ISC",
        "devDependencies": {
          "@types/express": "^4.16.1",
          "tslint": "^5.12.1",
          "typescript": "^3.3.3"
        },
        "dependencies": {
          "express": "^4.16.4"
        }
      }
      

      Im obigen Snippet haben Sie den Pfad main aktualisiert und den Befehl start dem Abschnitt scripts hinzugefügt. Wenn Sie sich den Befehl start ansehen, sehen Sie, dass zuerst der Befehl tsc ausgeführt wird, und dann der Befehl node. Dadurch wird die generierte Ausgabe mit node kompiliert und dann ausgeführt.

      Der Befehl tsc weist TypeScript an, die Anwendung zu kompilieren und die generierte Ausgabe .js im angegebenen Verzeichnis outDir zu platzieren, wie es in der Datei tsconfig.json festgelegt ist.

      Schritt 5 — Erstellen und Ausführen eines einfachen Express-Servers

      Nachdem TypeScript und dessen Linter konfiguriert sind, ist es an der Zeit, einen Node Express-Server zu erstellen.

      Erstellen Sie zunächst einen Ordner src im Stammverzeichnis Ihres Projektverzeichnisses:

      Erstellen Sie dann darin eine Datei namens app.ts:

      An diesem Punkt sollte die Ordnerstruktur wie folgt aussehen:

      ├── node_modules/
      ├── src/
        ├── app.ts
      ├── package-lock.json
      ├── package.json
      ├── tsconfig.json
      ├── tslint.json
      

      Öffnen Sie die Datei app.ts mit einem Texteditor Ihrer Wahl und fügen Sie das folgende Code-Snippet ein:

      src/app.ts

      import express from 'express';
      
      const app = express();
      const port = 3000;
      app.get('/', (req, res) => {
        res.send('The sedulous hyena ate the antelope!');
      });
      app.listen(port, err => {
        if (err) {
          return console.error(err);
        }
        return console.log(`server is listening on ${port}`);
      });
      

      Der obige Code erstellt einen Node-Server, der auf Port 3000 auf Anfragen lauscht. Führen Sie die Anwendung mit dem folgenden Befehl aus:

      Wenn sie erfolgreich ausgeführt wird, wird eine Nachricht im Terminal protokolliert:

      Output

      • server is listening on 3000

      Sie können nun http://localhost:3000 in Ihrem Browser besuchen, und Sie sollten die Nachricht sehen:

      Output

      • The sedulous hyena ate the antelope!

      Browserfenster mit der Nachricht: The sedulous hyena ate the antelope! (Die fleißige Hyäne fraß die Antilope!)

      Öffnen Sie die Datei dist/app.js und Sie finden die transpilierte Version des TypeScript-Codes:

      dist/app.js

      "use strict";
      
      var __importDefault = (this && this.__importDefault) || function (mod) {
          return (mod && mod.__esModule) ? mod : { "default": mod };
      };
      Object.defineProperty(exports, "__esModule", { value: true });
      const express_1 = __importDefault(require("express"));
      const app = express_1.default();
      const port = 3000;
      app.get('/', (req, res) => {
          res.send('The sedulous hyena ate the antelope!');
      });
      app.listen(port, err => {
          if (err) {
              return console.error(err);
          }
          return console.log(`server is listening on ${port}`);
      });
      
      //# sourceMappingURL=app.js.map
      

      An diesem Punkt haben Sie Ihr Node-Projekt erfolgreich für die Verwendung von TypeScript eingerichtet.

      Zusammenfassung

      In diesem Tutorial haben Sie erfahren, warum TypeScript nützlich ist, um zuverlässigen JavaScript-Code zu schreiben. Sie haben auch einige Vorteile der Arbeit mit TypeScript kennengelernt.

      Abschließend haben Sie ein Node-Projekt mit dem Express-Framework eingerichtet, das Projekt jedoch mit TypeScript kompiliert und ausgeführt.



      Source link

      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