Cluster mit Raspberry Pi (1)

Modellbau

Verteilte Container-Umgebungen lassen sich leicht mit Hilfe virtueller Maschinen simulieren. Eine interessante Alternative dazu ist, einen Cluster mit Raspberry-Rechnern aufzubauen, um darauf Docker und Co. zu betreiben. Wir geben Tipps zum Aufbau und zur Installation.
Die IT-Infrastruktur bildet das technische Fundament jedes modernen Unternehmens. In der Dezember-Ausgabe widmet sich IT-Administrator deshalb der physischen ... (mehr)

Richtig interessant wird der Einsatz von Containern typischerweise erst, wenn mehrere Hosts zur Verfügung stehen, auf denen ein sogenannter Orchestrator Container starten, stoppen und migrieren kann. Dies ermöglicht es beispielsweise, Funktionen wie Loadbalancing und Hochverfügbarkeit von Diensten auf der Basis von Containern zu realisieren. Für Test-Setups lässt sich dieses Szenario mit virtuellen Maschinen aufbauen, die als Host-Systeme für die Container dienen. Eine preisgünstige Alternative besteht darin, Raspberry-Pi-Rechner zu verwenden, um einen entsprechenden realen Cluster aufzubauen. Diesen Ansatz stellen wir im Folgenden näher vor.

Da alle Orchestrierungslösungen wenigstens aus einem Master- und einem Worker-Knoten bestehen, brauchen Sie mindestens zwei Minirechner. Wer etwa den Ausfall eines Nodes oder die Migration von Containern von einem Node auf einen anderen simulieren möchte, muss drei oder mehr Raspberries in seinen Einkaufskorb legen.

Zur Vernetzung der Maschinen gibt es mehrere Optionen. So bietet der Raspberry Pi 3 neben der schon bei den älteren Modellen vorhandenen Ethernet-Buchse auch WLAN. Eine Alternative besteht darin, die Rechner über USB-over-Ethernet zu vernetzen (mit USB/OTG-Port, den nur der Raspberry Zero besitzt). Wir haben uns für die einfachste und üblicherweise am wenigsten fehlerträchtige Methode entschieden, die Boards über Ethernet und einen Mini-Switch zu vernetzen. So funktioniert beispielsweise der virtuelle Netzwerk-Interface-Typ "macvlan" des Linux-Kernels nicht mit Wireless Devices.

Je nach Netzwerktopologie kann der Switch zur Verbindung der Rechner schon genügen. Wir verwenden zusätzlich einen günstigen WLAN-Router/Switch, der den Uplink des Clusters ins Internet bereitstellt und es darüber hinaus ermöglicht, sich über ein WLAN mit dem Cluster-Netzwerk zu verbinden, zum Beispiel von einem Laptop aus. Solche Geräte sind im Handel schon ab etwa 20 Euro zu haben. Die Stromversorgung übernimmt ein passender USB-Hub mit sechs Ports (Bild 1).

Da die Raspberry-Rechner keine SATA-Schnittstelle besitzen, bleiben als Massenspeicher nur die typischen Mini-SD-Karten. Wir haben hier 32 GByte große SD-Karten von Samsung verwendet, die eine vergleichsweise gute Performance bieten. Kleinere Karten mit acht oder 16 GByte genügen in der Regel auch, aber letztlich ist der preisliche Unterschied nicht so groß, dass sich hier das Sparen lohnt. Alles in allem belaufen sich die Kosten für unseren Cluster damit auf etwa 280 Euro.

Bild 1: Der Raspberry-Cluster mit USB-Hub und WLAN-Router.

Installation des Basissystems

Wer im Internet nach Anleitungen für die Installation von Docker auf Raspberry sucht, stößt schnell auf die "Docker Pirates ARMed with explosive stuff", die dazu eine eigene Linux-Distribution namens Hypriot verwenden. Seit geraumer Zeit gibt es aber auch Docker-Support für die offizielle Raspberry-Debian-Distribution namens Raspbian [1], die neuer ist als Hypriot, und die wir deshalb bevorzugen.

Wie beim Raspberry üblich, wird das Betriebssystem von einem PC oder Mac aus installiert, indem man das heruntergeladene Image von dort auf die Micro-SD-Karte schreibt. Dies kann entweder auf der Commandline mit dem Tool "dd" passieren oder mit einem grafischen Frontend wie Etcher [2], das für Windows, Linux und macOS zur Verfügung steht. Für unsere Zwecke genügt das Image von Raspbian Stretch Lite, das keine grafische Oberfläche mitbringt.

