ADMIN

2020

12

2020-11-29T12:00:00

Applikationsmanagement

PRAXIS

028

Virtualisierung

Cluster

Kubernetes

Kubernetes einrichten und betreiben (4)

Spieltag

von Andreas Stolzenberger

Veröffentlicht in Ausgabe 12/2020 - PRAXIS

Nachdem wir in den ersten drei Teilen unseren Kubernetes-Cluster auf OKD-Basis trainiert haben, geht es nun daran, diesen Cluster produktiv zu nutzen. Im Vergleich zu anderen Kubernetes-Distributionen, die oft nur bestehende, bereits containerisierte Applikationen laufen lassen, geht OKD einen etwas anderen Weg. Es schickt eine Reihe von Entwickleroptionen aufs Feld, die die Entwicklung moderner Scale-Out-Anwendungen vereinfachen.

Auf der Ops-Seite stellen Administratoren der Plattform die passenden Container-Images mit den integrierten Applikations-Runtimes zur Verfügung. Entwickler auf der Dev-Seite konzentrieren sich dann auf die Programmierung und müssen sich nicht um den Bau des Container-Images kümmern. Sobald der Code fertig ist, wandert er nach OKD. Dieses nutzt ein Basis-Image und – je nach verwendeter Entwicklungsumgebung – eine Build-Infrastruktur mit Tools wie Maven, Jenkins oder Tekton, um aus dem Code und der OS-Umgebung ein lauffähiges Container-Image zu erzeugen. Bei simplen Applikationen ist das ganz einfach.
Hello World aus dem Container
Unsere erste Applikation auf der OKD-Plattform ist einmal mehr das unvermeidliche "Hello World"- Beispiel, das wir in Python verfassen. Sie verfügen zum aktuellen Zeitpunkt über einen aktiven OKD-Cluster mit dem passenden Admin-Login für die Kommandozeile (export KUBECONFIG=<Pfad>/kubeconfig) und dem kubeadmin-Passwort für die Web-UI. Für die Entwickler-Demo wären jetzt ein oder mehrere Nutzerkonten ohne Root-Rechte sehr praktisch. Erzeugen Sie dazu erst einmal eine simple Passwort-Datei:
htpasswd -c pwdfile user1
Das Kommando erzeugt eine Passwort-Datei "pwdfile" und fügt den Nutzer "user1" hinzu. Das Passwort erfragt der htpasswd-Befehl interaktiv. Falls sie das Tool nicht auf dem Rechner haben, installieren Sie es über den Paketmanager (yum install httpd-tools). Sie können der Datei weitere Nutzer via htpasswd pwdfile user2 hinzufügen.
Auf der Ops-Seite stellen Administratoren der Plattform die passenden Container-Images mit den integrierten Applikations-Runtimes zur Verfügung. Entwickler auf der Dev-Seite konzentrieren sich dann auf die Programmierung und müssen sich nicht um den Bau des Container-Images kümmern. Sobald der Code fertig ist, wandert er nach OKD. Dieses nutzt ein Basis-Image und – je nach verwendeter Entwicklungsumgebung – eine Build-Infrastruktur mit Tools wie Maven, Jenkins oder Tekton, um aus dem Code und der OS-Umgebung ein lauffähiges Container-Image zu erzeugen. Bei simplen Applikationen ist das ganz einfach.
Hello World aus dem Container
Unsere erste Applikation auf der OKD-Plattform ist einmal mehr das unvermeidliche "Hello World"- Beispiel, das wir in Python verfassen. Sie verfügen zum aktuellen Zeitpunkt über einen aktiven OKD-Cluster mit dem passenden Admin-Login für die Kommandozeile (export KUBECONFIG=<Pfad>/kubeconfig) und dem kubeadmin-Passwort für die Web-UI. Für die Entwickler-Demo wären jetzt ein oder mehrere Nutzerkonten ohne Root-Rechte sehr praktisch. Erzeugen Sie dazu erst einmal eine simple Passwort-Datei:
htpasswd -c pwdfile user1
Das Kommando erzeugt eine Passwort-Datei "pwdfile" und fügt den Nutzer "user1" hinzu. Das Passwort erfragt der htpasswd-Befehl interaktiv. Falls sie das Tool nicht auf dem Rechner haben, installieren Sie es über den Paketmanager (yum install httpd-tools). Sie können der Datei weitere Nutzer via htpasswd pwdfile user2 hinzufügen.
Jetzt melden Sie sich als "kubeadmin" an. Die GUI begrüßt Sie dabei mit der blauen Zeile am oberen Rand "You are logged in as a temporary administrative user .…". Klicken Sie in dieser Zeile auf den Link "cluster OAuth configuration", um Ihren OKD-Cluster mit verschiedenen Identity-Providern zu verbinden. Für unser Test-Setup genügt es, wenn Sie auf "Add" klicken und aus der Drop-Down-Liste "HTPasswd" wählen. Laden Sie im folgenden Dialog das zuvor erstellte "pwdfile" hoch.
Die ersten 24 Stunden zählen
Ein wichtiger Hinweis: Alle Kommunikation in einem OKD-Cluster arbeitet verschlüsselt. Dabei gelten die initialen Zertifikate der Installation nur für 24 Stunden und werden im Laufe des ersten Tages durch ein längerfristig gültiges ersetzt. Wenn Sie einen neuen OKD-Cluster aufsetzen, dürfen Sie diesen auf keinen Fall direkt nach der Installation herunterfahren und dann zwei Tage lang nicht benutzen – er würde danach nicht mehr starten.
Wenn Sie sich nun abmelden, erhalten sie einen neuen Login-Screen, der ihnen die Optionen "Log in with..." "kube:admin" oder "htpasswd" offeriert. Melden Sie sich nun via "htpasswd" und einem der zuvor erstellten Nutzer an. Das Menü zur Linken lässt sich zwischen "Developer" und "Administrator" umschalten. Gehen Sie in die "Administrator"-Ansicht und erzeugen auf der Seite "Projects" ein neues Projekt für den Hello-World-Test. Schalten Sie dann zur Developer-Ansicht um. Das "Topology"-Fenster offeriert Ihnen nun eine Reihe von Möglichkeiten, um eine containerisierte Applikation zu starten. Für das Hello-World-Beispiel klicken Sie auf "From Git". In der Folge listet OKD eine ganze Reihe von potenziellen Builder-Images mit verschiedenen Umgebungen wie Java, NodeJS, PHP und anderen auf, aber hier müssen Sie keine manuelle Auswahl treffen. Tragen Sie in der Zeile "Git Repo Url" den Link "https://github.com/OpenShiftDemos/os-sample-python" ein. Das verweist auf eine sehr simple Hello-World-Applikation auf Basis von Flask und Python 3. In der GitHub-Gruppe "https://github. com/OpenShiftDemos" finden Sie übrigens viele andere Demos in Sprachen wie Ruby, PHP oder Java.
Sobald Sie die URL eingetragen haben, prüft OKD das Repository und findet selbständig heraus, dass es sich hierbei um Python-Code handelt. Dementsprechend schlägt die Umgebung auch "Python" als Builder-Image vor. Sie müssen jetzt nur noch auf "Create" klicken. OKD wird dann das Build-Image von der quay.io-Registry herunterladen, den Code aus dem Git-Repository hineinkopieren und die im Git festgelegten Build-Schritte durchführen. Dabei wird in diesem Fall die "requiremts.txt"-Datei ausgewertet, um die dort angegebenen Python-Dependencies in den Container zu laden. OKD richtet ferner automatisch die Route und den DNS-Namen für das Projekt ein. Nach dem erfolgten Build steht die HelloWorld-App sofort auf der URL "http://os-sample-python-git-test2.apps.okd.<Ihre Domain>/" zur Verfügung.
Die UI stellt ihre Anwendung in der Topology-Ansicht dar. Bei genauem Hinsehen werden Sie feststellen, dass in Ihrem Projekt nun weit mehr als ein einzelner Container existiert. Neben Routen, Service und dem eigentlichen Pod mit der Applikation erstellt OKD einen zweiten Build-Pod, der den eigentlichen Applikationscontainer zusammensetzt und startet. Bei komplexeren Build-Prozessen und kompilierten Sprachen würde dieser Build-Pod dann auch die zuvor definierte Build-Pipeline durchlaufen. Daneben gibt es eine Deployment-Config-Datei, die genau festlegt, wie Ihre Applikation laufen und skalieren muss.
Klicken Sie in der Topology-View auf das Python-Symbol der Anwendung und danach auf "Details" im rechten Panel. Dort sehen Sie, dass aktuell ein Pod mit Ihrer Applikation läuft. Über das Icon "Pod" stellen Sie den Pod-Count ein, erzeugen also direkt mehrere Instanzen Ihrer Anwendung. OKD wird dann sofort weitere Pods bauen. Für diese Demo ergibt das zwar keinen Sinn, aber kommerzielle Webapplikationen können so die Zahl der Webserver hinter einem Loadbalancer dynamisch anpassen.
Erste Schritte mit der Applikation
Im Tab "Ressources" sehen Sie Details der Applikation und des laufenden Pods. In dieser Ansicht können sie ruhig einmal den aktuellen Pod via "Actions" und "Delete Pod" löschen. OKD wird umgehend einen neuen erstellen und Ihre Applikation läuft weiter. In den Einstellungen zum "Deployment" Ihrer Applikation können Sie konfigurieren, auf wie viele Pods Ihre Anwendung skalieren soll. Zudem können Sie die "Build-Config" der Applikation mit Triggern und Hooks versehen. Vereinfacht gesagt: Ihre Applikation kann ein Git-Repository überwachen und sobald der Entwickler eine neue Version des Codes über einen "commit" hochlädt, ersetzt OKD den laufenden Pod durch einen neuen mit der geänderten Code-Version.
Das Gleiche gilt für die Ops-Seite: Sollte der Operator eine neue Version des Basis-Images in die Registry laden, kann OKD einen neuen Pod aus Image und Code bauen. Sobald Entwickler und Operatoren so über eine stabile Version der Applikation verfügen, lässt sich diese via "Source-to-Image" in ein Abbild fusionieren und dieser stabile Built dann in eine Produktionsumgebung übertragen. Genauso funktionieren auch die Beispiele für Ruby, PHP oder Java. Sie können diese Vorlagen in eigene Repositories klonen, Ihren eigenen Code hinzufügen oder ändern und nach dem Schema Applikationen ausrollen. Nutzen Sie lieber die Kommandozeile, dann hilft "odo" ("Openshift Do") [1]. Ohne großes Wissen über die Ops-Seite kann hier ein Anwendungsentwickler seinen Code direkt in ein Projekt hochladen und Pods erstellen.
In der realen Welt kommen weitaus komplexere Applikationen zum Einsatz. Moderne Scale-Out-Apps bestehen aus mehreren Web-Frontend-Containern hinter Loadbalancern, mehreren Pods mit der Programmlogik, einigen Pods für die dynamische Kommunikation zwischen den Pods (zum Beispiel Redis, Memcached, AMQ, Rabbimq, API-Management) und natürlich Pods zur Datenhaltung.
Selbstüberwachung in OKD
OKD installiert ein Reihe von Pods zur Überwachung des Clusters. Die Metriken sammelt Prometheus ein und Grafana stellt diese dar. In der UI-Ansicht "Administrator / Monitoring" finden Sie eine Reihe vorgegebener Grafen. Auf Basis dieser Metriken definiert OKD eine Serie von Alarmen, um den Admin bei Problemen zu benachrichtigen. Anwendungsentwickler können hier ihre eigene Applikation zur Überwachung registrieren. So kann das System Alarme auslösen, falls die Applikation nicht wie gewünscht läuft. Zudem können die Deployment-Regeln diese Metriken nutzen, um die eigene Applikation zu skalieren.
Probleme redundanter Datenhaltung
Die ursprüngliche Idee für containerisierte Applikationen war, dass sie keine persistente Datenhaltung benötigen. Die Daten einer Anwendung sollten auf vielen Containern redundant verteilt sein, sodass ein Ausfall von Knoten, Containern oder Netzwerkverbindungen nicht zu einem Datenverlust führt – soweit die Theorie.
Die Praxis sieht leider anders aus: Eine redundant verteilte Datenhaltung erfordert moderne Speicherkonzepte und Scale-Out-Datenbanken. Aber sowohl die Entwickler als auch die Admins wollen sich nicht von etablierten Technologien wie SQL-Datenbanken und Dateien in gesharten Filesystemen trennen. Und auch die Rufe nach regelmäßigen Pod-Sicherungen via Live-Snapshots werden immer lauter – obwohl eine moderne Scale-Out-Architektur genau auf diese altmodischen Backup-Mechanismen verzichten will.
Die meisten etablierten SQL-Datenbanken erlauben keinen Active-Active-Scale-Out-Betrieb des DB-Servers auf mehreren Pods. Ausnahmen sind kommerzielle Lösungen wie die Cluster-Version des Microsoft-SQL-Servers oder Nuo-DB. Also braucht es für Plattformen wie Postgres oder MariaDB ein Active-Passive-Konzept mit einem gemeinsamen Storage. Kubernetes wird bei Problemen mit einem Datenbank-Pod die Applikation via Failover auf einen neuen Pod übertragen und den gemeinsam genutzten Speicher vom alten auf den neuen Pod umhängen.
Anhand des Codes im Git-Repository erkennt OKD, welches Build-Image zum Projekt passt.
Mit persistenten Volumes und Claims arbeiten
Shared Storage gibt es in der Kubernetes-Welt über sogenannte "Persistent Volumes" (PV). Die Definition erfolgt wie immer über eine YML-Datei (Listing 1). Das Listing erklärt sich fast von allein: Das Persistent Volume beschreibt eine NFS-Freigabe auf dem Server 192.168.1.1, die sich mit einer Quote von 25 GByte belegen lässt. Technisch wird so ein Volume von einem der OKD-Worker-Nodes gemounted und dann via "Loop-Mount" in das Dateisystem des Pods eingeblendet. Der "AccessMode" gibt an, ob das Volume von mehreren Pods simultan benutzt werden darf. Bei "ReadWriteMany" dürfen mehrere PODs gleichzeitig schreiben – dann nutzt das Volume aber keinen Schreib-Cache.
Listing 1: Definition von Persistent Volumes
apiVersion: v1 kind: PersistentVolume metadata:     name: storage1 spec:     capacity:        storage: 25G     accessModes:        - ReadWriteMany     nfs:        server: 192.168.1.1        path: "/home/nfs/storage1
Neben File- kann auch Block-Storage zum Einsatz kommen, wie Listing 2 zeigt. Dabei stellt Kubernetes ein iSCSI-Laufwerk bereit und auch hier bindet einer der OKD-Nodes das Laufwerk ein, formatiert es mit XFS und stellt das resultierende Dateisystem via Loop-Mount dem Pod zur Verfügung. Benötigt eine Anwendung nun Zugriff auf persistenten Speicher, muss sie diesen über einen sogenannten "Claim" (PVC) bei OKD anfordern. Wie nicht anders zu erwarten, sieht so ein Claim in etwa so aus wie in Listing 3. Stellt eine Applikation einen Claim, prüft Kubernetes, ob der OKD-Cluster über ein freies, passendes Volume verfügt und weist dieses dann dem Claim zu. Falls kein passendes zur Verfügung steht, bleibt der Application-Rollout hängen und wartet, bis Kubernetes den angeforderten PVC erfüllt.
Listing 2: Persistent Volumes mit Block-Storage
apiVersion: v1 kind: PersistentVolume metadata:     name: iscsi1 spec:     capacity:        storage: 10Gi     accessModes:        - ReadWriteOnce     iscsi:        targetPortal: 192.168.1.1        iqn: iqn.2014-12.example.server: storage.target0        lun: 0        fsType: 'xfs'        readOnly: false
Listing 3: Claim-Zugriff auf persistenten Speicher
apiVersion: v1 kind: PersistentVolumeClaim metadata:     name: AppVolumeClaim spec:     accessModes:        - ReadWriteOnce     resources:        requests:           storage: 10Gi     volumeMode: Filesystem
Um das zu verhindern, müssen Sie als Administrator entweder eine Reihe PVs händisch konfigurieren (genügt für den Test-Cluster) und dem Cluster zur Verfügung stellen oder einen dynamischen Storage-Operator einsetzen. Diese "Treiber" kommunizieren mit dem Storage-Backend und erstellen PVs automatisch in dem Moment, wenn ein Claim eintrifft. Neben kommerziellen Storage-Operatoren wie "Trident" für NetApp-Storage gibt es Open-Source-Tools wie "Rook" [2], das mit mehreren Backends wie Ceph oder NFS arbeitet. Für unseren Test-Cluster genügt ein einfacher NFS-Provisioner. Dieser erstellt in einem Basis-Verzeichnis eines NFS-Servers je nach Bedarf weitere Unterverzeichnisse und stellt diese dann als PV den Pods zur Verfügung. Eine genaue Beschreibung des NFS-Provisioners bedürfte eines weiteren Artikels, daher verweisen wir an dieser Stelle lediglich auf das gut dokumentierte Git-Repository [3]. Für das Test-Setup reichen aber auch einige manuell erstellte PVs.
Persistente Images konfigurieren
Ihr OKD-Setup bringt eine eigene Image-Registry mit. Sollten Sie eigene Container-Images bauen, können Sie diese in der Registry ablegen und von dort aus in Ihren Applikationen einsetzen. Nach der Grundinstallation des OKD-Clusters nutzt diese Image-Registry jedoch keinen persistenten Speicher. Sollte der Registry-Pod neu starten oder ausfallen, sind die darin gespeicherten Images weg. Wer die Registry nur als Cache verwendet und seine eigenen Images in einer anderen externen Registry sichert (quay, pulp, GitLab, GitHub), kann mit dieser Einschränkung leben. Wer sie jedoch als Speicher für die eigens entwickelten Images nutzt, sollte ihr einen persistenten Speicher geben.
Erzeugen Sie dazu ein passendes PV mit mindestens 100 GByte Kapazität und dem Access Mode "ReadWriteMany". Danach editieren Sie die Konfiguration des Image-Registry-Operators. Der Befehl
oc edit configs.imageregistry.operator.openshift.io/cluster
öffnet einen Editor mit der YML-Deklaration der Operator-Konfiguration. Dort suchen Sie die Sektion "spec:" und darin die beiden Zeilen:
managementState: Removed
storage {}
Diese ändern sie ab auf:
managementState: Managed
storage:
   pvc:
      claim:
