Rook o non Rook — questo è il problema

Rook o non Rook — questo è il problema

All'inizio di questo mese, il 3 maggio, è stato annunciato un importante rilascio del "sistema di gestione per archiviazione distribuita dei dati in Kubernetes" — Rook 1.0.0. Più di un anno fa abbiamo già pubblicato 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 del nostro complesso SBIS 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 unica in una prestazione in fase) è rook-ceph-operator.

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)

Rook o non Rook — questo è il problema

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 CRD (Custom Resource Definitions), 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 shell-operator, che copiava automaticamente i segreti nei nuovi namespace (un esempio di questo hook è descritto in questo articolo).

#! /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

Tuttavia, quando si utilizza Rook, questo problema non esiste affatto. Il processo di montaggio avviene tramite i propri driver basati su Flexvolume o CSI (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 questo articolo.

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: Any

Aggiungiamo un corrispondente taint a ogni nodo riservato per l'archiviazione dei dati:

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

Dopo il quale installiamo il chart di Helm con il comando:

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

Ora è necessario creare un cluster e specificare la posizione OSD:

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 -s

Controlliamo 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.nodeName

Successivamente, se lo si desidera, si configurano componenti aggiuntivi. Maggiori dettagli sono forniti in documentazione. 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 è in grado di esportare metriche sull'uso dei volumi montati, il che ci priva del monitoraggio.
  • Flexvolume e CSI non sono in grado di 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 addon-operator, rendendo il suo utilizzo nei nostri numerosi cluster Kubernetes ancora più semplice e pratico.

P.S.

Leggi anche nel nostro blog:

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster