| .. | ||
| defaults | ||
| meta | ||
| tasks | ||
| README.md | ||
gpu_model_storage
Выделенный локальный RAID1-раздел под большие файлы моделей (Ollama и т.п.) на GPU-узлах кластера.
Зачем это нужно
gpu_workers намеренно исключены из Longhorn (longhorn_disks: [] в group_vars/gpu_workers.yml) —
GPU-узлы являются compute-only, а не storage-нодами.
При этом проект k8s_ai (Ollama + Ollama Proxy + Open WebUI) хранит файлы моделей через статический
hostPath PV (k8s_ai/k8s/ollama/pvc-models.yaml), привязанный к конкретному узлу через nodeAffinity.
Изначально этот hostPath указывал на /root/ollama-models, что физически находится на корневой
файловой системе узла.
Корневой раздел на k8s-worker-02 — это RAID1-массив md126 размером всего ~30 ГБ
(sda4/sdb4). Первая же загрузка модели среднего размера (обычно десятки гигабайт) заполнила бы
диск полностью и уронила бы узел — на / живут systemd, containerd, kubelet и вся остальная ОС.
Kubernetes при этом не проверяет заявленную ёмкость hostPath PV (capacity.storage — чисто
декларативное поле), так что переполнение диска не было бы предотвращено на уровне Kubernetes.
При этом два зеркальных диска узла (sda/sdb, по 4 ТБ каждый) используют под ОС (boot,
boot_efi, root) только первые ~34 ГБ — оставшиеся ~3.6 ТБ на каждом диске были полностью
неразмеченными и простаивали.
Решение: роль нарезает из этого свободного места отдельный RAID1-раздел, форматирует его и монтирует в отдельную точку — модели больше не могут повлиять на работоспособность ОС.
Архитектура
sda, sdb (по 4 ТБ, зеркало)
├─ sda1/sdb1 → md127 (boot, ext4, ~1 ГБ) — существовало до роли
├─ sda2/sdb2 → md125 (boot_efi, vfat, ~0.6 ГБ) — существовало до роли
├─ sda3/sdb3 → LVM (swap) — существовало до роли
├─ sda4/sdb4 → md126 (root, ext4, ~30 ГБ) — существовало до роли
└─ sda5/sdb5 → md1 (модели, xfs, gpu_model_storage_partition_size_gb ГБ) ← создаёт эта роль
└─ смонтирован в gpu_model_storage_mountpoint (по умолчанию /mnt/ollama-models)
Партиция №5 создаётся сразу после существующих ОС-партиций (старт фиксирован на 34GB, конец —
34 + gpu_model_storage_partition_size_gb ГБ), на каждом диске из gpu_model_storage_devices.
Из этих партиций собирается новый независимый mdadm-массив RAID1 (не имеет отношения к
md126/md127/md125 — отдельный массив, отдельная точка монтирования).
Как включить для узла/группы
Роль выключена по умолчанию (gpu_model_storage_devices: [] в defaults/main.yml) — по тому
же паттерну, что и longhorn_disks в longhorn_prereqs. Чтобы включить, переопределите список
дисков в group_vars/<group>.yml или hosts.yml для конкретного хоста:
# inventory/prod/group_vars/gpu_workers.yml
gpu_model_storage_devices:
- /dev/sda
- /dev/sdb
gpu_model_storage_partition_size_gb: 1000
Сейчас это включено для всей группы gpu_workers (на данный момент единственный член —
k8s-worker-02).
Запуск
ansible-playbook -i inventory/prod playbooks/setup_gpu_model_storage.yml
Плейбук нацелен на группу gpu_workers. Порядок относительно setup_worker_plane.yml /
setup_gpu.yml не важен — роль работает только с локальными дисками узла и не трогает
containerd/kubelet/сеть. Можно запускать в любой момент, включая до kubeadm join.
Все задачи идемпотентны:
community.general.partedне пересоздаёт партицию, если она уже существует с нужными параметрами.raid.ymlпроверяетmdadm --detail <device>перед созданием массива — повторный запуск не пересоздаёт RAID.filesystem.yml(community.general.filesystem) не переформатирует уже отформатированный раздел.mount.ymlиспользуетansible.posix.mountсstate: mounted— идемпотентно монтирует по UUID.
Все переменные
| Переменная | Значение по умолчанию | Описание |
|---|---|---|
gpu_model_storage_devices |
[] |
Список блочных устройств — членов будущего RAID1 (например /dev/sda, /dev/sdb). Пустой список = роль ничего не делает. |
gpu_model_storage_partition_size_gb |
1000 |
Размер новой партиции (пятой) на каждом устройстве, в гигабайтах. Партиция начинается сразу после существующих ОС-партиций (34GB). |
gpu_model_storage_raid_device |
/dev/md1 |
Имя нового RAID1-массива. Не должно совпадать с уже существующими (/dev/md125, /dev/md126, /dev/md127 на типовом узле). |
gpu_model_storage_mountpoint |
/mnt/ollama-models |
Точка монтирования нового раздела. |
gpu_model_storage_fstype |
xfs |
Файловая система (как в Longhorn-дисках — консистентность в репозитории). |
Ограничения и подводные камни
- Начало партиции захардкожено в
34GB. Это соответствует текущему размеру существующих ОС-партиций (boot+boot_efi+swap+root≈ 32.2 ГБ + небольшой запас) на узлах, разворачиваемых по стандартному Rocky Linux 9 + RAID1 layout. Если разметка ОС на новом узле отличается — проверьте фактическое свободное место (parted /dev/sdX unit GB print free) и скорректируйтеpart_startвroles/gpu_model_storage/tasks/partition.ymlперед запуском. - Имя RAID-устройства должно быть свободным. Перед первым запуском на новом узле проверьте
cat /proc/mdstat, чтобыgpu_model_storage_raid_deviceне совпадал с уже занятым/dev/mdN. - Роль не выполняет
dracut -f— это осознанно: массив не является частьюroot/boot, не участвует в загрузке системы, поэтому пересборка initramfs не требуется. Ядро подхватывает массив через udev по суперблоку при каждой загрузке, а запись в/etc/mdadm.conf— для надёжности (явное ARRAY-определение вместо авто-скана). - Диски используются только на ~1/4 (при
gpu_model_storage_partition_size_gb: 1000из ~3967 ГБ свободных) — сделано намеренно, с запасом на будущее (другие датасеты, второй RAID-раздел и т.д.). Увеличить размер существующего раздела после создания массива штатными средствами Ansible-роли нельзя — потребуется resize партиции, RAID и файловой системы вручную (parted resizepart,mdadm --grow,xfs_growfs). - Реальная доступная ёмкость меньше заявленных 1000 ГБ из-за разницы GB/GiB и служебных данных
файловой системы. В
k8s_ai/k8s/ollama/pvc-models.yamlhostPath/PVCобъявляют900Gi— безопасный запас под это расхождение (это не enforced-лимит, просто декларативное число).
Диагностика
# Статус RAID-массива
cat /proc/mdstat
mdadm --detail /dev/md1
# Точка монтирования
lsblk -f
df -h /mnt/ollama-models
# Если массив не поднялся после перезагрузки — проверить запись в mdadm.conf
grep md1 /etc/mdadm.conf
Если /mnt/ollama-models не смонтирован после перезагрузки — массив собран (mdadm --detail
показывает active), но запись в /etc/fstab (созданная ansible.posix.mount) использует
nofail, поэтому загрузка ОС не блокируется — нужно смонтировать вручную (mount -a) и
разобраться, почему массив не успел подняться до попытки монтирования при следующем прогоне роли.