Da wir außerdem möglichst darauf verzichten wollen, jeden Node unseres Clusters an einen Bildschirm anzuschließen und mit Keyboard und Maus auszustatten, um sie manuell einzurichten, legen wir die Netzwerkeinstellungen vor der Installation fest. Dazu ist es zunächst nötig, die Zip-Datei des Raspbian-Images zu entpacken. Anschließend mounten wir die darauf enthaltenen Partitionen als Loopback-Devices auf einem Linux-System. Die Loop-Devices für die Partitionen legt das Tool "losetup" mit dem entsprechenden Parameter automatisch an:

# losetup -P /dev/loop0 2017-09-07-raspbian-stretch-lite.img

In dem Image sind zwei Partitionen vorhanden: eine Boot-Partition und die Root-Partition des Debian-Systems. Die Boot-Partition mounten Sie mit dem folgenden Befehl:

# mount /dev/loop0p1 /mnt/

Nun finden Sie die Dateien der Boot-Partition unter "/mnt". Aus Sicherheitsgründen ist der Secure-Shell-Daemon per Default deaktiviert. Automatisch gestartet wird er, wenn auf der Boot-Partition eine Datei namens "ssh" mit beliebigem Inhalt existiert. Der folgende Befehl legt sie an und bindet die Boot-Partition wieder aus:

# touch /mnt/ssh
# umount /mnt

Nun fehlt noch die Netzwerkkonfiguration der Raspberry-Rechner. Dabei verwendet Raspbian nicht die von Debian gewohnten Konfigurationsdateien "/etc/network/interfaces", sondern startet automatisch den DHCP-Daemon, in dessen Konfiguration die statische IP-Adresse eingetragen werden muss. Wer einen Router mit DHCP betreibt und auf diesem die MAC-Adressen der Pi-Rechner einträgt, kann auf diesen Schritt gegebenenfalls verzichten.

Wir verwenden jedenfalls statische IP-Adressen und mounten dazu erst einmal die Root-Partition des Raspbian-Images auf dem Linux-Rechner, bevor wir die Konfigurationsdatei des DHCP-Servers editieren:

# mount /dev/loop0p2 /mnt/
# vi /mnt/etc/dhcpcd.conf

Suchen Sie in der Konfiguration nach dem Abschnitt für die statische IP-Adresse und setzen Sie dort etwa die folgenden Zeilen ein:

interface eth0
static ip_address=10.0.0.1/24
static routers=10.0.0.254
static domain_name_servers=8.8.8.8

Jetzt binden Sie die Partition wieder aus und entfernen die Loop-Devices, bevor Sie das geänderte Image auf die SD-Karte schreiben:

# umount /mnt
# losetup -d /dev/loop0
# sudo dd if=2017-09-07-raspbian-stretch-lite.img of=/dev/mmcblk0 bs=4M status=progress

Wenn Sie nun den Raspberry mit der Karte booten und an den Switch angeschlossen haben, sollten Sie sich über die Adresse 10.0.0.1 per SSH einloggen können (Login "pi", Passwort "raspberry"). Wiederholen Sie nun den zweiten Schritt (mit der Root-Partition) für die restlichen Raspberry-Rechner und passen Sie jeweils die IP-Adresse an. In unserem Fall sind anschließend alle Nodes unter den Adressen 10.0.0.1-4 erreichbar.

Konfiguration mit Ansible

Statt jeden Rechner einzeln als Docker-Host einzurichten, verwenden wir im Weiteren das Konfigurationsmanagement-Tool Ansible, da bis zu einem bestimmten Punkt das Setup der Nodes identisch ist. Als Erstes legen wir eine "Inventory"-Datei an, die die von Ansible gemanagten Nodes enthält, etwa unter dem Namen "hosts", mit folgendem Inhalt:

[cluster]
10.0.0.1
10.0.0.2
10.0.0.3
10.0.0.4

Jetzt können Sie die grundlegende Funktion von Ansible testen:

$ ansible -i hosts cluster -m ping -u pi -k

Hinter den Kulissen loggt sich Ansible dabei als User "pi" auf dem Node ein, wozu es beim Aufruf das Passwort abfragt ("-k"). Funktioniert alles, bekommen Sie für jeden Host eine Meldung ähnlich der folgenden zu sehen:

10.0.0.2 | SUCCESS => {
      "changed": false,
      "ping": "pong"
}

