В предыдущем посте я поделился комментариями и результатами бенчмарков по нескольким бесплатным и платным решениям для хранения в 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 и правда нет издержек, и это круто.