To Rook or not to Rook - det er spørsmålet

To Rook or not to Rook - det er spørsmålet

I begynnelsen av denne måneden, 3. mai, ble en større utgivelse av et "administrasjonssystem for distribuert datalagring i Kubernetes" annonsert - Rook 1.0.0. For mer enn et år siden har vi allerede publisert generell oversikt over Rook. Så ble vi bedt om å snakke om opplevelsen hans bruk i praksis — og nå, akkurat i tide til en så viktig milepæl i prosjektets historie, deler vi gjerne våre oppsamlede inntrykk.

Kort sagt, Rook er et sett operatører for Kubernetes, som tar full kontroll over distribusjon, administrasjon, automatisk gjenoppretting av datalagringsløsninger som Ceph, EdgeFS, Minio, Cassandra, CockroachDB.

For øyeblikket den mest utviklede (og den eneste в stabil trinn) er løsningen tårn-ceph-operatør.

Note: Blant de betydelige endringene i Rook 1.0.0-utgivelsen relatert til Ceph, kan vi merke oss støtte for Ceph Nautilus og muligheten til å bruke NFS for CephFS- eller RGW-bøtter. Det som skiller seg ut blant andre er modningen av EdgeFS-støtte til betanivå.

Så i denne artikkelen:

  • La oss svare på spørsmålet om hvilke fordeler vi ser ved å bruke Rook til å distribuere Ceph i en Kubernetes-klynge;
  • Vi deler vår erfaring og inntrykk av å bruke Rook i produksjon;
  • La oss fortelle deg hvorfor vi sier "Ja!" til Rook, og om planene våre for ham.

La oss starte med generelle begreper og teori.

"Jeg har en fordel av en Rook!" (ukjent sjakkspiller)

To Rook or not to Rook - det er spørsmålet

En av hovedfordelene med Rook er at interaksjon med datalagre utføres gjennom Kubernetes-mekanismer. Dette betyr at du ikke lenger trenger å kopiere kommandoene for å konfigurere Ceph fra arket til konsollen.

— Vil du distribuere CephFS i en klynge? Bare skriv en YAML-fil!
- Hva? Vil du også distribuere et objektlager med S3 API? Bare skriv en ny YAML-fil!

Rook er laget i henhold til alle reglene til en typisk operatør. Interaksjon med ham skjer ved hjelp av CRD (Custom Resource Definitions), der vi beskriver egenskapene til Ceph-enheter vi trenger (siden dette er den eneste stabile implementeringen, vil denne artikkelen som standard snakke om Ceph, med mindre annet er uttrykkelig angitt). I henhold til de spesifiserte parametrene vil operatøren automatisk utføre kommandoene som er nødvendige for konfigurasjonen.

La oss se på detaljene ved å bruke eksemplet med å lage en objektbutikk, eller rettere sagt - 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 }}

Parametrene som er angitt i oppføringen er ganske standard og trenger knapt kommentarer, men det er verdt å være spesielt oppmerksom på de som er allokert til malvariabler.

Det generelle arbeidsskjemaet kommer ned til det faktum at vi "bestiller" ressurser gjennom en YAML-fil, som operatøren utfører de nødvendige kommandoene for og returnerer oss en "ikke-så-ekte" hemmelighet som vi kan jobbe videre med (se nedenfor). Og fra variablene som er oppført ovenfor, vil kommandoen og det hemmelige navnet bli kompilert.

Hva slags lag er dette? Når du oppretter en bruker for objektlagring, vil Rook-operatøren inne i poden gjøre følgende:

radosgw-admin user create --uid="rook-user" --display-name="{{ .Values.s3.username }}"

Resultatet av å utføre denne kommandoen vil være en JSON-struktur:

{
    "user_id": "rook-user",
    "display_name": "{{ .Values.s3.username }}",
    "keys": [
        {
           "user": "rook-user",
           "access_key": "NRWGT19TWMYOB1YDBV1Y",
           "secret_key": "gr1VEGIV7rxcP3xvXDFCo4UDwwl2YoNrmtRlIAty"
        }
    ],
    ...
}

Keys - hvilke fremtidige applikasjoner trenger for å få tilgang til objektlagring via S3 API. Rook-operatøren velger dem og legger dem i navneområdet sitt i form av en hemmelighet med navnet rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }}.

For å bruke dataene fra denne hemmeligheten, legg dem bare til beholderen som miljøvariabler. Som et eksempel vil jeg gi en mal for Job, der vi automatisk lager bøtter for hvert brukermiljø:

{{- 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 handlinger oppført i denne jobben ble utført innenfor rammen av Kubernetes. Strukturene beskrevet i YAML-filer lagres i et Git-depot og gjenbrukes mange ganger. Vi ser på dette som et stort pluss for DevOps-ingeniører og CI/CD-prosessen som helhet.

Fornøyd med Rook og Rados

Bruk av Ceph + RBD-kombinasjonen pålegger visse begrensninger for montering av volumer til pods.

Spesielt må navneområdet inneholde en hemmelighet for å få tilgang til Ceph for at stateful-applikasjoner skal fungere. Det er ok hvis du har 2-3 miljøer i navneområdene deres: du kan kopiere hemmeligheten manuelt. Men hva om det for hver funksjon opprettes et eget miljø med eget navneområde for utviklere?

Vi løste dette problemet selv ved hjelp av shell-operatør, som automatisk kopierte hemmeligheter til nye navneområder (et eksempel på en slik krok er beskrevet i denne artikkelen).

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

Men når du bruker Rook, eksisterer dette problemet rett og slett ikke. Monteringsprosessen skjer ved å bruke sine egne drivere basert på Flexvolum eller CSI (fortsatt i betastadiet) og krever derfor ikke hemmeligheter.

Rook løser automatisk mange problemer, noe som oppmuntrer oss til å bruke det i nye prosjekter.

Beleiring av Rook

La oss fullføre den praktiske delen ved å distribuere Rook og Ceph slik at vi kan utføre våre egne eksperimenter. For å gjøre det lettere å storme dette ugjennomtrengelige tårnet, har utviklerne utarbeidet en Helm-pakke. La oss laste den ned:

$ helm fetch rook-master/rook-ceph --untar --version 1.0.0

I fil rook-ceph/values.yaml du kan finne mange forskjellige innstillinger. Det viktigste er å spesifisere toleranser for agenter og søk. Vi beskrev i detalj hva mekanismen for smuss/toleranse kan brukes til denne artikkelen.

Kort sagt, vi ønsker ikke at klientapplikasjonsputene skal være plassert på de samme nodene som datalagringsdiskene. Årsaken er enkel: på denne måten vil ikke arbeidet til Rook-agenter påvirke selve applikasjonen.

Så åpne filen rook-ceph/values.yaml med favorittredigereren din og legg til følgende blokk på slutten:

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

For hver node som er reservert for datalagring, legg til den tilsvarende flekkeren:

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

Installer deretter Helm-diagrammet med kommandoen:

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

Nå må du opprette en klynge og angi plasseringen 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"

Sjekker Ceph-status - forvent å se 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

La oss samtidig sjekke at podene med klientapplikasjonen ikke havner på noder reservert for Ceph:

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

Videre kan tilleggskomponenter konfigureres etter ønske. Flere detaljer om dem er angitt i dokumentasjon. For administrasjon anbefaler vi på det sterkeste å installere dashbordet og verktøykassen.

Rook og kroker: er Rook nok til alt?

Som du ser er utviklingen av Rook i full gang. Men det er fortsatt problemer som ikke lar oss fullstendig forlate manuell konfigurasjon av Ceph:

  • Ingen Rook Driver kan ikke eksportberegninger for bruk av monterte blokker, noe som fratar oss overvåking.
  • Flexvolume og CSI vet ikke hvordan endre størrelsen på volumene (i motsetning til den samme RBD), slik at Rook blir fratatt et nyttig (og noen ganger kritisk nødvendig!) verktøy.
  • Rook er fortsatt ikke like fleksibel som vanlig Ceph. Hvis vi ønsker å konfigurere bassenget for at CephFS-metadata skal lagres på SSD, og ​​selve dataene skal lagres på HDD, må vi registrere separate grupper av enheter i CRUSH-kart manuelt.
  • Til tross for at tårn-ceph-operatør anses som stabil, er det for tiden noen problemer ved oppgradering av Ceph fra versjon 13 til 14.

Funn

"Akkurat nå er Rook stengt av fra omverdenen med bønder, men vi tror at hun en dag vil spille en avgjørende rolle i spillet!" (sitat oppfunnet spesielt for denne artikkelen)

Rook-prosjektet har utvilsomt vunnet våre hjerter - vi mener at [med alle sine fordeler og ulemper] det definitivt fortjener din oppmerksomhet.

Våre fremtidsplaner koker ned til å gjøre rook-ceph til en modul for addon-operatør, som vil gjøre bruken i våre mange Kubernetes-klynger enda enklere og mer praktisk.

PS

Les også på bloggen vår:

Kilde: www.habr.com

Legg til en kommentar