Blog

Red Hat OpenShift Installation mit GlusterFS Storage

OpenShift ist ein PaaS (Platform as a Service) welches den Betrieb von Docker Containern in einem Cluster vereinfacht. Ein wesentlicher Bestandteil der Platform ist das Thema Storage. Jede Anwendung hat unterschiedliche Anforderung an Kapazität. Die Idee ist, dass die Anwendung einfach definiert wieviel Speicherplatz sie benötigt und die Plattform provisioniert dynamisch die geforderte Kapazität in Form von Volumes. Diese Volumes stehen dem Container zu Verfügung unabhängig davon auf welchem Node der Container läuft. Wie könnte eine Lösung aussehen. In einfachen Formen könnte ein SAN angebunden werden, ebenso NFS als shared Mount. Diese Technologien sind aber „old school“ und nicht für moderne Container Plattformen geeignet. In diesem Artikel beschreiben wir das von Red Hat eingeführte Container Native Storage (CNS). „Native“ bedeutet, dass alle Komponenten first class citizens sind und selbst als Container in OpenShift laufen. Im Gegensatz dazu steht das Container Ready Storage. Dabei ist ein Plattform externe Storage Lösung gemeint, die dann in OpenShift integriert wird. Bei beiden Lösungen werden Storage Cluster eingesetzt wie GlusterFS oder Ceph. Diese beiden Technologien versprechen theoretisch unendliche Erweiterbarkeit von Speicher. Wie werden im folgenden sehen wie eine Installation von GlusterFS in der CNS Variante funktioniert.

Ein GlusterFS Cluster besteht aus mindestens 3 Nodes. GlusterFS unterstützt unterschiedliche Volume Types die bestimmen wie Daten verteilt und ausfallsicher gemacht werden. OpenShift unterstützt nur die Distributed Replicated Variante. Für Interessierte hier mehr zu Volume Types. In einem OpenShift Cluster werden dabei einzelne Nodes mit angebundenen Raw Devices als Gluster Nodes definiert. Auf jedem dieser Nodes muss ein ein Gluster Docker Container laufen. Hierzu generiert OpenShift ein DaemonSet, der dafür sorgt, dass auf jedem Node ein Container plaziert wird. In unserem Beispiel werden wir insgesamt 3 Nodes starten: 1 Master und 2 Worker Nodes. Da dies nur zu Demonstrationszwecken dient wird auch der Master Node benutzt um ein Gluster Container zu deployen. In der Produktion sollte man den Master Node mit 

$ oadm manage-node <node1> <node2> --schedulable=false

auf nicht bespielbar setzen. D.h. das Cluster Management wird auf den Master keine Pods deployen. Zum Installieren von OpenShift auf AWS gibt es hier ein geeignetes GitHub Projekt. Folgen Sie einfach den beschriebenen Schritten in der Readme. Vorab muss allerdings das aws cli lokal installiert und mit Zugangsdaten konfiguriert werden. Hier gibt es eine Anleitung zum Installieren der aws cli. Hat man das Github Projekt geklont, kann man mit 

$ make infrastructure

eine Infrastruktur bestehend aus 1 Master und 2 Nodes auf AWS aufzubauen. Hierzu verwenden wir terraform, welches hervorragend geeignet ist, um Infrastrukturen automatisiert zu provisionieren. Zum Zugriff auf die 3 Nodes wird zusätzlich ein Bastion Host als Jump Server installiert, ein VPC und entsprechende Security Group. Es ergibt sich folgendes Bild:

Nun könnte man mit 

$ make openshift

die OpenShift Installation beginnen. Dabei nutzt das GitHub Projekt seinerseits die offiziellen OpenShift Ansible Playbooks. Es sei angemerkt, dass das GitHub Projekt noch nicht eine GlusterFS Installation unterstützt. Diese muss nachinstalliert werden, wie wir weiter unten sehen werden. Des Weiteren haben wir ein Pull Request erstellt, welches die GlusterFS Integration als CNS vollständig automatisiert. D.h. die Schritte, die wir nachfolgend ausführen werden in dem GitHub Projekt gemerged werden.

Eine Änderung müssen wir aber vor der Installation von OpenShift aus dem obigen Schritt noch durchführen. In der Datei: install-from-bastion.sh

ANSIBLE_HOST_KEY_CHECKING=False /usr/local/bin/ansible-playbook -i ./inventory.cfg ./openshift-ansible/playbooks/byo/config.yml

