Rook o non Rook: questo è il problema

Rook o non Rook: questo è il problema

All'inizio di questo mese, il 3 maggio, è stata annunciata un'importante versione del "sistema di gestione per l'archiviazione distribuita dei dati in Kubernetes" - Torre 1.0.0. Più di un anno fa già pubblicato panoramica generale di Rook. Poi ci è stato chiesto di parlare della sua esperienza utilizzare nella pratica — e ora, giusto in tempo per una pietra miliare così significativa nella storia del progetto, siamo felici di condividere le nostre impressioni accumulate.

In breve, Rook è un set operatori per Kubernetes, che assumono il pieno controllo dell'implementazione, gestione, ripristino automatico di soluzioni di archiviazione dati come Ceph, EdgeFS, Minio, Cassandra, CockroachDB.

Al momento il più sviluppato (e l'unico в stabile fase) la soluzione è operatore-torre-cefalo.

Nota: Tra le modifiche significative della versione 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. Ciò che risalta tra gli altri è la maturazione del supporto EdgeFS al livello beta.

Quindi, in questo articolo:

  • Rispondiamo alla domanda su quali vantaggi vediamo nell'utilizzo di Rook per distribuire Ceph in un cluster Kubernetes;
  • Condivideremo la nostra esperienza e impressioni sull'utilizzo di Rook in produzione;
  • Ti raccontiamo perché diciamo "Sì!" a Rook e quali sono i nostri piani per lui.

Cominciamo con concetti generali e teoria.

“Ho il vantaggio di una Torre!” (giocatore di scacchi sconosciuto)

Rook o non Rook: questo è il problema

Uno dei principali vantaggi di Rook è che l'interazione con gli archivi dati avviene tramite meccanismi Kubernetes. Ciò significa che non è più necessario copiare i comandi per configurare Ceph dal foglio alla console.

— Vuoi distribuire CephFS in un cluster? Basta scrivere un file YAML!
- Che cosa? Vuoi distribuire anche un archivio oggetti con l'API S3? Basta scrivere un secondo file YAML!

La torre viene creata secondo tutte le regole di un tipico operatore. L'interazione con lui avviene utilizzando CRD (definizioni di risorse personalizzate), in cui descriviamo le caratteristiche delle entità Ceph di cui abbiamo bisogno (poiché questa è l'unica implementazione stabile, per impostazione predefinita questo articolo parlerà di Ceph, se non diversamente specificato). In base ai parametri specificati, l'operatore eseguirà automaticamente i comandi necessari per la configurazione.

Diamo un'occhiata alle specifiche usando l'esempio della creazione di un archivio oggetti, o meglio: 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 abbastanza standard e difficilmente necessitano di commenti, ma vale la pena prestare particolare attenzione a quelli assegnati alle variabili del template.

Lo schema generale di lavoro si riduce al fatto che "ordiniamo" le risorse tramite un file YAML, per il quale l'operatore esegue i comandi necessari e ci restituisce un segreto "non così reale" con cui possiamo lavorare ulteriormente (vedi sotto). E dalle variabili elencate sopra, verranno compilati il ​​comando e il nome del segreto.

Che tipo di squadra è questa? Quando crei un utente per l'archiviazione di 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"
        }
    ],
    ...
}

Keys - quali applicazioni future avranno bisogno per accedere allo storage di oggetti tramite l'API S3. L'operatore Rook li seleziona gentilmente e li inserisce nel suo namespace sotto forma di segreto con il nome rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }}.

Per utilizzare i dati di questo segreto, aggiungili semplicemente al contenitore come variabili di ambiente. Ad 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 elencate in questo lavoro sono state eseguite nell'ambito di Kubernetes. Le strutture descritte nei file YAML sono archiviate in un repository Git e riutilizzate più volte. Consideriamo questo un enorme vantaggio per gli ingegneri DevOps e per il processo CI/CD nel suo insieme.

Felice con Rook e Rados

L'utilizzo della combinazione Ceph + RBD impone alcune restrizioni sul montaggio dei volumi sui pod.

