Praktisches Beispiel für die Anbindung eines Ceph-Speichers an ein Kubernetes-Cluster

Das Container Storage Interface (CSI) ist ein einheitlicher Schnittstellenstandard für die Interaktion zwischen Kubernetes und Speichersystemen. Wir haben bereits kurz darüber gesprochen, und berichteten, und heute betrachten wir genauer die Verbindung zwischen CSI und Ceph: wir zeigen, wie man Speicher Ceph zum Kubernetes-Cluster hinzufügt.
Der Artikel enthält einige reale, wenn auch etwas vereinfachte Beispiele zur besseren Verständlichkeit. Informationen zur Installation und Konfiguration von Ceph- und Kubernetes-Clustern werden hier nicht behandelt.

Interessiert es Sie, wie das funktioniert?

Praktisches Beispiel für die Anbindung eines Ceph-Speichers an ein Kubernetes-Cluster

Also haben Sie einen Kubernetes-Cluster, der beispielsweise mit kubespraybereits eingerichtet wurde. Nebenan läuft ein Ceph-Cluster, das man ebenfalls mit diesem Playbook-Seteinrichten kann. Ich hoffe, es muss nicht erwähnt werden, dass zwischen ihnen ein Netzwerk mit einer Bandbreite von mindestens 10 Gbit/s bestehen muss.

Wenn Sie all das haben, legen wir los!

Zuerst melden wir uns an einem der Knoten des Ceph-Clusters an und überprüfen, ob alles in Ordnung ist:

ceph health
ceph -s

Dann erstellen wir sofort einen Pool für RBD-Disketten:

ceph osd pool create kube 32
ceph osd pool application enable kube rbd

Gehen wir zum Kubernetes-Cluster. Dort installieren wir zunächst den Ceph CSI-Treiber für RBD. Wir werden es, wie es sich gehört, über Helm installieren.
Wir fügen das Repository mit dem Chart hinzu und erhalten eine Reihe von Variablen für das Chart ceph-csi-rbd:

helm repo add ceph-csi https://ceph.github.io/csi-charts
helm inspect values ceph-csi/ceph-csi-rbd > cephrbd.yml

Jetzt müssen wir die Datei cephrbd.yml ausfüllen. Dafür holen wir uns die Cluster-ID und die IP-Adressen der Monitore in Ceph:

ceph fsid  # so erfahren wir die clusterID
ceph mon dump  # und hier sehen wir die IP-Adressen der Monitore

Die ermittelten Werte tragen wir in die Datei cephrbd.yml ein. Gleichzeitig aktivieren wir die Erstellung von PSPs (Pod Security Policies). Die Optionen in den Abschnitten nodeplugin und provisioner sind bereits in der Datei vorhanden und können wie folgt geändert werden:

csiConfig:
  - clusterID: "bcd0d202-fba8-4352-b25d-75c89258d5ab"
    monitors:
      - "v2:172.18.8.5:3300/0,v1:172.18.8.5:6789/0"
      - "v2:172.18.8.6:3300/0,v1:172.18.8.6:6789/0"
      - "v2:172.18.8.7:3300/0,v1:172.18.8.7:6789/0"

nodeplugin:
  podSecurityPolicy:
    enabled: true

provisioner:
  podSecurityPolicy:
    enabled: true

Danach bleibt uns nur zu tun — das Chart im Kubernetes-Cluster installieren.

helm upgrade -i ceph-csi-rbd ceph-csi/ceph-csi-rbd -f cephrbd.yml -n ceph-csi-rbd --create-namespace

Ausgezeichnet, der RBD-Treiber funktioniert!
Lassen Sie uns eine neue StorageClass in Kubernetes erstellen. Dazu müssen wir wieder etwas mit Ceph arbeiten.

Wir erstellen einen neuen Benutzer in Ceph und gewähren ihm Schreibrechte für den Pool kube:

ceph auth get-or-create client.rbdkube mon 'profile rbd' osd 'profile rbd pool=kube'

Und nun schauen wir uns den Zugriffsschlüssel dort an:

ceph auth get-key client.rbdkube

Der Befehl wird etwas Ähnliches ausgeben:

AQCO9NJbhYipKRAAMqZsnqqS/T8OYQX20xIa9A==

Wir tragen diesen Wert in einen Secret im Kubernetes-Cluster ein — dorthin, wo er benötigt wird. userKey:

---
apiVersion: v1
kind: Secret
metadata:
  name: csi-rbd-secret
  namespace: ceph-csi-rbd
stringData:
  # Die Schlüsselwerte entsprechen dem Benutzernamen und dessen Schlüssel, wie im
  # Ceph-Cluster angegeben. Die ID des Benutzers muss Zugriffsrechte auf den Pool haben,
  # der in der Storage Class angegeben ist.
  userID: rbdkube
  userKey:

Und wir erstellen unser Geheimnis:

kubectl apply -f secret.yaml

Wir benötigen als Nächstes ein ungefähr solches Manifest für die StorageClass:

---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: csi-rbd-sc
provisioner: rbd.csi.ceph.com
parameters:
   clusterID: 
   pool: kube

   imageFeatures: layering

   # Diese Geheimnisse müssen Daten für die Authentifizierung
   # in Ihren Pool enthalten.
   csi.storage.k8s.io/provisioner-secret-name: csi-rbd-secret
   csi.storage.k8s.io/provisioner-secret-namespace: ceph-csi-rbd
   csi.storage.k8s.io/controller-expand-secret-name: csi-rbd-secret
   csi.storage.k8s.io/controller-expand-secret-namespace: ceph-csi-rbd
   csi.storage.k8s.io/node-stage-secret-name: csi-rbd-secret
   csi.storage.k8s.io/node-stage-secret-namespace: ceph-csi-rbd

   csi.storage.k8s.io/fstype: ext4

reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions:
  - discard

Muss ausgefüllt werden: clusterID, den wir bereits mit dem Befehl ceph fsid, und wenden dieses Manifest im Kubernetes-Cluster an:

kubectl apply -f storageclass.yaml

Um die Funktionalität der Cluster in Verbindung zu überprüfen, erstellen wir diesen PVC (Persistent Volume Claim):

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: rbd-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: csi-rbd-sc

Schauen wir uns sofort an, wie Kubernetes in Ceph das angeforderte Volume erstellt hat:

kubectl get pvc
kubectl get pv

Sieht gut aus! Und wie sieht es auf der Ceph-Seite aus?
Wir holen uns eine Liste der Volumes im Pool und sehen die Informationen zu unserem Volume an:

rbd ls -p kube
rbd -p kube info csi-vol-eb3d257d-8c6c-11ea-bff5-6235e7640653  # hier wird natürlich eine andere ID des Volumes angezeigt, die von dem vorherigen Befehl ausgegeben wurde.

Jetzt schauen wir uns an, wie die Größe des RBD-Volumes verändert wird.
Wir ändern die Größe des Volumes im Manifest pvc.yaml auf 2Gi und wenden es an:

kubectl apply -f pvc.yaml

Warten wir, bis die Änderungen wirksam werden, und sehen wir uns erneut die Größe des Volumes an.

rbd -p kube info csi-vol-eb3d257d-8c6c-11ea-bff5-6235e7640653

kubectl get pv
kubectl get pvc

Wir sehen, dass die Größe des PVC unverändert geblieben ist. Um den Grund herauszufinden, können wir Kubernetes auffordern, die Beschreibung des PVC im YAML-Format abzurufen:

kubectl get pvc rbd-pvc -o yaml

Und hier ist das Problem:

message: Warten auf den Benutzer, um einen Pod (neu) zu starten, um die Größenänderung des Dateisystems des Volumes auf dem Knoten abzuschließen. type: FileSystemResizePending

Das bedeutet, die Disk wurde vergrößert, aber das Dateisystem darauf — nicht.
Um das Dateisystem zu vergrößern, muss das Volume gemountet werden. Unser erstellter PVC/PV wird zurzeit jedoch nicht genutzt.

Wir können einen Test-Pod erstellen, zum Beispiel so:

---
apiVersion: v1
kind: Pod
metadata:
  name: csi-rbd-demo-pod
spec:
  containers:
    - name: web-server
      image: nginx:1.17.6
      volumeMounts:
        - name: mypvc
          mountPath: /data
  volumes:
    - name: mypvc
      persistentVolumeClaim:
        claimName: rbd-pvc
        readOnly: false

Und jetzt schauen wir uns das PVC an:

kubectl get pvc

Die Größe hat sich geändert, alles in Ordnung.

