← Все статьи

Переезд продового кластера с Longhorn на LINSTOR — и ожидание, которое не сбылось

Зачем мы заменили Longhorn на LINSTOR/DRBD на self-managed кластере, как каждая нагрузка переезжала своим способом и что пошло не так по дороге.

Кластер, на котором живут маркетинговые сайты Optimacros, — это self-managed kubeadm на виртуалках, с Longhorn для томов: MySQL InnoDB Cluster, трёхнодовый Vault, Prometheus, Grafana и Alertmanager. В августе я перевёл всё это на LINSTOR (Piraeus) с DRBD под капотом. Вот история о том, зачем, как и в чём я ошибся.

Не ради пропускной способности

Диски простаивали 96% времени. У MySQL — ноль медленных запросов, ноль innodb_log_waits и 99.99% попаданий в buffer pool. Пропускная способность причиной не была.

Причиной была операционная динамика, измеренная до любых изменений:

  • создание PVC до Bound: 21 секунда;
  • первый attach: 14 секунд;
  • переезд тома между нодами: 46 секунд плюс ошибка Multi-Attach;
  • задержка записи: 0.16 мс на локальном диске против 12 мс на томе Longhorn.

Меньше движущихся частей в пути данных и задержка локального диска стоили миграции.

Сначала проверить рискованную часть

DRBD — модуль ядра, и это единственный новый класс отказов, который приносит LINSTOR: Longhorn живёт целиком в user-space, DRBD — нет. Поэтому до любого плана я собрал DRBD 9.3.3 под ядро нод ровно тем образом-загрузчиком, который Piraeus будет использовать в проде, загрузил и выгрузил его на одной ноде. Secure Boot оказался неприменим (загрузка через legacy BIOS), а в репозиториях Ubuntu был drbd-utils, но не было drbd-dkms — что неважно: модуль собирает DaemonSet-загрузчик Piraeus, выбирая образ по ОС ноды.

Оставшийся риск записан прямо: каждое обновление ядра пересобирает модуль, и упавшая сборка оставит ноду без хранилища. Смягчение — загрузчик собирает модуль при старте пода, так что отказ проявится сразу после ребута, а не через дни, — а ядра и так патчатся по одной ноде.

Размер с обоснованием

127 ГиБ данных при двух репликах — 254 ГиБ по кластеру. Я попросил 200 ГиБ на воркер — с запасом в 3.2 раза — по трём конкретным причинам: снапшоты Longhorn заставили том Prometheus на 30 ГиБ занимать 68 ГиБ на дисках; во время миграции оба хранилища работают рядом; реплики никогда не ложатся ровно (на одном воркере было свободно 51 ГиБ, на другом — 156). Диски пришли отдельными устройствами; LINSTOR забирает их целиком в LVM thin pool.

От наименее критичного, каждый раз своим способом

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

  • Grafana (Deployment): rsync между двумя томами, затем existingClaim в values чарта.
  • Prometheus: rsync ровно один раз во временный том с Retain, потом отвязать и подключить заново под именем, которого ждёт StatefulSet.
  • Vault и MySQL: вообще без копирования. Raft и InnoDB clone восстанавливают участника от соседей, так что достаточно было удалять PVC одного участника за раз и давать ему подняться на новом storage class.

Что пошло не так

Поды, пережившие смену DNS накануне, всё ещё держали резолвер компонента, которого больше не было. Raft Vault разваливался на выборах с no such host, а MySQL не мог собрать группу. Лечится это только пересозданием подов, и MySQL пришлось поднимать через rebootClusterFromCompleteOutage, добирая участников клонированием.

И одна ошибка моя: я удалил StatefulSet MySQL с --cascade=orphan, не сохранив манифест. Оператор отказался пересоздавать его вокруг подов без owner reference, и я собрал StatefulSet вручную из спецификации живого пода. База маркетинговых сайтов была недоступна несколько минут, пока пересоздавался primary; сами сайты отвечали всё время.

Честный итог

Всё переехало в тот же день, linstor-r2 (LVM thin, две реплики) стал классом по умолчанию, а после этого Longhorn удалили полностью. Но ожидание, что ошибки Multi-Attach исчезнут, не оправдалось: они воспроизводятся одинаково на обоих хранилищах, потому что так Kubernetes обращается с RWO-томами, а не дефект Longhorn. Переезд тома ускорился лишь умеренно — 14 секунд вместо 19 при перемере. Выигрыш по задержке был настоящим; выигрыш по attach — в основном миф.

Выводы

  • Измеряй операции, которые хочешь улучшить, до миграции — и перемеряй после. Только так заметишь, что обещанный выигрыш не был реальным.
  • Сначала проверяй новый класс отказов. Для DRBD это модуль ядра, собранный тем же образом, что пойдёт в прод.
  • Разным нагрузкам — разные способы переезда; кластерные базы часто восстанавливаются сами, без копирования.
  • Перед --cascade=orphan всегда сохраняй манифест.