Стадия жизненного цикла модуля: Experimental
У модуля есть требования для установки
Эта страница проводит один заказ от начала до конца и говорит, почему каждая часть устроена именно так.
Всё описанное здесь держится начиная с версии v0.0.1 модуля.
Учиться сегодняшнему поведению вкратце здесь не стоит. Для этого:
- Руководство пользователя — заказать инференс, прочитать состояние, узнать, почему заказ ждёт;
- Руководство администратора — включить модуль, написать классы, выдать права, следить, отключить;
- Примеры — манифесты, без рассказа.
Контроллер ai-inference разворачивает рабочую нагрузку заказа сам: нагрузку с контейнером среды исполнения запроса, службу, публикацию, заявку на устройство и остальные объекты заказа. Сторонний пакет доставки не участвует.
При включении модуля Helm создаёт кластерный класс default-llm (External, Token) с label module: ai-inference, границами числа копий и политикой ускорителя, разрешающей целое устройство в режиме разделения. Его можно использовать сразу: ускоритель заказ не запрашивает — устройство, их число и долю назначает платформа.
1. InferenceServiceClass
Поставляемый класс (модуль)
После установки модуля в кластере уже есть:
apiVersion: ai.deckhouse.io/v1alpha1
kind: InferenceServiceClass
metadata:
name: default-llm
spec:
modelPolicy:
allowedEndpointTypes:
- Chat
- Embeddings
- Rerank
exposurePolicy:
type: External
authentication: Token
https:
mode: CertManager
certManager:
clusterIssuerName: selfsignedОжидание: status.phase: Ready.
Именно allowedEndpointTypes позволяет заказу называть только класс и модель: заказ договор не
называет, и платформа берёт его из этого перечня. Поставляемый класс разрешает все договоры,
которые модуль поддерживает — Chat, Embeddings и Rerank, — и порядок перечня важен:
платформа берёт первый элемент перечня класса, суженного тем, что об умениях модели говорит её источник.
На пути каталога ai-models сужение происходит: модель векторизации обслуживается как модель
векторизации на этом же классе. На прямом пути Hugging Face таких сведений нет, поэтому перечень
остаётся целиком и побеждает первый договор — Chat. Заказ модели векторизации на этом пути
будет обслужен как чат, и никто его не отклонит. Если это ваш случай, объявите собственный
InferenceServiceClass, чей перечень называет один нужный договор.
Дополнительный класс администратора (опционально)
Поле spec.acceleratorPolicy.allowedDeviceClasses опционально — задаёт список допустимых DeviceClass для заказов на этот класс. Заказ может сузить этот перечень своим полем spec.resources.accelerator.deviceClasses (раздел 2 ниже); пустое пересечение — отказ.
apiVersion: ai.deckhouse.io/v1alpha1
kind: InferenceServiceClass
metadata:
name: llm-chat-shared
spec:
acceleratorPolicy:
allowedDeviceClasses:
- nvidia-h100-mps-mig
allowedSharingModes:
- Shared
allowedPlacementTypes:
- WholeDevice
maxAcceleratorCount: 1
minSharePercent: 25
maxSharePercent: 100
exposurePolicy:
type: External
authentication: TokenОжидание: status.phase: Ready, conditions Validated=True, Ready=True.
2. InferenceService (пользователь namespace)
apiVersion: ai.deckhouse.io/v1alpha1
kind: InferenceService
metadata:
name: support-llm
namespace: support
spec:
inferenceServiceClassName: default-llm
model:
ref:
name: Qwen/Qwen2.5-32B-Instruct-AWQ
src: HuggingFaceЗаказ — это ссылка на класс, блок модели и опционально сужение перечня классов устройства. Иных полей у его спецификации нет.
Сама ссылка на класс тоже опциональна: заказ, вовсе не задающий
spec.inferenceServiceClassName, пропускает проверки ниже, зависящие от класса — сужение перечня допуска
нет, status.model.endpointType не появляется, — и получает экспозицию и допуск неймспейсов, зашитые в
платформу (те же значения, что несёт поставляемый класс default-llm на день выхода этого среза, не живая
ссылка на этот объект). Любая другая политика уже ведёт себя без класса вовсе так же, как с классом, не
задающим соответствующий блок.
Заказ может сузить перечень допустимых DeviceClass класса своим полем
spec.resources.accelerator.deviceClasses — список из нескольких элементов класса из примера выше
позволяет заказу выбрать один:
apiVersion: ai.deckhouse.io/v1alpha1
kind: InferenceService
metadata:
name: support-llm
namespace: support
spec:
inferenceServiceClassName: default-llm
model:
ref:
name: Qwen/Qwen2.5-32B-Instruct-AWQ
src: HuggingFace
resources:
accelerator:
deviceClasses:
- nvidia-h100-mps-migПустое пересечение с acceleratorPolicy.allowedDeviceClasses класса — отказ с reason
ClaimDeviceMissing; частичное пересечение допускается, в план запуска уходит пересечение, а не
исходный перечень заказа.
Тип договора API инференса выбирает платформа из перечня класса, пересечённого со
сведениями каталога модели: единственный разрешённый договор либо первый из
нескольких. Выбранное значение видно в status.model.endpointType — это
единственное окно, где владелец заказа видит выбор, потому что класс ему для
чтения недоступен. Заказу, которому нужен иной договор, нужен иной класс.
Имя среды исполнения и все параметры запуска задаёт запись рецепта плана запуска,
скомпилированная под выбранное оборудование. Мультимодальные ключи каталога
(limitMmPerPrompt и аналоги) — там же, в рецепте. Иное значение — это иной
рецепт или иной класс, то есть право администратора.
Классу перечень разрешённых договоров API обязателен и непуст. Класс без него API-сервер отклоняет на записи: договор выбирается из перечня, заказ его не называет, и класс без перечня оставил бы свои заказы без договора вовсе.
Заказ, всё ещё несущий блок среды исполнения, при строгой проверке полей отклоняется, а клиент без неё принимает его с молчаливым усечением.
API-сервер до сохранения объекта проверяет союз источника модели. Он отклоняет: поле
model.ref.namespace у ссылок HuggingFace и ClusterModel; классы ClusterLocal с https; и ветки
сертификата, не соответствующие https.mode.
3. Что делает контроллер
- Проверка: класс существует,
Readyи объявляет перечень договоров API;model.src=HuggingFace. - Сборка объектов рабочей нагрузки заказа и их применение в namespace
support; каждый объект называется по заказу. - Вычисление
settings.domain(напримерsupport-llm.services.company.com) и публикацияstatus.endpoint. - При
authentication: Token— Secret{name}-authиstatus.authSecretName. - HTTP-проба Inference API;
phase: Readyтолько после успеха. - После
Ready— периодическая проверка работоспособности; при устойчивой ошибке API —phase: Failed, reasonServiceUnhealthy(bootstrap до первогоReadyостаётсяPending, reasonHealthCheckFailed).
4. Проверка результата
Чтение состояния готового заказа описано один раз, в Руководстве пользователя; разделы ниже говорят только о том, что добавляет к этому состоянию каждый сценарий.
5. Hugging Face token (опционально)
Для gated-моделей задайте Secret в namespace заказа и ссылку в spec.model.authSecretRef:
spec:
model:
ref:
name: meta-llama/Llama-3.1-8B
src: HuggingFace
authSecretRef:
name: hf-token
key: token # опционально; ключ по умолчанию — "token"Контроллер проверяет Secret вместе с заказом и монтирует его в загрузчик артефакта модели рабочей нагрузки как HF_TOKEN.
RBAC
| Уровень / роль | Ресурсы | Verbs |
|---|---|---|
User (d8:user-authz:ai-inference:user, rbacv2 use/view) |
inferenceservices |
get, list, watch |
| User / use/view | inferenceserviceclasses/placement-preview |
create |
| User / use/view | inferenceserviceclasses/cluster-view |
get |
Editor (:editor, rbacv2 use/edit) |
inferenceservices |
create, update, patch, delete, deletecollection |
ClusterEditor (:cluster-editor) |
inferenceserviceclasses |
get, list, watch, create, update, patch, delete, deletecollection |
| ClusterEditor / manage | UI-subresources выше | create / get |
| manage/view | moduleconfigs/ai-inference, ISC |
get, list, watch (+ UI-subresources) |
| manage/edit | moduleconfigs/ai-inference, ISC |
мутации (+ UI-subresources) |
Намеренно закрыто людям: inferenceserviceclasses/planner, */status, Secrets,
bind / escalate / impersonate. Дельты PrivilegedUser / Admin / ClusterAdmin
пусты (новых действий нет).
Шаблоны: templates/user-authz-cluster-roles.yaml, templates/rbacv2/**.
Политики класса
Пример класса embeddings-dedicated с admission/scaling/update и без аутентификации:
apiVersion: ai.deckhouse.io/v1alpha1
kind: InferenceServiceClass
metadata:
name: embeddings-dedicated
spec:
admissionPolicy:
allowedNamespaces: ["*"]
exposurePolicy:
type: ClusterLocal
authentication: None
scalingPolicy:
allowedPriorityClassNames:
- demo-inference-high
minReplicas: 1
maxReplicas: 4
updatePolicy:
strategy: RollingUpdateЗаказ под этим классом не называет границ числа копий, поэтому к нему применяются границы класса выше:
apiVersion: ai.deckhouse.io/v1alpha1
kind: InferenceService
metadata:
name: search-embeddings
namespace: ml
spec:
inferenceServiceClassName: embeddings-dedicated
model:
ref:
name: intfloat/multilingual-e5-large
src: HuggingFaceОжидание при Ready:
status.constraints.minReplicasравен 2,status.constraints.maxReplicasравен 4- нагрузка заказа обновляется по очереди (
updateStrategy.type: RollingUpdate), а число копий ведёт HPA - при значении
maxReplicasбольше единицы conditionScalingHealthyотражает состояние HPA: истина — норма, ложь — ограничение роста
status.endpoint.type отражает exposurePolicy.type (ClusterLocal / External). Какие договоры inference API допускает класс, решает allowedEndpointTypes на нём — обязательный и непустой; сам заказ договор не называет.
Причина отказа этой проверки: NamespaceNotAllowed.
Интеграция с ai-models
Требуется модуль ai-models с его внутренним запросом сведений, когда интеграция с
каталогом включена. Публичный ModuleConfig задаёт catalog.mode (Enabled
по умолчанию или None — отключить клиенты каталога). Внутренний транспорт
по умолчанию — aiInference.aiModels.catalogTransport: Rest: контроллер и
планировщик вызывают GET /api/internal/v1/models/lookup по model.ref. При
недоступности Rest в том же согласовании сразу читают CR Model /
ClusterModel. Явный откат — внутренний catalogTransport: Kubernetes
(только чтение CR). API распространения каталога и API импорта каталога для
запроса сведений не используются.
ModuleConfig (публичный режим каталога)
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: ai-inference
spec:
settings:
catalog:
mode: Enabled # None — отключить клиенты каталога и RBACВнутренний транспорт каталога (не ModuleConfig)
В openapi/values.yaml (внутренние значения модуля, не публичный ModuleConfig):
aiInference:
aiModels:
catalogTransport: Rest # по умолчанию; Kubernetes — явный откат
catalogLookup: # используется при catalogTransport Rest
baseURL: https://ai-models-controller.d8-ai-models.svc.cluster.local:8080
path: /api/internal/v1/models/lookup
timeoutSeconds: 10
notReadyRequeueSeconds: 30InferenceServiceClass с modelPolicy
apiVersion: ai.deckhouse.io/v1alpha1
kind: InferenceServiceClass
metadata:
name: llm-chat-shared
spec:
acceleratorPolicy:
allowedDeviceClasses:
- nvidia-h100-mps-mig
allowedSharingModes:
- Shared
allowedPlacementTypes:
- Partition
maxAcceleratorCount: 1
admissionPolicy:
allowedNamespaces: ["*"]
modelPolicy:
allowedEndpointTypes:
- Chat
maxParameterCount: 70B
exposurePolicy:
type: External
authentication: Token
scalingPolicy:
allowedPriorityClassNames:
- demo-inference-normal
minReplicas: 1
maxReplicas: 1
updatePolicy:
strategy: RollingUpdateInferenceService с model.ref (каталог)
apiVersion: ai.deckhouse.io/v1alpha1
kind: InferenceService
metadata:
name: support-llm
namespace: support
spec:
inferenceServiceClassName: llm-chat-shared
model:
ref:
kind: ClusterModel
name: qwen2-5-32b-instruct-awq
src: ai-modelsДля namespace-Model укажите kind: Model, name и опционально namespace в объекте ref; в собранной нагрузке namespace модели не передаётся.
Поведение контроллера
- Проверка класса и его политик.
- Запрос сведений по
model.ref(по умолчанию — по HTTP; при недоступности Rest — сразу чтение CR; приKubernetes— только чтениеstatusCR). - Локальная матрица каталога (
modelPolicyпротив фактов): при успехеModelResolved=True, иначеModelResolved=Falseиreason(ModelNotFound,ModelNotReady, …). - До
ModelResolved=Trueнагрузка заказа не собирается. - При успехе платформа собирает объекты заказа сама; выбранный ею договор API публикуется в
status.model.endpointType, а область модели — вstatus.constraints.modelScope. - Проверка работоспособности API и
phase: Ready.
Проверка status
Ожидание при Ready:
status.model.endpointType: Chat— договор API, выбранный платформой из перечня классаstatus.constraints.modelScope: Cluster— из фактов каталогаstatus.endpoint.type: External— зеркалоexposurePolicy.type, не тип API- condition
ModelResolved: True
kubectl get inferenceservice support-llm -n support -o jsonpath='{.status.model.endpointType}{"\n"}{.status.endpoint.type}{"\n"}'
kubectl get inferenceservice support-llm -n support -o jsonpath='{.status.constraints.modelScope}{"\n"}'Планировщик ресурсов
Продолжение раздела о каталоге выше (model.ref, ModelResolved). Добавляются план запуска, condition Planned и status.resolved. Требуются модуль gpu для инвентаря GPU и Deployment планировщика ресурсов, поставляемый модулем ai-inference.
Совместимость: встроенный default-llm и классы без acceleratorPolicy: контроллер не вызывает планировщик и не публикует Planned / status.resolved.
Предусловия
- Готовый
ClusterModel/Model, из раздела о каталоге выше. - Модуль
gpuс инвентарёмPhysicalGPU, доступным планировщику. - Служба планировщика по умолчанию модуля (
aiInference.planner.baseURLв значениях Helm-пакета; см.openapi/values.yaml).
InferenceServiceClass с acceleratorPolicy
К классу llm-chat-shared из раздела о каталоге выше добавьте acceleratorPolicy (манифест администратора):
apiVersion: ai.deckhouse.io/v1alpha1
kind: InferenceServiceClass
metadata:
name: llm-chat-shared
spec:
acceleratorPolicy:
allowedSharingModes: [Shared]
allowedPlacementTypes: [Partition, WholeDevice]
maxAcceleratorCount: 1
allowedDeviceClasses:
- nvidia-h100-mps-mig
admissionPolicy:
allowedNamespaces: ["*"]
modelPolicy:
allowedEndpointTypes:
- Chat
maxParameterCount: 70B
exposurePolicy:
type: External
authentication: Token
scalingPolicy:
allowedPriorityClassNames:
- demo-inference-normal
minReplicas: 1
maxReplicas: 4
updatePolicy:
strategy: RollingUpdateОжидание: status.phase: Ready.
InferenceService с accelerator и launchStrategy
apiVersion: ai.deckhouse.io/v1alpha1
kind: InferenceService
metadata:
name: support-llm
namespace: support
spec:
inferenceServiceClassName: llm-chat-shared
model:
ref:
kind: ClusterModel
name: qwen2-5-32b-instruct-awq
src: ai-models
launchStrategy: ThroughputlaunchStrategy выбирает ветку скомпилированного рецепта (Latency, Throughput или Balance; значение по умолчанию при отсутствии — Latency). Ветки на подавляющем большинстве железа действительно расходятся — более широкий горизонт контекста или упор на пропускную способность достижимы этим полем, не сменой рецепта или класса целиком. Число устройств заказом не задаётся — его назначает план запуска в пределах acceleratorPolicy.maxAcceleratorCount класса.
Долю устройства НАЗНАЧАЕТ план запуска, заказ её не называет. План берёт наименьшую целую долю, которой
хватает потребности видеопамяти модели на выбранном устройстве, зажимает её пределами minSharePercent и
maxSharePercent класса и отдаёт целое устройство, если класс разделения не разрешает, если выбранное
размещение не есть целое устройство в режиме разделения либо если одна из двух величин памяти неизвестна.
Назначенное значение публикуется в status.resolved.sharePercent — по нему владелец заказа видит, какая
часть карты его. Для доли меньше 100 выбранное устройство DRA обязано публиковать
allowMultipleAllocations и политику запроса sharePercent. Для доли 1..99 контроллер дополнительно
передаёт capacity.requests.memory по потребности модели из плана запуска, которая записывается в заявку
как gpu.deckhouse.io/memory. Память модели и доля вычислительных ресурсов MPS учитываются независимо.
Поведение контроллера
- Проверка и матрица каталога →
ModelResolved=True. - Политика ускорителя класса ограничивает исход планирования; у заказа своего запроса, который можно было бы с ней сверить, нет.
POST /api/v1/launch-plan— планировщик возвращает размещение и рецепт среды исполнения; при успехе:status.resolved— размещение, по которому заказ запущен:deviceClass,acceleratorProductName,acceleratorMemoryGiB,placementMode,sharingMode,sharePercent; остальное, что вычислил планировщик, уезжает в план запуска заказа и в состоянии не публикуется;- condition
Planned=True, reasonLaunchPlanCalculated.
- При отказе планировщика —
Planned=Falseсо стабильнымreason(NoCapacity,QuantizationMismatch, …); объекты нагрузки не создаются и не обновляются, пока планирование не успешно. - После
Planned=Trueрецепт из плана переносится в аргументы контейнераruntime; затем проверка работоспособности →phase: Ready, как в разделах выше. - При
maxReplicas > 1— наблюдение HPA и фоновый цикл вытеснения; встроенный классdefault-llmвне области.
Проверка status
На этапе согласования ожидайте Planned до появления нагрузки заказа. При Ready:
status:
phase: Ready
resolved:
deviceClass: nvidia-hopper-s3-shared
acceleratorProductName: NVIDIA H100 80GB HBM3
acceleratorMemoryGiB: 24
placementMode: Partition
sharingMode: Shared
replanCount: 0
conditions:
- type: ModelResolved
status: "True"
- type: Planned
status: "True"
reason: LaunchPlanCalculated
- type: Ready
status: "True"kubectl get inferenceservice support-llm -n support -o jsonpath='{.status.resolved}{"\n"}{.status.conditions[?(@.type=="Planned")]}{"\n"}'
kubectl get statefulset support-llm -n support -o jsonpath='{.spec.template.spec.containers[0].command}{"\n"}'Примеры отказа планировщика: Planned=False, reason: NoCapacity или QuantizationMismatch.
Резервный контур планировщика
Продолжение раздела о планировщике ресурсов выше. Если для пары (family, model.name) нет записи в runtime-recipes.compiled.yaml, а резервный контур включён, планировщик синтезирует обобщённый vLLM-рецепт из справочников images/catalogs/ (recipe-taxonomy.yaml и platform-hardware-families.yaml), оценивает vramMinimumGiB и возвращает recipeSource: fallback. Скомпилированная запись и предустановка всегда приоритетнее резервного контура.
Совместимость: при aiInference.planner.runtimeFallback.enabled: false
(внутренние значения модуля / openapi/values.yaml, не публичный ModuleConfig)
поведение идентично разделу о планировщике ресурсов выше — без синтетического рецепта. Классы без
acceleratorPolicy не затрагиваются.
Настройка модуля
В openapi/values.yaml (не публичный ModuleConfig):
aiInference:
planner:
runtimeFallback:
enabled: true # по умолчанию trueКонтроллер передаёт флаг планировщику как runtimeFallback.enabled. Отключение:
aiInference:
planner:
runtimeFallback:
enabled: falseМинимум метаданных для резервного контура
| Поле | Источник |
|---|---|
parameterCount на прямом пути HuggingFace |
spec.model.parameterCount → разбор model.name (32b, 360m, …) |
parameterCount на пути ai-models |
доступный факт каталога → разбор model.name (32b, 360m, …). На этом пути заказ размер не называет вовсе: spec.model.parameterCount отклоняется на записи |
quantization |
каталог → парсинг суффикса в model.name (awq, nvfp4, …) → default |
format, supportedEndpointTypes |
каталог (проброс контроллером; для оценки VRAM v1 не обязательны) |
Контроллер не парсит квантизацию из имени — только планировщик в ветке резервного контура. Для modelPolicy.maxParameterCount контроллер судит то, что разрешил путь заказа: факт каталога на пути ai-models, значение заказа на прямом пути и то же правило разбора model.name, если первая ступень пуста.
Сведения Model и ClusterModel сегодня не возвращают геометрию тензоров.
Планировщик получает её из поставляемой предустановки рецепта, если она есть;
иначе резервная оценка не использует геометрию. Выставление геометрии из
ai-models сегодня недоступно, и модуль не предполагает будущую форму полей
каталога.
Пример: модель без скомпилированного рецепта
ClusterModel с именем qwen2-5-32b-instruct-awq, в сведениях которого нет
parameterCount и quantization. Заказ также не задаёт
spec.model.parameterCount, поэтому размер и квантизация извлекаются из
model.name:
apiVersion: ai.deckhouse.io/v1alpha1
kind: InferenceService
metadata:
name: support-llm-fallback
namespace: support
spec:
inferenceServiceClassName: llm-chat-shared
model:
ref:
kind: ClusterModel
name: qwen2-5-32b-instruct-awq
src: ai-modelsОжидание после того, как инвентарь GPU заполнен:
status:
resolved:
deviceClass: nvidia-hopper-s3-shared
acceleratorProductName: NVIDIA H100 80GB HBM3
acceleratorMemoryGiB: 24
placementMode: Partition
sharingMode: Shared
conditions:
- type: Planned
status: "True"
reason: LaunchPlanCalculatedПри отсутствии распознаваемого размера (нет каталога, нет spec.model.parameterCount, имя без токена 32b/360m) — Planned=False, reason NoCompatibleRuntimeAvailable.
Проверка status
kubectl get inferenceservice support-llm-fallback -n support \
-o jsonpath='{.status.resolved.deviceClass}{"\n"}{.status.resolved.acceleratorProductName}{"\n"}'
kubectl get statefulset support-llm-fallback -n support \
-o jsonpath='{.spec.template.spec.containers[0].command}{"\n"}'