
Anfang dieses Monats, am 3. Mai, wurde die große Veröffentlichung des "Management-Systems für verteilte Datenlager in Kubernetes" angekündigt — . Vor über einem Jahr haben wir bereits allgemeine Übersicht über Rook veröffentlicht. Damals wurden wir gebeten, über die Erfahrungen mit dessen praktischer Anwendung zu berichten — und nun, rechtzeitig zu diesem bedeutenden Meilenstein in der Geschichte des Projekts, freuen wir uns, die 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 übernehmen.
Derzeit ist die am weitesten entwickelte (und in stabile in der) .
Hinweis: zu den bedeutenden Änderungen in der Veröffentlichung Rook 1.0.0, die Ceph betreffen, gehört die Unterstützung von Ceph Nautilus und die Möglichkeit, NFS für CephFS- oder RGW-Buckets zu verwenden. Unter anderen fällt die "Reifung" der Unterstützung von EdgeFS auf Beta-Level auf.
In diesem Artikel werden wir:
- die Frage beantworten, welche Vorteile wir in der Nutzung von Rook für die Bereitstellung von Ceph in einem Kubernetes-Cluster sehen;
- unsere Erfahrungen und Eindrücke von der Nutzung von Rook in der Produktion teilen;
- erklären, warum wir Rook "Ja!" sagen und welche Pläne wir damit haben.
Beginnen wir mit den allgemeinen Konzepten und der Theorie.
„Ich habe einen Vorteil von einem Turm!“ (unbekannter Schachspieler)

