Blockstor: LINSTOR-совместимая система хранения для Kubernetes, написанная с нуля на Go

Команда Cozystack открыла исходный код Blockstor — плоскости управления блочным хранилищем в Kubernetes: LVM и ZFS как backend-ы, репликация через DRBD и LINSTOR-совместимый REST API. Проект живёт в организации cozystack и разрабатывается как часть Cozystack — платформы, принятой в CNCF Sandbox. Лицензия — Apache 2.0.

Главное, что делает проект интересным: это не форк и не обёртка. Blockstor написан с нуля на Go, но говорит на том же REST API, что и LINSTOR — поэтому все клиентские инструменты, которыми вы уже пользуетесь, продолжают работать без единого изменения: CLI linstor, linstor-csi, piraeus-operator и библиотека golinstor.

Почему Blockstor выбрал другой подход

LINSTOR — зрелая система, и она годами работала в продакшене в Cozystack. Мы не столкнулись с потолком функциональности — мы столкнулись с моделью.

Оригинальный контроллер устроен на основе запросов: для большинства вызовов API он в реальном времени обращается к узлам и опрашивает их состояние, чтобы собрать ответ. Отсюда два следствия. Во-первых, такая архитектура плохо масштабируется. Во-вторых, при отсутствии цикла согласования (reconciliation loop) автоматическое восстановление после сбоев приходится навешивать снаружи.

Blockstor построен так, как обычно строят операторы Kubernetes: желаемое состояние хранится в CRD, а набор reconciler-ов на controller-runtime приводит кластер к этому состоянию. Из этого следуют три практических вывода:

  • Нет внешней базы данных, которую нужно резервировать и о которой нужно беспокоиться
  • Нет состояния в памяти, которое можно потерять при перезапуске контроллера
  • Нет опроса узлов на стороне контроллера, который может отставать от реальности

Сателлиты сами наблюдают за API и записывают наблюдаемое состояние обратно через Server-Side Apply, используя отдельные field manager-ы. Spec принадлежит контроллеру, Status — сателлиту, и это разделение соблюдается строго.

Из чего это состоит

Три компонента, все — обычные нагрузки Kubernetes:

КомпонентРоль
blockstor-controllerDeployment, запускающий reconciler-ы на controller-runtime
blockstor-apiserverStateless, LINSTOR-совместимый REST-фронтенд, работающий на CRD. Именно с ним общаются linstor, CSI и Piraeus
blockstor-satelliteDaemonSet: поднимает слои DRBD, LUKS и STORAGE на узле и вызывает drbdadm, lvs, zfs и cryptsetup

Объекты живут в группе blockstor.cozystack.io/v1alpha1: Node, StoragePool, ResourceGroup, ResourceDefinition, Resource, Snapshot, PhysicalDevice и ControllerConfig. CRD спроектированы как публичная точка интеграции, с валидацией на уровне схемы и безопасной моделью множественной записи для Status, чтобы GitOps-инструменты и мониторинг могли работать с ними напрямую.

Что уже работает

  • Реплицированные тома DRBD на основе LVM, LVM-thin, ZFS, ZFS-thin и файловых backend-ов
  • Режим без DRBD — одна реплика, дисковая или бездисковая
  • Шифрование тома на уровне LUKS; слои стекируются как DRBD → LUKS → STORAGE
  • Автоматическое размещение с ограничениями: зоны, свойства узлов и распределение реплик
  • Политики TieBreaker и кворума — одна из наиболее тщательно протестированных частей системы
  • Снапшоты: создание, откат, клонирование и восстановление в новый ресурс
  • Передача снапшотов внутри кластера с помощью zfs send/recv и thin-send-recv
  • Онлайн-изменение размера тома. Уменьшение по умолчанию отключено и требует явного force=true — здесь мы намеренно строже оригинала
  • Создание пулов из физических дисков
  • Ребалансировка и миграция реплик: автоматическая эвакуация с уходящего узла, автоматическое повышение до дисковой реплики и восстановление после split-brain
  • Пропуск начальной синхронизации при добавлении реплики за счёт заполнения Generation Identifier. Добавление третьей реплики к многотерабайтному тому не превращается в многочасовой resync
  • mTLS на API с горячей перезагрузкой сертификатов, метрики Prometheus и образы для amd64 и arm64
  • RWX — подтверждено end-to-end тестом через linstor-csi и NFS-Ganesha

Чего пока нет

Мы предпочли написать об этом в анонсе, а не дать вам обнаружить это на третий день.

Следующее не реализовано и честно возвращает 501 Not Implemented, а не тихий 404: передача снапшотов между кластерами, бэкапы и очередь бэкапов, расписания, удалённые backend-ы вроде S3, а также драйверы SPDK, NVMe-oF, OpenFlex и Exos. Нет Helm-чарта — установка идёт через обычные манифесты. Версия пока 0.x.

Список отличий в поведении CLI от оригинала ведётся публично, вместе с реестром известных проблем и разбором тестов csi-sanity, которые не проходят. Проще говоря: проект сам публикует список собственных пробелов.

Почему этому можно доверять

Переписанная с нуля плоскость управления хранилищем — это заявление, которое требует доказательств, а не обещаний. Наш ответ — тесты.

