Rook ці не Rook – вось у чым пытанне

Rook ці не Rook – вось у чым пытанне

У пачатку гэтага месяца, 3 траўня, быў анансаваны буйны рэліз "сістэмы кіравання для размеркаваных сховішчаў дадзеных у Kubernetes". Ладдзя 1.0.0. Больш за год таму мы ўжо публікавалі агульны агляд Rook. Тады ж нас прасілі расказаць пра вопыт яго выкарыстання на практыцы - І вось, як раз да такой значнай вехі ў гісторыі праекта, мы рады падзяліцца назапашанымі ўражаннямі.

Калі коратка, Rook ўяўляе сабой набор аператараў для Kubernetes, якія цалкам бяруць пад кантроль разгортванне, кіраванне, аўтаматычнае аднаўленне такіх рашэнняў для захоўвання дадзеных, як Ceph, EdgeFS, Minio, Cassandra, CockroachDB.

На дадзены момант самым развітым (і адзіным в стабільнай стадыі) рашэннем з'яўляецца rook-ceph-operator.

Заўвага: сярод значных змен у рэлізе Rook 1.0.0, звязаных з Ceph, можна адзначыць падтрымку Сeph Nautilus і магчымасць выкарыстоўваць NFS для CephFS- або RGW-бакетаў. З іншых вылучаецца паспяванне падтрымкі EdgeFS да ўзроўня бэты.

Такім чынам, у гэтым артыкуле мы:

  • адкажам на пытанне, якія плюсы бачым у выкарыстанні Rook для разгортвання Ceph у кластары Kubernetes;
  • падзелімся досведам і ўражаннямі ад выкарыстання Rook у production;
  • раскажам, чаму мы гаворым Rook'у «Так!», і пра свае планы на яго.

Пачнём з агульных канцэпцый і тэорыі.

«У мяне перавага ў адну ладдзю!» (невядомы шахматыст)

Rook ці не Rook – вось у чым пытанне

Адным з галоўных пераваг Rook з'яўляецца тое, што ўзаемадзеянне са сховішчамі дадзеных вядзецца праз механізмы Kubernetes. Гэта азначае, што больш не трэба капіяваць каманды для наладкі Ceph з лісточка ў кансоль.

- Хочаш разгарнуць у кластары CephFS? Проста напішы YAML-файл!
- Што? Жадаеш разгарнуць яшчэ і object store з S3 API? Проста напішы другі YAML-файл!

Rook створаны па ўсіх правілах тыповага аператара. Узаемадзеянне з ім адбываецца пры дапамозе CRD (Custom Resource Definitions), у якіх мы апісваем неабходныя нам характарыстыкі сутнасцяў Ceph (паколькі гэта адзіная стабільная рэалізацыя, па змаўчанні ў артыкуле будзе ісці гаворка менавіта пра Ceph, калі відавочна не пазначана іншае). Згодна з зададзенымі параметрамі, аператар аўтаматычна выканае неабходныя для настройкі каманды.

Канкрэтыку давайце разгледзім на прыкладзе стварэння Object Store, а дакладней - 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 }}

Паказаныя ў лістынгу параметры досыць стандартныя і ці наўрад маюць патрэбу ў каментарах, аднак варта звярнуць адмысловую ўвагу на тыя, што вылучаныя ў зменныя шаблонаў.

Агульная схема працы зводзіцца да таго, што праз YAML-файл мы "заказваем" рэсурсы, для чаго аператар выконвае патрэбныя каманды і вяртае нам "не самы сапраўдны" сакрэт, з якім мы можам далей працаваць (гл. ніжэй). А са зменных, што пазначаны вышэй, будзе складзена каманда і імя сакрэту.

Што ж гэта за каманда? Пры стварэнні карыстача для аб'ектнага сховішча Rook-аператар усярэдзіне pod'а выканае наступнае:

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

Вынікам выканання гэтай каманды стане JSON-структура:

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

Keys - тое, што запатрабуецца ў будучыні прыкладанням для доступу да аб'ектнага сховішча праз S3 API. Rook-аператар ласкава выбірае іх і складае ў свой namespace у выглядзе сакрэту з імем rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }}.

Каб выкарыстоўваць дадзеныя з гэтага сакрэту, дастаткова дадаць іх у кантэйнер у якасці зменных асяроддзя. Як прыклад прывяду шаблон для Job, у якім мы аўтаматычна ствараем bucket'ы для кожнага карыстацкага асяроддзя:

{{- 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 }}

Усе дзеянні, пералічаныя ў гэтым Job'е, былі зроблены, не выходзячы за рамкі Kubernetes. Апісаныя ў YAML-файлах структуры складзеныя ў Git-рэпазітар і шматкроць паўторна выкарыстаныя. У гэтым мы бачым велізарны плюс для DevOps-інжынераў і працэсу CI/CD у цэлым.