Eines der größten Vorteile von Rook ist, dass die Interaktion mit Datenspeichern über die Mechanismen von Kubernetes erfolgt. Das bedeutet, dass man keine Befehle mehr von einem Blatt Papier in die Konsole kopieren muss, um Ceph einzurichten.
— Möchtest du CephFS in einem Cluster bereitstellen? Schreib einfach eine YAML-Datei!
— Was? Möchtest du auch einen Object Store mit S3 API bereitstellen? Schreib einfach eine zweite YAML-Datei!
Rook wurde nach allen Regeln eines typischen Operators erstellt. Die Interaktion mit ihm erfolgt über , in denen wir die erforderlichen Merkmale von Ceph-Einheiten beschreiben (da dies die einzige stabile Implementierung ist, wird im Artikel standardmäßig über Ceph gesprochen, sofern nicht ausdrücklich anders angegeben). Je nach den festgelegten Parametern wird der Operator die notwendigen Befehle zur Einrichtung automatisch ausführen.
Konkret wollen wir das am Beispiel 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 ziemlich Standard und bedürfen kaum eines Kommentars, jedoch sollte besonderes Augenmerk auf jene gelegt werden, die in Vorlagendateien hervorgehoben sind.
Das allgemeine Funktionsschema besteht darin, dass wir über eine YAML-Datei Ressourcen "bestellen", wofür der Operator die notwendigen Befehle ausführt und uns ein "nicht ganz echtes" Geheimnis zurückliefert, 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 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 — das, was in Zukunft von Anwendungen benötigt wird, um über die S3 API auf den Objektspeicher zuzugreifen. Der Rook-Operator wählt diese aus und speichert sie in seinem Namensraum als Geheimnis mit dem Namen rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }}.
Um die Daten aus diesem Geheimnis zu verwenden, reicht es aus, sie als Umgebungsvariablen in den Container hinzuzufügen. Als Beispiel gebe ich eine Vorlage für einen Job an, 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 Aktionen wurden innerhalb Kubernetes durchgeführt. Die in den YAML-Dateien beschriebenen Strukturen sind in einem Git-Repository gespeichert und werden mehrfach wiederverwendet. Dies stellt einen deutlichen Vorteil für DevOps-Ingenieure und den CI/CD-Prozess insgesamt dar.
Mit Rook und Rados macht es Freude.
Die Verwendung von Ceph + RBD bringt bestimmte Einschränkungen bei der Anbindung von Volumes an Pods mit sich.
Insbesondere muss im Namespace ein Geheimnis für den Zugriff auf Ceph vorhanden sein, damit zustandsbehaftete Anwendungen funktionieren können. Es ist in Ordnung, wenn Sie 2-3 Umgebungen in Ihren Namensräumen haben: Dann kann man das Geheimnis manuell kopieren. Aber was, wenn für jedes Feature der Entwickler eine eigene Umgebung mit einem eigenen Namespace erstellt wird?
Wir haben dieses Problem bei uns gelöst durch , der die Geheimnisse automatisch in neue Namespaces kopierte (ein Beispiel für einen solchen Hook wird 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 Vorgang der Anbindung erfolgt über eigene Treiber basierend auf oder (derzeit im Beta-Stadium) und erfordert daher keine Geheimnisse.
Rook löst automatisch viele Probleme, was uns dazu bewegt, es in neuen Projekten zu verwenden.
Die Belagerung von Rook
Wir werden den praktischen Teil mit der Bereitstellung von Rook und Ceph beenden, um eigene Experimente durchführen zu können. Um diesen unzugänglichen Turm leichter erobern zu können, 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 es gibt viele verschiedene Einstellungen zu finden. Das Wichtigste ist, Toleranzen für Agenten und Suchvorgänge anzugeben. Wie der Mechanismus von Taints/Tolerations genutzt werden kann, haben wir ausführlich in der .
Kurz gesagt, wir möchten nicht, dass Pods mit der Client-Anwendung auf denselben Knoten platziert werden, auf denen sich die Datenspeicher befinden. Der Grund ist einfach: So beeinträchtigt die Arbeit der Rook-Agenten nicht die Anwendung selbst.
Also, öffnen wir die Datei rook-ceph/values.yaml mit unserem Lieblingseditor und fügen am Ende den folgenden Block hinzu:
discover:
toleration: NoExecute
tolerationKey: node-role/storage
agent:
toleration: NoExecute
tolerationKey: node-role/storage
mountSecurityMode: AnyWir fügen jedem Knoten, der für die Datenspeicherung reserviert ist, den entsprechenden Taint hinzu:
$ kubectl taint node ${NODE_NAME} node-role/storage="":NoExecuteDanach installieren wir das Helm-Diagramm mit diesem Befehl:
$ helm install --namespace ${ROOK_NAMESPACE} ./rook-cephJetzt müssen wir einen Cluster erstellen und den Standort 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" Wir überprüfen den Status von Ceph - wir erwarten, " 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 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.nodeNameAls nächstes können nach Wunsch zusätzliche Komponenten konfiguriert werden. Weitere Details dazu finden Sie in der . Für die Verwaltung empfehlen wir dringend, das Dashboard und das Toolbox zu installieren.
Rook-Fesseln: Reicht es für Rook?
Wie man sieht, ist die Entwicklung von Rook voll im Gange. Doch es gibt immer noch Probleme, die uns daran hindern, vollständig auf die manuelle Konfiguration von Ceph zu verzichten:
- Kein Rook-Treiber Metriken zur Nutzung der gemounteten Blöcke exportieren, was uns der Überwachung beraubt.
- Flexvolume und CSI die Größe von Volumes nicht ändern (im Gegensatz zu RBD), daher fehlt Rook ein nützliches (und manchmal kritisches!) Werkzeug.
- Rook ist immer noch nicht so flexibel wie das herkömmliche Ceph. Wenn wir die Konfiguration vornehmen möchten, dass der Pool für CephFS-Metadaten auf SSD und die Daten selbst auf HDD gespeichert wird, müssen wir manuell separate Gerätegruppen in den CRUSH-Maps angeben.
- Obwohl der rook-ceph-operator als stabil gilt, gibt es derzeit bestimmte Probleme beim Upgrade von Ceph von Version 13 auf 14.
Das DBMS Tarantool ist ein attraktives, zukunftsträchtiges Produkt zur Erstellung von hochbelasteten Anwendungen.
„Momentan ist die Rook vor der Außenwelt durch Bauern geschützt, aber wir glauben, dass sie eines Tages eine entscheidende Rolle im Spiel spielen wird!“ (Zitat speziell für diesen Artikel erstellt)
Das Projekt Rook hat zweifellos unsere Herzen erobert – wir sind der Meinung, dass [mit all seinen Vor- und Nachteilen] es definitiv Ihre Aufmerksamkeit verdient.
Unsere zukünftigen Pläne bestehen darin, Rook-Ceph zu einem Modul für , was seine Nutzung in unseren zahlreichen Kubernetes-Clustern noch einfacher und bequemer machen wird.
P.S.
Lesen Sie auch in unserem Blog:
- «»;
- «»;
- «»;
- «»;
- «».
Quelle: habr.com
