Блог Слёрм

Как быстро установить и использовать Linstor в Kubernetes

Мы перевели пост из блога финского веб-разработчика Vito Botta, который работает над платформой Brella.
 

В предыдущем посте я поделился комментариями и результатами бенчмарков по нескольким бесплатным и платным решениям для хранения в Kubernetes. Еще я сказал, что распрощался с Kubernetes из-за разных проблем с хранилищами. Один читатель предложил попробовать Linstor — еще одно опенсорс-решение с возможностью платной поддержки, о котором я раньше не слышал.

У меня было столько проблем с другими вариантами, что настроен я был скептически. Но попробовал, и мне понравилось. Быстрая репликация на основе DRBD работает отлично. Пока у меня возникла только одна проблема с томами, которые неправильно отсоединяются с версией 0.6.4 плагина CSI, но разработчики быстро выпустили новую версию (0.7.0), и там это, вроде, исправлено. Хорошо бы в Linstor были off site бэкапы на основе снапшотов… В остальном неплохое решение. Может, у Kubernetes еще есть шанс. Буду тестить дальше. Очень надеюсь, что других проблем не будет, и я смогу использовать его, забыв про Heroku!

Как бы там ни было, документация обширная и хорошая, но кое-что устарело, и я решил сам написать, как быстро установить и использовать Linstor в Kubernetes. Предполагаю, что вы хотите просто установить хранилище и поддержку CSI в Kubernetes, чтобы динамически подготавливать тома с классом хранилища. Это инструкции для Ubuntu. Я пытался что-то сделать в CentOS, но, судя по всему, модуль ядра DRBD для этого дистрибутива доступен, а другие пакеты — нет. Linbit (компания, которая делает Linstor) предоставляет эти пакеты с репозиторием ppa, так что я использовал Ubuntu.

Подробно вся технология описана в документации к Linstor. Есть открытые контакты, куда можно задавать вопросы.

Установка в Ubuntu

У меня был тестовый кластер с тремя нодами, по 100 ГБ дискового пространства каждый, с подключением через Wireguard VPN, так что весь трафик между нодами зашифрован. Это немного влияет на производительность, но хотя мой облачный провайдер (Hetzner Cloud) теперь предлагает частные сети, он все равно рекомендует шифровать трафик для конфиденциальных данных. В VPN ноды настроены как linstor-master1, linstor-master2 и linstor-master3 с IP 192.168.37.1, 192.168.37.2 и 192.168.37.3 соответственно. Инструкции, понятное дело, нужно адаптировать к вашей системе.

Сначала установим заголовки ядра, потому что репликация DRBD основана на модуле ядра, который должен быть создан на всех нодах:

apt-get install linux-headers-$(uname -r)

Потом добавим репозиторий ppa:

add-apt-repository ppa:linbit/linbit-drbd9-stack
apt-get update

На всех нодах установим эти пакеты:

apt install drbd-utils drbd-dkms lvm2

Загрузим модуль ядра DRBD:

modprobe drbd

Проверим, что он точно загрузился:

lsmod | grep -i drbd

Убедимся, что он автоматически загружается при запуске:

echo drbd > /etc/modules-load.d/drbd.conf

Кластер Linstor состоит из одного активного контроллера, который управляет всей информацией о кластере, и спутников — нод, которые предоставляют хранилище. На ноде, которая будет контроллером, выполним команду:

apt install linstor-controller linstor-satellite linstor-client

Эта команда делает контроллер еще и спутником. В моем случае контроллером был linstor-master1. Чтобы сразу запустить контроллер и включить его автоматический запуск при загрузке, выполним команду:

systemctl enable --now linstor-controller
systemctl start linstor-controller

На оставшихся нодах-спутниках установим следующие пакеты:

apt install linstor-satellite linstor-client

Запустим спутник и сделаем так, чтобы он запускался при загрузке:

systemctl enable --now linstor-satellite
systemctl start linstor-satellite

Теперь можно добавить к контролеру спутники, включая саму эту ноду:

linstor node create linstor-master1 192.168.37.1
linstor node create linstor-master2 192.168.37.2
linstor node create linstor-master3 192.168.37.3

Подождем пять секунд и убедимся, что ноды онлайн:

linstor node list

Получится что-то вроде этого:

Теперь нужно настроить хранилище. Linstor работает с LVM или ZFS, чтобы управлять хранилищем. Не знаю, в чем разница, но я больше знаком с LVM, его и возьму.

Сначала подготовим физический диск или диски на каждом узле. В моем случае это /dev/sdb:

pvcreate /dev/sdb

Создадим группу томов:

vgcreate vg /dev/sdb

Я назову группу «vg», а вы — как хотите.

