Docker-Volumes im Cluster: Von Hetzner zu DigitalOcean (Erfahrungsbericht)

Wie in vorherigen Artikeln bereits erwähnt, arbeite ich derzeit privat an einer Applikation, welche aus diversen Microservices besteht. In diesem Beitrag möchte ich etwas näher darauf eingehen, wie diese Applikation (bisher) technisch aufgebaut ist und welche Infrastruktur ich hierfür nutze.

Die Applikation – im Prinzip eine Suchmaschine – besteht aus mehreren Microservices: Crawler, Index, Webgui für die Crawler-Administration. Als Crawler kommt nutch (http://nutch.apache.org/) zum Einsatz, als Index Lucene/Solr (http://lucene.apache.org/solr/) und für die Webgui arbeite ich mit Vaadin 10 (https://vaadin.com/). Vaadin ist übrigens ein ganz grandioses java-basiertes Webframework, welchem ich definitiv auch noch den einen oder anderen Artikel widmen werde. Ich habe schon viele Applikationen/GUIs mit Vaadin entwickelt und es macht einfach unglaublich viel Spaß. Die neue Version 10 gefällt mir wirklich sehr gut.

Zurück zur Infrastruktur:

Die bisher vorhandenen Services betreibe ich derzeit auf einem Single Host System. Hierfür habe ich mir eine Cloud-Maschine bei Hetzner (https://www.hetzner.de/) gemietet. Diese sind wirklich sehr günstig und funktionieren auch einwandfrei. Der Support von Hetzner gefällt mir ebenfalls ausgesprochen gut.

Auf dieser Maschine starte ich meine Services (derzeit noch) via Docker Compose. Der Crawler hat ein Volume gemounted, wohin er seine Crawling-Ergebnisse schreibt. Der Index hat ein Volume gemounted, um dort beim Herunterfahren des Services einen Snapshot des Indexes zu speichern um diesen beim Hochfahren wieder einzulesen). Ansonsten kommuniziert der Crawler direkt mit dem Index. Die Administrations-GUI für den Crawler kommuniziert mit einer API, welche ebenfalls im Container-Service läuft.

Ich habe also bisher: drei Microservices, zwei Volumes, keine Datenbank-Anbindung.

Ich habe auf der Hetzner-Maschine einen apache eingerichtet welcher, je nach eingegebener (sub-)Domain eben auf die Webgui für den Crawler, die Webgui für den Index oder die (noch nicht vorhandene) Webgui für die Suchmaschine an sich weiterleitet.

Mein Plan war eigentlich, als nächstes weitere Maschinen bei Hetzner anzumieten um dort ein Kubernetes-Cluster aufzubauen, in welchem meine Services repliziert laufen. Hierzu hatte ich mir beispielsweise schonmal das Tool Kompose angesehen, mit welchem es möglich ist, den Inhalt einer Docker-Compose-Datei in entsprechende Kubernetes-Konfigurations-Files zu übersetzen. Ich hatte mir auch schon eine zweite Maschine angemietet und tatsächlich einen kleinen Cluster aufgebaut – inkl. rennendem Hello-World-Service.

Nachdem ich nun am DevOps Docker Camp teilgenommen habe und dort erstmalig die Verwendung von Docker Swarm Mode live miterlebt habe, habe ich mich nun dazu entschlossen, als Orchestrierungs-Tool doch erstmal Swarm Mode einzusetzen und meinen bisherigen Stack darauf ans laufen zu bekommen.

Eines der ersten Probleme, die ich in Angriff nehmen wollte: Volumes im Cluster.

Hierzu wollte ich zuerst meinen Crawler-Service dahingehend anpassen, dass dieser im Cluster laufen kann. Problem: Ich arbeite derzeit mit einem Bind Mount (was sowieso eine schlechte Idee ist). Nicht nur wird hierdurch das Volume gelöscht, wenn der Container gelöscht wird, der Service bindet sich auch explizit an ein Verzeichnis innerhalb des Hosts. Das ist Mist. Einem Service im Cluster sollte egal sein auf welchem Rechner er gerade läuft / angesprochen wird. Er sollte nichts über die Verzeichnisstruktur der einzelnen Hosts wissen. Ich möchte dem Service sagen: Hier ist dein Volume, schreibe die Daten da rein. Was hinter diesem Volume steckt, soll der Container gar nicht wissen. Es soll egal sein, ob der Service auf Maschine A, B oder C läuft. Er soll einfach clusterweit immer das selbe Volume zur Verfügung gestellt bekommen, damit er dort seine Daten reinschreiben kann.

Hierfür bieten sich im Docker-Cosmos named Volumes an. Diese haben den Vorteil, containerunabhängig existieren zu können (man kann sie in beliebige Container mounten). Weiterer Vorteil: Mit Hilfe von Volume Plugins kann man einem Volume nicht mehr nur ein bestimmtes Verzeichnis zuordnen, sondern beispielsweise Verzeichnisse, die gar nicht direkt auf dem Host liegen (NFS, …).

Man muss sich das klar machen: Wenn ich für einen Service ein Bindmount-Volume (mit beispielsweise dem Host-Verzeichnis /var/meinvolume und dem Containerverzeichnis /volume) verwende, sage ich dem Service: Lege deine Daten im Container im Verzeichnis /volume ab, sodass diese dann auf dem Host-Rechner (auf dem der Container läuft) im Verzeichnis /var/meinvolume abgelegt werden. Ein Container im Cluster sollte aber gar nicht wissen und es sollte ihn nicht interessieren auf welchem Rechner er gerade läuft. Selbst wenn mein Crawler zeitweise auf Rechner A, zeitweise auf Rechner B und zeitweise auf Rechner C läuft, so möchte ich doch die gecrawlten Daten nicht verteilt auf Rechner A, Rechner B und Rechner C haben. Ich möchte EINEN Ort haben, wo DIE gecrawlten Daten zu finden sind.

Nun habe ich überlegt, wie ich das am besten realisieren kann. Ich muss dazu sagen, ich habe hier keinerlei Vorerfahrung gehabt und da das Thema Docker Plugins / Volume Plugins im DevOps Docker Camp leider nicht bearbeitet wurde, musste ich mich erstmal anderweitig informieren, was ich tun kann. Ich bin hierbei durch ein ziemliches Tal der Schmerzen gegangen – so viel steht fest. Vielleicht kann ich das dem ein oder anderen ersparen indem ich meinen (Irr)-Weg hier festhalte:

Ich wollte also die Crawler-Daten nicht verteilt auf meinen Cluster-Maschinen wissen. Ich wollte eine zentrale Stelle definieren, wo die Daten abgelegt werden sollen. Und ich wollte ein Volume erstellen, welches ich dem Container zur Verfügung stelle, sodass die Daten (egal auf welchem Rechner der Service läuft) immer an der gleichen Stelle abgelegt werden.

Hierbei war meine erste Idee: Beim Anlegen der Hetzner-Maschine hatte ich ausgewählt, dass der Speicherplatz als CEPH angelegt werden soll. Ich wusste, dass es Docker Volume Plugins für CEPH gibt und wollte entsprechend diesen CEPH-Speicher nutzen. Das habe ich schnell aufgegeben, da ich mich mit CEPH gar nicht auskenne, und ich nichtmal herausgefunden habe, wie ich an die Zugangsdaten hierfür komme.

Meine nächste Idee war: Ich nehme ein Amazon S3-Bucket (https://aws.amazon.com/de/s3) und erstelle mit Hilfe eines passenden Volume Plugins ein Volume, welches die Daten dann eben genau in diesem S3-Bucket ablegt. Ich stellte schnell fest, dass S3 nicht das richtige für mich ist. Hierbei handelt es sich meiner Meinung nach eher um Cloud-Speicher oder meinetwegen eine SNAPSHOT/Backup-Ablage.

Ich bin dann auf Amazon EBS (https://aws.amazon.com/de/ebs) gestoßen. Dies schien mir schon eher zu meinem Use Case zu passen, doch: Ich habe kein entsprechendes Volume Plugin zum laufen gebracht. Rexray konnte ich nicht erfolgreich installieren und ich wollte auch nicht mit Docker for AWS arbeiten. Denn ich wollte ja bei Hetzner bleiben und lediglich für mein Volume auf eine Amazon Komponente zugreifen (und nicht etwa mit meinem Cluster zu Amazon EC2 wechseln).

Als nächstes wurde ich auf DigitalOcean aufmerksam. Hier kann man sich sowohl virtuelle Maschinen (Droplets) anlegen, als auch Volumes, welche man (optional) Droplets zuordnen kann. Ich sah, dass es für DigitalOcean Volumes ebenfalls Docker Volume Plugins gab. Also habe ich mir testhalber einen DigitalOcean-Account angelegt und dort ein Volume erstellt (Zufällig hatte ich neulich das Humble Software Bundle gekauft, in welchem unter anderem 50$ DigitalOcean Guthaben enthalten waren :D). Um das Docker Volume Plugin zu testen, habe ich mir testhalber auch eine virtuelle Maschine bei DigitalOcean hochgezogen. Nach Anlage habe ich dort Docker installiert – samt entsprechendem Volume Plugin (https://github.com/omallo/docker-volume-plugin-dostorage) und was soll ich sagen: Es funktionierte einfach! Die Plugin-Installation war schmerzfrei und ich konnte einen Container Starten, ihm das Named Volume zuordnen und Dateien reinschreiben, welche dann im Volume waren. Das hat mich wirklich begeistert.

Ich bin dann zurück zu meiner Hetzner-Maschine, habe dort das DigitalOcean-Volume-Plugin installiert und wollte dort dann auch ein Named Volume anlegen, welches auf das DigitalOcean-Volume verweist. Das hat nicht funktioniert und ohne mich in der Tiefe damit beschäftigt zu haben, so glaube ich, dass das Plugin tatsächlich nur dazu fähig ist, auf Droplets von DigitalOcean korrekt zu funktionieren.

Ich habe mich dann erkundigt, ob es bei Hetzner auch eine Möglichkeit gibt „Volumes“ wie bei DigitalOcean anzulegen: Gibt es nicht. Jedenfalls nicht für die Cloud-Maschinen. Wer sich näher dafür interessiert wie man etwas ähnliches bei Hetzner realisieren kann, dem sei dieser Artikel ans Herz gelegt: http://kartoza.com/en/blog/using-a-sambacifs-mount-as-a-docker-volume/ . Ein Prima-Artikel der mir sehr geholfen hat. Der mich aber auch darin bestärkt hat, Hetzner „Lebe Wohl!“ zu sagen und stattdessen auf DigitalOcean umzusteigen.

Hier sind die Maschinen in der Tat etwas teurer als bei Hetzner (die billigsten gibt es für ~5$/Monat), aber dafür hat man hier auch mehr Optionen. Außerdem arbeitet DigitalOcean gerade an einer Kubernetes-Integration, welche ich gespannt verfolgen werde.

Ich habe heute übrigens ein Terraform-Script eingerichtet, welches mir bei DigitalOcean entsprechend viele Maschinen hochzieht und diese direkt mit Docker, Docker-Compose und dem Docker Volume Plugin für DO bestückt. Ich kann schon jetzt sagen: Ich liebe Terraform (https://www.terraform.io/) und bedanke mich recht herzlich bei Erkan Yanar – dem Trainer des DevOps Docker Camps – für die Inspiration!

Ich ziehe nun also von Hetzner zu DigitalOcean weiter und werde hier weiter berichten, wie meine Erfahrungen dort sind.

UPDATE: Ich habe meine Services inzwischen im Swarm Mode Cluster auf DigitalOcean ans laufen bekommen und habe auch die Volume-Problematik bereits gelöst. Allerdings bin ich nicht bei dem Docker-Volume-Plugin für DigitalOcean-Spaces geblieben (Problem war, dass ein Space jeweils nur einem einzigen Droplet zugeordnet werden konnte..ich hatte es mir eher so vorgestellt, dass wenn ich 3 Swarm Mode Nodes habe, dann allen dreien den selben Space zur Verfügung stellen kann). Stattdessen installiere ich nun auf allen Master-Nodes einen GlusterFS-Server-Verbund mit zwei Volumes (crawler und index) und auf allen Worker-Nodes installiere ich den GlusterFS-Client und mounte dort jeweils die GlusterFS-Volumes, sodass im Endeffekt auf jedem Worker beide Volumes zu jeder Zeit im selben Zustand vorhanden sind. Mehr dazu findet ihr in folgendem Blog-Post: CI/CD-Pipeline for a java microservice application running on Docker Swarm mode cluster on DigitalOcean with Maven/Docker/Gitlab/Terraform/Ansible

6 Gedanken zu “Docker-Volumes im Cluster: Von Hetzner zu DigitalOcean (Erfahrungsbericht)

    • Hi firmasaga und vielen Dank für Dein Feedback! Du hast Recht: Wenn man sich die letztlich finale Lösung anschaut (nämlich das installieren von GlusterFS auf allen Maschinen) hätte man dafür ebenso Hetzner-Server verwenden können. Das Ding ist: Ich habe die Applikations-Infrastruktur inzwischen erweitert und automatisiert. Ich verwende das Tool Terraform (Infrastructure as Code) um sämtliche Infrastruktur bei DigitalOcean automatisiert erstellen und konfigurieren zu lassen – inkl. Droplets, Volumes (BlockStorage), Domains, … DigitalOcean bietet für sämtlich Ressourcen, die sie anbieten eine API an und gleichzeitig gibt es einen sehr gut gepflegten DigitalOcean-Terraform-Provider wodurch ich eben leicht via Terraform den Infrastruktur-Code schreiben kann, welcher dann von meiner Pipeline ausgeführt wird sodass die ganzer Infrastruktur-Erstellung und -Konfiguration völlig automatisiert funktioniert (habe ich auch in ein paar anderen Artikeln genauer beschrieben, z.B. https://codinghaus.de/2018/08/26/ci-cd-pipeline-for-a-microservice-based-java-webapplication-on-digitalocean-with-maven-docker-gitlab-terraform-ansible/).

      Wenn ich mich recht entsinne gab es zum Zeitpunkt der Blogpost-Erstellung noch keinen (guten) Terraform-Provider für Hetzner. Selbiges gilt für BlockStorage. Damals (also vor ca. einem halben Jahr) bot Hetzner keine Storage-Funktion an, die den DigitialOcean-Volumes entsprach (oder Amazon EBS). Ich habe gerade gesehen, dass die das inzwischen auch anbieten (Als ich mich einloggte wurde mir direkt ein Popup angezeigt „Volumes sind jetzt allgemein verfügbar“, der Terraform-Provider supported dieses sogar auch schon (https://www.terraform.io/docs/providers/hcloud/r/volume.html). Dementsprechend kann es durchaus sein, dass Hetzner inzwischen auch für das von mir beschriebene Szenario in Betracht gezogen werden könnte. Aber im Juni 2018 war das definitiv noch nicht der Fall.

      Und ich muss sagen, dass ich bei DigitalOcean inzwischen recht glücklich bin. Die API für sämtliche Ressourcen ist super einfach, der Terraform-Provider wird immer aktuell gehalten und sie bieten inzwischen auch viele Dinge an, die Hetzner definitiv (noch) nicht anbietet. Zum Beispiel: Managed Kubernetes und Managed Postgres-Datenbanken (beta). Außerdem bietet DO für alle Funktionalitäten massig Hilfeseiten und Tutorials an – wobei das meiste eh ziemlich einfach ist und meist intuitiv funktioniert. Hetzner bietet meiner Meinung nach auch derzeit noch einen geringeren Funktionsumfang und weniger Hilfequellen an.

      Like

      • Hallo
        Herzlichen Dank für die ausführliche Antwort. Ich verstehe nun warum trotzdem DO verwendet wurde. Bezüglich Managed Kubernetes wäre ich noch etwas vorsichtig. Man kriegt da root access was mir sehr komisch erschient für ein System, welches wirklich managed sein soll. So wie ich das sehe ist das nur ein einmal aufgesetztes Cluster das gemonitored wird. Um updates und alles andere muss man sich wohl selbst kümmern. Darum ist das Pricing wohl auch so tief angesetzt aber man kann das nicht mit Managed Kubernetes von z.B. Google vergleichen. Leider konnte ich nicht genau herausfinden ob das wirklich so ist.

        Like

  1. Toller Artikel, vielen Dank dafür!

    Was ich noch nicht ganz verstehe: Wenn ich nun ein Hetzner-Volume am Rechner gemounted habe (z.B. /mnt/volume), dann kann ich das natürlich auch auf mehreren Hetzner-Cloud-Maschinen so machen, sodass der Pfad derselbe ist. Aber in der Docker Compose YAML würde ich dennoch den Pfad „hardcoden“ müssen und kann nicht direkt aufs Cloud-Volume verweisen. Dafür gibt es keine bessere Alternative, oder? Es widerstrebt mir ein wenig, absolute Systempfade in einer Docker Compose Config einzutragen.

    Gefällt 1 Person

  2. Hi Werner und vielen Dank für Dein Feedback erstmal. Ich bin derzeit nicht mehr so richtig tief in der Thematik, aber ich schreibe mal was ich dazu denke..vielleicht hilft Dir das trotzdem irgendwie weiter: Das Problem an dieser Lösung mit Hetzner Volume ist ja nicht bloß der hartkodierte Pfad zum Volume. Wenn ich das richtig sehe (ich habe Hetzner Volumes nie ausprobiert / Zum Zeitpunkt der Erstellung des Artikels gab es die noch gar nicht) scheinen die ja ganz ähnlich wie DigitalOcean Volumes zu funktionieren. Das heißt man kann ein Volume nur an einen Hetzner Cloudserver binden. Das wiederum heißt Du bräuchtest pro Node schonmal ein Volume. Und davon abgesehen hättest Du dann eben auch nur den (kleinen) Vorteil, dass der entsprechende Container, der von dem Volume Gebrauch macht, seine Daten darin ablegen kann. Was dadurch nicht gelöst ist, ist dass die Daten auf allen Nodes trotzdem unterschiedlich sind.
    Sagen wir du hast einen Service der in einem Container läuft.. der läuft dann erstmal auf Hetzner CloudServer A. Irgendwann entscheidet dein Cluster (benutzt Du einen Orchestrierer? Swarm o.Ä.?) dass dein Container besser auf Rechner B laufen sollte. Bis dahin hat er 10 Dateien auf dem Volume von Server A abgelegt. Wenn er nun auf Rechner B startet, greift er auf ein ANDERES Volume zu. Es liegt unter dem selben Pfad, aber es ist ein anderes Volume. Das heißt dein Service hat keinen Zugriff auf die bereits im anderen Volume abgelegten 10 Dateien und fängt von vorne an. Deshalb habe ich auch mit GlusterFS gearbeitet, damit die Volumes sich jeweils synchronisieren: Wenn auf Rechner B eine Datei ins Volume gepackt wird, wird diese auch auf Rechner A und C ins Volume gepackt (Was im Beispiel mit Rechner A, B und C die dreifache Datenmenge verursacht).

    Ich bin der Meinung, wenn man mit Containern arbeiten und diese ernsthaft auch produktiv betreiben möchte, kommt man um einen Orchestrierer nicht mehr umher. Und hier sei natürlich direkt auf Kubernetes verwiesen. Ich habe selber kaum Erfahrung mit Kubernetes (möchte ich zeitnah ändern), aber ich weiß, dass K8S beispielsweise Funktionalität anbietet, die sich „Persistent Volume“ nennt. Und ich glaube, das ist genau das, was man haben möchte. Der Orchestrierer kümmert sich um alles..sowohl auf welchen Nodes welcher Container läuft als auch wo genau der „State“ deiner Applikation abgelegt wird. Du sagst dem Orchestrierer quasi: „Das sind meine Rechner, das ist mein Speicherplatz..kümmer du dich drum, dass meine Container Zugriff auf den Speicher haben, egal wo im Cluster sie gerade genau laufen“. Im Prinzip geht das also in die gleiche Richtig wie die Docker Volume Plugins für Swarm Mode, welche ja aber zumindest zum Zeitpunkt der Erstellung des Blog Posts schon alles andere, als einfach zu erstellen waren..und ich glaube da hat sich zwischenzeitlich nicht viel verändert.

    Da ich wie gesagt noch keine Erfahrung auf dem Gebiet habe, kann ich nicht genau sagen wie das funktioniert.. aber wenn es darum geht, Container inklusive Persistenz produktiv zu betreiben, scheint man um K8S nicht mehr umher zu kommen. Auf dieser Seite https://portworx.com/tutorial-kubernetes-persistent-volumes/ habe ich diese Zusammenfassung gefunden: „Persistent Volumes are simply a piece of storage in your cluster. Similar to how you have a disk resource in a server, a persistent volume provides storage resources for objects in the cluster. At the most simple terms you can think of a PV as a disk drive. It should be noted that this storage resource exists independently from any pods that may consume it. Meaning, that if the pod dies, the storage should remain intact assuming the claim policies are correct. Persistent Volumes are provisioned in two ways, Statically or Dynamically.“. Ich habe vor, mich demnächst durch diese Kubernetes Einführung durchzuarbeiten: https://www.digitalocean.com/community/curriculums/kubernetes-for-full-stack-developers

    Ich habe damit noch nicht angefangen, aber da es von DigitalOcean kommt, denke ich, dass es was taugen wird. Vielleicht ist das ja für dich auch von Interesse 🙂 Persistent Volumes scheinen jedenfalls auch thematisiert zu werden.

    Viele Grüße,

    Marco

    Gefällt 1 Person

    • Danke für deine ausführliche Antwort!

      Die Hoffnung war in meinem Fall, auch erstmal ohne Orchestrierer auszukommen, da das einen großen Schub an Komplexität mit sich bringt. Und wie du sagst, das wäre die optimale Lösung, aber da muss man sich erst gut einarbeiten.

      In meinem Fall ging es nur darum, Docker-Daten auf einem Host auch in ein Volume auslagern zu können, das später mal größer werden kann. Ich brauchte auch gar nicht mehrere Hosts oder Skalierbarkeit. Ich habe inzwischen das Projekt https://github.com/costela/docker-volume-hetzner gefunden, welches eine gute Zwischenlösung ist: Da kann das Volume prozedural angelegt werden, und man kann es später, bei Bedarf vergrößern. Natürlich hat das weiterhin den Nachteil, dass es eben nur an einem Host hängen kann. Als Zwischenlösung reicht das aber mal aus.

      Like

Hinterlasse einen Kommentar