
All'inizio di questo mese, il 3 maggio, è stato annunciato un importante rilascio del "sistema di gestione per archiviazione distribuita dei dati in Kubernetes" — . Più di un anno fa abbiamo già una panoramica generale su Rook. Allora, ci era stato chiesto di parlare della nostra esperienza con il suo utilizzo nella pratica — ed ecco, proprio in coincidenza con un traguardo così significativo nella storia del progetto, siamo lieti di condividere le impressioni accumulate.
In breve, Rook è un insieme per Kubernetes, che prende completamente il controllo del dispiegamento, della gestione e del recupero automatico di soluzioni di archiviazione dei dati come Ceph, EdgeFS, Minio, Cassandra, CockroachDB.
Attualmente, la soluzione più sviluppata (e in una prestazione in fase) è .
Nota: tra le modifiche significative nel rilascio di Rook 1.0.0, relative a Ceph, possiamo notare il supporto per Ceph Nautilus e la possibilità di utilizzare NFS per i bucket CephFS o RGW. Tra le altre, spicca il "maturare" del supporto per EdgeFS fino al livello beta.
Quindi, in questo articolo:
- risponderemo alla domanda su quali vantaggi vediamo nell'utilizzo di Rook per il dispiegamento di Ceph nel cluster Kubernetes;
- condivideremo esperienze e impressioni sull'uso di Rook in produzione;
- spiegheremo perché diciamo di sì a Rook e quali sono i nostri piani per esso.
Iniziamo con concetti e teorie generali.
«Ho un vantaggio di un Torre!» (sconosciuto giocatore di scacchi)

