Многие начинающие DevOps'ы, осваивающие kubernetes сталкиваются с вопросом: "Как организовать Persistent Storage в своём kubernetes-кластере?" Для этой цели есть много вариантов: ceph, nfs, mayastor, iscsi, linstor, longhorn. Сегодня мы рассмотрим один из них - linstor (он же piraeus). Мы настроим свой Persistent Storage и подключим его к нашему kubernetes-кластеру.

Предисловие:

Про PersistentStorage - что это и для чего вы можете прочитать вот в этой статье.

Что такое linstor и как он работает подробно написано здесь.

Исходное оборудование:

Здесь мы настраивали однонодовый kubernetes-кластер для наших экспериментов. Именно к нему мы будем подключать наш PersistentStorage.
Для реализации целей нашего эксперимента нам понадобится 7ГБ оперативной памяти для виртуалки с кубер-кластером.
Айпи-адрес виртуалки с нашим однонодовым кубер-кластером: 172.20.0.31/24

Для linstor-кластера мы будем использовать три виртуальные машины с RedOS8. Три виртуалки нам нужно для того чтобы drbd-инстансы смогли собрать кворум для выбора primary-инстанса.
В виду скудности моих ресурсов, конфигурация каждой виртуалки будет следующая:
CPU: 2 / RAM: 2GB / HDD: 8GB + ещё один диск на котором будет располагаться само хранилище, чем больше этот диск тем больше места в нашем Storage. У меня этот диск будет размером 10ГБ.
Хостнеймы моих виртуалок пусть будут s1, s2, s3 (ниже эти хостнеймы будут использоваться при настройке linstor-кластера).
Айпи-адреса виртуалок: 172.20.0.21/24, 172.20.0.22/24, 172.20.0.23/24

Подготовка виртуалок:

Далее описаны действия, которые необходимо осуществить на всех виртуалках нашего будущего linstor-кластера, а также на виртуалках kubernetes-кластера.

Прежде всего нам необходимо обновить все виртуалки:

dnf update -y && reboot

Устанавливаем необходимые пакеты (на ВМ kubernetes-кластера docker-ce можно не ставить):

dnf install -y docker-ce drbd drbd-kmod drbd-reactorzfs zfs-kmod

Настраиваем автозапуск модулей ядра и запускаем эти модули:

echo -e "dm_cache\ndm_crypt\ndm_thin_pool\ndm_snapshot\ndm_writecache\ndrbd\ndrbd_transport_tcp\nlibcrc32c\nloop\nnvmet_rdma\nnvme_rdma\n" > /etc/modules-load.d/drbd.conf
echo "options drbd usermode_helper=disabled" > /etc/modprobe.d/drbd.conf
modprobe -a $(cat /etc/modules-load.d/drbd.conf)

Немного тюнингуем lvm:

sed -i 's/# global_filter = .*/global_filter = \[ \"r\|\^\/dev\/drbd\|\" \]/' /etc/lvm/lvm.conf

Установка и запуск linstor-кластера

На всех трёх виртуалках (s1, s2, s3) предназначенных для linstor-кластера выполняем следующие действия.

Создаём файл /etc/systemd/system/linstor-satellite.service для запуска linstor-satellit'a

cat <<EOF > /etc/systemd/system/linstor-satellite.service
[Unit]
Description=Linstor-Satellite container
After=docker.service
Wants=network-online.target docker.socket
Requires=docker.socket

[Service]
Restart=always
ExecStartPre=/bin/bash -c "/usr/bin/docker container inspect linstor-satellite 2> /dev/null || /usr/bin/docker run -d --name=linstor-satellite --net=host -v /dev:/dev -v /var/lib/drbd:/var/lib/drbd --privileged quay.io/piraeusdatastore/piraeus-server:v1.29.1"
ExecStart=/usr/bin/docker start -a linstor-satellite
ExecStop=/usr/bin/docker stop -t 10 linstor-satellite

[Install]
WantedBy=multi-user.target
EOF

Запускаем linstor-satellite:

systemctl daemon-reload && systemctl enable --now linstor-satellite

На виртуалке s1 (только на ней) создаём файл /etc/systemd/system/linstor-controller.service для запуска linstor-контроллера:

cat <<EOF > /etc/systemd/system/linstor-controller.service
[Unit]
Description=Linstor-Controller container
After=docker.service
Wants=network-online.target docker.socket
Requires=docker.socket

[Service]
Restart=always
ExecStartPre=/bin/bash -c "/usr/bin/docker container inspect linstor-controller 2> /dev/null || /usr/bin/docker run -d --name=linstor-controller --net=host -v /dev:/dev -v /var/lib/linstor/:/var/lib/linstor/ --privileged quay.io/piraeusdatastore/piraeus-server:v1.29.1 startController"
ExecStart=/usr/bin/docker start -a linstor-controller
ExecStop=/usr/bin/docker stop -t 10 linstor-controller

[Install]
WantedBy=multi-user.target
EOF

Запускаем наш linstor-controller:

systemctl daemon-reload && systemctl enable --now linstor-controller

Проверить успешность запуска linstor-controller'a и linstor-satellite можно командой:

docker ps

Настройка linstor-кластера

На ВМ s1 (там где у нас запущен linstor-cotroller) производим нехитрые действия по настройке linstor-кластера.
Подчеркну, что все действия выполняемые в этом пункте статьи "Настройка linstor-кластера" необходимо выполнять только на виртуалке, на которой запущен linstor-controller.

Используем уже запущенный docker-контейнер linstor-controller: запускаем внутри контейнера консольную утилиту для управления linstor-кластером с необходимыми нам параметрами.

