
All'inizio di questo mese, il 3 maggio, è stata annunciata una grande release del "sistema di gestione per i dati distribuiti in Kubernetes" — . Più di un anno fa avevamo già una panoramica generale su Rook. Allora ci era stato chiesto di raccontare l'esperienza del suo utilizzo pratico — e ora, proprio in occasione di questo importante traguardo nella storia del progetto, siamo lieti di condividere le impressioni accumulate.
In breve, Rook rappresenta un insieme di per Kubernetes, che si occupano completamente del deployment, della gestione e del ripristino automatico di soluzioni di archiviazione come Ceph, EdgeFS, Minio, Cassandra, CockroachDB.
Attualmente, la soluzione più avanzata (e in in fase stabile) è rook-ceph-operator .
Nota: среди значимых изменений в релизе Rook 1.0.0, связанных с Ceph, можно отметить поддержку Сeph Nautilus и возможность использовать NFS для CephFS- или RGW-бакетов. Из прочих выделяется «созревание» поддержки EdgeFS до уровня беты.
Quindi, in questo articolo:
- risponderemo alla domanda su quali vantaggi vediamo nell'utilizzo di Rook per il deployment di Ceph in un cluster Kubernetes;
- condivideremo l'esperienza e le impressioni derivate dall'uso di Rook in produzione;
- Scopriremo perché diciamo «Sì!» a Rook e quali sono i nostri piani per lui.
Iniziamo con concetti e teorie generali.
«Ho un vantaggio di una Torre!» (un giocatore di scacchi ignoto)

Uno dei principali vantaggi di Rook è che l'interazione con i data storage avviene tramite meccanismi Kubernetes. Questo significa che non è più necessario copiare i comandi per configurare Ceph da un foglietto alla console.
— Vuoi distribuire in un cluster CephFS? Basta scrivere un file YAML!
— Cosa? Vuoi distribuire anche un object store con S3 API? Basta scrivere un secondo file YAML!
Rook è creato secondo tutte le regole di un tipico operatore. L'interazione con esso avviene tramite , in cui descriviamo le caratteristiche necessarie delle entità Ceph (dato che è l'unica implementazione stabile, per default in questo articolo si parlerà di Ceph, salvo esplicita indicazione contraria). In base ai parametri impostati, l'operatore eseguirà automaticamente i comandi necessari alla configurazione.
Vediamo i dettagli con l'esempio di creazione di un Object Store, e 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 elencati nell'elenco sono abbastanza standard e non necessitano di commenti particolari, tuttavia è importante prestare particolare attenzione a quelli evidenziati come variabili nel template.
Il funzionamento generale si basa sul fatto che attraverso il file YAML richiediamo delle risorse, per cui l'operatore esegue i comandi necessari e ci restituisce un "segreto" che non è del tutto reale, con il quale possiamo poi lavorare. (vedi sotto). Dalle variabili sopra indicate sarà formulato il comando e il nome del segreto.
Qual è quindi questo comando? Quando si crea un utente per lo storage oggetti, 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ò che sarà necessario in futuro alle applicazioni per accedere allo storage object tramite l'API S3. L'operatore Rook si occupa di selezionarli e conservarli nel proprio namespace come segreto chiamato rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }}.
Per utilizzare i dati di questo segreto, è sufficiente aggiungerli al contenitore come variabili d'ambiente. Per esempio, ecco un modello per un Job in cui creiamo automaticamente i bucket per ogni 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 elencate in questo lavoro sono state effettuate senza uscire dall'ambito di Kubernetes. Le strutture descritte nei file YAML sono state consolidate in un repository Git e riutilizzate più volte. Questo rappresenta un grande vantaggio per gli ingegneri DevOps e per il processo CI/CD nel suo complesso.
Con Rook e Rados è un piacere.
L'uso della combinazione Ceph + RBD impone determinate limitazioni nel montaggio dei volumi ai pod.
In particolare, nel namespace deve necessariamente trovarsi un segreto per accedere a Ceph, affinché le applicazioni stateful possano funzionare. È normale avere 2-3 ambienti nei propri spazi dei nomi: si può andare a copiare manualmente il segreto. Ma cosa fare se per ogni feature viene creato un ambiente separato 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, nell'uso di Rook, questo problema non esiste. 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'assedio di Rook
Concludiamo la parte pratica implementando Rook e Ceph per poter effettuare esperimenti. Per rendere più facile l'assalto a questa torre inespugnabile, 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 puoi trovare molte impostazioni diverse. La cosa più importante è specificare le tolerations per gli agenti e la ricerca. Di cosa possiamo utilizzare il meccanismo taints/tolerations, ne abbiamo parlato dettagliatamente in .
In breve, non vogliamo che i pod con l'applicazione client si trovino sugli stessi nodi dove sono situati i dischi per l'archiviazione dei dati. La ragione è semplice: in questo modo il lavoro degli agenti Rook non influenzerà l'applicazione stessa.
Quindi, apriamo il file rook-ceph/values.yaml con il nostro editor preferito e aggiungiamo in fondo il seguente blocco:
discover:
toleration: NoExecute
tolerationKey: node-role/storage
agent:
toleration: NoExecute
tolerationKey: node-role/storage
mountSecurityMode: AnyA ogni nodo riservato per l'archiviazione dei dati, aggiungiamo il corrispondente taint:
$ kubectl taint node ${NODE_NAME} node-role/storage="":NoExecuteDopo di che installiamo il chart 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 inoltre che i pod dell'applicazione client non vengano eseguiti sui nodi riservati di Ceph:
$ kubectl -n ${APPLICATION_NAMESPACE} get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeNameSuccessivamente, se lo si desidera, si possono configurare i componenti aggiuntivi. Maggiori dettagli sono forniti in . Per l'amministrazione, consigliamo vivamente di installare dashboard e toolbox.
Rook e i suoi limiti: Rook è in grado di soddisfare tutte le esigenze?
Come si può vedere, lo sviluppo di Rook avanza a tutta velocità. Tuttavia, 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 ridimensionare i volumi (a differenza dell'RBD), quindi Rook manca di uno strumento utile (e talvolta critico!).
- Rook non è ancora flessibile come un Ceph tradizionale. Se volessimo configurare il pool per i metadati CephFS per essere memorizzato su SSD e i dati stessi su HDD, sarà necessario specificare manualmente gruppi separati di dispositivi nelle CRUSH maps.
- Nonostante il fatto che il rook-ceph-operator sia considerato stabile, ci sono attualmente alcuni problemi nell'aggiornare Ceph dalla versione 13 alla 14.
Conclusioni
«Attualmente la Barca è chiusa al mondo esterno dai pedoni, ma crediamo che un giorno svolgerà 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 vostra attenzione.
I nostri piani futuri si concentrano sul rendere Rook-Ceph un modulo per , semplificando ulteriormente il suo utilizzo nei nostri numerosi cluster Kubernetes.
P.S.
Leggete anche nel nostro blog:
- «»;
- «»;
- «»;
- «»;
- «».
Fonte: habr.com