ändern in

ANSIBLE_HOST_KEY_CHECKING=False /usr/local/bin/ansible-playbook -i ./inventory.cfg ./openshift-ansible/playbooks/byo/config.yml --extra-vars "openshift_storage_glusterfs_wipe=True" -vvv

ändern. OpenShift CNS nutzt Heketi zum dyamischen Provisionieren des Storage. Heketi bietet eine REST basierte Schnittstelle um von dem GlusterFS Cluster Storage anzufordern und wieder freizugeben. Heketi erwartet, dass die definierten GlusterNodes mit Raw Devices bestückt sind. Also z.B. Festplatten (oder auch EBS) ohne Volumes und ohne Formatierung, da Heketi das selbst übernimmt. Sollte bei einem Installationsschritt etwas schiefgehen und evt. schon Volumes angelegt worden sein, kann man die Installation dann nicht einfach wiederholen. In diesen Fällen muss man mit dem Befehl wipefs die entsprechenden Devices Löschen. Das Setting oben sorgt dafür, dass dies automatisch geschieht bei der Einrichtung von Node Storage. Nach dem Setzen des Settings kann man nun die Installation von OpenShift wie oben beschrieben ausführen. Das dauert ca. 15 Minuten und danach kann man mit

$ make browse-openshift

einen Browser öffnen mit der OpenShift Url auf AWS. Einloggen kann man sich mit dem Usernamen: admin und dem Passwort: 123. Als Identity Provider ist Htpasswd eingtragen, sodass sich nur bestimmte Nutzer einloggen können. OpenShift bietet diverse identity Provider wie Ldap, DenyAll oder AllowAll. Der Admin Nutzer ist nicht gleich zu setzen mit dem cluster-admin. Dazu fehlen ihm noch noch die Rechte.

Nun wollen wir die ersten Schritte Richtung GlusterFS unternehmen. Es sei angemerkt, dass sich mit der Installation schon dynamische Storage Volumes anlegen lassen, allerdings als EBS Volumes auf AWS. Eine StorageClass mit dem Kubernetes/aws-ebs Provisioner ist nämlich schon Bestandteil. D.h. wenn eine Anwendung einen Speicherplatz Bedarf in Form von einem PersistenVolumeClaim anmeldet, dann erstellt die StorageClass auf AWS ein EBS Volume. Dieser wird dann an den Container gemountet, der in der yaml Datei sein Volume Bedarf definiert.

Als Betriebssystem haben wir RHEL Enterprise Linux in der Version 7.5 Im Einsatz und OpenShift Container Platform 3.6. Als erstes müssen wir nun unsere Red Hat subscription auf allen Nodes einrichten. Dabei nutzen wir unsere Developer Lizenzen. Zum einloggen auf den jeweiligen Node nutzen wir

$ make ssh-master oder make ssh-node1 oder make ssh-node2

$ sudo subscription-manager register --username <username> --password <password> --auto-attach

Danach fügen wir die Red Hat Gluster Repo hinzu. Die nachfolgenden Befehle auf dem Master ausführen.

sudo subscription-manager repos --enable=rh-gluster-3-client-for-rhel-7-server-rpms

Nun installieren wir das CNS und Heketi Client Tool. Damit steht uns das cns-deploy zu Verfügung, mit dem wir eine Topology für unser GlusterFS Cluster deployen können, ebenso die heketi-cli zum Administrieren.

sudo yum install cns-deploy heketi-client

Damit sich die Gluster Nodes untereinander unterhalten können müssen wir auf jedem Node Firewall Regeln hinzufügen:

sudo vi /etc/sysconfig/iptables
Die folgenden Regeln vor dem letzten COMMIT einfügen und die Datei speichern:

-A OS_FIREWALL_ALLOW -p tcp -m state --state NEW -m tcp --dport 24007 -j ACCEPT
-A OS_FIREWALL_ALLOW -p tcp -m state --state NEW -m tcp --dport 24008 -j ACCEPT
-A OS_FIREWALL_ALLOW -p tcp -m state --state NEW -m tcp --dport 2222 -j ACCEPT
-A OS_FIREWALL_ALLOW -p tcp -m state --state NEW -m multiport --dports 49152:49664 -j ACCEPT

Dann die Regeln neu einlesen mit:

$ sudo systemctl reload iptables

