Практический пример подключения хранилища на базе Ceph в кластер Kubernetes

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

В прошлой статье были показаны различные варианты использования CSI, в том числе упоминалась и связка с Ceph.


Хотелось бы более подробно остановиться на этом тандеме и вкратце рассказать вам, как мы подключаем хранилище Ceph к кластеру Kubernetes. Будут использоваться реальные примеры, но немного упрощённые для удобства восприятия. Сразу хочется уточнить, что установку и настройку кластеров Ceph и Kubernetes в этой статье мы освещать не будем.


Вам интересно, как это работает?



Итак, у вас под рукой есть кластер Kubernetes, развернутый, к примеру, kubespray. Вдобавок к нему где-то рядом работает кластер Ceph — его можно также поставить, например, вот этим набором плейбуков. Надеюсь, не нужно упоминать, что для продакшена между ними должна быть сеть с пропускной способностью не менее 10 Гбит/с.


Сначала зайдем на одну из нод кластера Ceph и проверяем, что всё в порядке:


ceph health
ceph -s

Далее тут же создадим пул для RBD дисков:


ceph osd pool create kube 32```
ceph osd pool application enable kube rbd

Переходим в кластер Kubernetes, там первым делом нам нужно установить ceph CSI драйвер для RBD. Ставить будем как и положено, через Helm.


Добавляем репозиторий с чартом, получаем набор переменных чарта ceph-csi-rbd:


helm repo add ceph-csi https://ceph.github.io/csi-charts
helm inspect values ceph-csi/ceph-csi-rbd > cephrbd.yml

Далее нам нужно посмотреть кое-какие данные в Ceph. Для этого выполним там команды:


ceph fsid  # так мы узнаем clusterID```
ceph mon dump  # а так увидим IP-адреса мониторов

Далее с учетом этой информации мы уже можем заполнить переменные в файле cephrbd.yml


Заносим сюда свои значения clusterID и адреса мониторов. Попутно включаем создание политик PSP (расшифровка для любознательных: Pod Security Policies). Опции в разделах nodeplugin и provisioner уже есть в файле, их можно исправить так, как показано ниже.


csiConfig:
  - clusterID: "bcd0d202-fba8-4352-b25d-75c89258d5ab"
    monitors:
      - "v2:172.18.8.5:3300/0,v1:172.18.8.5:6789/0"
      - "v2:172.18.8.6:3300/0,v1:172.18.8.6:6789/0"
      - "v2:172.18.8.7:3300/0,v1:172.18.8.7:6789/0"

nodeplugin:
  podSecurityPolicy:
    enabled: true

provisioner:
  replicaCount: 1
  podSecurityPolicy:
    enabled: true

Далее всё что нам остаётся — установить чарт в кластер Kubernetes.


helm upgrade -i ceph-csi-rbd ceph-csi/ceph-csi-rbd -f cephrbd.yml -n ceph-csi-rbd --create-namespace

Отлично, RBD драйвер у нас уже работает, теперь нам нужно создать в Kubernetes новый StorageClass. Но для этого нам опять потребуется немного поработать с Ceph.


Мы создаем нового пользователя в Ceph и выдаем ему права на запись в пул kube:


ceph auth get-or-create client.rbdkube mon 'profile rbd' osd 'profile rbd pool=kube'

А теперь посмотрим ключ доступа всё там же:


ceph auth get-key client.rbdkube

Команда выдаст нечто подобное:


AQCO9NJbhYipKRAAMqZsnqqS/T8OYQX20xIa9A==

Это мы занесём в Secret в кластере Kubernetes, туда где нужен userKey:


---
apiVersion: v1
kind: Secret
metadata:
  name: csi-rbd-secret
  namespace: ceph-csi-rbd
stringData:
  # Key values correspond to a user name and its key, as defined in the
  # ceph cluster. User ID should have required access to the 'pool'
  # specified in the storage class
  userID: rbdkube
  userKey: <user-key>

И создаём наш секрет:


kubectl apply -f secret.yaml

Далее нам нужен примерно такой манифест стораджкласса:


---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: csi-rbd-sc
provisioner: rbd.csi.ceph.com
parameters:
   clusterID: <cluster-id>
   pool: kube

   imageFeatures: layering

   # The secrets have to contain Ceph credentials with required access
   # to the 'pool'.
   csi.storage.k8s.io/provisioner-secret-name: csi-rbd-secret
   csi.storage.k8s.io/provisioner-secret-namespace: ceph-csi-rbd
   csi.storage.k8s.io/controller-expand-secret-name: csi-rbd-secret
   csi.storage.k8s.io/controller-expand-secret-namespace: ceph-csi-rbd
   csi.storage.k8s.io/node-stage-secret-name: csi-rbd-secret
   csi.storage.k8s.io/node-stage-secret-namespace: ceph-csi-rbd

   csi.storage.k8s.io/fstype: ext4

reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions:
  - discard

Нужно заполнить clusterID, его ранее мы уже узнали с помощью команды ceph fsid, и применить этот манифест в кластере Kubernetes (kubectl apply -f storageclass.yaml).
Теперь можно проверять работу кластеров в связке, для этого создадим например вот такой PVC (Persistent Volume Claim):


apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: rbd-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: csi-rbd-sc

И можно сразу проверить, как Kubernetes создал нам в Ceph запрошенный том:


kubectl get pvc
kubectl get pv

Окей, вроде бы всё отлично, а как это выглядит на стороне Ceph?
Получаем список томов в пуле и просматриваем информацию о нашем томе:


rbd ls -p kube
rbd -p kube info csi-vol-eb3d257d-8c6c-11ea-bff5-6235e7640653  # тут, конечно же, будет другой ID тома, который выдала предыдущая команда

Теперь давайте посмотрим, как работает изменение размера тома RBD. Изменяем размер тома в манифесте pvc.yaml до 2Gi и применяем его kubectl apply -f pvc.yaml
Подождем некоторое время для применения наших изменений, и можем посмотреть, как изменился размер тома:


rbd -p kube info csi-vol-eb3d257d-8c6c-11ea-bff5-6235e7640653

kubectl get pv
kubectl get pvc

Видим, что размер не изменился. Чтобы узнать причину, можно запросить у Kubernetes описание PVC в формате YAML:


kubectl get pvc rbd-pvc -o yaml

А вот и проблема:


message: Waiting for user to (re-)start a pod to finish file system resize of volume on node. type: FileSystemResizePending


Необходимо смонтировать том для того, чтобы увеличить на нем файловую систему. А у нас этот PVC/PV никак не используется.


Можем создать тестовый Pod, например вот так:


---
apiVersion: v1
kind: Pod
metadata:
  name: csi-rbd-demo-pod
spec:
  containers:
    - name: web-server
      image: nginx:1.17.6
      volumeMounts:
        - name: mypvc
          mountPath: /data
  volumes:
    - name: mypvc
      persistentVolumeClaim:
        claimName: rbd-pvc
        readOnly: false

И теперь посмотрим на PVC:


kubectl get pvc

Размер изменился, всё в порядке.


В первой части мы работали с блочным устройством RBD (оно так и расшифровывается – Rados Block Device), но это не лучший выбор, если требуется одновременная работа с этим диском разных микросервисов. Для работы с файлами, а не с образом диска, намного лучше подходит CephFS. Давайте же попробуем на примере наших кластеров Ceph и Kubernetes настроить CSI и остальные необходимые сущности для работы с CephFS.


Начнём с получения значений из нужного нам нового Helm-чарта:


helm inspect values ceph-csi/ceph-csi-cephfs > cephfs.yml

Нам снова нужно заполнить этот файл, как и ранее, нам помогут команды Ceph:


ceph fsid
ceph mon dump

И заполняем файл со значениями примерно так:


csiConfig:
  - clusterID: "bcd0d202-fba8-4352-b25d-75c89258d5ab"
    monitors:
      - "172.18.8.5:6789"
      - "172.18.8.6:6789"
      - "172.18.8.7:6789"

nodeplugin:
  httpMetrics:
    enabled: true
    containerPort: 8091
  podSecurityPolicy:
    enabled: true

provisioner:
  replicaCount: 1
  podSecurityPolicy:
    enabled: true

Обратите внимание, что адреса мониторов указываются в простой форме address:port. Они передаются в модуль ядра для монтирования cephfs на узел, а этот модуль ядра еще не умеет работать с протоколом мониторов v2.


Устанавливаем Helm-чарт в кластер Kubernetes:


helm upgrade -i ceph-csi-cephfs ceph-csi/ceph-csi-cephfs -f cephfs.yml -n ceph-csi-cephfs --create-namespace

Переходим к хранилищу данных Ceph, чтобы создать там отдельного пользователя. В документации указано, что провизионеру CephFS необходимы права доступа администратора кластера. Но мы создадим отдельного пользователя fs с ограниченными правами:


ceph auth get-or-create client.fs mon 'allow r' mgr 'allow rw' mds 'allow rws' osd 'allow rw pool=cephfs_data, allow rw pool=cephfs_metadata'

И сразу же посмотрим его ключ доступа, он нам пригодится далее:


ceph auth get-key client.fs

Далее мы создадим отдельные Secret и StorageClass. Ничего нового, мы это уже видели на примере RBD:


---
apiVersion: v1
kind: Secret
metadata:
  name: csi-cephfs-secret
  namespace: ceph-csi-cephfs
stringData:
  # Required for dynamically provisioned volumes
  adminID: fs
  adminKey: <вывод предыдущей команды>

Применяем манифест:


kubectl apply -f secret.yaml

А теперь – отдельный StorageClass:


