Многие начинающие 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 создан, подключен к кластеру и работает как надо.
Поздравляю с успешным завершением работы, коллеги!