GlusterFS und Heketi nutzen LVM zum managen von Storage Devices. Damit das reibungslos funktioniert müssen bestimmte Kernelmodule installiert sein:

dm_thin_pool
dm_multipath
dm_mirror
dm_snapshot

Falls eines der Module fehlen sollte (Mit $ lsmod | grep dm prüfen) dann mit $ modprobe <modul> einfach hinzufügen. Jetzt können wir in OpenShift ein neues Projekt hinzufügen (auf dem Master Node ausführen):

$ oc new-project storage-project

In diesem Projekt deployen wir die notwendigen Pods für Heketi, den Deployer selbst usw. Da Gluster Container projektübergreifend Storage bereitstellen müssen, müssen sie als Priviledged Container deployed werden. Somit erhalten Sie zugriff auf den Host und weitere Pods.

$ oadm policy add-scc-to-user privileged -z default
$ oadm policy add-scc-to-user privileged -z router
$ oadm policy add-scc-to-user privileged -z default

Jetzt den Router einrichten, der dafür sorgt, dass Anfragen an Heketi auch den Pos erreichen können.

$ oadm router storage-project-router --replicas=1

Jetzt können wir uns dran machen die Topology Yaml Datei für Heketi zu erstellen. In dieser Datei definieren wir die einzelen Gluster Nodes und deren angebundene Festplatten.

{
    "clusters": [
        {
            "nodes": [
                {
                    "node": {
                        "hostnames": {
                            "manage": [
                                "ip-10-0-1-66.eu-central-1.compute.internal"
                            ],
                            "storage": [
                                "10.0.1.66"
                            ]
                        },
                        "zone": 1
                    },
                    "devices": [
                        "/dev/docker-vg/testlv"
                    ]
                },
                {
                    "node": {
                        "hostnames": {
                            "manage": [
                                "ip-10-0-1-69.eu-central-1.compute.internal"
                            ],
                            "storage": [
                                "10.0.1.69"
                            ]
                        },
                        "zone": 1
                    },
                    "devices": [
                        "/dev/docker-vg/testlv"
                    ]
                },
                {
                    "node": {
                        "hostnames": {
                            "manage": [
                                "ip-10-0-1-144.eu-central-1.compute.internal"
                            ],
                            "storage": [
                                "10.0.1.144"
                            ]
                        },
                        "zone": 1
                    },
                    "devices": [
                        "/dev/docker-vg/testlv"
                    ]
                }
            ]
        }
    ]
}

Hier gibt es einiges zu beachten. In node.hostnames.manage muss ein FQDN hostname stehen, also keine IP. In node.hostname.storage muss eine IP stehen. Jeder Node muss hier aufgeführt werden und in den devices können mehrere Komma separierte Raw Devices stehen.

Das erwähnte GitHub Projekt installiert zusätzlich zum vorhandenen EC2 root device von 50GB noch eine EBS Volume welches ausschliesslich für die Docker Storage bei der Installation eingerichtet wird. Ich hatte weiter oben erwähnt, dass Heketi jedoch ausschließlich raw devices oder Partitionen erwartet. Damit das funktioniert habe ich über die AWS Console einfach weitere EBS Volumes erstellt und den Instanzen (Nodes) Master, Node1 und Node2 angehängt. Bei AWS werden die EBS Volumes ohne Neustart der ec2 Instanzen auf einen definierten Pfad gemountet. 

Ich habe für die Heketi Topology das /dev/xvdg, also eins neues, frisches EBS Volume gewählt (für jeden Node). Auf dem Master findet man in dem Ordner /usr/share/heketi/topology-sample.json. Diese kann man als Ausgangsbasis nehmen oder die Datei, die ich oben eingefügt habe. Bitte beachten Sie, dass sie Hostnamen und IPs natürlich auch ändern müssen. Hat man solch eine Toplogie Datei erstellen, kann man das Deployment anstossen mit:

sudo cns-deploy -n storage-project -g topology.json

Leider kann an dieser Stelle sehr viel schief gehen. Wenn dem so ist kann man mit

sudo cns-deploy -n storage-project -g topology.json --abort das Deployment abbrechen, Änderungen vornehmen und wieder deployen. genau hier greift auch das besprochene Setting am Anfang bei der OpenShift Installation. Hätten wir dieses Setting nicht, müssten wir auf jedem Node manuell die Platte / das Device mit wipefs leer machen.