Теперь создадим «тонкий» пул для thin provisioning (то есть возможности создавать тома больше доступного места, чтобы потом расширять хранилище по необходимости) и снапшотов:

lvcreate -l 100%FREE --thinpool vg/lvmthinpool

Эта команда создает логический том, который занимает весь диск.

Пора создать пул хранилища на каждой ноде, так что на контроллере выполняем команду:

linstor storage-pool create lvmthin linstor-master1 linstor-pool vg/lvmthinpool
linstor storage-pool create lvmthin linstor-master2 linstor-pool vg/lvmthinpool
linstor storage-pool create lvmthin linstor-master3 linstor-pool vg/lvmthinpool

Я назову пул «linstor-pool». Убедимся, что пул создан:

linstor storage-pool list

Получится что-то вроде этого:


Основная настройка Linstor готова.

Kubernetes


Чтобы Kubernetes динамически подготавливал тома, нужно установить плагин CSI и создать класс хранилища. На момент написания поста последняя версия была 0.7.0. Узнайте, не появилось ли новых.

Выполняем установку этой командой:

TAG=v0.7.0
CONTROLLER_IP=192.168.37.1

curl https://raw.githubusercontent.com/LINBIT/linstor-csi/$TAG/examples/k8s/deploy/linstor-csi-1.14.yaml | sed "s/linstor-controller.example.com/$CONTROLLER_IP/g" | kubectl apply -f -

Вместо TAG и CONTROLLER_IP укажите свои значения. Ждем, пока поды не заработают:

watch kubectl -n kube-system get all

Наконец, устанавливаем класс хранилища:

REPLICAS=3

cat <<EOF | kubectl apply -f -
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
     name: linstor
provisioner: linstor.csi.linbit.com
parameters:
     autoPlace: "$REPLICAS"
     storagePool: "linstor-pool"
EOF

Указываем число реплик по числу нод. autoPlace гарантирует, что тома будут автоматически размещаться и распределяться по нодам/пулам.

Наконец, делаем pvc и проверяем, что подготовка работает:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
     name: test-pvc
spec:
     storageClassName: linstor
     accessModes:
         -  ReadWriteOnce
     resources:
         requests:
            storage: 1Gi
EOF

kubectl get pvc

Если все нормально, через несколько секунд мы увидим, что pvc привязан (bound):

NAME  STATUS VOLUME                  CAPACITY ACCESS MODES STORAGECLASS AGE
test-pvc Bound    pvc-af6991ee-b922-11e9-bbca-9600002d2434 1Gi RWO linstor 10s

Проверим, как дела на контроллере в Linstor:

linstor volume list

Получится что-то вроде этого:


Заодно уже прогоним простой бенчмарк и посмотрим на производительность, создав pvc и задание:

cat <<EOF | kubectl apply -f -
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
     name: dbench-linstor
spec:
     storageClassName: linstor
     accessModes:
         -   ReadWriteOnce
     resources:
        requests:
           storage: 5Gi
---
apiVersion: batch/v1
kind: Job
metadata:
     name: dbench-linstor
spec:
    template:
        spec:
            containers:
            - name: dbench
              image: sotoaster/dbench:latest
              imagePullPolicy: IfNotPresent
              env:
                  - name: DBENCH_MOUNTPOINT
                     value: /data
                  - name: FIO_SIZE
                     value: 1G
              volumeMounts:
             - name: dbench-pv
               mountPath: /data
          restartPolicy: Never
          volumes:
          - name: dbench-pv
             persistentVolumeClaim:
                 claimName: dbench-linstor
        backoffLimit: 4
EOF

Подождем готовности пода задания, а потом проверим логи:

kubectl logs -f $(kubectl get pods | awk '/dbench/ {print $1;exit}')

В итоге получится что-то вроде этого:

==================
= Dbench Summary =
==================
Random Read/Write IOPS: 7495/4468. BW: 300MiB/s / 68.4MiB/s
Average Latency (usec) Read/Write: 945.99/
Sequential Read/Write: 301MiB/s / 62.6MiB/s
Mixed Random Read/Write IOPS: 7214/2401

В моем случае все показатели идентичны тестированию диска напрямую, кроме скорости записи — она ниже из-за репликации и VPN-шифрования. Иначе бы и эти цифры совпали. У Linstor и правда нет издержек, и это круто.

Заключение

Настраивать Linstor, может, и посложнее, чем применить пару yaml, как у конкурентов, зато это совсем не сложно и можно автоматизировать с помощью Ansible или чего-то похожего. Пока увидел только одну проблему, но и она исправлена. Надеюсь, других не найду. Я по-прежнему мечтаю использовать самоуправляемый Kubernetes вместо Heroku. Надеюсь, этот пост показался вам полезным и сэкономил время.
Kubernetes