---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: csi-cephfs-sc
provisioner: cephfs.csi.ceph.com
parameters:
  clusterID: <cluster-id>

  # Имя файловой системы CephFS, в которой будет создан том
  fsName: cephfs

  # (необязательно) Пул Ceph, в котором будут храниться данные тома
  # pool: cephfs_data

  # (необязательно) Разделенные запятыми опции монтирования для Ceph-fuse
  # например:
  # fuseMountOptions: debug

  # (необязательно) Разделенные запятыми опции монтирования CephFS для ядра
  # См. man mount.ceph чтобы узнать список этих опций. Например:
  # kernelMountOptions: readdir_max_bytes=1048576,norbytes

  # Секреты должны содержать доступы для админа и/или юзера Ceph.
  csi.storage.k8s.io/provisioner-secret-name: csi-cephfs-secret
  csi.storage.k8s.io/provisioner-secret-namespace: ceph-csi-cephfs
  csi.storage.k8s.io/controller-expand-secret-name: csi-cephfs-secret
  csi.storage.k8s.io/controller-expand-secret-namespace: ceph-csi-cephfs
  csi.storage.k8s.io/node-stage-secret-name: csi-cephfs-secret
  csi.storage.k8s.io/node-stage-secret-namespace: ceph-csi-cephfs

  # (необязательно) Драйвер может использовать либо ceph-fuse (fuse), 
  # либо ceph kernelclient (kernel).
  # Если не указано, будет использоваться монтирование томов по умолчанию,
  # это определяется поиском ceph-fuse и mount.ceph
  # mounter: kernel
reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions:
  - debug

Заполним тут clusterID и применим в Kubernetes:


kubectl apply -f storageclass.yaml

Проверяем, как оно работает


Для этого, как и в прошлом примере, создадим PVC:


---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: csi-cephfs-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
  storageClassName: csi-cephfs-sc

И проверяем наличие PVC/PV:


kubectl get pvc
kubectl get pv

Если хочется посмотреть на файлы и каталоги в CephFS, можно примонтировать эту файловую систему куда-нибудь, например, как показано ниже.


Сходим на одну из нод кластера Ceph и выполним там вот такие действия:


# Точка монтирования
mkdir -p /mnt/cephfs

# Создаем файл с ключом администратора
ceph auth get-key client.admin >/etc/ceph/secret.key

# Добавляем запись в /etc/fstab
# !! Изменяем ip адрес на адрес нашего узла
echo "172.<xx>.<yyy>.6:6789:/ /mnt/cephfs ceph name=admin,secretfile=/etc/ceph/secret.key,noatime,_netdev    0       2" >> /etc/fstab

mount /mnt/cephfs

Конечно же, вот такое монтирование ФС на ноде Ceph подходит исключительно для целей обучения, чем мы и занимаемся на наших курсах Слёрм. Не думаю, что кто-то станет это делать в продакшене, велик риск случайно затереть важные файлы.


Ну и напоследок давайте проверим, как в случае с CephFS обстоят дела с изменением размеров тома. Возвращаемся в Kubernetes и отредактируем наш манифест для PVC, увеличим там размер, к примеру, до 7Gi.


Применим отредактированный файл:


kubectl apply -f pvc.yaml

И теперь можно посмотреть на примонтированном каталоге, как изменилась квота:


getfattr -n ceph.quota.max_bytes <каталог-с-данными>

Для работы этой команды, возможно, потребуется установить в систему пакет attr.


С виду, конечно же, все эти заклинания и длинные манифесты YAML кажутся очень сложными, но на практике наши студенты разбираются с ними довольно быстро. Конечно же, мы не углублялись в дебри настроек кластеров и драйверов CSI — для этого существует официальная документация.
В нашем курсе Kubernetes База вы можете пойти ещё чуть дальше и развернуть в Kubernetes реальное приложение, которое будет использовать CephFS в качестве хранилища для файлов и посредством GET/POST запросов вы сможете передавать файлы и получать их из Ceph.


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


Автор статьи: Александр Швалов, инженер Southbridge. Администратор с 6-летним стажем. Certified Kubernetes Administrator. Настройка и сопровождение Kubernetes-проектов в Southbridge. Разработчик курсов Слёрм.

Источник: https://habr.com/ru/company/southbridge/blog/519642/


Интересные статьи

Интересные статьи

Примечание: это не полноценная статья-руководство, а скорее напоминание/подсказка для тех, кто уже пользуется ConfigMap в Kubernetes или только готовит своё приложение для работы в нём. Пр...
Прим. перев.: Эта статья, написанная Galo Navarro, что занимает должность Principal Software Engineer в европейской компании Adevinta, — увлекательное и поучительное «расследование» в области экс...
В этой статье будем реализовывать так называемую Host-based Card Emulation (HCE, Эмуляция банковской карты на телефоне). В сети много подробных описаний этой технологии, здесь я сделал акцент име...
Эта статья продолжает наш недавний материал про миграцию RabbitMQ и посвящена MongoDB. Поскольку мы обслуживаем множество кластеров Kubernetes и MongoDB, пришли к естественной необходимости м...
Прим. перев.: Автор статьи — Reuven Harrison — имеет более 20 лет опыта в разработке программного обеспечения, а на сегодняшний день является техническим директором и соучредителем компании T...