Turm oder nicht Turm - das ist die Frage



Anfang dieses Monats, am 3. Mai, wurde eine Hauptversion des „Managementsystems für verteilte Data Warehouses in Kubernetes“ angekündigt - Rook 1.0.0 . Vor mehr als einem Jahr haben wir bereits einen allgemeinen Überblick über Rook veröffentlicht. Dann wurden wir gebeten, über die Erfahrungen mit seiner praktischen Anwendung zu sprechen - und jetzt, rechtzeitig für einen so bedeutenden Meilenstein in der Geschichte des Projekts, freuen wir uns, unsere gesammelten Eindrücke zu teilen.

Kurz gesagt, Rook ist eine Gruppe von Operatoren für Kubernetes, die die vollständige Kontrolle über die Bereitstellung, Verwaltung und automatische Wiederherstellung von Speicherlösungen wie Ceph, EdgeFS, Minio, Cassandra und CockroachDB übernehmen.

Derzeit ist der Turm-Ceph-Operator die am weitesten entwickelte (und nur in einem stabilen Stadium) Lösung.

Hinweis : Unter den signifikanten Änderungen in der Version von Rook 1.0.0 in Bezug auf Ceph sind die Unterstützung von Ceph Nautilus und die Möglichkeit, NFS für CephFS- oder RGW-Buckets zu verwenden, zu vermerken. Unter anderem fällt die "Reifung" der EdgeFS-Unterstützung auf Beta-Ebene auf.

Also, in diesem Artikel haben wir:

  • Beantworten Sie die Frage, welche Vorteile sich aus der Verwendung von Rook für die Bereitstellung von Ceph im Kubernetes-Cluster ergeben.
  • Erfahrungen und Eindrücke über die Verwendung von Rook in der Produktion teilen;
  • Wir werden Ihnen sagen, warum wir Rook und unseren Plänen für ihn "Ja" sagen.

Beginnen wir mit allgemeinen Konzepten und Theorien.

"Ich habe einen Vorteil in einem Turm!" (unbekannter Schachspieler)




Einer der Hauptvorteile von Rook ist, dass die Interaktion mit Data Warehouses über Kubernetes-Mechanismen erfolgt. Dies bedeutet, dass Sie die Befehle zum Konfigurieren von Ceph nicht mehr aus der Broschüre in die Konsole kopieren müssen.

- Möchten Sie in einem CephFS-Cluster bereitstellen? Schreiben Sie einfach eine Yaml-Datei!
- Was? Möchten Sie einen Objektspeicher mit der S3-API bereitstellen? Schreiben Sie einfach eine zweite Yaml-Datei!

Rook wird nach allen Regeln eines typischen Operators erstellt. Die Interaktion damit erfolgt mithilfe von CRD (Custom Resource Definitions) , in denen wir die Merkmale der von uns benötigten Ceph-Entitäten beschreiben (da dies die einzige stabile Implementierung ist, wird in dem Artikel standardmäßig über Ceph gesprochen, sofern nicht ausdrücklich anders angegeben) . Entsprechend den angegebenen Parametern führt der Bediener automatisch die zum Einrichten erforderlichen Befehle aus.

Schauen wir uns die Besonderheiten am Beispiel des Erstellens eines Objektspeichers bzw. CephObjectStoreUser .

 apiVersion: ceph.rook.io/v1 kind: CephObjectStore metadata: name: {{ .Values.s3.crdName }} namespace: kube-rook spec: metadataPool: failureDomain: host replicated: size: 3 dataPool: failureDomain: host erasureCoded: dataChunks: 2 codingChunks: 1 gateway: type: s3 sslCertificateRef: port: 80 securePort: instances: 1 allNodes: false --- apiVersion: ceph.rook.io/v1 kind: CephObjectStoreUser metadata: name: {{ .Values.s3.crdName }} namespace: kube-rook spec: store: {{ .Values.s3.crdName }} displayName: {{ .Values.s3.username }} 

Die in der Auflistung angegebenen Parameter sind ziemlich Standard und benötigen wahrscheinlich keine Kommentare. Sie sollten jedoch besonders auf diejenigen achten, die in den Vorlagenvariablen hervorgehoben sind.

Das allgemeine Arbeitsschema beruht auf der Tatsache, dass wir über die YAML-Datei Ressourcen „bestellen“, für die der Bediener die erforderlichen Befehle ausführt und uns das „nicht echte“ Geheimnis zurückgibt, mit dem wir weiterarbeiten können (siehe unten) . Und aus den oben angegebenen Variablen werden der Befehl und der Name des Geheimnisses zusammengesetzt.

Was für ein Team ist das? Wenn Sie einen Benutzer für die Objektspeicherung erstellen, führt die Rook-Anweisung im Pod Folgendes aus:

 radosgw-admin user create --uid="rook-user" --display-name="{{ .Values.s3.username }}" 

