Im ersten Teil dieser zweiteiligen Artikelserie ging es um die
Frage, welche Möglichkeiten es gibt das für ein Projekt benötigte Tool Set, wie Compiler,
Laufzeitumgebungen oder IDEs von einem Entwicklungsteam effizient gemanagt
und installiert werden kann.
In diesem zweiten Teil befassen wir uns mit der Bereitstellung von Services
wie Datenbanken oder Message Brokern. Diese werden häufig zum Testen von
Anwendungen während der Entwicklung benötigt und deshalb ebenfalls allen
Mitgliedern eines Entwicklungsteams zur Verfügung stehen und sich unkompliziert
einrichten lassen.
Anders als bei den Tools erscheint Docker für die Services als das Mittel der Wahl. Da sie im Hintergrund laufen, entfällt nach dem einmaligen Starten das wiederholte Hantieren mit der Docker Kommandozeile. Üblicherweise modifizieren die Services auch keine Dateien auf dem Host System, was die Handhabung ebenfalls einfacher macht. Graphische Oberflächen, die mit Containern schwierig zu handhaben sind, kommen hier üblicherweise nicht zum Einsatz und die erstellten Container lassen sich beliebig starten und stoppen und müssen nicht bei jedem Aufruf neu erstellt werden. Nicht zuletzt kommen Container auch in vielen Produktivumgebungen und so sind an diese angelehnte Entwicklungsumgebungen möglich.
Wir betrachten zwei unterschiedliche Möglichkeiten: Zum einen die Installation der Dienste auf dem lokalen Entwicklerrechner, zum anderen die Nutzung eines Kubernetes Clusters in einer Cloud Umgebung und dessen Einbindung in die lokale Entwicklungsumgebung. Ziel ist es auch im letzteren Fall, dass jeder Entwickler eine eigene Umgebung mit allen benötigten Services zur Verfügung hat in der er ohne zeitraubenden Abstimmungs-Overhead neue Services starten oder Testdatenstände einspielen kann.
In beiden Setup Varianten wird man in vielen Fällen aus Kosten- und Ressourcengründen darauf verzichten mehrere zur Produktivumgebung komplett identische Umgebungen bereitzustellen, sondern sich auf die notwendigen Services beschränken, beispielsweise eine einzelne Datenbankinstanz anstatt eines Clusters.
Services auf dem lokalen Rechner
Das gute alte Docker Compose ist immer
noch eine gute Wahl um mehrere Container
gemeinsam zu verwalten. Die Konfiguration erfolgt über ein YAML-Skript. Damit
können auch Abhängigkeiten zwischen Containern, Umgebungsvariablen und Volumes
unkompliziert deklariert werden. Volumes können beispielsweise für Testdaten
genutzt werden, die beim Anlegen des Containers automatisch eingespielt werden.
Die Container können gemeinsam gestartet und gestoppt werden und automatisch
neugestartet werden. Der Status der Container kann mit den üblichen Tools, wie
docker ps
überprüft werden.
Bei der Desktopversion von Docker für Windows und macOS wird Compose bereits
mitinstalliert, für Linux kann es separat installiert werden, z. B. mit dem in
Teil 1 vorgestellten Packagemanagern nix oder conda.
Beispiel:
MicroK8s ist eine leichtgewichtige Möglichkeit einen einzelnen Kubernetes Knoten zu betreiben – allerdings ist es derzeit nur für Linux verfügbar. Installiert werden kann es mittels des in Teil 1 vorgestellten Snap.
Es bringt eine vorkonfigurierte kubectl
Installation mit,
die mittels microk8s.kubectl
aufgerufen werden kann. Services und
Anwendungen können wie gewohnt mittels Helm oder Manifesten installiert werden.
Außerdem beinhaltet es eine Reihe von Add-ons, die bei Bedarf per Kommandozeile
aktiviert werden können. Beispielsweise ist es möglich eine lokale Docker Registry
einzurichten, das Service Mesh Istio zu starten oder GPUs
einzubinden. Damit ist MicroK8s nicht nur eine gute Wahl um für die Entwicklung benötigte
Services wie Message Broker bereitzustellen, sondern auch für einen
schnellen Check von Deployment Skripten und Docker Images.
MicroK8s erstellt im Netzwerksetup des Hostrechners eine Bridge. Die innerhalb von
MicroK8s laufenden Services werden auf IP-Adressen aus dem Adressbereich der
Bridge gemappt und können mittels kubectl get services
abgefragt werden.
Wem das Hantieren mit den Adressen zu umständlich ist und lieber mit den Service
Namen arbeiten möchte, kann auch das weiter unten vorgestellte kubefwd
verwenden.
Minikube ist eine weitere Möglichkeit einen einzelnen Kubernetes Knoten zu betreiben. Es bringt wie MicroK8s eine Reihe von Add-ons mit, die bei Bedarf aktiviert werden können, beispielsweise Dashboards, allerdings ist das Angebot deutlich knapper als bei MicroK8s, so wird unter anderem istio nicht als Add-on angeboten. Dafür unterstützt Minikube anders als MicroK8s Profile, mit denen es bei mehreren Projekten möglich ist jeweils spezifische Umgebungen in Form eines eigenen Clusters zu deklarieren. Anwendungen oder komplette Entwicklungssetups können hier ebenfalls auf dem gewohnten Kubernetes Weg mittels Helm Charts oder Manifesten konfiguriert und installiert werden. Minikube kann unter Linux, macOS und Windows in einer virtuellen Maschine installiert werden. Unter Linux ist auch eine Installation nur mit Docker möglich, das wird aufgrund von Risiken für die Systemstabilität allerdings nicht empfohlen.
Für Windows 10 Pro und macOS gibt es außerdem noch die Möglichkeit den in Docker Desktop eingebauten Kubernetes Knoten zu nutzen. Dafür muss er in den Settings zunächst aktiviert werden. Ein vorkonfiguriertes kubectl wird mitgeliefert.
Während diese vier Tools dem Entwickler ein Arbeiten unabhängig von externen Ressourcen ermöglichen, sind die Setups begrenzt durch die Ressourcen des lokalen Rechners. Das ist nicht immer ausreichend, sodass es in einigen Fällen sinnvoll ist, über die Nutzung eines Kubernetes Clusters nachzudenken.
Services im Kubernetes Cluster
Der Kommandozeilenclient von Kubernetes, kubectl
, beinhaltet das Kommando
kubectl port-forward.
Diese ermöglicht das Forwarden eines oder mehrerer lokaler Ports zu einem Pod
eines Services im Kubernetes Cluster.
Beispiel Postgres Datenbank: Das Kommando
kubectl port-forward svc/my-postgres-postgresql 5432:5432
ermöglicht den Aufruf der Datenbank unter localhost:5432
. Allerdings erfordert
das pro Service einen Aufruf von kubectl
.
Eine komfortablere Möglichkeit Portforwarding zu nutzen, bietet das
Kommandozeilentool kubefwd.
Anders als mit kubectl
kann mit einem Aufruf nicht nur ein einzelner Service
geforwarded werden, sondern mehrere oder alle Services eines Kubernetes Namespaces.
Dabei werden auch die Namen der Services mitgemappt.
kubefwd
fügt dafür temporär, solange das Forwarding aktiv ist, neue Einträge
zur Datei /etc/hosts
hinzu um den Namen auf eine lokale Loopback IP-Adresse zu
mappen, dafür werden Superuser Rechte benötigt.
Für nix und conda existieren derzeit leider noch keine Packages in den offiziellen
Repositories, auf der kubewd
Webseite werden aber mehrere Paketformate
für Linux, MacOS und Windows bereitgestellt.
Beispiel (gekürzt):
Danach lassen sich die Datenbanken lokal mittels my-postgres-postgresql:5432
bzw. my-influx-influxdb:8086
aufrufen – den gleichen Hostnamen und Ports unter denen sie auch innerhalb des Kubernetes Clusters erreichbar sind.
Gelegentlich gibt es Situationen, bei denen man nicht nur auf im Kubernetes Cluster laufenden Services zugreifen möchte, sondern kurzzeitig auch Anfragen vom Cluster auf den lokalen Rechner umleiten möchte um Probleme in einer Anwendung zu debuggen oder Sonderfälle zu testen. An dieser Stelle kommt Telepresence ins Spiel, ein Kommandozeilentool das eine Art Zwei-Wege Netzwerk Proxy anlegt: Er erlaubt sowohl den Zugriff auf im Cluster laufende Dienste wie Datenbanken, wie auch umgekehrt den Aufruf auf lokale Anwendungen aus dem Cluster heraus. Dafür wird je Anwendung im Cluster ein Pod mit einem Proxy Container angelegt sowie lokal ein Proxy Container gestartet. Dies geschieht automatisch, sobald Telepresence lokal gestartet wird. Mittels Parameter lassen sich die Ports mitgeben, die von außen erreichbar sein sollen. Beim Entwickeln wird die Anwendung in den lokalen Container gemountet. Telepresence ist momentan unter Linux und macOS verfügbar.