Wenn alles gut geht sind die Gluster Pods und Heketi Deployt. Damit wir die heketi cli direkt vom Master Node aus aufrufen können führen Sie bitte folgenden Befehl aus:

$ sudo export HEKETI_CLI_SERVER=$(oc describe svc/heketi | grep "Endpoints:" | awk '{print "http://"$2}')

Jetzt können wir uns mit sudo heketi-cli topology info die GlusterFS Toplogy anschauen. Es wird so etwas erscheinen wie:

Nun müssen wir ein StorageClass definieren und deployen. StorageClasses sind die Schnittstelle zwischen OpenShift und Storage Lösungen wie EBS, Gluster, usw. D.h. eine StorageClass nimmt die Anfrage entgegen und weiß je nach Implementierung wie sie sich den Storage besorgt. Im Falle von AWS weiß sie wie die AWS API aufzurufen ist. Im Falle von GlusterFS, weiß sie wie sie Heketi ansprechen muss, usw. Für GlusterFS erstellen wir auf dem Master eine entsprechende Yaml Datei. Bei mir weiterhin in /usr/share/heketi.

sudo vim /usr/share/heleti/glusterfs-storage-class.yaml

kind: StorageClass
metadata:
  name: glusterfs-storage
provisioner: kubernetes.io/glusterfs
parameters:
  resturl: "http://heketi-storage-project.18.197.68.255.xip.io"
annotations:
  storageclass.beta.kubernetes.io/is-default-class: "true"
  storageclass.kubernetes.io/is-default-class: "true"

Bitte beachten Sie, dass die resturl der Url Ihrer Heketi Installation entsprechen sollte. Des Weiteren ist zu beachten, dass wir als Annotation ein „is-default“ gesetzt haben. Das bedeutet, dass jede Storage Anforderung von OpenShift Standardäßig über GlusterFS bereitgestellt wird. Ansonsten muss man es explizit jedesmal definieren welche StorageClass benutzt werden soll. In dem GitHub Projekt ist zusätzlich die AWS EBS Version erhalten (Das sieht man übrigens an der „provisioner“ property.

$ sudo oc get storageclass
NAME                          TYPE
glusterfs-storage (default)   kubernetes.io/glusterfs
gp2                           kubernetes.io/aws-ebs

Damit kann man sehen, ob die Storage Class erfolgreich deployed wurde. Leider besteht in der OpenShift Version welche wir benutzen ein Bug. Jetzt haben wir zwei default Einträge als StorageClass. Um unsere neue StorageClass wirklich als default zu setzen müssen wir die Default Annotation bei dem bestehenden (alten) StorageClass löschen. Dies geht mit dem Befehl:

sudo oc edit storageclass gp2

Dann einfach die „true“ auf „false“ setzen und speichern. 

Es ist geschafft. Nun können wir entweder Storage direkt in OpenShift erstellen und Pods zuweisen. Oder per Yaml in der Pod Definition direkt anfordern. Um das zu testen haben wir ein Beispiel Projekt (Django-Psql-Persistent) aus dem OpenShift Catalog in der Web GUI benutzt. Dieses Beispiel Projekt deployed eine PostgreSql und fragt nach einem Volume Claim mit dem Namen postgresql. Da wir unsere StorageClass mit GlusterFS als default gesetzt haben nutzt OpenShift diese StorageClass, um Speicher bereitzustellen.

 

Noch deutlicher wird es in der Yaml Datei des Pods:

Zum Schluss noch ein Screenshot wenn man manuell ein Storage einrichtet. In dem Dropdown kann man zwischen den verschiedenen StorageClasses wählen:

Wie Sie merken ist das Thema nicht ganz trivial und es kann eine Menge schief gehen währen der Installation. Hat man es aber geschafft, so hat man ein sehr flexibles und automatisiertes System als Speicherlösung. Nodes können einfach zum Cluster hinzugefügt und als GlusterNode definiert werden. Hier muss man noch beachten, dass Red Hat eine maximale Anzahl von 300 Nodes empfiehlt.

Dipl. Informatiker, CEO skillbyte GmbH

Mission: Mit Blockchain und Künstlicher Intelligenz die HR Welt verändern.
http://www.GravityCv.com, http://www.blockchain-hr.de
Experte in: Java Enterprise, CoreMedia, Spring, Microservices, DevOps, Machine Learning/AI

Masiar IghaniRed Hat OpenShift Installation mit GlusterFS Storage