454 lines
21 KiB
Markdown
454 lines
21 KiB
Markdown
# 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** (выделенный) | 1–2 |
|
||
| Репликация | **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 | 2–5 минут |
|
||
| 100 GiB | 10–20 минут |
|
||
| 1 TiB | 1–4 часа |
|
||
|
||
#### Типичные проблемы при переходе 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/)
|