Docker Swarm Cluster auf Openmediavault

Als Grundlage für mein Homelab habe ich ein Proxmox Cluster installiert, der Hochverfügbar eingerichtet ist. Dazu sind drei Cluster-Nodes installiert, die einen gemeinsamen Speicher über Ceph für die Virtuellen Maschinen (VMs) haben. Damit können VMs im Falle eines Neustarts zwischen den Proxmox Nodes ohne Verzögerung hin und her wandern.

Da ich Docker Container nutzen möchte, benötige ich zumindest eine VM. Wird diese VM jedoch im Rahmen von regelmäßigen Updates neugestartet, sind alle Docker Container im Rahmen dieses Updates nicht erreichbar. Ein Docker Swarm Cluster kann hierfür eine Lösung sein. Weiterhin ist das Loadbalancing auf die drei physikalischen Knoten mit nur einer Docker VM nicht möglich. Auch dafür kann der Docker Swarm Cluster eine Lösung darstellen.

Ich schreibe diese Anleitung in erster Linie für mich als „Installationsdokumentation“, damit ich bei eventuellen späteren Neuinstallationen eine Gedächtnisstütze habe. Es kann deswegen gut sein, dass manche Teile durch Updates in Zukunft nicht mehr korrekt sind.

Meine Quellen waren für diese Installation im Wesentlichen:

Openmediavault Installation

Openmediavault basiert auf Debian, hat aber zusätzlich noch eine schöne Weboberfläche, über die die Administration vereinfacht wird. Insbesondere wenn bestehende persistente Docker Verzeichnisse verschoben werden sollen, ist es einfach, einen SMB/CIFS Share zu erstellen und dort einfach alles drauf zu kopieren. Zusätzlich ist es einfacher neue Festplatten einzuhängen oder bestehende zu vergrößern.

Für die VM habe ich folgende Einstellungen gewählt:

  • 4 Cores, 1 Socket
  • 16G RAM
  • OS-Festplatte: 30G
  • Zusätzliche Festplatte für microceph: 100G
  • Eine Netzwerkkarte

Die Installation geht recht einfach vonstatten, nachdem die VM mit der Openmediavault ISO gebootet ist:

  • Sprachen-Einstellungen: Deutsch, Deutschland, Deutsch
  • Rechnername: docker-swarm-01 (und natürlich 02 und 03 für die anderen zwei Nodes)
  • Domainnamen vergeben
  • Root Passwort vergeben
  • Spiegelserver in der Nähe auswählen
  • Gerät für Bootloader-Installation auswählen
  • Reboot & CD auswerfen

Jetzt kann man sich im Webinterface auf dem Server anmelden mit den Default Login-Daten. User: admin – Password: openmediavault. Das wird als erstes geändert im Menü. Rechts oben auf das User-Icon klicken und „Passwort ändern“ auswählen. Neues Passwort eintippen und fertig.

Updates werden einfach übers Webinterface gestartet:

System -> Aktualisierungsverwaltung -> Aktualisierungen -> Pfeil runter mit der „Nummer“ klicken -> Bestätigen. Warten. Konfigurationsänderungen bestätigen. Reboot.

Jetzt noch eine statische IP vergeben für das System:

Netzwerk -> Schnittstellen -> Interface bearbeiten:

  • IPv4
  • Methode: Statisch
  • Adresse: Freie Adresse im Heimnetzwerk, beispielsweise 10.0.1.11 und für die anderen zwei Nodes hinten die 12 und die 13
  • Netzmaske: 255.255.255.0
  • Gateway: Sollte bekannt sein, üblicherweise der Router. Beispielsweise: 10.0.1.1
  • DNS Server: Sollte bekannt sein, üblicherweise ebenfalls der Router. Beispielsweise 10.0.1.1
  • Ausstehende Konfigurationsänderungen anwenden
  • Es scheint einzufrieren, einfach die neu konfigurierte IP im Browser aufrufen. Die IP wurde schon umgestellt.

Installation von Docker und Docker Compose

Zuerst installiere ich kleinere Helferlein:

apt install qemu-guest-agent htop snap snapd -y

Nun geht es nach der offiziellen Docker Dokumentation weiter:

# Add Docker's official GPG key:
apt update
apt install ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update