Im ersten Teil haben wir mit dem Blockgerät RBD gearbeitet (was auch für Rados Block Device steht), aber das ist nicht möglich, wenn verschiedene Mikrodienste gleichzeitig auf dieses Laufwerk zugreifen sollen. Für die Arbeit mit Dateien, und nicht mit einem Abbild des Laufwerks, ist CephFS viel besser geeignet.
Anhand von Ceph- und Kubernetes-Clustern richten wir CSI und andere erforderliche Entitäten für die Arbeit mit CephFS ein.

Wir erhalten die Werte aus dem benötigten neuen Helm-Chart:

helm inspect values ceph-csi/ceph-csi-cephfs > cephfs.yml

Wieder müssen wir die Datei cephfs.yml ausfüllen. Wie zuvor helfen uns die Ceph-Befehle:

ceph fsid
ceph mon dump

Das Ausfüllen der Datei mit Werten sieht ungefähr so aus:

csiConfig:
  - clusterID: "bcd0d202-fba8-4352-b25d-75c89258d5ab"
    monitors:
      - "172.18.8.5:6789"
      - "172.18.8.6:6789"
      - "172.18.8.7:6789"

nodeplugin:
  httpMetrics:
    enabled: true
    containerPort: 8091
  podSecurityPolicy:
    enabled: true

provisioner:
  replicaCount: 1
  podSecurityPolicy:
    enabled: true

Bitte beachten Sie, dass die Adressen der Monitore in einfacher Form address:port angegeben werden. Für das Einhängen von cephfs auf dem Knoten werden diese Adressen an das Kernel-Modul übergeben, das noch nicht mit dem v2-Monitorprotokoll umgehen kann.
Den Port für httpMetrics (woher Prometheus Metriken für das Monitoring abruft) ändern wir, um Konflikte mit dem nginx-proxy zu vermeiden, der von Kubespray installiert wird. Möglicherweise benötigen Sie das nicht.

Wir installieren das Helm-Chart im Kubernetes-Cluster:

helm upgrade -i ceph-csi-cephfs ceph-csi/ceph-csi-cephfs -f cephfs.yml -n ceph-csi-cephfs --create-namespace

Wir wechseln zum Ceph-Datenspeicher, um dort einen separaten Benutzer zu erstellen. In der Dokumentation heißt es, dass der Provisionierer für CephFS Administratorrechte im Cluster benötigt. Aber wir werden einen separaten Benutzer erstellen fs mit eingeschränkten Rechten:

ceph auth get-or-create client.fs mon 'allow r' mgr 'allow rw' mds 'allow rws' osd 'allow rw pool=cephfs_data, allow rw pool=cephfs_metadata'

Und wir sehen uns sofort seinen Zugriffsschlüssel an, der uns später nützlich sein wird:

ceph auth get-key client.fs

Wir erstellen separate Secret und StorageClass.
Nichts Neues, das haben wir bereits am Beispiel von RBD gesehen:

---
apiVersion: v1
kind: Secret
metadata:
  name: csi-cephfs-secret
  namespace: ceph-csi-cephfs
stringData:
  # Notwendig für dynamisch erstellte Volumes
  adminID: fs
  adminKey:

Wir wenden das Manifest an:

kubectl apply -f secret.yaml

Und jetzt – eine separate StorageClass:

---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: csi-cephfs-sc
provisioner: cephfs.csi.ceph.com
parameters:
  clusterID: 

  # Name des CephFS-Dateisystems, in dem das Volume erstellt wird
  fsName: cephfs

  # (optional) Ceph-Pool, in dem die Daten des Volumes gespeichert werden
  # pool: cephfs_data

  # (optional) Durch Kommas getrennte Mount-Optionen für Ceph-fuse
  # z. B.:
  # fuseMountOptions: debug

  # (optional) Durch Kommas getrennte Mount-Optionen für das CephFS-Kernel
  # Siehe man mount.ceph für die Liste dieser Optionen. Zum Beispiel:
  # kernelMountOptions: readdir_max_bytes=1048576,norbytes

  # Die Geheimnisse müssen Zugangsdaten für den Administrator und/oder Benutzer von Ceph enthalten.
  csi.storage.k8s.io/provisioner-secret-name: csi-cephfs-secret
  csi.storage.k8s.io/provisioner-secret-namespace: ceph-csi-cephfs
  csi.storage.k8s.io/controller-expand-secret-name: csi-cephfs-secret
  csi.storage.k8s.io/controller-expand-secret-namespace: ceph-csi-cephfs
  csi.storage.k8s.io/node-stage-secret-name: csi-cephfs-secret
  csi.storage.k8s.io/node-stage-secret-namespace: ceph-csi-cephfs

  # (optional) Der Treiber kann entweder ceph-fuse (fuse) oder den Ceph-Kernelclient (kernel) verwenden.
  # Wenn nichts angegeben ist, wird die standardmäßige Volume-Montierung verwendet,
  # dies wird durch die Suche nach ceph-fuse und mount.ceph bestimmt.
  # mounter: kernel
reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions:
  - debug

Wir fügen hier etwas hinzu clusterID und wenden es in Kubernetes an:

kubectl apply -f storageclass.yaml

Überprüfung

Zur Überprüfung, wie im vorherigen Beispiel, erstellen wir ein PVC:

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: csi-cephfs-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
  storageClassName: csi-cephfs-sc

Und wir überprüfen das Vorhandensein von PVC/PV:

kubectl get pvc
kubectl get pv

Wenn Sie die Dateien und Verzeichnisse in CephFS ansehen möchten, können Sie dieses Dateisystem irgendwo einhängen. Zum Beispiel, wie unten gezeigt.

Gehen wir zu einem der Knoten im Ceph-Cluster und führen wir folgende Schritte aus:

# Точка монтирования
mkdir -p /mnt/cephfs

# Создаём файл с ключом администратора
ceph auth get-key client.admin >/etc/ceph/secret.key

# Добавляем запись в /etc/fstab
# !! Изменяем ip адрес на адрес нашего узла
echo "172.18.8.6:6789:/ /mnt/cephfs ceph name=admin,secretfile=/etc/ceph/secret.key,noatime,_netdev    0       2" >> /etc/fstab

mount /mnt/cephfs

Natürlich ist solch eine FS-Montage auf einem Ceph-Knoten ausschließlich zu Ausbildungszwecken geeignet, was wir auch in unseren Slurm-Kursen. Ich denke nicht, dass das jemand in der Produktion machen würde, da die Gefahr besteht, wichtige Dateien versehentlich zu überschreiben.

Und schließlich wollen wir überprüfen, wie es bei CephFS mit der Größenänderung des Volumes aussieht. Kehren wir zu Kubernetes zurück und bearbeiten wir unser Manifest für PVC – wir erhöhen die Größe beispielsweise auf 7Gi.

Wenden Sie die bearbeitete Datei an:

kubectl apply -f pvc.yaml

Sehen wir uns im montierten Verzeichnis an, wie sich das Kontingent geändert hat:

getfattr -n ceph.quota.max_bytes

Um diesen Befehl auszuführen, müssen Sie möglicherweise das Paket in Ihrem System installieren attr.

Die Augen haben Angst, aber die Hände machen

Auf den ersten Blick erscheinen all diese Zaubersprüche und langen YAML-Manifeste komplex, aber in der Praxis kommen die Studierenden von Sljorm damit ziemlich schnell zurecht.
In diesem Artikel sind wir nicht ins Detail gegangen – dafür gibt es die offizielle Dokumentation. Wenn Sie an den Einzelheiten der Konfiguration des Ceph-Speichers in Verbindung mit dem Kubernetes-Cluster interessiert sind, helfen Ihnen die folgenden Links:

Allgemeine Funktionsprinzipien von Kubernetes mit Volumes
Dokumentation zu RBD
Integration von RBD und Kubernetes aus der Perspektive von Ceph
Integration von RBD und Kubernetes aus der Perspektive von CSI
Allgemeine Dokumentation zu CephFS
Integration von CephFS und Kubernetes aus der Perspektive von CSI

Im Sljorm-Kurs Kubernetes Basis können Sie noch einen Schritt weiter gehen und eine echte Anwendung in Kubernetes bereitstellen, die CephFS als dateispeicher nutzt. Mithilfe von GET/POST-Anfragen können Sie Dateien übertragen und aus Ceph abrufen.

Und wenn Sie sich mehr für die Datenspeicherung interessieren, melden Sie sich für den neuen Kurs zu Cephan. Während der Beta-Testphase kann der Kurs mit Rabatt erworben werden und Sie können auf dessen Inhalt Einfluss nehmen.

Autor des Artikels: Alexander Schvalov, praktizierender Ingenieur Southbridge, Certified Kubernetes Administrator, Autor und Entwickler der Sljorm-Kurse.

Quelle: habr.com

60GB SSD 8Gb DDR4