Das Ergebnis dieses Befehls ist eine JSON-Struktur:

 { "user_id": "rook-user", "display_name": "{{ .Values.s3.username }}", "keys": [ { "user": "rook-user", "access_key": "NRWGT19TWMYOB1YDBV1Y", "secret_key": "gr1VEGIV7rxcP3xvXDFCo4UDwwl2YoNrmtRlIAty" } ], ... } 

Keys sind das, was zukünftige Anwendungen benötigen, um über die S3-API auf den Objektspeicher zuzugreifen. Der Rook-Operator wählt sie freundlicherweise aus und legt sie als Geheimnis mit dem Namen rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }} in seinem Namespace ab.

Um Daten aus diesem Geheimnis zu verwenden, fügen Sie sie einfach als Umgebungsvariablen zum Container hinzu. Als Beispiel gebe ich eine Vorlage für Job an, in der wir automatisch Buckets für jede Benutzerumgebung erstellen:

 {{- range $bucket := $.Values.s3.bucketNames }} apiVersion: batch/v1 kind: Job metadata: name: create-{{ $bucket }}-bucket-job annotations: "helm.sh/hook": post-install "helm.sh/hook-weight": "2" spec: template: metadata: name: create-{{ $bucket }}-bucket-job spec: restartPolicy: Never initContainers: - name: waitdns image: alpine:3.6 command: ["/bin/sh", "-c", "while ! getent ahostsv4 rook-ceph-rgw-{{ $.Values.s3.crdName }}; do sleep 1; done" ] - name: config image: rook/ceph:v1.0.0 command: ["/bin/sh", "-c"] args: ["s3cmd --configure --access_key=$(ACCESS-KEY) --secret_key=$(SECRET-KEY) -s --no-ssl --dump-config | tee /config/.s3cfg"] volumeMounts: - name: config mountPath: /config env: - name: ACCESS-KEY valueFrom: secretKeyRef: name: rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }} key: AccessKey - name: SECRET-KEY valueFrom: secretKeyRef: name: rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }} key: SecretKey containers: - name: create-bucket image: rook/ceph:v1.0.0 command: - "s3cmd" - "mb" - "--host=rook-ceph-rgw-{{ $.Values.s3.crdName }}" - "--host-bucket= " - "s3://{{ $bucket }}" ports: - name: s3-no-sll containerPort: 80 volumeMounts: - name: config mountPath: /root volumes: - name: config emptyDir: {} --- {{- end }} 

Alle in diesem Job aufgeführten Aktivitäten wurden durchgeführt, ohne über Kubernetes hinauszugehen. Die in den YAML-Dateien beschriebenen Strukturen werden in das Git-Repository gefaltet und wiederholt wiederverwendet. Darin sehen wir ein großes Plus für die DevOps-Ingenieure und den gesamten CI / CD-Prozess.

Mit Turm und Rados zur Freude


Die Verwendung einer Reihe von Ceph + RBD unterwirft bestimmte Einschränkungen für das Mounten von Volumes an Pods.

Insbesondere muss der Namespace ein Geheimnis haben, um auf Ceph zugreifen zu können, damit statusbehaftete Anwendungen funktionieren können. Es ist in Ordnung, wenn Sie 2-3 Umgebungen in Ihren Namespaces haben: Sie können das Geheimnis manuell kopieren. Was aber, wenn jede Entwicklerfunktion eine separate Umgebung mit einem eigenen Namespace erstellt?

Wir haben dieses Problem mit Hilfe eines Shell-Operators gelöst, der Geheimnisse automatisch in den neuen Namespace kopierte (ein Beispiel für einen solchen Hook wird in diesem Artikel beschrieben ).

 #! /bin/bash if [[ $1 == “--config” ]]; then cat <<EOF {"onKubernetesEvent":[ {"name": "OnNewNamespace", "kind": "namespace", "event": ["add"] } ]} EOF else NAMESPACE=$(kubectl get namespace -o json | jq '.items | max_by( .metadata.creationTimestamp ) | .metadata.name') kubectl -n ${CEPH_SECRET_NAMESPACE} get secret ${CEPH_SECRET_NAME} -o json | jq ".metadata.namespace=\"${NAMESPACE}\"" | kubectl apply -f - fi 

Bei Verwendung von Rook besteht dieses Problem jedoch einfach nicht. Der Montageprozess erfolgt mit Ihren eigenen Treibern, die auf Flexvolume oder CSI basieren (noch in der Beta) und erfordert daher keine Geheimnisse.

Rook löst automatisch viele Probleme, was uns ermutigt, es in neuen Projekten zu verwenden.

Belagerung des Turmes


Wir vervollständigen den praktischen Teil mit dem Einsatz von Rook und Ceph für die Möglichkeit, eigene Experimente durchzuführen. Um diesen uneinnehmbaren Turm zu stürmen, war es einfacher, die Entwickler haben ein Helm-Paket vorbereitet. Laden wir es herunter:

 $ helm fetch rook-master/rook-ceph --untar --version 1.0.0 

