
Anfang dieses Monats, am 3. Mai, wurde eine bedeutende Veröffentlichung des „Managementsystems für verteilte Datenspeicher in Kubernetes“ angekündigt — . Vor über einem Jahr haben wir bereits eine umfassende Übersicht über Rook gegeben. Damals wurden wir gebeten, über unsere Erfahrungen mit dessen praktischer Anwendung zu berichten, und nun, genau zu diesem wichtigen Meilenstein in der Geschichte des Projekts, freuen wir uns, unsere gesammelten Eindrücke zu teilen.
Kurz gesagt, Rook ist eine Sammlung für Kubernetes, die die Bereitstellung, Verwaltung und automatische Wiederherstellung von Speicherlösungen wie Ceph, EdgeFS, Minio, Cassandra und CockroachDB vollständig übernimmt.
Derzeit ist die am weitesten entwickelte (und in stabile in der Phase) Lösung .
Hinweis: Zu den wesentlichen Änderungen in der Veröffentlichung von Rook 1.0.0, die Ceph betreffen, gehören die Unterstützung von Ceph Nautilus und die Möglichkeit, NFS für CephFS- oder RGW-Buckets zu verwenden. Außerdem hebt sich die „Reifung“ der Unterstützung von EdgeFS auf Beta-Niveau hervor.
In diesem Artikel werden wir:
- die Vorteile diskutieren, die wir bei der Verwendung von Rook zur Bereitstellung von Ceph in einem Kubernetes-Cluster sehen;
- unsere Erfahrungen und Eindrücke aus der Nutzung von Rook in der Produktion teilen;
- Wir erzählen Ihnen, warum wir Rook mit einem «Ja!» willkommen heißen und welche Pläne wir dafür haben.
Beginnen wir mit den allgemeinen Konzepten und der Theorie.
„Ich habe einen Vorteil von einer Dame!“ (unbekannter Schachspieler)

