- PVSM.RU - https://www.pvsm.ru -

Rook или не Rook — вот в чём вопрос

Rook или не Rook — вот в чём вопрос - 1

В начале этого месяца, 3 мая, был анонсирован крупный релиз «системы управления для распределённых хранилищ данных в Kubernetes» — Rook 1.0.0 [1]. Более года назад мы уже публиковали [2] общий обзор Rook. Тогда же нас просили рассказать об опыте его использования на практике — и вот, как раз к столь значимой вехе в истории проекта, мы рады поделиться накопленными впечатлениями.

Если кратко, Rook представляет собой набор операторов [3] для Kubernetes, которые полностью берут под контроль развертывание, управление, автоматическое восстановление таких решений для хранения данных, как Ceph, EdgeFS, Minio, Cassandra, CockroachDB.

На данный момент самым развитым (и единственным [4] в стабильной стадии) решением является rook-ceph-operator [5].

Примечание: среди значимых изменений в релизе Rook 1.0.0, связанных с Ceph, можно отметить поддержку Сeph Nautilus и возможность использовать NFS для CephFS- или RGW-бакетов. Из прочих выделяется «созревание» поддержки EdgeFS до уровня беты.

Итак, в этой статье мы:

  • ответим на вопрос, какие плюсы видим в использовании Rook для развертывания Ceph в кластере Kubernetes;
  • поделимся опытом и впечатлениями от использования Rook в production;
  • расскажем, почему мы говорим Rook’у «Да!», и о своих планах на него.

Начнём с общих концепций и теории.

«У меня преимущество в одну Ладью!» (неизвестный шахматист)

Rook или не Rook — вот в чём вопрос - 2

Одним из главных преимуществ Rook является то, что взаимодействие с хранилищами данных ведется через механизмы Kubernetes. Это означает, что больше не нужно копировать команды для настройки Ceph с листочка в консоль.

— Хочешь развернуть в кластере CephFS? Просто напиши YAML-файл!
­— Что? Хочешь развернуть ещё и object store с S3 API? Просто напиши второй YAML-файл!

Rook создан по всем правилам типичного оператора. Взаимодействие с ним происходит при помощи CRD (Custom Resource Definitions) [6], в которых мы описываем необходимые нам характеристики сущностей 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:master
        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:master
        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 [7], который автоматически копировал секреты в новые namespace (пример подобного хука описан в этой статье [8]).

#! /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 [9] или CSI [10] (пока в бета-стадии) и поэтому не требует секретов.

Rook автоматически решает многие проблемы, что и подталкивает нас использовать его в новых проектах.

Осада Rook

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

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

В файле rook-ceph/values.yaml можно найти множество различных настроек. Самое важное — указать tolerations для агентов и поиска. Для чего можно использовать механизм taints/tolerations, мы подробно рассказывали в этой статье [11].

Если вкратце, мы не хотим, чтобы 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 [12]:

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

Далее по желанию настраиваются дополнительные компоненты. Подробнее о них указано в документации [13]. Для администрирования настоятельно рекомендуем установить dashboard и toolbox.

Rook’и-крюки: на всё ли хватает Rook?

Как видно, разработка Rook идёт полным ходом. Но всё ещё остаются проблемы, которые не позволяют нам полностью отказаться от ручной настройки Ceph:

  • Ни один драйвер Rook не умеет [14] экспортировать метрики по использованию смонтированных блоков, что лишает нас мониторинга.
  • Flexvolume и CSI не умеют [15] изменять размер томов (в отличие от того же RBD), поэтому Rook лишается полезного (а иногда и критически нужного!) инструмента.
  • Rook всё ещё не такой гибкий, как обычный Ceph. Если мы захотим настроить, чтобы пул для метаданных CephFS хранился на SSD, а сами данные — на HDD, потребуется прописывать отдельные группы устройств в CRUSH maps вручную.
  • Несмотря на то, что rook-ceph-operator считается стабильным, на данный момент существуют определенные проблемы при обновлении Ceph c версии 13 до 14.

Выводы

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

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

У нас же дальнейшие планы сводятся к тому, что сделать rook-ceph модулем для addon-operator [16], что сделает его использование в наших многочисленных Kubernetes-кластерах ещё более простым и удобным.

P.S.

Читайте также в нашем блоге:

Автор: nabokihms

Источник [19]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/sistemnoe-administrirovanie/317668

Ссылки в тексте:

[1] Rook 1.0.0: https://blog.rook.io/rook-v1-0-a-major-milestone-689ca4c75508

[2] публиковали: https://habr.com/ru/company/flant/blog/348044/

[3] операторов: https://habr.com/ru/company/flant/blog/326414/

[4] единственным: https://github.com/rook/rook#project-status

[5] rook-ceph-operator: https://github.com/rook/rook/blob/master/Documentation/ceph-quickstart.md

[6] CRD (Custom Resource Definitions): https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/

[7] shell-operator: https://github.com/flant/shell-operator

[8] этой статье: https://habr.com/ru/company/flant/blog/447442/

[9] Flexvolume: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-storage/flexvolume.md

[10] CSI: https://habr.com/ru/company/flant/blog/424211/

[11] этой статье: https://habr.com/ru/company/flant/blog/432748/

[12] OSD: http://docs.ceph.com/docs/mimic/man/8/ceph-osd/

[13] документации: https://github.com/rook/rook/tree/release-1.0/cluster/examples/kubernetes/ceph

[14] не умеет: https://github.com/rook/rook/issues/1659

[15] не умеют: https://github.com/rook/rook/issues/1169

[16] addon-operator: https://github.com/flant/addon-operator

[17] Создаём постоянное хранилище с provisioning в Kubernetes на базе Ceph: https://habr.com/ru/company/flant/blog/329666/

[18] Базы данных и Kubernetes (обзор и видео доклада): https://habr.com/ru/company/flant/blog/431500/

[19] Источник: https://habr.com/ru/post/451818/?utm_campaign=451818