Стадия жизненного цикла модуля: Experimental
У модуля есть требования для установки

Руководство предназначено для пользователя пространства имён и оператора приложения. Оно показывает, как создать модель, импортировать модель из внешнего каталога и подключить модель в фазе Ready к рабочей нагрузке Kubernetes без ручной настройки учётных данных реестра, initContainer и Secret.

Модуль не запускает среду инференса. Он готовит файлы модели в поддерживаемом формате и монтирует их в Pod, а приложение само читает эти файлы.

Быстрый старт

  1. Создайте Model или ClusterModel с одним источником: url, upload или catalog.
  2. Дождитесь status.phase=Ready.
  3. Добавьте ai.deckhouse.io/model или ai.deckhouse.io/clustermodel в верхний metadata рабочей нагрузки.
  4. Проверьте, что Pod рабочей нагрузки стартовал и видит модель в /data/modelcache/models/<model-name>.

Model или ClusterModel

Ресурс Область Когда использовать
Model пространство имён Модель принадлежит одному пространству имён.
ClusterModel кластер Модель курируется администратором и доступна нескольким пространствам имён.

Model может использовать Secret из своего пространства имён для приватного репозитория Hugging Face. ClusterModel не ссылается на Secret из пространства имён.

Что происходит после создания модели

После создания Model или ClusterModel модуль подготавливает локальную копию модели, которой дальше пользуются рабочие нагрузки.

Этапы:

  1. Контроллер читает spec.source и определяет, откуда получить данные.
  2. Рабочий процесс получает данные из Hugging Face, Ollama, загрузочной сессии или внешнего каталога.
  3. Рабочий процесс проверяет формат, контрольную сумму и метаданные.
  4. Контроллер записывает status.artifact.digest, status.artifact.sizeBytes, status.resolved.* и условия.
  5. 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

Для приватного репозитория создайте Secret в том же пространстве имён. Поддерживаются ключи 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:latest

URL должен иметь вид 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. Имя Secret с URL загрузки и токеном доступно в status.upload.secretName:

d8 k -n ai-demo get model uploaded-model -o jsonpath='{.status.upload.secretName}{"\n"}'

Для загрузки нужен доступ get к этому Secret. Контроллер создаёт Role ai-model-upload-reader-<model-name>, которая читает только один Secret.

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

Контроллер фиксирует ревизию внешнего каталога и удалённую контрольную сумму в status.source.catalog, копирует артефакт в локальный DMCR и только затем переводит объект в Ready. Доставка в рабочую нагрузку всегда использует локальную копию.

Если у внешнего каталога временно сломался токен, сертификат или готовность, объект может перейти в Failed с причиной CatalogAuthFailed, CatalogTLSInvalid или CatalogSourceNotReady. Эти состояния восстанавливаются: после исправления Secret, сертификата, RBAC или состояния источника контроллер повторяет импорт той же ревизии каталога и той же удалённой контрольной суммы.

Статусы

Фаза Значение
Pending Контроллер ожидает предварительную проверку или ещё не начал подготовку.
WaitForUpload Создана сессия загрузки, нужно передать файл.
Publishing Идёт подготовка модели: получение, проверка и сохранение локальной копии.
Ready Данные модели проверены, локальная копия сохранена, метаданные рассчитаны.
Failed Подготовка завершилась ошибкой. Причина указана в status.conditions.
Deleting Выполняется очистка.

Полезные поля:

  • status.artifact.digest — контрольная сумма подготовленного артефакта;
  • status.artifact.sizeBytes — размер артефакта;
  • status.resolved.formatSafetensors, 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 — в локальном DMCR не хватает места.

Подключение модели к рабочей нагрузке

Добавьте аннотацию в верхний 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.