Uno dei principali vantaggi di Rook è che l'interazione con i sistemi di archiviazione dei dati avviene attraverso meccanismi di Kubernetes. Questo significa che non è più necessario copiare i comandi per configurare Ceph da un foglio alla console.
— Vuoi dispiegare CephFS nel cluster? Scrivi semplicemente un file YAML!
— Cosa? Vuoi anche dispiegare un object store con S3 API? Scrivi un secondo file YAML!
Rook è progettato secondo le regole tipiche di un operatore. L'interazione avviene tramite , nei quali descriviamo le caratteristiche necessarie delle entità Ceph (poiché questa è l'unica implementazione stabile, per default in questo articolo parleremo specificamente di Ceph, salvo indicazione contraria). Secondo i parametri forniti, l'operatore eseguirà automaticamente i comandi necessari per la configurazione.
Vediamo nel dettaglio con un esempio della creazione dell'Object Store, più precisamente — 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 }}I parametri indicati nell'elenco sono piuttosto standard e difficilmente necessitano di commenti, tuttavia vale la pena prestare particolare attenzione a quelli evidenziati nelle variabili del modello.
Lo schema generale di funzionamento si riduce al fatto che tramite un file YAML ordiniamo risorse, per cui l'operatore esegue i comandi necessari e ci restituisce un "segreto" non completamente autentico, con il quale possiamo lavorare ulteriormente. (vedi sotto). E dalle variabili indicate sopra sarà composta la comando e il nome del segreto.
Qual è dunque questo comando? Quando si crea un utente per lo storageoggetto, l'operatore Rook all'interno del pod eseguirà quanto segue:
radosgw-admin user create --uid="rook-user" --display-name="{{ .Values.s3.username }}"Il risultato dell'esecuzione di questo comando sarà una struttura JSON:
{
"user_id": "rook-user",
"display_name": "{{ .Values.s3.username }}",
"keys": [
{
"user": "rook-user",
"access_key": "NRWGT19TWMYOB1YDBV1Y",
"secret_key": "gr1VEGIV7rxcP3xvXDFCo4UDwwl2YoNrmtRlIAty"
}
],
...
} Chiavi — ciò di cui le applicazioni avranno bisogno in futuro per accedere allo storageoggetto tramite l'API S3. L'operatore Rook le seleziona cortesemente e le salva nel proprio namespace come un segreto con il nome rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }}.
Per utilizzare i dati di questo segreto, è sufficiente aggiungerli al container come variabili d'ambiente. Come esempio, fornirò un modello per Job, in cui creiamo automaticamente bucket per ciascun ambiente utente:
{{- 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 }}Tutte le azioni descritte in questo Job sono state eseguite senza uscire da Kubernetes. Le strutture descritte nei file YAML sono state archiviate in un repository Git e riutilizzate più volte. Questo rappresenta un grande vantaggio per gli ingegneri DevOps e per il processo di CI/CD nel suo complesso.
Con Rook e Rados è un piacere
L'uso della combinazione Ceph + RBD impone alcune limitazioni nel montare i volumi nei pod.
In particolare, nello namespace deve necessariamente trovarsi un segreto per accedere a Ceph, affinché le applicazioni stateful possano funzionare. Va bene se hai 2-3 ambienti nei tuoi spazi dei nomi: puoi semplicemente copiare il segreto manualmente. Ma cosa fare se per ogni funzionalità si crea un ambiente separato per gli sviluppatori con il proprio namespace?
Noi abbiamo risolto questo problema tramite , che copiava automaticamente i segreti nei nuovi namespace (un esempio di questo hook è descritto 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 -
fiTuttavia, quando si utilizza Rook, questo problema non esiste affatto. Il processo di montaggio avviene tramite i propri driver basati su o (attualmente in fase beta) e quindi non richiede segreti.
Rook risolve automaticamente molti problemi, il che ci spinge a utilizzarlo in nuovi progetti.
L'assalto a Rook
Concludiamo la parte pratica con il dispiegamento di Rook e Ceph per la possibilità di condurre esperimenti personali. Per rendere più semplice l'assalto a questa torre inaccessibile, gli sviluppatori hanno preparato un pacchetto Helm. Scarichiamolo:
$ helm fetch rook-master/rook-ceph --untar --version 1.0.0 Nel file rook-ceph/values.yaml si possono trovare molte impostazioni diverse. La cosa più importante è specificare le tolleranze per gli agenti e la ricerca. A cosa serve il meccanismo di taints/tolerations, ne abbiamo parlato in dettaglio in .
In breve, non vogliamo che i pod con l'applicazione client siano collocati sugli stessi nodi dove si trovano i dischi per l'archiviazione dei dati. Il motivo è semplice: in questo modo il funzionamento degli agenti Rook non influirà sull'applicazione stessa.
Quindi, apriamo il file rook-ceph/values.yaml con il nostro editor preferito e aggiungiamo alla fine il seguente blocco:
discover:
toleration: NoExecute
tolerationKey: node-role/storage
agent:
toleration: NoExecute
tolerationKey: node-role/storage
mountSecurityMode: AnyAggiungiamo un corrispondente taint a ogni nodo riservato per l'archiviazione dei dati:
$ kubectl taint node ${NODE_NAME} node-role/storage="":NoExecuteDopo il quale installiamo il chart di Helm con il comando:
$ helm install --namespace ${ROOK_NAMESPACE} ./rook-cephOra è necessario creare un cluster e specificare la posizione :
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" Controlliamo lo stato di Ceph — ci aspettiamo di vedere 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 -sControlliamo anche che i pod con l'applicazione client non siano collocati sui nodi riservati per Ceph:
$ kubectl -n ${APPLICATION_NAMESPACE} get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeNameSuccessivamente, se lo si desidera, si configurano componenti aggiuntivi. Maggiori dettagli sono forniti in . Per la gestione, raccomandiamo vivamente di installare il dashboard e il toolbox.
Rook e i suoi punti critici: Rook è sufficiente per tutto?
Come si può vedere, lo sviluppo di Rook sta procedendo a pieno ritmo. Ma ci sono ancora problemi che ci impediscono di rinunciare completamente alla configurazione manuale di Ceph:
- Nessun driver Rook esportare metriche sull'uso dei volumi montati, il che ci priva del monitoraggio.
- Flexvolume e CSI modificare le dimensioni dei volumi (a differenza di RBD), quindi Rook si priva di uno strumento utile (e a volte anche critico!).
- Rook non è ancora flessibile come un Ceph normale. Se desideriamo impostare affinché il pool per i metadati CephFS venga memorizzato su SSD, mentre i dati stessi siano su HDD, sarà necessario specificare separatamente i gruppi di dispositivi nelle mappe CRUSH manualmente.
- Nonostante il fatto che il rook-ceph-operator sia considerato stabile, attualmente ci sono problemi specifici nell'aggiornare Ceph dalla versione 13 alla 14.
Conclusioni
«Attualmente la Nave è chiusa al mondo esterno da pedoni, ma crediamo che un giorno avrà un ruolo decisivo nella partita!» (citazione inventata appositamente per questo articolo)
Il progetto Rook ha sicuramente conquistato i nostri cuori: riteniamo che [con tutti i suoi pro e contro] meriti sicuramente anche la tua attenzione.
I nostri piani futuri mirano a rendere rook-ceph un modulo per , rendendo il suo utilizzo nei nostri numerosi cluster Kubernetes ancora più semplice e pratico.
P.S.
Leggi anche nel nostro blog:
- «»;
- «»;
- «»;
- «»;
- «».
Fonte: habr.com
