One place for hosting & domains

      Implementieren eines sanften Bildlaufs in React


      Einführung

      Sanfter Bildlauf bedeutet, dass der Benutzer nicht auf eine Schaltfläche klickt und sofort zu einem anderen Teil der (selben) Seite geführt wird, sondern über eine Bildlaufnavigation dorthin navigiert wird. Es ist eine dieser subtilen Funktionen der Benutzeroberfläche auf einer Seite, die einen ästhetischen Unterschied ausmacht.

      In diesem Artikel verwenden Sie das Paket react-scroll auf npm zur Implementierung des sanften Bildlaufs.

      Voraussetzungen

      Zur Absolvierung dieses Tutorials benötigen Sie Folgendes:

      Dieses Tutorial wurde mit Node v13.14.0, npm v6.14.5, react v16.13.1 und react-scroll v.1.7.16 verifiziert.

      In diesem Tutorial erstellen Sie eine einfache Anwendung. Wenn Sie aber einen schnellen Überblick über die Funktionsweise von react-scroll erhalten möchten, können Sie diese komprimierten Schritte nachschlagen:

      Installieren Sie react-scroll:

      npm i -S react-scroll
      

      Importieren Sie das Paket react-scroll:

      import { Link, animateScroll as scroll } from "react-scroll";
      

      Fügen Sie die Link-Komponente hinzu. Die Komponente <Link />verweist auf einen bestimmten Bereich Ihrer Anwendung:

      <Link to="section1">
      

      Sehen wir uns dies genauer an und erstellen eine kleine React-Anwendung mit sanftem Bildlauf.

      Schritt 1 — Installieren und Ausführen einer React-Anwendung

      Der Einfachheit halber verwendet dieses Tutorial ein React-Startprojekt (unter Verwendung von Create React App 2.0), das eine Navigationsleiste (oder navbar) am oberen Rand sowie fünf verschiedene Inhaltsbereiche aufweist.

      Die Links in der Navigationsleiste sind an dieser Stelle nur Anker-Tags. Sie werden Sie jedoch in Kürze aktualisieren, um einen sanften Bildlauf zu ermöglichen.

      Sie finden das Projekt unter React With Smooth Scrolling. Bitte beachten Sie, dass dieser Link für den Start-Zweig ist. Der Haupt-Zweig enthält alle abgeschlossenen Änderungen.

      GitHub Repo-Screenshot

      Um das Projekt zu klonen, können Sie den folgenden Befehl verwenden:

      git clone https://github.com/do-community/React-With-Smooth-Scrolling.git
      

      Wenn Sie sich das Verzeichnis src/Components ansehen, finden Sie eine Datei Navbar.js, die die <Navbar> mit nav-items enthält, die fünf verschiedene <Section>s entsprechen.

      src/Components/Navbar.js

      import React, { Component } from "react";
      import logo from "../logo.svg";
      
      export default class Navbar extends Component {
        render() {
          return (
            <nav className="nav" id="navbar">
              <div className="nav-content">
                <img
                  src={logo}
                  className="nav-logo"
                  alt="Logo."
                  onClick={this.scrollToTop}
                />
                <ul className="nav-items">
                  <li className="nav-item">Section 1</li>
                  <li className="nav-item">Section 2</li>
                  <li className="nav-item">Section 3</li>
                  <li className="nav-item">Section 4</li>
                  <li className="nav-item">Section 5</li>
                </ul>
              </div>
            </nav>
          );
        }
      }
      

      Wenn Sie dann die Datei App.js im Verzeichnis src öffnen, sehen Sie, wo die <Navbar> zusammen mit den fünf tatsächlichen <Section>s enthalten ist.

      src/Components/App.js

      import React, { Component } from "react";
      import logo from "./logo.svg";
      import "./App.css";
      import Navbar from "./Components/Navbar";
      import Section from "./Components/Section";
      import dummyText from "./DummyText";
      class App extends Component {
        render() {
          return (
            <div className="App">
              <Navbar />
              <Section
                title="Section 1"
                subtitle={dummyText}
                dark={true}
                id="section1"
              />
              <Section
                title="Section 2"
                subtitle={dummyText}
                dark={false}
                id="section2"
              />
              <Section
                title="Section 3"
                subtitle={dummyText}
                dark={true}
                id="section3"
              />
              <Section
                title="Section 4"
                subtitle={dummyText}
                dark={false}
                id="section4"
              />
              <Section
                title="Section 5"
                subtitle={dummyText}
                dark={true}
                id="section5"
              />
            </div>
          );
        }
      }
      
      export default App;
      

      Jede Komponente <Section> nimmt einen title und einen subtitle auf.

      Da in diesem Projekt in den verschiedenen Abschnitten Dummytext verwendet wird, wurde dieser Text zur Reduzierung von sich wiederholendem Code einer Datei DummyText.js hinzugefügt, importiert und an jede Komponente <Section> übergeben.

      Um die Anwendung auszuführen, können Sie die folgenden Befehle verwenden.

      • cd React-With-Smooth-Scrolling
      • npm install
      • npm start

      Dadurch wird die Anwendung im Entwicklungsmodus gestartet und automatisch aktualisiert, wenn Sie eine Ihrer Dateien speichern. Sie können sie im Browser unter localhost:3000 anzeigen.

      Screenshot der Anwendung im Browser

      Jetzt ist es an der Zeit, das Paket react-scroll zu installieren und diese Funktionalität hinzuzufügen. Informationen zu dem Paket finden Sie auf npm.

      Das Paket react-scroll auf npm

      Um das Paket zu installieren, führen Sie den folgenden Befehl aus:

      Öffnen Sie als Nächstes die Sicherungskopie der Datei Navbar.js und fügen Sie einen import für zwei benannte Importe, Link und animateScroll hinzu.

      src/Components/Navbar.js

      import { Link, animateScroll as scroll } from "react-scroll";
      

      Beachten Sie, dass ich animatedScroll zur einfacheren Verwendung zu scroll aliasiert habe.

      Nachdem alle Ihre Importe definiert sind, können Sie nun Ihre nav-items aktualisieren, um die Komponente <Link> zu verwenden. Diese Komponente nimmt mehrere Eigenschaften an. Sie können auf der Dokumentationsseite über alle diese Eigenschaften nachlesen.

      Achten Sie vorerst besonders auf activeClass, to, spy, smooth, offset und duration.

      • activeClass – die Klasse, die bei Erreichen des Elements angewendet wird.
      • to – das Ziel, zu dem geblättert werden soll.
      • spy – um Link auszuwählen, wenn sich scoll an der Position des Ziels befindet.
      • smooth – um den Bildlauf zu animieren.
      • offset – um zusätzliche px zu scrollen (wie Padding).
      • duration – die Zeit der Bildlaufanimation. Dies kann eine Zahl oder eine Funktion sein.

      Die Eigenschaft to ist der wichtigste Teil, da sie der Komponente mitteilt, zu welchem Element gescrollt werden soll. In diesem Fall ist dies jede Ihrer <Section>s.

      Mit der Eigenschaft offset können Sie eine zusätzliche Anzahl von Bildläufen definieren, um zu jeder <Section> zu gelangen.

      Hier ist ein Beispiel für die Eigenschaften, die Sie für jede Komponente <Link> verwenden werden. Der einzige Unterschied zwischen ihnen ist die Eigenschaft to, da sie jeweils auf eine andere <Section> verweist.

      <Link
          activeClass="active"
          to="section1"
          spy={true}
          smooth={true}
          offset={-70}
          duration={500}
      >
      

      Sie müssen jedes der nav-items entsprechend aktualisieren. Wenn Sie diese hinzugefügt haben, sollten Sie in der Lage sein, zu Ihrem Browser zurückzukehren (Ihre Anwendung sollte bereits automatisch neu gestartet worden sein) und einen sanften Bildlauf zu sehen.

      Die Eigenschaft activeClass ermöglicht es Ihnen, eine Klasse zu definieren, die auf die Komponente <Link> angewendet wird, wenn ihr Element to aktiv ist. Ein <Link> wird als aktiv betrachtet, wenn sein Element to in der Nähe des oberen Seitenrandes sichtbar ist. Dies kann durch Klicken auf den <Link> selbst oder durch manuelles herunterscrollen zu <Section> ausgelöst werden.

      Um dies zu beweisen, öffnete ich die Chrome DevTools und inspizierte den fünften <Link>, wie nachfolgend gezeigt. Als ich auf diesen <Link> klickte oder manuell bis um Ende der Seite scrollte, bemerkte ich, dass die aktive Klasse tatsächlich angewendet wird.

      Browseransicht der React-Anwendung

      Um dies auszunutzen, können Sie eine aktive Klasse erstellen und dem Link eine Unterstreichung hinzufügen. Sie können dieses Stück CSS zu der Datei App.css im Verzeichnis src hinzufügen:

      src/App.css

      .nav-item > .active {
          border-bottom: 1px solid #333;
      }
      

      Wenn Sie nun zu Ihrem Browser zurückkehren und um ein wenig herumscrollen, sollten Sie sehen, dass der entsprechende <Link> unterstrichen ist.

      Aktualisierte Browseransicht der React-Anwendung

      Schritt 4 — Hinzufügen zusätzlicher Funktionen

      Für einen letzten Teil des Inhalts bietet dieses Paket auch einige Funktionen, die direkt aufgerufen werden können, wie scrollToTop, scrollToBottom, usw., sowie verschiedene Ereignisse, die Sie handhaben können.

      In Bezug auf diese Funktionen führt das Anwendungslogo in einer Navigationsleiste den Benutzer zu der Startseite oder zum Anfang der aktuellen Seite.

      Als einfaches Beispiel dafür, wie eine dieser bereitgestellten Funktionen aufgerufen wird, habe ich dem nav-logo einen Click-Handler hinzugefügt, den den Benutzer wie folgt zu zum Anfang der Seite scrollt:

      src/Components/Navbar.js

      scrollToTop = () => {
          scroll.scrollToTop();
      };
      

      Zurück im Browser sollten Sie in der Lage sein, auf der Seite nach unten zu scrollen, auf das Logo in der Navigationsleiste zu klicken und wieder an den Anfang der Seite zurückgeleitet zu werden.

      Zusammenfassung

      Der sanfte Bildlauf ist eine dieser Funktionen, die Ihrer Anwendung einen hohen ästhetischen Wert verleihen können. Das Paket react-scroll ermöglicht Ihnen, diese Funktion ohne nennenswerten Overhead zu nutzen.

      In diesem Tutorial haben Sie einer Anwendung einen sanften Bildlauf hinzugefügt und mit verschiedenen Einstellungen experimentiert. Wenn Sie neugierig geworden sind, verbringen Sie etwas Zeit damit, die anderen Funktionen und Ereignisse zu erkunden, die dieses Paket zu bieten hat.



      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