Um die Verwendung von Ansible und die Installation von Software zu vereinfachen, legen wir einen Benutzer mit SSH-Key an, der sich ohne Passwort einloggen darf. Außerdem wird er der Gruppe "docker" hinzugefügt, damit er später Docker ohne Root-Rechte bedienen darf. Diese Änderungen nehmen wir über das Playbook in Listing 1 vor. Um das Passwort direkt im Playbook angeben zu können (nicht unbedingt eine Security-Best-Practice), wird der Filter "password_hash" verwendet, der die Installation des Python-Moduls "passlib" voraussetzt (»sudo -H pip install passlib« ). Abgespielt wird das Playbook mit dem Kommando "ansible-playbook", das über den Schalter "-b" (become) per sudo Root-Rechte erlangt. Ein Passwort ist für sudo nicht nötig, da die Default-Konfiguration von Raspbian dafür kein Passwort verlangt.

$ ansible-playbook -i hosts -bk -u pi setup-users.yml

Listing 1: setup-users.yml



---
- hosts: cluster
   remote_user: pi
   become: yes
   become_method: sudo
   tasks:
   - name: create new user
     user:
         name: oliver
         shell: /bin/bash
         groups: docker,sudo
         update_password: always
         password: "{{ 'pi' | password_hash('sha512') }}"
     - name: add key to authorized_keys
       authorized_key:
         user: oliver
         state: present
         key: "{{ lookup('file', '/home/oliver/.ssh/id_rsa.pub') }}"

Wer einen Cluster betreibt, muss sicherstellen, dass die Nodes zeitlich synchronisiert sind. Dazu verwenden wir NTP, das wir per Ansible und mit einer Community-Rolle konfigurieren, die Ansible Galaxy im lokalen Verzeichnis installiert:

$ ansible-galaxy install -p roles geerlingguy.ntp

Das entsprechende Playbook, das auch die Timezone auf "Europe/Berlin" setzt, ist in Listing 2 zu sehen.

Listing 2: setup-time-ntp.yml



---
- hosts: cluster
   remote_user: oliver
   become: true
   tasks:
      - name: set timezone
        timezone
           name: Europe/Berlin
   roles
      - geerlingguy.ntp

Viele Softwarepakete setzen außerdem eine funktionierende Zuordnung von Hostnamen zu IP-Adressen voraus. Da wir aber keinen ausgewachsenen Nameserver betreiben wollen, pflegen wir per Ansible ein Hosts-File, das wir auf alle beteiligten Knoten verteilen.

Listing 3: hosts



[cluster]
10.0.0.1 hostname=pi1
10.0.0.2 hostname=pi2
10.0.0.3 hostname=pi3
10.0.0.4 hostname=pi4

Auch dies lässt sich per Ansible einigermaßen einfach umsetzen. Wir erweitern dazu das Inventory-File um eine Variable, in der der Hostname steht, den der Node bekommen soll (Listing 3). Eine Schleife im Ansible-Playbook stellt sicher, dass alle am Cluster beteiligten Knoten auch im Hosts-File landen. Nützlich ist darüber hinaus die Playbook-Anweisung "linein", die sicherstellt, dass die entsprechende Zeile in einer Datei vorhanden ist, und nur dann eine Änderung vornimmt, wenn sie fehlt. Das Playbook in Listing 4 spielen sie so ab:

$ ansible-playbook -i hosts -bK set-hostnames.yml

Wenn Sie sich jetzt auf einem Node einloggen, sollten Sie in der Datei "/etc/hosts" alle beteiligten Rechner sehen und von jedem Knoten per Hostnamen alle anderen erreichen können.

comments powered by Disqus

Artikel der Woche

Rechneranalyse mit Microsoft-Sysinternals-Tools

Der Rechner verhält sich eigenartig oder Sie haben eine unbekannte Applikation im Task Manager entdeckt und möchten erfahren, worum es sich dabei genau handelt und ob sie möglicherweise gefährlich ist? In so einem Fall helfen die Sysinternals-Tools von Microsoft. Dieser Beitrag stellt die drei Werkzeuge Autoruns, Process Explorer und TCPView vor. (mehr)
Einmal pro Woche aktuelle News, kostenlose Artikel und nützliche ADMIN-Tipps.
Ich habe die Datenschutzerklärung gelesen und bin einverstanden.

Konfigurationsmanagement

Ich konfiguriere meine Server

  • von Hand
  • mit eigenen Skripts
  • mit Puppet
  • mit Ansible
  • mit Saltstack
  • mit Chef
  • mit CFengine
  • mit dem Nix-System
  • mit Containern
  • mit anderer Konfigurationsmanagement-Software

Google+

Ausgabe /2018