Стадия жизненного цикла модуля: Experimental
У модуля есть требования для установки
Руководство предназначено для пользователя неймспейса и оператора приложения.
Оно показывает, как создать модель, импортировать модель из внешнего каталога
и подключить модель в фазе Ready к рабочей нагрузке Kubernetes без ручной
настройки учётных данных реестра, initContainer и секрет.
Модуль не запускает среду инференса. Он готовит файлы модели в поддерживаемом формате и монтирует их в Pod, а приложение само читает эти файлы.
Быстрый старт
- Создайте
ModelилиClusterModelс одним источником:url,uploadилиcatalog. - Дождитесь
status.phase=Ready. - Добавьте
ai.deckhouse.io/modelилиai.deckhouse.io/clustermodelв верхнийmetadataрабочей нагрузки. - Проверьте, что Pod рабочей нагрузки стартовал и видит модель в
/data/modelcache/models/<model-name>.
Model или ClusterModel
| Ресурс | Область | Когда использовать |
|---|---|---|
Model |
неймспейс | Модель принадлежит одному неймспейсу. |
ClusterModel |
кластер | Модель курируется администратором и доступна нескольким неймспейсам. |
Model может использовать секрет из своего неймспейса для приватного
репозитория Hugging Face. ClusterModel не ссылается на секрет из неймспейса
приложения.
Что происходит после создания модели
После создания Model или ClusterModel модуль подготавливает локальную
копию модели, которой дальше пользуются рабочие нагрузки.
Этапы:
- Контроллер читает
spec.sourceи определяет, откуда получить данные. - Рабочий процесс получает данные из Hugging Face, Ollama, загрузочной сессии или внешнего каталога.
- Рабочий процесс проверяет формат, контрольную сумму и метаданные.
- Контроллер записывает
status.artifact.digest,status.artifact.sizeBytes,status.resolved.*и условия. status.phaseстановитсяReady, после чего модель можно подключать к рабочей нагрузке.
Внутренний путь в DMCR, тег и служебная контрольная сумма остаются внутренними
данными контроллера. Пользователь работает с именем Model или
ClusterModel.
status.phase: Ready означает, что данные модели проверены, локальная копия
сохранена в DMCR, а выбранный способ доставки может подготовить монтирование
для Pod.
Модель из Hugging Face
apiVersion: ai.deckhouse.io/v1alpha1
kind: Model
metadata:
name: bge-m3
namespace: ai-demo
spec:
source:
url: https://huggingface.co/BAAI/bge-m3Для приватного репозитория создайте секрет в том же неймспейсе.
Поддерживаются ключи token, HF_TOKEN, HUGGING_FACE_HUB_TOKEN.
apiVersion: v1
kind: Secret
metadata:
name: hf-private-token
namespace: ai-demo
type: Opaque
stringData:
token: hf_xxx
---
apiVersion: ai.deckhouse.io/v1alpha1
kind: Model
metadata:
name: private-llm
namespace: ai-demo
spec:
source:
url: https://huggingface.co/acme/private-llm
authSecretRef:
name: hf-private-tokenПроверка подготовки:
d8 k -n ai-demo get model bge-m3
d8 k -n ai-demo describe model bge-m3Модель из Ollama
apiVersion: ai.deckhouse.io/v1alpha1
kind: ClusterModel
metadata:
name: qwen-gguf
spec:
source:
url: https://ollama.com/library/qwen3.6:latestURL должен иметь вид https://ollama.com/library/<name>[:tag]. Контроллер
читает манифест, конфигурацию и слои реестра, выбирает один слой GGUF,
проверяет контрольную сумму и заголовок GGUF, затем сохраняет модель как
обычный OCI-артефакт каталога.
Ошибки формы URL попадают в status.phase=Failed с причиной
UnsupportedSource. Ошибки реестра, тега, манифеста, описателя или данных GGUF
попадают в status.phase=Failed с причиной PublicationFailed. Точный текст
ошибки смотрите в status.conditions.
Загрузка файла
Загрузка используется для локального файла или архива:
- прямой файл:
GGUF-модель, обычно файл*.gguf; - архивы:
tar,tar.gz,tgz,zip,tar.zst,tar.zstd,tzst; - содержимое архива:
GGUF,SafetensorsилиDiffusers.
Для Safetensors в архиве должен быть корневой config.json и один или
несколько файлов *.safetensors. Для Diffusers должен быть корневой
model_index.json и файлы весов *.safetensors или *.bin. Для GGUF
достаточно файла *.gguf.
apiVersion: ai.deckhouse.io/v1alpha1
kind: Model
metadata:
name: uploaded-model
namespace: ai-demo
spec:
source:
upload: {}После создания ресурс переходит в WaitForUpload. Имя секрета с URL загрузки и
токеном доступно в status.upload.secretName:
d8 k -n ai-demo get model uploaded-model -o jsonpath='{.status.upload.secretName}{"\n"}'Для загрузки нужен доступ get к этому секрету. Контроллер создаёт Role
ai-model-upload-reader-<model-name>, которая читает только один секрет.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: uploaded-model-uploader
namespace: ai-demo
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: ai-model-upload-reader-uploaded-model
subjects:
- kind: ServiceAccount
name: model-uploader
namespace: ai-demoЗагрузите файл:
UPLOAD_SECRET=$(d8 k -n ai-demo get model uploaded-model -o jsonpath='{.status.upload.secretName}')
UPLOAD_URL=$(d8 k -n ai-demo get secret "$UPLOAD_SECRET" -o jsonpath='{.data.url}' | base64 -d)
UPLOAD_TOKEN=$(d8 k -n ai-demo get secret "$UPLOAD_SECRET" -o jsonpath='{.data.token}' | base64 -d)
curl -fS --progress-bar \
-H "Authorization: Bearer ${UPLOAD_TOKEN}" \
-T ./model.gguf \
"$UPLOAD_URL" | catДля архива передайте имя файла явно. По расширению контроллер определяет тип архива:
curl -fS --progress-bar \
-H "Authorization: Bearer ${UPLOAD_TOKEN}" \
-T ./model-bundle.zip \
"$UPLOAD_URL?filename=model-bundle.zip" | catИмпорт из внешнего каталога
Внешний каталог настраивает администратор. Пользователь выбирает модель по имени каталога, без контрольной суммы и OCI-ссылки:
apiVersion: ai.deckhouse.io/v1alpha1
kind: Model
metadata:
name: qwen3-8b
namespace: ai-demo
spec:
source:
catalog:
sourceName: dmz
name: qwen3-8bЕсли в кластере ровно один ModelCatalogSource в фазе Ready,
sourceName можно не указывать:
spec:
source:
catalog:
name: qwen3-8bИсточник, указанный в sourceName, может быть каталогом между кластерами или
каталогом-провайдером Hugging Face — поток импорта одинаков. Для источника
Hugging Face name — это идентификатор репозитория, например
openai-community/gpt2. Для межкластерного каталога name — это имя
ClusterModel в раздающем кластере: это единственное имя, которое обязано
совпадать, и единственное значение, по которому раздающий кластер позволяет
выбрать модель. Имя локального Model или ClusterModel, который вы создаёте,
выбираете вы, и повторять его не обязательно.
Контроллер фиксирует ревизию внешнего каталога и удалённую контрольную сумму в
status.source.catalog, копирует артефакт в локальный DMCR и
только затем переводит объект в Ready. Доставка в рабочую нагрузку всегда
использует локальную копию. Для источника Hugging Face контроллер заново
резолвит запись у провайдера в момент импорта; он отказывает в импорте записи,
которая больше не резолвится, либо gated, приватна или вне политики источника, с
понятным статусом.
Чтобы узнать, что предлагает источник, перед импортом его можно просмотреть:
настроенный администратором каталог отдаёт список источников и их записей только
на чтение (API modelcatalogsources/catalog-import). Каждая запись показывает
имя, стадию жизненного цикла и наличие локальной копии Model/ClusterModel.
Описание API и его публикация — в руководстве администратора.
Если у внешнего каталога временно сломался токен, сертификат или готовность,
объект может перейти в Failed с причиной CatalogAuthFailed,
CatalogTLSInvalid или CatalogSourceNotReady. Эти состояния
восстанавливаются: после исправления секрета, сертификата, RBAC или состояния
источника контроллер повторяет импорт той же ревизии каталога и той же
удалённой контрольной суммы.
Статусы
| Фаза | Значение |
|---|---|
Pending |
Контроллер ожидает предварительную проверку или ещё не начал подготовку. |
WaitForUpload |
Создана сессия загрузки, нужно передать файл. |
Publishing |
Идёт подготовка модели: получение, проверка и сохранение локальной копии. |
Ready |
Данные модели проверены, локальная копия сохранена, метаданные рассчитаны. |
Failed |
Подготовка завершилась ошибкой. Причина указана в status.conditions. |
Deleting |
Выполняется очистка. |
Полезные поля:
status.artifact.digest— контрольная сумма подготовленного артефакта;status.artifact.sizeBytes— размер артефакта;status.resolved.format—Safetensors,GGUFилиDiffusers;status.resolved.family— семейство модели;status.resolved.supportedEndpointTypes— поддерживаемые типы конечных точек;status.resolved.supportedFeatures— возможности модели.
Для импорта из внешнего каталога полезны также:
status.source.catalog.sourceName— выбранныйModelCatalogSource;status.source.catalog.catalogRevision— ревизия каталога, по которой была выбрана модель;status.source.catalog.remoteDigest— контрольная сумма внешнего артефакта, который контроллер импортирует локально.
Типовые причины для импорта из внешнего каталога:
CatalogAuthFailed— не прошли учётные данные или RBAC внешнего каталога;CatalogTLSInvalid— сертификат внешнего каталога отсутствует или невалиден;CatalogSourceNotReady— внешний каталог ещё не готов;ManifestInvalid— импортированный артефакт не прошёл проверку;InsufficientStorage— модель не укладывается в бюджет хранилища модуля.
Причины сбоев из-за хранилища образуют закрытый словарь, поэтому клиент может
опираться на причину, а не разбирать сообщения. На самой модели при скачивании
сообщается InsufficientStorage — артефакт не влезает в бюджет модуля.
Сбои хранилища на этапе доставки — это отказ общего тома для рабочей нагрузки, а
не отказ скачивания, поэтому они сообщаются на самой нагрузке, в аннотации
ai.deckhouse.io/model-delivery-blocked-reason, со своим закрытым словарём:
SharedPVCStorageClassMissing — отсутствующий или неоднозначный StorageClass,
SharedPVCQuotaExceeded — исчерпанная ResourceQuota неймспейса. Клиенту,
который показывает, почему модель непригодна, нужно читать оба места: conditions
модели и аннотацию нагрузки.
Подключение модели к рабочей нагрузке
Добавьте аннотацию в верхний metadata рабочей нагрузки. Это основной источник
выбора модели.
apiVersion: apps/v1
kind: Deployment
metadata:
name: embedder
namespace: ai-demo
annotations:
ai.deckhouse.io/model: bge-m3
spec:
selector:
matchLabels:
app: embedder
template:
metadata:
labels:
app: embedder
spec:
containers:
- name: app
image: registry.example.com/embedder:latestДля кластерной модели:
metadata:
annotations:
ai.deckhouse.io/clustermodel: gemma-smallДля нескольких моделей:
metadata:
annotations:
ai.deckhouse.io/model: bge-m3,bge-reranker
ai.deckhouse.io/clustermodel: gemma-smallКонтроллер сам добавляет выбранный способ доставки. В контейнере доступны:
AI_MODELS_MODELS_DIR=/data/modelcache/models;AI_MODELS_MODELS— JSON со списком моделей;- путь каждой модели:
/data/modelcache/models/<model-name>.
Имена моделей в одной рабочей нагрузке должны быть уникальными.
Удаление
d8 k -n ai-demo delete model bge-m3
d8 k delete clustermodel gemma-smallПосле удаления контроллер выполняет очистку через finalizer. Пока очистка
идёт, объект может оставаться в фазе Deleting.