Стадия жизненного цикла модуля: Experimental
У модуля есть требования для установки
Руководство описывает административные действия: включить модуль, подключить S3-совместимое хранилище, выбрать способ доставки моделей, подготовить локальный кэш на узлах, настроить публичный каталог и проверить рабочее состояние.
Требования
- Deckhouse Kubernetes Platform
>= 1.74. - Kubernetes
>= 1.30. - S3-совместимое объектное хранилище и bucket для данных DMCR и временных данных подготовки моделей.
- Secret в
d8-systemс ключамиaccessKeyиsecretKey. - RWX
StorageClassдля режимаSharedPVC. - Модули
sds-node-configuratorиsds-local-volumeдля режимаNodeCache.
Включение
Создайте Secret с доступом к объектному хранилищу:
apiVersion: v1
kind: Secret
metadata:
name: ai-models-artifacts
namespace: d8-system
type: Opaque
stringData:
accessKey: "<access-key>"
secretKey: "<secret-key>"Включите модуль:
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: ai-models
spec:
enabled: true
version: 1
settings:
logLevel: Info
artifacts:
bucket: ai-models
endpoint: https://s3.example.com
region: us-east-1
credentialsSecretName: ai-models-artifacts
usePathStyle: trueЕсли объектное хранилище использует частный центр сертификации, добавьте
ca.crt в отдельный Secret в d8-system и укажите
artifacts.caSecretName. Также можно положить ca.crt в Secret с учётными
данными: модуль будет использовать его как доверенный сертификат.
Служебный Secret в d8-ai-models создаётся Helm’ом из данных, которые
подготовил hook синхронизации. Администратор управляет только исходным Secret
в d8-system.
Доставка моделей
delivery.type выбирает способ подключения моделей в фазе Ready к рабочей
нагрузке.
Эта настройка действует внутри одного кластера; она не является источником
данных модели и не описывает внешний каталог. Если блок delivery не задан,
используется SharedPVC.
SharedPVC
SharedPVC подходит для кластеров с хранилищем, которое поддерживает
ReadWriteMany:
spec:
settings:
delivery:
type: SharedPVC
sharedPVCStorageClassName: rwx-storage-classЕсли sharedPVCStorageClassName пустой, класс хранения выбирается в таком
порядке:
global.modules.storageClass;global.defaultClusterStorageClass;- default
StorageClassKubernetes.
Выбранный класс должен существовать. После этого провайдер хранилища должен
создать PVC с ReadWriteMany. Если класс не найден, контроллер оставляет
рабочую нагрузку в ожидании с причиной SharedPVCStorageClassMissing. Если в
кластере несколько default StorageClass, контроллер оставляет рабочую
нагрузку в ожидании с причиной SharedPVCStorageClassAmbiguous; задайте класс в
настройках модуля или в глобальных настройках Deckhouse, чтобы выбор был
однозначным. Если PVC не создаётся или не привязывается, причину нужно смотреть
в событиях PVC.
Локальный RWO PVC не является отдельным режимом доставки. Если нужно хранить
модель рядом с приложениями на выбранных узлах, используйте NodeCache: модуль
создаёт кэш на узле и передаёт модель в рабочую нагрузку через CSI-монтирование
только для чтения.
NodeCache
NodeCache подходит для больших моделей и повторного использования модели
несколькими рабочими нагрузками на одной ноде.
-
Включите
sds-node-configuratorиsds-local-volume. -
Пометьте ноды для кэша:
d8 k label node <node-name> ai.deckhouse.io/model-cache=true -
Пометьте свободные
BlockDevice:d8 k label blockdevice <block-device-name> ai.deckhouse.io/model-cache=true -
Включите режим доставки:
spec: settings: delivery: type: NodeCache nodeCacheSize: 200Gi
По умолчанию ноды и диски выбираются по label
ai.deckhouse.io/model-cache=true. Если в кластере уже есть своя схема
меток, задайте delivery.nodeCacheNodeSelector и
delivery.nodeCacheBlockDeviceSelector.
Проверка подготовленных ресурсов:
d8 k get blockdevices.storage.deckhouse.io -o wide
d8 k get lvmvolumegroupsets.storage.deckhouse.io
d8 k get lvmvolumegroups.storage.deckhouse.io
d8 k get localstorageclasses.storage.deckhouse.io
d8 k -n d8-ai-models get pods,pvc -l app=ai-models-node-cache-runtime -o wideДиск должен быть свободным и иметь consumable=true.
Лимит хранилища
artifacts.capacityLimit задаёт общий лимит для моделей, которыми управляет
модуль:
spec:
settings:
artifacts:
capacityLimit: 500GiПри включённом лимите шлюз загрузки принимает файл только когда известен
размер данных. Обычный curl -T передаёт Content-Length, multipart-клиент
передаёт размер на /probe.
Объектное хранилище и DMCR
Bucket из artifacts.bucket принадлежит модулю. Не размещайте в нём сторонние
данные и не удаляйте объекты вручную: контроллер и DMCR используют
собственные связи между объектами, а ручное удаление может сломать локальную
копию модели или повторную доставку в рабочую нагрузку.
DMCR (Deckhouse Model Container Registry) — служебный OCI-реестр модуля.
Он хранит подготовленные модели как OCI-артефакты поверх настроенного
S3-compatible bucket. Администратор настраивает bucket и доступ к нему, но не
управляет OCI-путями, тегами, служебными ссылками и объектами DMCR вручную.
Модель в DMCR не хранится как один файл. Контроллер упаковывает исходные
файлы модели во внутренний OCI-артефакт ModelPack, не меняя формат весов
модели.
Поэтому в интерфейсе объектного хранилища одна модель может выглядеть как
десятки или сотни объектов. Часть объектов — это манифесты, конфигурации, слои
и ссылки реестра; часть — промежуточные данные загрузки или зеркалирования
источника; часть — служебные маркеры, которые позволяют безопасно возобновлять
подготовку и удалять только неиспользуемые данные.
В интерфейсе S3-совместимого хранилища можно ориентироваться на такие группы. Имена префиксов являются внутренней структурой и не считаются стабильным API.
| Группа объектов | Для чего нужна | Когда удаляется |
|---|---|---|
docker/registry/... |
Данные и метаданные внутреннего OCI-реестра: манифесты, конфигурации, ссылки реестра и слои моделей. | После удаления владельца и успешной сборки мусора. Общие слои остаются, пока нужны другой модели. |
raw/... |
Промежуточные данные подготовки: загруженный файл, снимок источника HuggingFace/Ollama или данные для повторного запуска подготовки. | После удаления модели или после завершения связанной процедуры очистки. |
_ai_models/direct-upload/... |
Физические объекты прямой загрузки и multipart-сессии, которые затем привязываются к OCI-артефакту. | После успешной финализации или как устаревшие сиротские данные. |
| Открытые multipart-загрузки | Незавершённые части загрузки, которые не всегда видны как обычные объекты в интерфейсе. | Сборка мусора прерывает устаревшие multipart-загрузки отдельно от удаления объектов. |
Количество объектов не равно количеству моделей. Один маленький объект может быть служебной ссылкой, а один слой большой модели может занимать гигабайты. Общий слой может использоваться несколькими артефактами, поэтому удаление одной модели не всегда сразу уменьшает занятое место на размер этой модели.
Удаление Model или ClusterModel запускает асинхронную очистку:
- Контроллер убирает ссылку модели из каталога и ставит запрос на очистку.
- Служебный процесс DMCR объединяет запросы, включает режим обслуживания и ждёт подтверждения от реплик.
- Затем удаляются устаревшие префиксы промежуточных данных, прерываются старые multipart-загрузки и запускается сборка мусора OCI-реестра.
- Результат очистки публикуется в метриках и логах.
Поэтому сразу после удаления модели в bucket ещё могут оставаться объекты.
Это нормально, пока нет алертов D8AIModelsPublicationCleanupBacklogStale или
D8AIModelsPublicationCleanupFailed, а дашборд показывает завершённые циклы
очистки.
Безопасные проверки:
d8 k get models.ai.deckhouse.io -A
d8 k get clustermodels.ai.deckhouse.io
d8 k -n d8-ai-models get secrets -l ai.deckhouse.io/dmcr-gc-request=true
d8 k -n d8-ai-models logs deploy/dmcr -c dmcr-garbage-collection --since=2hВ логах ищите сообщения dmcr garbage collection completed: в них есть
количество удалённых объектов, освобождённые байты и число удалённых слоёв
DMCR. Если объекты в bucket растут, а запросы очистки зависли или падают,
сначала исправьте причину по алерту и логам. Ручное удаление объектов из
bucket допустимо только как отдельная аварийная процедура с подтверждённым
списком префиксов.
Как проходят данные модели
У модуля один путь подготовки модели и два способа доставки в рабочие нагрузки.
Подготовка читает источник модели, проверяет данные и упаковывает исходные
файлы во внутренний OCI-артефакт ModelPack. Это не конвертация весов модели:
GGUF остаётся GGUF, Safetensors остаётся Safetensors. DMCR сохраняет
полученный артефакт как локальную проверенную копию. В мониторинге видно
исходный размер модели и фактически занятое место, поэтому оператор понимает,
дают ли разбиение на части и сжатие экономию в bucket.
SharedPVC переносит модель из DMCR в управляемый контроллером RWX PVC в
пространстве имён рабочей нагрузки. Служебная Job не использует Kubernetes
API для чтения модели. Прогресс считается в DMCR по подписанному
разрешению на чтение, выданному именно этой Job, поэтому дашборд показывает
ожидаемый объём, уже прочитанный объём и скорость по каждой Job.
NodeCache переносит модель из DMCR в локальный кэш на узле.
Долгоживущий служебный компонент node-cache отдаёт ожидаемый и загруженный
объём по узлу и модели, а также занятое место кэша и задержку CSI-запросов.
Раздача каталога использует отдельную передачу данных. Потребляющий кластер сначала читает понятный каталог, затем импортирует выбранные OCI-артефакты как локальные копии. DMCR логирует чтение с идентификатором потребителя и отдаёт метрики скорости и объёма, сгруппированные по назначению передачи.
Раздача каталога между контурами
Раздача каталога отвечает за обмен ClusterModel в фазе Ready
между сетевыми зонами. Она не меняет способ подключения модели к рабочей
нагрузке.
Типовой сценарий:
- В DMZ работает раздающий кластер, который отдаёт
ClusterModelв фазеReady. - Во внутреннем периметре потребляющий кластер импортирует выбранные модели как локальные копии.
- Доставка в рабочие нагрузки во внутреннем кластере остаётся
SharedPVCилиNodeCache.
Такой сценарий полезен, когда кластер в DMZ только раздаёт каталог: он
подготавливает и отдаёт модели, но в нём нет рабочих нагрузок с аннотациями.
Поэтому раздача каталога не должна превращаться в третье значение
delivery.type; это отдельная ось конфигурации.
Включите публичный каталог в раздающем кластере:
spec:
settings:
distribution:
mode: PublicCatalogПосле применения настройки модуль готовит публичный адрес и маршруты:
https://ai-models.example.com/api/distribution/v1/models
https://ai-models.example.com/v2/api/distribution/v1/models отдаёт понятный каталог: только ClusterModel
в фазе Ready, без внутренних имён реестра и служебных UID.
/v2 остаётся OCI-путём для управляемого контроллером копирования и импорта.
Доступ потребителей
Доступ к публичному каталогу проверяется через аутентификацию и авторизацию Kubernetes в раздающем кластере. Модуль не создаёт отдельную CRD для потребителей.
Создайте ServiceAccount для каждого потребляющего кластера, организации или периметра и привяжите его к роли чтения каталога:
apiVersion: v1
kind: ServiceAccount
metadata:
name: perimeter-a
namespace: d8-ai-models
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ai-models-distribution-reader-perimeter-a
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: d8:ai-models:distribution:reader
subjects:
- kind: ServiceAccount
name: perimeter-a
namespace: d8-ai-modelsВыпустите токен в раздающем кластере и передайте администратору потребляющего кластера только его значение по защищённому внешнему каналу:
d8 k -n d8-ai-models create token perimeter-a --duration=720hДля долгоживущих эксплуатационных учётных данных можно создать Kubernetes
service-account-token Secret в раздающем кластере и прочитать ключ token
после заполнения Kubernetes:
apiVersion: v1
kind: Secret
metadata:
name: perimeter-a-token
namespace: d8-ai-models
annotations:
kubernetes.io/service-account.name: perimeter-a
type: kubernetes.io/service-account-tokenМодуль не выпускает и не переносит этот токен автоматически между кластерами:
для этого нужен внешний доверенный канал или secret-manager. Ротация
выполняется выпуском нового токена в раздающем кластере и обновлением Secret в
потребляющем кластере. После обновления Secret контроллер перечитает
ModelCatalogSource и продолжит работу с новым токеном.
Отзыв доступа выполняется удалением RoleBinding или ServiceAccount. Это закрывает новые запросы каталога и новые разрешения на чтение; уже выданные короткоживущие разрешения на чтение истекают по TTL.
Подключение потребляющего кластера
Потребляющий кластер описывает внешние каталоги через ModelCatalogSource:
apiVersion: ai.deckhouse.io/v1alpha1
kind: ModelCatalogSource
metadata:
name: dmz
spec:
url: https://ai-models.dmz.example.com
credentialsSecretName: ai-models-dmz-read
caSecretName: ai-models-dmz-caSecret из credentialsSecretName и caSecretName находятся в d8-system.
Контроллер читает их напрямую для обновления каталога и выдачи разрешений на
чтение. Secret из credentialsSecretName содержит токен ServiceAccount,
выпущенный раздающим кластером; caSecretName содержит ca.crt и нужен
только для частного CA внешнего каталога. Эти Secret не копируются в
пространства имён приложений.
Модели выбираются не в ModuleConfig. Раздающий кластер экспортирует все
ClusterModel в фазе Ready, а пользователи потребляющего кластера
импортируют нужные модели через
spec.source.catalog.name.
Представление внешнего каталога является кластерным. Оно показывает удалённые
записи и локальные копии как ссылки на Model или ClusterModel, включая
пространство имён для Model. Это представление доступно только ролям
управления модулем и не содержит URL источника, имена Secret, токены,
OCI-репозитории, теги или списки blob.
Аудит
API публичного каталога проверяет токен через TokenReview и авторизует
запросы через SubjectAccessReview: список каталога требует list на
clustermodels.ai.deckhouse.io, просмотр модели и выдача разрешения на чтение
требуют get на выбранный ClusterModel.
Контроллер пишет структурированные события аудита для catalog_list,
catalog_get, pull_grant_issued, catalog_auth_denied, а также для
конфликтов и ошибок. В события попадают имя пользователя Kubernetes, UID,
группы, IP-адрес клиента, имя модели, контрольная сумма и результат проверки.
DMCR пишет события чтения манифеста и слоёв с той же
идентичностью. Сырой токен и его хэш в аудит не попадают.
RBAC
Модуль использует модель уровней доступа Deckhouse:
| Уровень | Доступ |
|---|---|
User |
чтение Model, ClusterModel и статусов; |
Editor |
управление Model в пространстве имён; |
ClusterEditor |
управление ClusterModel; |
ClusterAdmin |
управление ModelCatalogSource, привязками доступа к публичному каталогу и представлением импортов; |
rbacv2/use |
использование Model в пространстве имён; |
rbacv2/manage |
управление Model, ClusterModel, ModelCatalogSource, ModuleConfig и представлением импортов. |
Доступ к загрузке выдаётся отдельной Role на один Secret из
status.upload.secretName. Role создаётся в пространстве имён модели и
называется ai-model-upload-reader-<model-name> либо получает стабильный хэш
для длинных имён.
Восстановление импорта из внешнего каталога
Импорт хранит зафиксированное происхождение: выбранный внешний каталог, имя модели, ревизию каталога и удалённую контрольную сумму. Это защищает рабочую нагрузку от незаметного переезда на другую версию модели при следующем согласовании.
Следующие ошибки восстанавливаются после исправления причины:
CatalogAuthFailed— истёк токен, Secret обновлён или исправлен RBAC на раздающем кластере;CatalogTLSInvalid— исправленcaSecretNameили содержимоеca.crt;CatalogSourceNotReady— внешний каталог снова перешёл в фазуReady.
После восстановления ModelCatalogSource контроллер повторяет импорт той же
зафиксированной модели. Ошибки ManifestInvalid, InsufficientStorage и
нарушенный контракт каталога не повторяются автоматически: сначала нужно
исправить исходный артефакт, лимит хранилища или спецификацию каталога.
Проверка:
d8 k get modelcatalogsources.ai.deckhouse.io
d8 k describe modelcatalogsource <name>
d8 k -n <namespace> describe model <name>Мониторинг
Проверьте ресурсы мониторинга:
d8 k -n d8-ai-models get podmonitor,prometheusruleОсновные разделы дашбордов:
- Обзор кластера. Показывает общий инвентарь
ModelиClusterModel, количество объектов в фазахPublishing,ReadyиFailed, общий размер подготовленных локальных копий, число управляемых рабочих нагрузок и ссылки на модели, которые контроллер не смог разрешить. Начинайте диагностику с этого раздела: ненулевыеFailedи unresolved references означают, что нужно перейти к конкретной модели или рабочей нагрузке. - Состояние каталога. Отдельные дашборды для namespace-scoped
Modelи cluster-scopedClusterModelпомогают понять, где находится проблема: в конкретном namespace, в общем каталоге кластера или в отдельной модели. Смотрите фазу, готовность, условия, источник, формат, размер локальной копии, потребителей модели и таблицу рабочих нагрузок с неразрешённой доставкой. - Подготовка моделей. Раздел показывает текущие объекты в подготовке,
прогресс загрузки и упаковки, скорость передачи данных, ошибки завершения
или проверки, а также повторные попытки. Если прогресс долго не меняется,
сравните скорость передачи с состоянием bucket/DMCR и проверьте события
соответствующего
ModelилиClusterModel. - DMCR и bucket. Панели ёмкости показывают настроенный лимит, занятое,
зарезервированное и доступное место для подготовленных локальных копий.
Отдельно отображается эффективность хранения
ModelPack: логический размер модели и фактически занятое место могут отличаться из-за разбиения на слои, архивирования и переиспользования данных. Очередь очистки показывает pending, active и failed cleanup requests после удаления моделей. - Доставка в рабочие нагрузки. Раздел показывает, какие workloads
управляются модулем, сколько Pod’ов готово, какой способ доставки выбран и
почему. Для
SharedPVCсмотрите состояние PVC, очередь копирования и throughput materialize Job. Для неизвестного способа доставки или неразрешённой ссылки проверьте имя модели, namespace и права на её использование. - Кэш на узлах. Раздел нужен для режима
NodeCache: он показывает служебные Pod’ы, привязанные PVC, занятое и доступное место на каждом узле, число записей в кэше, скорость копирования, параллелизм подготовки и задержки CSI mount/unmount-запросов. Рост задержек или нехватка эффективного свободного места обычно указывает на проблему локального диска, PVC или node-cache runtime. - Раздача каталога. Если включена раздача между контурами, смотрите частоту запросов к публичному каталогу, выдачу разрешений на чтение, задержки API и throughput импортов. Ошибки авторизации или рост задержек нужно сопоставлять с RBAC потребителей, состоянием API-сервера и аудитом раздающего кластера.
Эксплуатационные проверки
Проверить компоненты:
d8 k -n d8-ai-models get pods -o wide
d8 k get models.ai.deckhouse.io -A
d8 k get clustermodels.ai.deckhouse.ioПроверить модель:
d8 k -n <namespace> describe model <name>
d8 k get clustermodel <name> -o yamlКлючевые поля:
status.phase;status.conditions;status.artifact.digest;status.artifact.sizeBytes;status.resolved.format;status.resolved.supportedEndpointTypes;status.resolved.supportedFeatures.
Выключение
При spec.enabled=false удаляются временные служебные ресурсы, которыми
владеет модуль: node-cache runtime Pod/PVC, CSIDriver,
LocalStorageClass, LVMVolumeGroupSet, managed LVMVolumeGroup и StorageClass
ai-models-node-cache.
Model, ClusterModel и уже подготовленные локальные копии моделей остаются.
Удаление модели выполняется через удаление соответствующего Model или
ClusterModel; контроллер завершает очистку через finalizer и GC-request.