k8s/research/longhorn.md
2026-07-15 11:14:21 +03:00

454 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Longhorn — исследование для развёртывания в кластере
## Что такое Longhorn
Longhorn — распределённое блочное хранилище для Kubernetes (CNCF-проект, изначально Rancher).
Предоставляет PersistentVolume через CSI-драйвер с репликацией данных между нодами, инкрементальными снапшотами и встроенным бэкапом.
---
## На каких нодах нужны диски
Longhorn Manager запускается как **DaemonSet на нодах кластера**. Каждая нода, где работает Longhorn Manager, может хранить реплики томов.
**Применительно к этому кластеру:**
| Нода | Группа | Роль в Longhorn |
|---|---|---|
| k8s-master-01 (10.203.0.97) | control_plane | Может участвовать (тейнт уже снят), но нежелательно в production |
| k8s-worker-NN | workers | **Основные ноды для хранилища** — диски должны быть здесь |
| k8s-manager-01 (10.203.0.92) | manager_nodes | Не входит в кластер, диски не нужны |
**Вывод:** диски нужны на **worker-нодах**. Желательно не смешивать роль control plane и хранилища.
---
## Минимальное количество нод
| Режим | Нод с дисками | Репликация | Отказоустойчивость |
|---|---|---|---|
| Dev / тест | 1 | 1 | Нет |
| Minimum HA | **3** | 2 | Выдерживает отказ 1 ноды |
| Production HA | **3** | 3 | Выдерживает отказ 2 нод |
| Extended HA | 5+ | 3 | Рекомендуется для критичных данных |
### Почему именно 3 ноды — минимум для стабильной работы
Longhorn по умолчанию размещает реплики тома на **разных нодах** (replica node anti-affinity).
- При `replication=3` требуются 3 разные ноды, иначе Longhorn деградирует том или откажет в создании.
- При `replication=2` достаточно 2 нод, но потеря любой из них оставит данные без резерва — том продолжит работу, но уязвим.
- 3 ноды + `replication=3` — классическая кворум-схема: можно потерять 1 ноду и данные остаются защищёнными.
**Практические варианты для этого кластера:**
- **Рекомендуется:** 3 worker-ноды + control plane (без хранилища)
- **Допустимо (временно):** 2 worker-ноды + control plane с Longhorn — итого 3 ноды с дисками, но смешение ролей нежелательно
---
## Минимальное количество дисков на ноде
- **Минимум: 1 диск на ноду.** Longhorn работает с одним диском.
- **Рекомендуется: 1 выделенный диск** (отдельно от ОС-диска `/dev/sda`).
- Longhorn поддерживает **несколько дисков на одной ноде** — реплики балансируются между ними автоматически.
### ОС-диск vs выделенный диск
| Параметр | ОС-диск | Выделенный диск |
|---|---|---|
| Изоляция от системы | Нет | Да |
| `minimalAvailableStoragePercentage` | ≥25% | 10% |
| Риск DiskPressure | Высокий | Низкий |
| Рекомендация | Только dev/тест | Production |
**Минимальная жизнеспособная production-конфигурация:**
```
3 worker-ноды × 1 выделенный диск = 3 диска суммарно
```
---
## Принципы репликации
Longhorn реплицирует данные **посинхронно** (synchronous replication) между репликами при записи.
```
Pod (write) → Longhorn Engine → Replica 1 (node-1 /dev/sdb)
→ Replica 2 (node-2 /dev/sdb)
→ Replica 3 (node-3 /dev/sdb)
```
- Запись подтверждается только когда все реплики записали данные.
- При отказе реплики Longhorn автоматически перестраивает её на другой ноде (Rebuild).
- Значение `replication` задаётся на уровне StorageClass или отдельного тома.
### Рекомендуемые настройки anti-affinity
| Настройка | Значение | Смысл |
|---|---|---|
| `replicaNodeLevelSoftAntiAffinity` | `false` | Запрет размещения 2 реплик на одной ноде (hard rule) |
| `replicaDiskLevelSoftAntiAffinity` | `true` | Разрешить несколько реплик на одной ноде, но на разных дисках |
---
## Prerequisites — что нужно установить на все ноды кластера
### Пакеты (Rocky Linux 9)
```bash
dnf install -y iscsi-initiator-utils nfs-utils cryptsetup
```
| Пакет | Зачем |
|---|---|
| `iscsi-initiator-utils` | iSCSI-стек для подключения томов |
| `nfs-utils` | NFS-поддержка для RWX-томов и бэкапов |
| `cryptsetup` | LUKS2-шифрование томов (опционально) |
### Сервисы
```bash
systemctl enable --now iscsid
```
### Kernel modules
```
iscsi_tcp — iSCSI over TCP
dm_crypt — Device-mapper шифрование
```
Добавить в `/etc/modules-load.d/longhorn.conf`:
```
iscsi_tcp
dm_crypt
```
### Утилиты (должны быть в PATH)
`bash`, `curl`, `findmnt`, `grep`, `awk`, `blkid`, `lsblk` — как правило, уже присутствуют на Rocky Linux 9.
---
## SELinux workaround (Rocky Linux 9 — обязательно)
Rocky Linux 9 с `container-selinux > 2.189.0` блокирует `iscsiadm` через SELinux, что приводит к бесконечному циклу attach/detach томов.
### Ручной патч (на каждой ноде)
```bash
echo '(allow iscsid_t self (capability (dac_override)))' > /tmp/local_longhorn.cil
semodule -vi /tmp/local_longhorn.cil
```
### Автоматически через DaemonSet (после установки Longhorn)
```bash
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/prerequisite/longhorn-iscsi-selinux-workaround.yaml
```
---
## Firewall (firewalld)
Добавить на ноды кластера:
| Порт | Протокол | Зачем |
|---|---|---|
| 2049 | tcp | NFS (для RWX томов и бэкапов) |
| 111 | tcp/udp | portmapper (NFS) |
Порт 10250/tcp (kubelet API) уже открыт в существующей роли `k8s_control_plane`.
---
## Установка — Helm chart (рекомендуемый метод)
```bash
helm repo add longhorn https://charts.longhorn.io
helm repo update
helm install longhorn longhorn/longhorn \
--namespace longhorn-system \
--create-namespace \
--version 1.7.2 \
--set defaultSettings.defaultReplicaCount=3 \
--set defaultSettings.replicaNodeLevelSoftAntiAffinity=false \
--set defaultSettings.minimalAvailableStoragePercentage=10
```
### Ключевые Helm-параметры
| Параметр | Рекомендуемое значение | Описание |
|---|---|---|
| `defaultSettings.defaultReplicaCount` | `3` | Реплик по умолчанию на новый том |
| `defaultSettings.replicaNodeLevelSoftAntiAffinity` | `false` | Запрет реплик на одной ноде |
| `defaultSettings.minimalAvailableStoragePercentage` | `10` (dedicated) / `25` (OS disk) | Резерв свободного места |
| `defaultSettings.storageOverProvisioningPercentage` | `100` | Overprovisioning (200 = двойной запас) |
---
## Проверка готовности нод (preflight)
Longhorn предоставляет утилиту `longhornctl`:
```bash
# Скачать
curl -sSfL -o longhornctl https://github.com/longhorn/cli/releases/latest/download/longhornctl-linux-amd64
chmod +x longhornctl
# Проверить prerequisites
./longhornctl check preflight
# Автоматически установить зависимости
./longhornctl install preflight
```
---
## StorageClass после установки
Longhorn создаёт StorageClass `longhorn` автоматически:
```yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: longhorn
provisioner: driver.longhorn.io
parameters:
numberOfReplicas: "3"
staleReplicaTimeout: "2880"
fromBackup: ""
reclaimPolicy: Delete
volumeBindingMode: Immediate
```
---
## Итоговые минимумы для production
| Параметр | Минимум (stable) | Рекомендуется |
|---|---|---|
| Нод с дисками | **3** | 3+ |
| Дисков на ноду | **1** (выделенный) | 12 |
| Репликация | **2** | 3 |
| Пакеты | open-iscsi + nfs-utils | + cryptsetup |
| SELinux патч | **Обязательно** на Rocky Linux 9 | — |
| Отдельный диск от ОС | **Рекомендуется** | — |
---
---
## Масштабирование: добавление дисков и нод
### Сценарий A: добавление диска к существующей ноде
#### Как это работает
Longhorn не отслеживает диски автоматически — каждый новый диск нужно **явно зарегистрировать** через Longhorn UI или kubectl. После регистрации диск мгновенно становится доступен для новых томов.
#### Подготовка диска (на уровне ОС)
```bash
# 1. Отформатировать (ext4 или xfs — обязательно extent-based)
mkfs.xfs /dev/sdb
# 2. Создать точку монтирования
mkdir -p /mnt/longhorn-disk2
# 3. Получить UUID (надёжнее device-name)
blkid /dev/sdb
# 4. Добавить в /etc/fstab (UUID, nofail — обязательно)
echo "UUID=<uuid> /mnt/longhorn-disk2 xfs defaults,nofail 0 2" >> /etc/fstab
# 5. Смонтировать
mount -a
```
> **Важно:** не использовать symlink в пути — Longhorn-поды не разрешают символические ссылки корректно.
#### Регистрация диска в Longhorn
**Через kubectl (рекомендуется для Ansible):**
```bash
kubectl patch nodes.longhorn.io <node-name> -n longhorn-system \
--type merge \
-p '{"spec":{"disks":{"disk2":{"path":"/mnt/longhorn-disk2","allowScheduling":true,"diskType":"filesystem"}}}}'
```
**Через UI:** Nodes → выбрать ноду → Edit node and disks → Add Disk.
#### Автоматический ребалансинг реплик на новый диск?
**Нет.** Существующие реплики **не переезжают** автоматически на новый диск.
- Новый диск начинает использоваться только для **новых томов**.
- Если включена функция `Replica Auto Balance` (режим `best-effort`), Longhorn постепенно перемещает часть реплик для выравнивания нагрузки между дисками.
- Настройка: Longhorn UI → Settings → Replica Auto Balance → `best-effort`.
#### Сложности при добавлении диска
| Сложность | Причина | Решение |
|---|---|---|
| Диск не виден Longhorn | Не смонтирован до регистрации | `mount \| grep /mnt/longhorn-disk2` |
| Ошибка дублирования | Тот же filesystem UUID уже в кластере | Проверить `lsblk -f` перед добавлением |
| Disk не используется | `allowScheduling: false` | Включить через UI или patch |
| Резерв места слишком велик | `storageReserved` ≥ объёму диска | Понизить до 10% от объёма |
#### Теги дисков (routing для специфичных воркеров)
Можно пометить диск тегом (например `ssd`, `fast`) и указывать его в StorageClass:
```yaml
# StorageClass
parameters:
diskSelector: "ssd"
```
Тогда реплики создаются только на дисках с нужными тегами.
---
### Сценарий B: добавление ноды к кластеру
#### Автоматическое обнаружение
Когда новая нода присоединяется к Kubernetes, **Longhorn обнаруживает её автоматически** через DaemonSet — никаких ручных действий не требуется. Longhorn-manager pod поднимается на новой ноде и создаёт Longhorn Node CR.
По умолчанию Longhorn сразу создаёт диск по пути из настройки `Default Data Path` (`/var/lib/longhorn`).
#### Проверка после появления ноды
```bash
# Убедиться, что нода появилась в Longhorn
kubectl get node.longhorn.io -n longhorn-system
# Проверить диск на ноде
kubectl get node.longhorn.io <node-name> -n longhorn-system -o yaml
# Ищем: spec.disks, status.diskStatus — должны быть schedulable: true
```
#### Ребалансинг существующих реплик на новую ноду?
**По умолчанию — нет.** Существующие реплики не переезжают на новую ноду автоматически.
Чтобы включить ребалансинг:
```
Longhorn UI → Settings → Replica Auto Balance → best-effort
```
| Режим | Поведение |
|---|---|
| `disabled` (по умолчанию) | Реплики не перемещаются |
| `least-effort` | Минимальное перемещение — только для восстановления fault-tolerance |
| `best-effort` | Равномерное распределение по всем нодам |
#### Ноды с Выделенными дисками (не `/var/lib/longhorn`)
Если диск подключён как `/dev/sdb` и смонтирован в `/mnt/longhorn`, нужно:
1. Подготовить и смонтировать диск на новой ноде (аналогично Сценарию A).
2. Зарегистрировать диск в Longhorn вручную или через аннотацию ноды (до или после присоединения к кластеру):
```bash
kubectl annotate node <node-name> \
node.longhorn.io/default-disks-config='[{"path":"/mnt/longhorn","allowScheduling":true}]'
```
#### Сложности при добавлении ноды
| Сложность | Причина | Решение |
|---|---|---|
| Нода есть в K8s, но не в Longhorn | DaemonSet не запустился (taint, ресурсы) | Проверить `kubectl get pods -n longhorn-system -o wide` |
| Нода есть в Longhorn, но не планирует реплики | `allowScheduling: false` или нет свободного места | Включить scheduling, проверить space |
| Реплики не переехали | Auto Balance выключен | Включить `best-effort` |
| Existing volumes не используют новую ноду | Replication count не обновлён | Обновить replica count (см. ниже) |
| Prerequisites не установлены | Нет `iscsid`, `nfs-utils` | Запустить роль prereqs перед join |
> **Критично:** перед добавлением ноды к кластеру на ней **обязаны быть установлены** `iscsi-initiator-utils`, `nfs-utils` и применён SELinux-патч (для Rocky Linux 9). Иначе Longhorn Manager pod запустится, но тома не смогут монтироваться на этой ноде.
---
### Сценарий C: миграция 1 нода → 3 ноды (увеличение репликации)
#### Ситуация
Старт: 1 worker-нода, `replication=1` — данные есть, избыточности нет.
Цель: 3 ноды, `replication=3` — полная HA.
#### Окно риска
Пока replica count увеличивается с 1 до 3, том находится в состоянии **Degraded**: старая реплика работает, новые перестраиваются. В этот момент:
- Том **доступен** для чтения и записи (без даунтайма).
- Если нода со старой единственной репликой упадёт **во время перестройки****данные потеряны**.
Поэтому перед увеличением replica count — **сделать снапшот или бэкап**.
#### Процедура
```bash
# 1. Убедиться, что новые ноды видны Longhorn
kubectl get node.longhorn.io -n longhorn-system
# 2. Включить Auto Balance (опционально, для автоматического выравнивания)
# Longhorn UI → Settings → Replica Auto Balance → best-effort
# 3. Обновить replica count для существующих томов
kubectl patch volume <volume-name> -n longhorn-system \
--type merge \
-p '{"spec":{"numberOfReplicas":3}}'
# 4. Мониторинг перестройки
kubectl get volume <volume-name> -n longhorn-system \
-o jsonpath='{.status.state} {.status.replicaCount}'
# Ожидаем: "healthy 3"
```
#### Время перестройки (ориентир)
| Объём тома | Время rebuild |
|---|---|
| 10 GiB | 25 минут |
| 100 GiB | 1020 минут |
| 1 TiB | 14 часа |
#### Типичные проблемы при переходе 1→3
| Проблема | Симптом | Решение |
|---|---|---|
| Том завис в Degraded | `replicaCount` не растёт > 1 часа | Проверить ноды: space, scheduling, taints |
| Replica count остался 1 | `spec.numberOfReplicas=3`, но `status.replicaCount=1` | Новые ноды `unschedulable`? `kubectl get node.longhorn.io` |
| Только 2 реплики создались | Третья нода без свободного места | Добавить диск или ноду с бо́льшим объёмом |
| `Data Locality: guaranteed` мешает | Том не перемещается | Сменить на `best-effort` на период миграции |
#### Обновить StorageClass для новых томов
Изменить `numberOfReplicas` в StorageClass, чтобы все **новые** PVC создавались сразу с нужной репликацией:
```bash
kubectl patch storageclass longhorn \
-p '{"parameters":{"numberOfReplicas":"3"}}'
```
Это не влияет на уже существующие тома — только на новые.
---
### Итоговая таблица: сводка по масштабированию
| Действие | Автоматически? | Даунтайм? | Главный риск | Что сделать |
|---|---|---|---|---|
| Добавить диск на ноду | Нет (ручная регистрация) | Нет | Диск не смонтирован до регистрации | Смонтировать → зарегистрировать через kubectl/UI |
| Добавить ноду в кластер | Да (DaemonSet) | Нет | Prerequisites не установлены | Запустить prereqs-роль до join |
| Ребалансинг реплик | Нет (нужно включить) | Нет | Реплики остаются на старых нодах/дисках | Включить `Replica Auto Balance: best-effort` |
| Поднять replica count 1→3 | Нет (вручную) | Нет (том в Degraded) | Потеря данных при падении ноды во время rebuild | Снапшот перед изменением, мониторинг rebuild |
---
## Источники
- [Longhorn Official Documentation 1.11](https://longhorn.io/docs/1.11.2/)
- [Longhorn Installation Guide](https://longhorn.io/docs/1.11.2/deploy/install/)
- [Longhorn Best Practices](https://longhorn.io/docs/1.11.2/best-practices/)
- [Longhorn SELinux Troubleshooting](https://longhorn.io/kb/troubleshooting-volume-attachment-fails-due-to-selinux-denials/)
- [Longhorn Multiple Disk Support](https://longhorn.io/docs/1.10.1/nodes-and-volumes/nodes/multidisk/)