Wie bei YAML üblich, müssen sie auf die Anzahl an Leerzeichen für die Einrückung achten. Sobald Sie die Datei sichern, wird der Operator aktiv, löscht den aktuellen Image-Registry-Pod, erstellt den Claim für das Persistent Volume und rollt die Image-Registry neu aus. Diesmal jedoch mit dem persistenten Volume, sodass gesicherte Images nicht mehr verloren gehen.
Und wenn Sie schon die Konfiguration des Clusters anpassen, können sie gleich die "Putzkolonne" aktivieren. Der "Pruner"-Dienst sucht nach alten, unbenutzten Images und löscht diese vom Cluster. Sie passen dessen Konfiguration an via:
oc edit imagepruners.imageregistry.operator.openshift.io/cluster
Wiederum in der Sektion "spec:" finden sie das Schlüsselwort schedule "", das sie auf schedule "0 0 * * *" ändern und damit den Cron-Job aktivieren. Somit startet der Pruner jede Nacht um 24 Uhr und entfernt ungenutzte Images vom System.
Cluster-Update durchführen
Im Tab "Administration" finden Sie Informationen zum Cluster, wie die Basisversion und den aktiven "Kanal". Hier unterscheidet OKD in "candidate", "fast", und "stable". Wie der Name schon sagt, enthält "stable" nur sichere Updates, während "candidate" auch instabile Pakete mitbringen kann. Und "fast" liegt irgendwo zwischen "candidate" und "stable".
Sollte es für den gewählten Kanal Updates geben, zeigt OKD diese im Updatestatus an und erlaubt die Installation über einen Klick. Ein Cluster-Update funktioniert im laufenden Betrieb. OKD geht dabei Schritt für Schritt die einzelnen Cluster-Operatoren durch, lädt die neuen Images herunter und ersetzt einen Pod nach dem anderen durch eine neue Version.
Verwirrende Meldungen
Leider ist die Statusanzeige des laufenden OKD-Updates etwas verwirrend. Die Prozentangabe hüpft gerne mal von 99 auf 1 Prozent Update-Status, denn sie zeigt nicht den Fortschritt des gesamten Updates, sondern der einzelnen Schritte an. Zudem gibt es hier öfter Fehlermeldungen, speziell auf kleinen langsameren Clustern. Wenn der ein oder andere Operator etwas länger mit seinem Update braucht, schaltet die Prozessanzeige nicht selten auf "Fehler". Dieses Problem behebt sich jedoch in der Regel von selbst.
OKD als freie Version bietet bei Release-Updates nicht immer automatisch einen passenden Upgrade-Kanal. Hier müssen Sie im Zweifelsfall manuell das Image bestimmen. Unter "Administration / Cluster Settings" gibt es die "Cluster Version Configuration". Die YAML-Ansicht des Dokuments listet unter "spec:" neben dem aktiven Kanal auch die exakte Registry-URL des Images und die Versionsnummer. Kopieren Sie den unter dem Schlüsselwort "upstream" angegeben Link aus dem YAML in einen Browser-Tab, erhalten sie eine JSON-formatierte Liste aller aktuell verfügbaren OKD-Builds, jeweils mit Versionsnummer und Registry-URL. Darunter sind möglicherweise aber auch instabile Developer-Builds. Eine Liste der getesteten Quellen finden Sie unter [4]. Aus dieser können Sie die "Pull From:"-URL und Versionsnummer in die Cluster-Version-YAML-Datei übertragen und damit ihren Cluster auf diese Version updaten.
Fazit
Am Ende unseres langen KubernetesWorkshops haben Sie nun einen funktionierenden OKD-Cluster und einen Überblick über dessen Funktionen. Jetzt können Sie beginnen, mit Hilfe der DevOps-Tools ihre Scale-Out-Anwendungen zu bauen und zu testen. Bekannte Entwicklungsumgebungen und Editoren wie Eclipse oder Vscode offerieren eine direkte Integration in die OKD-Umgebung. Alternativ gibt es "Eclipse Che" [5], das Sie direkt über den zugehörigen Operator auf ihren Cluster installieren. Che offeriert eine vollständig in den Browser und Kubernetes integrierte Entwicklungsumgebung.
Probieren Sie jedoch nicht, bestehende monolithische Applikationen einfach 1-zu-1 in Container zu zwängen und auf OKD oder einer anderen Kubernetes-Plattform laufen zu lassen. Eine moderne
Scale-Out-Architektur funktioniert nur effizient, wenn die Applikationen und deren Architektur dazu passen. Eine Plattform wie OKD bietet die dafür nötigen Tools, um den Einstieg in die Entwicklung zu vereinfachen.
(jp)
Link-Codes