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

Container Storage Interface (CSI) – це уніфікований інтерфейс взаємодії Kubernetes та систем зберігання даних. Коротко про нього ми вже розповідали, а сьогодні докладніше розглянемо зв'язку CSI та Ceph: покажемо, як підключити сховище Ceph до кластера Kubernetes.
У статті наведено реальні, хоч і трохи спрощені для зручності сприйняття приклади. Встановлення та налаштування кластерів 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

Тепер необхідно заповнити файл cephrbd.yml. Для цього дізнаємося ID кластера та IP-адреси моніторів у Ceph:

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

Отримані значення заносимо у файл cephrbd.yml. Принагідно включаємо створення політик PSP (Pod Security Policies). Опції у розділах nodeplugin и провізор вже є у файлі, їх можна виправити так, як показано нижче:

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:
  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 та видаємо йому права на запис у пул Кубе:

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:
  # Значения ключей соответствуют имени пользователя и его ключу, как указано в
  # кластере Ceph. ID юзера должен иметь доступ к пулу,
  # указанному в storage class
  userID: rbdkube
  userKey: <user-key>

І створюємо наш секрет:

kubectl apply -f secret.yaml

Далі нам потрібний приблизно такий маніфест StorageClass:

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

   # Эти секреты должны содержать данные для авторизации
   # в ваш пул.
   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

Бачимо, що розмір PVC не змінився. Щоб дізнатися про причину, можна запитати у Kubernetes опис PVC у форматі YAML:

kubectl get pvc rbd-pvc -o yaml

А ось і проблема:

message: Використовуйте для використання (re-)start a під кінець файлу системи, щоб переглянути обсяг на 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

Знову потрібно заповнити файл 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.
Порт для httpMetrics (туди ходитиме Prometheus за метриками для моніторингу) ми змінюємо для того, щоб він не конфліктував з nginx-proxy, який встановлюється Kubespray'ем. Вам це, можливо, не буде потрібно.

Встановлюємо 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:
  # Необходимо для динамически создаваемых томов
  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.18.8.6:6789:/ /mnt/cephfs ceph name=admin,secretfile=/etc/ceph/secret.key,noatime,_netdev    0       2" >> /etc/fstab

mount /mnt/cephfs

Звичайно ж, ось таке монтування FS на ноді Ceph підходить виключно для цілей навчання, ніж ми і займаємось на наших курсах Слерм. Не думаю, що хтось це робитиме в продакшені, великий ризик випадково затерти важливі файли.

Ну і наостанок давайте перевіримо, як у випадку з CephFS справи зі зміною розмірів тому. Повертаємось у Kubernetes і відредагуємо наш маніфест для PVC – збільшимо там розмір, наприклад, до 7Gi.

Застосуємо відредагований файл:

kubectl apply -f pvc.yaml

Подивимося на примонтованому каталозі, як змінилася квота:

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

Для роботи цієї команди, можливо, вам знадобиться встановити в систему пакет атр.

Очі бояться, а руки роблять

На вигляд усі ці заклинання та довгі маніфести YAML здаються складними, але на практиці студенти Слерма розбираються з ними досить швидко.
У цій статті ми не заглиблювались у нетрі — для цього є офіційна документація. Якщо вас цікавлять деталі налаштування сховища Ceph спільно з кластером Kubernetes, допоможуть ці посилання:

Загальні принципи роботи Kubernetes з томами
Документація з RBD
Інтеграція RBD та Kubernetes з точки зору Ceph
Інтеграція RBD та Kubernetes з точки зору CSI
Загальна документація щодо CephFS
Інтеграція CephFS та Kubernetes з точки зору CSI

На курсі Слерм Kubernetes База ви можете піти ще трохи далі і розгорнути в Kubernetes реальну програму, яка буде використовувати CephFS як сховища для файлів. За допомогою GET/POST запитів ви зможете передавати файли та отримувати їх із Ceph.

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

Автор статті: Олександр Швалов, практикуючий інженер Південний міст, Certified Kubernetes Administrator, автор та розробник курсів Слерм.

Джерело: habr.com