Присоединяем к кластеру ноду, находящуюся на виртуалке s1, айпи-адрес которой 172.20.0.21

docker exec -it linstor-controller linstor node create s1 172.20.0.21

Точно также присоединяем к кластеру ноды s2 (172.20.0.22) и s3 (172.20.0.23):

docker exec -it linstor-controller linstor node create s2 172.20.0.22
docker exec -it linstor-controller linstor node create s3 172.20.0.23

Проверяем результат:

docker exec -it linstor-controller linstor node list

Смотрим сколько у нас места есть для нашего PersistentStorage:

docker exec -it linstor-controller linstor physical-storage list

Отлично! Видим, что на каждой ноде есть свободный диск /dev/sdb по 10ГБ каждый.

Создаём на этих дисках (/dev/sdb) StoragePool с именем sp01 на каждой ноде по отдельности: s1, s2, s3 (все команды запускать на виртуалке s1 с linstor-controller'ом):

docker exec -it linstor-controller linstor physical-storage create-device-pool --pool-name lvmpool LVMTHIN s1 /dev/sdb --storage-pool sp01
docker exec -it linstor-controller linstor physical-storage create-device-pool --pool-name lvmpool LVMTHIN s2 /dev/sdb --storage-pool sp01
docker exec -it linstor-controller linstor physical-storage create-device-pool --pool-name lvmpool LVMTHIN s3 /dev/sdb --storage-pool sp01

Любуемся результатом:

docker exec -it linstor-controller linstor storage-pool list

Подключаем PersistentStorage к kubernetes-кластеру

На виртуалке нашего однонодового кубер-кластера выполняем следующие действия.

Создадим рабочую директорию:

mkdir ~/piraeus && cd ~/piraeus

Клонируем piraeus-operator

git clone --branch v2 https://github.com/piraeusdatastore/piraeus-operator

Установим в наш кубер-кластер helm-чарт piraeus-operator:

helm install piraeus-operator piraeus-operator/charts/piraeus --create-namespace -n piraeus-datastore --set installCRDs='true'

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

Создадим файл с настройкой linstor-cluster'a в нашем кубере:

cat <<EOF > LinstorCluster.yaml
apiVersion: piraeus.io/v1
kind: LinstorCluster
metadata:
  name: linstorcluster
spec:
  externalController:
    url: http://172.20.0.21:3370
EOF

Файл с настройкой linstor-satellite:

cat <<EOF > LinstorSatelliteConfiguration.yaml
apiVersion: piraeus.io/v1
kind: LinstorSatelliteConfiguration
metadata:
  name: satellite
spec:
  podTemplate:
    spec:
      initContainers:
        - name: drbd-module-loader
          \$patch: delete
      hostNetwork: true
EOF

Глубинный смысл этих двух файлов следующий: мы в нашем кубер-кластере создадим ещё одну (или несколько если кластер многонодовый) дополнительную ноду нашего linstor-кластера. Эта нода(ы) подключится к нашему внешнему linstor-кластеру и будет реплицировать drbd-разделы на ноду кубер-кластера.

Итак... применяем созданные yaml-файлы:

kubectl apply -f LinstorCluster.yaml
kubectl apply -f LinstorSatelliteConfiguration.yaml

Ждём пару-тройку минут и проверяем результат:

kubectl get pods -n piraeus-datastore

Успех! Все нужные поды запустились.

Создаём StorageClass:

cat << EOF > StorageClass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: linstor
provisioner: linstor.csi.linbit.com
parameters:
  autoPlace: "2"
  storagePool: sp01
  resourceGroup: DfltRscGrp
EOF
kubectl apply -f StorageClass.yaml

На этом настройка закончена!

Проверка работы PersistentStorage

Создаём наш тестовый PersistentVolumeClaim, благодаря которому кубер создаст PersistentVolume подключенный к нашему linstor-кластеру:

cat <<EOF > testpvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: testpvc
spec:
  storageClassName: linstor
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 500Mi
EOF
kubectl apply –f testpvc.yaml
kubectl get pvc

Создаём pod, который подключится к testpvc, подмонтирует его внутри контейнера к директории /data и начнёт раз в 10 секунд сохранять в /data по одному файлу:

cat <<EOF > testpod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: alpine
  namespace: default
spec:
  containers:
  - name: alpine
    image: alpine
    command: [/bin/sh]
    args: ["-c", "while true; do echo $(date '+%Y%m%d-%H%M%S') > /data/$(date '+%Y%m%d-%H%M%S') ; sleep 10; done"]
    volumeMounts:
    - name: testpvc
      mountPath: /data
  volumes:
  - name: testpvc
    persistentVolumeClaim:
      claimName: "testpvc"
EOF
kubectl apply –f testpod.yaml

Смотрим что получилось:

kubectl get pods

Заходим в shell pod'a и проверяем содержимое директории /data:

kubectl exec -it alpine -- ls -la /data

Прекрасно! Видим, что в /data каждые 10 секунд появляются файлы.

Теперь удаляем наш pod и создаём его заново с теми же настройками:

kubectl delete pod alpine && kubectl apply –f testpod.yaml

При удалении pod'a кубер удаляет всё, что было сохранено внутри pod'a за исключением PersistentStorage. Проверяем содержимое директории /data во вновь созданном pod'e:

kubectl exec -it alpine -- ls -la /data

Как видим, созданные в предыдущем pod'e файлы остались в директории /data PersistentStorage создан, подключен к кластеру и работает как надо.
Поздравляю с успешным завершением работы, коллеги!

Комментарии (1)


  1. D1abloRUS
    02.10.2024 09:52

    А теперь запустите запись и пороняйте ноды с хранилкой по питанию/сети(не grace), а потом уроните их все и включите обратно