In particolare, il namespace deve contenere un segreto per l'accesso a Ceph affinché le applicazioni stateful possano funzionare. Va bene se hai 2-3 ambienti nei loro spazi dei nomi: puoi andare a copiare il segreto manualmente. Ma cosa succederebbe se per ogni funzionalità venisse creato un ambiente separato con il proprio spazio dei nomi per gli sviluppatori?

Abbiamo risolto noi stessi questo problema utilizzando operatore di shell, che copiava automaticamente i segreti in nuovi spazi dei nomi (un esempio di tale 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 semplicemente non esiste. Il processo di montaggio avviene utilizzando i propri driver basati su Volume flessibile o CSI (ancora in fase beta) e quindi non necessita di segreti.

Rook risolve automaticamente molti problemi, il che ci incoraggia a utilizzarlo in nuovi progetti.

Assedio di Torre

Completiamo la parte pratica utilizzando Rook e Ceph in modo da poter condurre i nostri esperimenti. Per rendere più semplice 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

In archivio rook-ceph/values.yaml puoi trovare molte impostazioni diverse. La cosa più importante è specificare le tolleranze per gli agenti e la ricerca. Abbiamo descritto in dettaglio per cosa può essere utilizzato il meccanismo di contaminazione/tolleranza questo articolo.

In breve, non vogliamo che i pod dell'applicazione client si trovino sugli stessi nodi dei dischi di archiviazione dei dati. Il motivo è semplice: in questo modo il lavoro degli agenti Rook non influenzerà l'applicazione stessa.

Quindi, apri il file rook-ceph/values.yaml con il tuo editor preferito e aggiungi alla fine il seguente blocco:

discover:
  toleration: NoExecute
  tolerationKey: node-role/storage
agent:
  toleration: NoExecute
  tolerationKey: node-role/storage
  mountSecurityMode: Any

Per ogni nodo riservato all'archiviazione dei dati, aggiungi la contaminazione corrispondente:

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

Installa quindi il grafico Helm con il comando:

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

Ora devi 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"

Controllo dello stato di Ceph: aspettati 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

Allo stesso tempo controlliamo che i pod con l’applicazione client non finiscano su nodi riservati a Ceph:

$ kubectl -n ${APPLICATION_NAMESPACE} get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName

Inoltre è possibile configurare a piacere componenti aggiuntivi. Maggiori dettagli su di essi sono indicati in documentazione. Per l'amministrazione, consigliamo vivamente di installare il dashboard e la casella degli strumenti.

Torre e ganci: la Torre basta per tutto?

Come puoi vedere, lo sviluppo di Rook è in pieno svolgimento. Ma ci sono ancora dei problemi che non ci permettono di abbandonare completamente la configurazione manuale di Ceph:

  • Nessun pilota della torre non posso esportare metriche sull'uso dei blocchi montati, il che ci priva del monitoraggio.
  • Flexvolume e CSI non so come cambiare la dimensione dei volumi (al contrario dello stesso RBD), quindi Rook è privato di uno strumento utile (e talvolta estremamente necessario!).
  • Rook non è ancora flessibile come il normale Ceph. Se desideriamo configurare il pool per i metadati CephFS da archiviare su SSD e i dati stessi da archiviare su HDD, dovremo registrare manualmente gruppi separati di dispositivi nelle mappe CRUSH.
  • Nonostante rook-ceph-operator sia considerato stabile, attualmente ci sono alcuni problemi durante l'aggiornamento di Ceph dalla versione 13 alla 14.

risultati

"Al momento la Torre è chiusa fuori dal mondo esterno dai pedoni, ma crediamo che un giorno giocherà un ruolo decisivo nel gioco!" (citazione inventata appositamente per questo articolo)

Il progetto Rook ha senza dubbio conquistato i nostri cuori: crediamo che [con tutti i suoi pro e contro] meriti sicuramente la tua attenzione.

I nostri piani futuri si riducono a rendere rook-ceph un modulo per operatore-add-on, che renderà ancora più semplice e conveniente il suo utilizzo nei nostri numerosi cluster Kubernetes.

PS

Leggi anche sul nostro blog:

Fonte: habr.com

Aggiungi un commento