Eines der Hauptmerkmale von Rook ist, dass die Interaktion mit Datenspeichern über Kubernetes-Mechanismen erfolgt. Das bedeutet, dass Sie keine Befehle mehr von einem Blatt Papier in die Konsole kopieren müssen, um Ceph einzurichten.
— Möchten Sie CephFS im Cluster bereitstellen? Schreiben Sie einfach eine YAML-Datei!
— Was? Möchten Sie auch noch einen Object Store mit S3-API bereitstellen? Schreiben Sie einfach eine zweite YAML-Datei!
Rook wurde gemäß den typischen Betreiberrichtlinien entwickelt. Die Interaktion erfolgt über , in denen wir die erforderlichen Eigenschaften der Ceph-Entitäten beschreiben. (da dies die einzige stabile Implementierung ist, wird standardmäßig in diesem Artikel über Ceph gesprochen, es sei denn, es wird ausdrücklich etwas anderes angegeben). Entsprechend den festgelegten Parametern führt der Operator automatisch die notwendigen Befehle zur Konfiguration aus.
Lassen Sie uns die Details anhand des Beispiels der Erstellung eines Object Stores betrachten, genauer gesagt — 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 im Listing angegebenen Parameter sind recht standardisiert und benötigen wahrscheinlich keine weiteren Kommentare. Es ist jedoch wichtig, besondere Aufmerksamkeit auf die hervorgehobenen Template-Variablen zu richten.
Das generelle Funktionsschema ist, dass wir über eine YAML-Datei Ressourcen "anfordern", für die der Operator die entsprechenden Befehle ausführt und uns ein "nicht echtes" Geheimnis zurückgibt, mit dem wir weiterarbeiten können. (siehe unten). Aus den oben angegebenen Variablen wird ein Befehl und der Name des Geheimnisses erstellt.
Was ist das für ein Befehl? Bei der Erstellung eines Benutzers für den Rook-Objektspeicher führt der Rook-Operator innerhalb des Pods Folgendes aus:
radosgw-admin user create --uid="rook-user" --display-name="{{ .Values.s3.username }}"Das Ergebnis der Ausführung dieses Befehls wird eine JSON-Struktur sein:
{
"user_id": "rook-user",
"display_name": "{{ .Values.s3.username }}",
"keys": [
{
"user": "rook-user",
"access_key": "NRWGT19TWMYOB1YDBV1Y",
"secret_key": "gr1VEGIV7rxcP3xvXDFCo4UDwwl2YoNrmtRlIAty"
}
],
...
} Schlüssel — dies wird in Zukunft von Anwendungen benötigt, um über die S3-API auf den Objektspeicher zuzugreifen. Der Rook-Operator wählt diese freundlich aus und speichert sie in seinem Namespace als Geheimnis mit dem Namen rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }}.
Um die Daten aus diesem Geheimnis zu verwenden, fügen Sie sie einfach als Umgebungsvariablen in den Container ein. Hier ein Beispiel für eine Vorlage für einen Job, in dem 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 innerhalb von Kubernetes durchgeführt. Die in den YAML-Dateien beschriebenen Strukturen wurden in ein Git-Repository eingelegt und mehrfach wiederverwendet. Dies stellt einen großen Vorteil für DevOps-Engineering und den gesamten CI/CD-Prozess dar.
Mit Rook und Rados macht es Freude.
Die Verwendung der Kombination Ceph + RBD bringt bestimmte Einschränkungen beim Anbinden von Volumes an Pods mit sich.
Insbesondere muss im Namespace ein Geheimnis für den Zugang zu Ceph vorhanden sein, damit zustandsbehaftete Anwendungen funktionieren können. Es ist in Ordnung, wenn Sie 2-3 Umgebungen in Ihren Namespaces haben: Sie können das Geheimnis manuell kopieren. Aber was ist, wenn für jedes Feature der Entwickler eine separate Umgebung mit eigenem Namespace erstellt wird?
Wir haben dieses Problem mit Hilfe von gelöst, das Geheimnisse automatisch in neue Namespaces kopiert (ein Beispiel für einen solchen Hook ist in ).
#! /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 -
fiBei der Verwendung von Rook existiert dieses Problem jedoch einfach nicht. Der Montageprozess erfolgt über eigene Treiber auf Basis von oder (derzeit in der Betaphase) und benötigt daher keine Geheimnisse.
Rook löst automatisch viele Probleme, was uns dazu bringt, es in neuen Projekten zu verwenden.
Die Belagerung von Rook
Beenden wir den praktischen Teil mit der Bereitstellung von Rook und Ceph, um eigene Experimente durchzuführen. Um diese unüberwindbare Festung leichter zu erobern, haben die Entwickler ein Helm-Paket vorbereitet. Lassen Sie uns dieses herunterladen:
$ helm fetch rook-master/rook-ceph --untar --version 1.0.0 In der Datei rook-ceph/values.yaml findet man eine Vielzahl von verschiedenen Einstellungen. Am wichtigsten ist, die Tolerierungen für Agenten und die Suche anzugeben. Für was der Mechanismus aus Taints/Tolerierungen verwendet werden kann, haben wir ausführlich in .
Wenn man es kurz zusammenfasst, wollen wir nicht, dass Pods mit der Client-Anwendung auf den gleichen Knoten platziert werden, auf denen die Speicherlaufwerke liegen. Der Grund ist einfach: So wird die Arbeit der Rook-Agenten die Anwendung nicht beeinflussen.
Öffnen wir also die Datei rook-ceph/values.yaml mit dem bevorzugten Editor und fügen am Ende den folgenden Block hinzu:
discover:
toleration: NoExecute
tolerationKey: node-role/storage
agent:
toleration: NoExecute
tolerationKey: node-role/storage
mountSecurityMode: AnyFügen Sie jedem Knoten, der für die Datenspeicherung reserviert ist, den entsprechenden Taint hinzu:
$ kubectl taint node ${NODE_NAME} node-role/storage="":NoExecuteDann installieren wir das Helm-Chart mit dem Befehl:
$ helm install --namespace ${ROOK_NAMESPACE} ./rook-cephJetzt müssen wir einen Cluster erstellen und den Speicherort 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 Status von Ceph – wir erwarten zu sehen 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 -sGleichzeitig überprüfen wir, dass die Pods mit der Client-Anwendung nicht auf die für Ceph reservierten Knoten gelangen:
$ kubectl -n ${APPLICATION_NAMESPACE} get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeNameOptional können zusätzliche Komponenten konfiguriert werden. Detaillierte Informationen dazu finden Sie in . Für die Verwaltung empfehlen wir dringend die Installation des Dashboards und des Werkzeugkastens.
Rook-Kuren: Reicht Rook für alles aus?
Wie man sieht, ist die Entwicklung von Rook in vollem Gange. Es gibt jedoch nach wie vor Probleme, die uns daran hindern, vollständig auf die manuelle Konfiguration von Ceph zu verzichten:
- Kein Rook-Treiber Metriken zur Nutzung der montierten Blöcke exportieren, was uns der Überwachung beraubt.
- Flexvolume und CSI ändern die Größe von Volumes (im Gegensatz zu RBD), sodass Rook ein nützliches (und manchmal sogar kritisches!) Werkzeug entbehrt.
- Rook ist nach wie vor nicht so flexibel wie das herkömmliche Ceph. Wenn wir beispielsweise den Pool für die Metadaten von CephFS auf SSDs speichern und die Daten auf HDDs ablegen möchten, müssen wir manuell separate Gerätegruppen in den CRUSH-Maps definieren.
- Obwohl der rook-ceph-operator als stabil gilt, gibt es derzeit bestimmte Probleme beim Upgrade von Ceph von Version 13 auf 14.
Fazit
„Momentan ist der Turm durch Bauern von der Außenwelt abgeschottet, aber wir glauben, dass er eines Tages eine entscheidende Rolle im Spiel spielen wird!“ (Zitat wurde speziell für diesen Artikel erfunden)
Das Projekt Rook hat zweifellos unsere Herzen erobert – wir sind der Meinung, dass [es mit all seinen Vor- und Nachteilen] definitiv Ihre Aufmerksamkeit verdient.
Unsere zukünftigen Pläne sehen vor, das Rook-Ceph-Modul für , was die Nutzung in unseren zahlreichen Kubernetes-Clustern noch einfacher und benutzerfreundlicher gestaltet.
P.S.
Lesen Sie auch in unserem Blog:
- «»;
- «»;
- «»;
- «»;
- «».
Quelle: habr.com