Den Erfolg des Befehls kann man sich mit dem nachfolgenden Befehl anschauen. Hier sollte der Eintrag zum Repository von Docker zu sehen sein:

nano /etc/apt/sources.list.d/docker.list

Dann wird Docker installiert:

apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Den Erfolg der Installation von Docker kann man auch wieder überprüfen. Hierzu wird einfach ein „hello-world“ image heruntergeladen und gestartet. Als Ergebnis gibt es eine Bestätigungsmeldung und der Container beendet sich wieder.

docker run hello-world

Wiederholen für Node 2 und Node 3

Den bisherigen Teil der Anleitung für die zwei weiteren Nodes durchführen, damit alle auf dem gleichen Zustand sind. Danach kann man mit Swarm loslegen.

Docker Swarm installation

Auf Node 1 starten wir die Initialisierung vom Swarm cluster mit folgendem Befehl:

docker swarm init --advertise-addr 10.0.1.10

Als Ergebnis wird der Join-Command angezeigt mit dem dafür notwendigen Token. Den Join-Command kopiert man und tippt ihn in die Node 2 und 3 ein.

docker swarm join --token <<<YOURTOKEN>>> 10.0.1.11:2377

Damit werden die zwei hinzugefügten Nodes „worker“ im Swarm. Das wird nun dem Node 1 bekannt gemacht, indem man im Node 1 eintippt:

docker node promote docker-swarm-02
docker node promote docker-swarm-03
docker node ls

Der letzte Befel listet alle aktuellen Hosts auf.

Virtuelle IP-Adressen

Damit die Docker Container nach außen mit einer IP Adresse sprechen können und zwar unabhängig vom jeweiligen Docker Host auf dem sie laufen, wird nun eine Virtuelle IP Adresse erstellt. Dazu gibt es da Paket keepalived, das installiert wird:

apt install keepalived -y

Auf Node 1 wird keepalived konfiguriert:

nano /etc/keepalived/keepalived.conf

Und folgender Inhalt eingefügt (Achtung, der Interface Name kann abweichen!). Dabei werden unter „unicast_peer“ die IP-Adressen der anderen Hosts eingetragen, unter „virtual_ipadress“ die zusätzliche virtuelle IP. Das auth_pass ändern:

vrrp_instance VI_1 {
  state MASTER
  interface ens192
  virtual_router_id 51
  priority 120
  advert_int 1
  authentication {
    auth_type PASS
    auth_pass 123456789Password
  }
  unicast_peer {
    10.0.1.12
    10.0.1.13
  }
  virtual_ipaddress {
    10.0.1.15
  }
}

Für die Nodes 2 und 3 wird ebenfalls die Konfigurationsdatei erstellt:

nano /etc/keepalived/keepalived.conf

Und ein ähnlicher Inhalt eingetragen. Geädert ist „state“ – hier nur als BACKUP. Die Priority muss niedriger sein als die von Node 1. Die unicast peers sind jeweils die zwei anderen Nodes, also nicht die Node, auf der gerade die Datei angelegt wird. Bswp. für Node 2:

vrrp_instance VI_1 {
  state BACKUP
  interface ens192
  virtual_router_id 51
  priority 110
  advert_int 1
  authentication {
    auth_type PASS
    auth_pass 123456789Password
  }
  unicast_peer {
    10.0.1.11
    10.0.1.13
  }
  virtual_ipaddress {
    10.0.1.15
  }
}

Und für Node 3 beispielsweise (Priority nochmals niedriger als bei Node 2):

vrrp_instance VI_1 {
  state BACKUP
  interface ens192
  virtual_router_id 51
  priority 100
  advert_int 1
  authentication {
    auth_type PASS
    auth_pass 123456789Password
  }
  unicast_peer {
    10.0.1.11
    10.0.1.12
  }
  virtual_ipaddress {
    10.0.1.15
  }
}

Dann wird der Service keepalived auf allen drei nodes gestartet und für Neustarts aktiviert:

systemctl start keepalived
systemctl enable keepalived
systemctl status keepalived

Jetzt kann man die neue IP Adresse pingen und erhält eine Antwort.

ping 10.0.1.15

Gemeinsamer Speicher mit Ceph

Gemeinsamer Speicher kann hier recht einfach über Ceph – genauer microceph realisiert werden. Microceph ist deutlich einfacher zu installieren als Ceph selbst. Es wird auf allen drei Nodes installiert:

export PATH=$PATH:/snap/bin
snap install microceph
snap refresh --hold microceph

Mit dem hold Befehl wird die aktuelle Version gepinnt, sodass Updates nur dann ausgeführt werden, wenn das aktiv angestoßen wird. Auf Node 1 wird Ceph initialisiert und die Nodes 2 und 3 hinzugefügt:

microceph cluster bootstrap
microceph status
microceph cluster add docker-swarm-02
microceph cluster add docker-swarm-03

Auf Node 2 und Node 3 verwendet man die über den add erzeugten Token, um dem Cluster beizutreten:

microceph cluster join <<<YOURTOKEN>>>

Jetzt können Festplatten zum Cluster hinzugefügt werden. Über lsblk sieht man die noch nicht verwedendete Festplatte. Bei mir lautet der Name: /dev/sdb. Aber dieser Name kann je nach Node abweichend sein! Den folgenden Befehl (angepasst) auf allen drei Nodes ausführen.

microceph status
microceph disk add /dev/sdb --wipe

Als Ergebnis wird jeweils success angezeigt. Auf Node 1 geht es dann mit den eingebundenen Festplatten weiter:

microceph status
ceph osd pool create cephfs_data 64
ceph osd pool create cephfs_metadata 64
ceph fs new cephfs cephfs_metadata cephfs_data
ceph fs ls

Jetzt wird auf jedem Node ein Verzeichnis angelegt, in das das Ceph Dateisystem hineingemountet werden kann.

mkdir /mnt/cephfs

Als nächstes wird ein cefh admin token benötigt, mit dem der Mount durchgeführt werden kann. Dafür auf Node 1 ausführen:

ceph auth get-key client.admin

Mit diesem Token wird die fstab für alle drei Nodes angepasst:

nano /etc/fstab

und folgende Zeile am Ende eingefügt. Dabei wird hinter „secret=“ das Token eingesetzt und die IP-Adressen vorne entsprechen allen IPs der Nodes:

10.0.1.11:6789,10.0.1.12:6789,10.0.1.13:6789:/ /mnt/cephfs ceph name=admin,secret=<<<TOKEN>>>,_netdev 0 0

Mit “ mount -a “ wird das neu Laden der fstab erzwungen. Danach wird der Deamon neu geladen. Per df wird dann das neue Dateisystem angezeigt.

mount -a
systemctl daemon-reload
df -h

Damit ist in /mnt/cephfs ein persistenter Speicher hinterlegt.

Portainer installation

Bei Portainer gibt es die Möglichkeit für drei Nodes eine kostenlose Lizenz für die Enterprise Edition zu holen. Dazu einfach kurz googlen, anmelden und recht zügig erhält man die Lizenzschlüssel per Mail.

mkdir /mnt/cephfs/containers/container-portainer/data
cd /mnt/cephfs/containers/container-portainer
nano compose.yaml

Mit folgendem Inhalt:

version: '3.2'
services:
  agent:
    image: portainer/agent
    environment:
      # REQUIRED: Should be equal to the service name prefixed by "tasks." when
      # deployed inside an overlay network
      AGENT_CLUSTER_ADDR: tasks.agent
      # AGENT_PORT: 9001
      # LOG_LEVEL: debug
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /var/lib/docker/volumes:/var/lib/docker/volumes
    networks:
      - agent_network
    deploy:
      mode: global
      placement:
        constraints: [node.platform.os == linux]

  portainer:
    image: portainer/portainer
    command: -H tcp://tasks.agent:9001 --tlsskipverify
    ports:
      #- "9000:9000"
      - "9443:9443"
    volumes:
      - /mnt/cephfs/containers/container-portainer/data:/data
    networks:
      - agent_network
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]

networks:
  agent_network:
    driver: overlay
    attachable: true

Mit den folgenden Befehlen startet Portainer hoch und ist per https auf dem Port 9443 zu erreichen.

docker node ls
docker stack deploy --compose-file compose.yaml portainer --detach=false
docker service ls
docker stack services portainer

Beim ersten Start erstellt man für Portainer den Benutzer admin und vergibt selbst ein sicheres Passwort.

YAML als Stack deployen

    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure

Schreiben Sie einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahren Sie mehr darüber, wie Ihre Kommentardaten verarbeitet werden .