Реализация — 94 000 строк. Тесты — 170 000 строк на Go и ещё 46 000 строк shell-скриптов. И это не только модульные тесты:

  • 108 интеграционных тестов запускают настоящий Python-клиент linstor против envtest при каждом PR
  • Контрактные тесты запускают настоящие drbdmeta и drbdadm в Docker на loopback-устройствах
  • 89 end-to-end сценариев запускаются на стенде Talos и QEMU с настоящим DRBD
  • 91 ячейка CLI-матрицы и 74 сценария повторного воспроизведения покрывают рабочие процессы оператора
  • Гарнесс сверки паритета сравнивает ответы Blockstor с работающим оригинальным LINSTOR и валит CI при любом расхождении, не внесённом в список допустимых

Релиз v0.1.11 заслуживает отдельного упоминания: он воспроизвёл и закрыл 48 граничных случаев, взятых непосредственно из баг-трекера linstor-server — на живом стенде, а спорные случаи решались сверкой с работающим оригиналом.

Совместимость и юридическая сторона

Blockstor возвращает 1.33.2+git=blockstor для linstor controller version и реализует те эндпоинты, которые на самом деле вызывают linstor-csi и piraeus-operator. Piraeus подключается в режиме external-controller — укажите адрес apiserver-а Blockstor, и linstor-csi продолжит работать без изменений.

LINSTOR распространяется под GPL, а Blockstor — под Apache 2.0, поэтому исходники оригинала не использовались. Проект — реализация в чистой комнате (clean-room): совместимые типы взяты из golinstor, библиотеки под Apache 2.0, и никакой код не скопирован и не сгенерирован из GPL-источников. Это не декларация, а проверяемое правило: при каждом PR запускается лицензионный шлюз в CI, который не допускает GPL, AGPL, LGPL и SSPL в граф выполнения — включая код, сгенерированный из спецификации под GPL.

И прямо об этом: LINSTOR, LINBIT и DRBD — торговые марки LINBIT. Blockstor — независимый проект, не аффилированный с LINBIT, не одобренный и не спонсируемый ею. Мы благодарны LINBIT и сообществам DRBD, LINSTOR и Piraeus: Blockstor намеренно говорит на API LINSTOR именно для того, чтобы экосистема, построенная сообществом, продолжала работать.

Как попробовать

Приятная деталь для тех, кто уже живёт на LINSTOR: Blockstor можно установить на те же узлы, рядом с работающим LINSTOR. Диапазоны TCP-портов и диапазоны минорных номеров DRBD намеренно не пересекаются с оригинальными, так что можно попробовать, ничего не выключая.

На узлах нужны модуль ядра DRBD 9, drbd-utils, lvm2 и cryptsetup; для ZFS — модуль и zfsutils-linux. В Talos это расширения siderolabs/drbd и siderolabs/zfs.

kubectl apply -f config/crd/bases/

# затем манифесты из stand/: controller, apiserver, satellite

kubectl -n blockstor-system rollout status deploy/blockstor-controller
kubectl -n blockstor-system rollout status deploy/blockstor-apiserver
kubectl -n blockstor-system rollout status daemonset/blockstor-satellite

Дальше — обычный linstor, без изменений:

kubectl -n blockstor-system port-forward deploy/blockstor-apiserver 3370:3370
export LS_CONTROLLERS=http://localhost:3370
...
linstor node create worker-1 10.0.0.11
linstor physical-storage create-device-pool --pool-name data --storage-pool data zfs worker-1 /dev/sdb
linstor resource-group create mygroup --place-count 3 --storage-pool data
linstor volume-group create mygroup
linstor resource-group spawn mygroup myvolume 10G
linstor resource list

В выводе вы увидите две дисковые реплики в статусе UpToDate и одну TieBreaker.

Что дальше и где нужна помощь

Blockstor уже прошёл через end-to-end набор тестов Cozystack на трёхузловом стенде: PVC обслуживаются через неизменённый CSI и неизменённые StorageClass-ы, включая трёхстороннюю репликацию DRBD. Но эта интеграция всё ещё proof of concept — она доказала заменимость, но не стала стандартом по умолчанию.

Инструмент для миграции существующего кластера LINSTOR «на месте» находится в разработке: он переносит метаданные в CRD и подхватывает существующие zvol-ы, LV и работающие устройства DRBD без копирования данных и без resync, сохраняя минорные номера, ID узлов, порты и общий секрет DRBD. Там, где что-то нельзя перенести без догадок, инструмент отказывается гадать и сообщает об этом.

Проект молодой и написан в основном одним человеком — и именно здесь помощь со стороны особенно важна. Вот направления, где помощь сейчас важнее всего:

  • Тестирование за пределами Talos — Ubuntu и другие дистрибутивы пока не проверялись
  • Helm-чарт — его пока нет
  • Проверка совместимости с ha-controller — она заявлена, но не покрыта тестом
  • Дашборды и алерты Grafana для метрик Blockstor
  • Самая сложная и самая ценная область — уровень ядра DRBD: сборка и подключение файловой системы на сателлите, split-brain и настоящая синхронизация. Сам проект называет это своим главным оставшимся риском

Если вы используете DRBD в продакшене, самый полезный вклад — установить Blockstor рядом с ним и рассказать нам, что сломалось.

Вопросы, отчёты об ошибках и «у меня не работает» — в issues репозитория или в чатах ниже. Отчёт о том, что не сработало, сейчас ценнее звёздочки на GitHub.

Присоединяйтесь к сообществу