З Rook і Rados у радасць

Выкарыстанне звязка Ceph + RBD накладвае пэўныя абмежаванні на мантаванне тамоў да pod'ах.

У прыватнасці, у namespace абавязкова павінен ляжаць сакрэт для доступу да Ceph, каб stateful-прыкладанні маглі функцыянаваць. Нармальна, калі ў вас ёсць 2-3 асяроддзі ў сваіх прасторах імёнаў: можна пайсці і скапіяваць сакрэт уручную. Але што рабіць, калі на кожную feature для распрацоўшчыкаў ствараецца асобнае асяроддзе са сваім namespace?

У сябе мы вырашылі дадзеную праблему пры дапамозе shell-operator, які аўтаматычна капіяваў сакрэты ў новыя namespace (прыклад падобнага хука апісаны ў гэтым артыкуле).

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

Аднак пры выкарыстанні Rook дадзенай праблемы папросту не існуе. Працэс мантавання адбываецца пры дапамозе ўласных драйвераў на базе Flexvolume або CSI (пакуль у бэта-стадыі) і таму не патрабуе сакрэтаў.

Rook аўтаматычна вырашае многія праблемы, што і падштурхоўвае нас выкарыстоўваць яго ў новых праектах.

Аблога Rook

Завершым практычную частку разгортваннем Rook і Ceph для магчымасці правядзення ўласных эксперыментаў. Для таго, каб браць штурмам гэтую непрыступную вежу было лягчэй, распрацоўшчыкі падрыхтавалі Helm-пакет. Давайце спампуем яго:

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

У файле rook-ceph/values.yaml можна знайсці мноства розных налад. Самае важнае - паказаць tolerations для агентаў і пошуку. Для чаго можна выкарыстоўваць механізм taints/tolerations, мы падрабязна расказвалі ў гэтым артыкуле.

Калі сцісла, мы не жадаем, каб pod'ы з кліенцкім дадаткам размяшчаліся на тых жа вузлах, дзе размешчаныя дыскі для захоўвання дадзеных. Чыннік простая: так праца агентаў Rook не будзе ўплываць на само прыкладанне.

Такім чынам, адчыняны файл rook-ceph/values.yaml каханым рэдактарам і дадаем у канец наступны блок:

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

На кожны вузел, зарэзерваваны пад захоўванне дадзеных, дадаем адпаведны taint:

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

Пасля чаго ўсталёўваны Helm-чарт камандай:

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

Цяпер неабходна стварыць кластар і пазначыць месцазнаходжанне 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"

Правяраем статус Ceph - чакаем убачыць 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

Заадно праверым, што pod'ы з кліенцкім дадаткам не пападаюць на зарэзерваваныя пад Ceph вузлы:

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

Далей па жаданні наладжваюцца дадатковыя кампаненты. Больш падрабязна пра іх пазначана ў дакументацыі. Для адміністравання настойліва раім усталяваць dashboard і toolbox.

Rook'і-гакі: ці на ўсё хапае Rook?

Як відаць, распрацоўка Rook ідзе поўным ходам. Але ўсё яшчэ застаюцца праблемы, якія не дазваляюць нам цалкам адмовіцца ад ручной наладкі Ceph:

  • Ніводны драйвер Rook не ўмее экспартаваць метрыкі па выкарыстанні змантаваных блокаў, што пазбаўляе нас маніторынгу.
  • Flexvolume і CSI не ўмеюць змяняць памер тамоў (у адрозненне ад таго ж RBD), таму Rook пазбаўляецца карыснай (а часам і крытычна патрэбнай!) прылады.
  • Rook усё яшчэ не такі гнуткі, як звычайны Ceph. Калі мы захочам наладзіць, каб пул для метададзеных CephFS захоўваўся на SSD, а самі дадзеныя - на HDD, запатрабуецца прапісваць асобныя групы прылад у CRUSH maps уручную.
  • Нягледзячы на ​​тое, што rook-ceph-operator лічыцца стабільным, на дадзены момант існуюць пэўныя праблемы пры абнаўленні Ceph з версіі 13 да 14.

Высновы

«Цяпер Ладдзя зачынена ад знешняга свету пешкамі, але мы верым, што аднойчы яна адыграе вырашальную ролю ў партыі!» (цытата прыдумана спецыяльна для гэтага артыкула)

Праект Rook, несумненна, заваяваў нашыя сэрцы — мы лічым, што [з усімі сваімі плюсамі і мінусамі] ён сапраўды заслугоўвае і вашай увагі.

У нас жа далейшыя планы зводзяцца да таго, што зрабіць rook-ceph модулем для addon-operator, Што зробіць яго выкарыстанне ў нашых шматлікіх Kubernetes-кластарах яшчэ больш простым і зручным.

PS

Чытайце таксама ў нашым блогу:

Крыніца: habr.com

Дадаць каментар