In der Datei rook-ceph/values.yaml finden Sie viele verschiedene Einstellungen. Geben Sie vor allem Toleranzen für Agenten und die Suche an. Warum Sie den Mechanismus für Verschmutzungen / Toleranzen verwenden können, haben wir in diesem Artikel ausführlich beschrieben .

Kurz gesagt, wir möchten nicht, dass sich Pods mit der Clientanwendung auf denselben Knoten befinden, auf denen sich die Datenträger für die Datenspeicherung befinden. Der Grund ist einfach: Auf diese Weise wirkt sich die Arbeit der Rook-Agenten nicht auf die Anwendung selbst aus.

Öffnen Sie also die Datei rook-ceph/values.yaml bevorzugten Editor und fügen Sie am Ende den folgenden Block hinzu:

 discover: toleration: NoExecute tolerationKey: node-role/storage agent: toleration: NoExecute tolerationKey: node-role/storage mountSecurityMode: Any 

Fügen Sie für jeden für die Datenspeicherung reservierten Knoten den entsprechenden Makel hinzu:

 $ kubectl taint node ${NODE_NAME} node-role/storage="":NoExecute 

Installieren Sie dann die Helmkarte mit dem Befehl:

 $ helm install --namespace ${ROOK_NAMESPACE} ./rook-ceph 

Jetzt müssen Sie einen Cluster erstellen und den Speicherort des OSD angeben:

 apiVersion: ceph.rook.io/v1 kind: CephCluster metadata: clusterName: "ceph" finalizers: - cephcluster.ceph.rook.io generation: 1 name: rook-ceph spec: cephVersion: image: ceph/ceph:v13 dashboard: enabled: true dataDirHostPath: /var/lib/rook/osd mon: allowMultiplePerNode: false count: 3 network: hostNetwork: true rbdMirroring: workers: 1 placement: all: tolerations: - key: node-role/storage operator: Exists storage: useAllNodes: false useAllDevices: false config: osdsPerDevice: "1" storeType: filestore resources: limits: memory: "1024Mi" requests: memory: "1024Mi" nodes: - name: host-1 directories: - path: "/mnt/osd" - name: host-2 directories: - path: "/mnt/osd" - name: host-3 directories: - path: "/mnt/osd" 

Überprüfen Sie den Ceph-Status - erwarten Sie HEALTH_OK :

 $ kubectl -n ${ROOK_NAMESPACE} exec $(kubectl -n ${ROOK_NAMESPACE} get pod -l app=rook-ceph-operator -o name -o jsonpath='{.items[0].metadata.name}') -- ceph -s 

Überprüfen Sie gleichzeitig, ob Pods mit der Clientanwendung nicht zu den für Ceph reservierten Knoten gelangen:

 $ kubectl -n ${APPLICATION_NAMESPACE} get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName 

Weitere optionale Komponenten sind konfiguriert. Weitere Informationen dazu finden Sie in der Dokumentation . Für die Verwaltung empfehlen wir dringend, das Dashboard und die Toolbox zu installieren.

Türme und Haken: Ist Turm genug für alles?


Wie Sie sehen können, ist die Entwicklung von Rook in vollem Gange. Dennoch gibt es Probleme, die es uns nicht ermöglichen, die manuelle Konfiguration von Ceph vollständig aufzugeben:

  • Kein Rook-Treiber kann Metriken zur Verwendung von montierten Blöcken exportieren, was uns die Überwachung entzieht.
  • Flexvolume und CSI wissen nicht, wie die Größe von Volumes geändert werden soll (im Gegensatz zu derselben RBD), daher verliert Rook ein nützliches (und manchmal kritisch notwendiges!) Tool.
  • Rook ist immer noch nicht so flexibel wie reguläres Ceph. Wenn Sie den Pool für CephFS-Metadaten zum Speichern auf der SSD und die Daten selbst auf der Festplatte konfigurieren möchten, müssen Sie einzelne Gerätegruppen manuell in CRUSH-Maps registrieren.
  • Trotz der Tatsache, dass der Turm-Ceph-Operator als stabil angesehen wird, gibt es derzeit bestimmte Probleme beim Aktualisieren von Ceph von Version 13 auf 14.

Schlussfolgerungen


"Jetzt ist Rook durch Bauern von der Außenwelt verschlossen, aber wir glauben, dass sie eines Tages eine entscheidende Rolle in der Partei spielen wird!" (Ein Zitat wurde speziell für diesen Artikel erfunden)

Das Rook-Projekt hat zweifellos unsere Herzen erobert - wir glauben, dass es [mit all seinen Vor- und Nachteilen] definitiv Ihre Aufmerksamkeit verdient.

Unsere weiteren Pläne bestehen darin, rook-ceph zu einem Modul für Addon-Operatoren zu machen , das die Verwendung in unseren vielen Kubernetes-Clustern noch einfacher und bequemer macht.

PS


Lesen Sie auch in unserem Blog:

Source: https://habr.com/ru/post/de451818/


All Articles