Стадия жизненного цикла модуля: Experimental
У модуля есть требования для установки
Это руководство для администратора кластера. Вы включаете модуль, решаете, что его заказам позволено, и следите за ним. Сам заказ инференса — дело пользователя namespace, об этом Руководство пользователя.
Разделение труда — это и есть смысл модуля, и стоит сказать его один раз: заказ называет класс и модель, и больше ничего. Всё, чего пользователь выразить не может — какой ускоритель, какая доля, какая среда исполнения, какой договор API инференса, сколько копий, — либо назначает платформа, либо ограничивает класс, который пишете вы. Класс и есть вся ваша поверхность управления.
Что нужно
- Deckhouse Kubernetes Platform
>= 1.75.0; - Kubernetes
>= 1.34. Пол совпадает с модулямиgpuиai-modelsна том же контуре; основной API Dynamic Resource Allocationresource.k8s.io/v1на этой версии уже есть; - редакции
feиee; - ускорители, отданные через Dynamic Resource Allocation, с классами устройств, по которым платформа умеет отбирать. Размещение планируется по настоящим сведениям о железе, а не запрашивается вручную.
Включение модуля
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: ai-inference
spec:
enabled: true
version: 1У модуля ровно одна настройка, и она про связь с модулем каталога ai-models:
spec:
settings:
catalog:
mode: Enabled # или NoneEnabled — значение по умолчанию — оставляет обращения к каталогу и права на межмодульный доступ. None
выключает клиентов каталога и эти права; заказы, называющие прямой источник Hugging Face, продолжают работать.
Выключайте, если ваши заказы каталогом не пользуются, а ai-models не установлен. Полный справочник —
Конфигурация.
Модуль работает в namespace d8-ai-inference и поставляет две составляющие: контроллер, который согласует
заказы с рабочими нагрузками, и планировщик ресурсов, который решает о размещении.
Написание класса заказа
InferenceServiceClass живёт на уровне кластера. Это единственное место, где живёт политика, и заказ не может
переопределить ничего из неё.
apiVersion: ai.deckhouse.io/v1alpha1
kind: InferenceServiceClass
metadata:
name: llm-chat-external
spec:
exposurePolicy:
type: External
authentication: Token
https:
mode: CertManager
certManager:
clusterIssuerName: letsencrypt
admissionPolicy:
allowedNamespaces:
- support
- analytics
modelPolicy:
allowedEndpointTypes:
- Chat
scalingPolicy:
minReplicas: 1
maxReplicas: 4
acceleratorPolicy:
allowedSharingModes:
- Shared
- Dedicated
allowedPlacementTypes:
- WholeDevice
- Partition
maxAcceleratorCount: 1
updatePolicy:
strategy: RollingUpdateО чём решает каждый блок:
| Блок | О чём решает | Если не задан |
|---|---|---|
exposurePolicy |
ClusterLocal или External, нужен ли токен, какая ветвь TLS |
обязателен |
admissionPolicy.allowedNamespaces |
какие namespace могут заказывать этим классом | обязателен |
modelPolicy.allowedEndpointTypes |
какие договоры API получат заказы класса | обязателен и непуст |
modelPolicy.allowedFormats, maxParameterCount |
какие модели допускаются вообще | без ограничений |
scalingPolicy |
пол и потолок числа копий, разрешённые классы важности | одна копия, автоматики роста нет |
acceleratorPolicy |
какие классы устройств, размещения, способы разделения, сколько устройств, границы доли | заказ берёт ускоритель целиком |
modelStorageClassName |
класс хранения тома модели, для томов, созданных после того, как вы его задали | значение платформы по умолчанию |
updatePolicy.strategy |
как переводятся копии заказа, когда платформа вправе их двигать: RollingUpdate |
обязателен |
modelStorageClassName доезжает только до новых томов. Том на другой класс хранения не переезжает, а
сервер отвергает любое изменение шаблонов заявки у стоящей нагрузки — поэтому заказ, чей том уже создан,
остаётся на том классе, на котором сделан, что бы класс теперь ни говорил. Ничего при этом не ломается и не
встаёт: платформа сохраняет стоящую заявку и записывает расхождение на заказе событием SettledRegionKept.
Чтобы перевести существующий заказ на новый класс, снесите его нагрузку и стоящую за ней заявку — артефакт
после этого будет скачан заново.
Три из них требуют предупреждения: не задать их — тоже решение, а не отсутствие решения.
modelPolicy.allowedEndpointTypes обязателен и непуст — проверка стоит на самом ресурсе. Договор
заказа выбирается из этого перечня; без перечня заказы остались бы вовсе без договора, поэтому сервер
отвергает такой класс при записи.
Порядок перечня — часть решения. Платформа берёт первый элемент вашего перечня, суженного тем, что об умениях модели говорит её источник. На источнике, который несёт сведения о модели — в каталоге, — модель представлений получит договор представлений. На источнике без сведений стоит весь перечень, побеждает первый элемент, и модель представлений, заказанная по голой ссылке Hugging Face, будет обслужена как беседа. Если для ваших пользователей это важно, объявите класс, чей перечень называет один договор.
Класс без scalingPolicy даёт своим заказам одну копию и никакой автоматики роста. Это решение о
вместимости, и модуль не принимает его за вас.
Класс без acceleratorPolicy отдаёт каждому заказу ускоритель целиком. Два заказа, каждый из которых
поместился бы в половине карты, займут тогда две карты, и совместного размещения, ради которого модуль
существует, на таком классе не случится само.
Встроенный класс
Модуль поставляет класс уровня кластера с именем default-llm. Он разрешает все три договора, Chat первым,
допускает оба способа разделения и оба вида размещения при одном устройстве на копию, не называет границ доли —
поэтому план назначает наименьшую долю, вмещающую память модели, — и ограничивает число копий от одной до двух.
Публикует он External с authentication: Token.
Это отправная точка, а не политика вашего кластера: выбор admissionPolicy.allowedNamespaces он за вас не
делает, а его потолок в две копии — наименьшее число, при котором автоматика роста вообще может существовать.
TLS на внешней точке входа
exposurePolicy.https.mode решает, откуда у точки входа, опубликованной с
type: External, берётся сертификат. Значений четыре, и у каждого свой блок
рядом:
mode |
Что получает точка входа | Что пишется рядом |
|---|---|---|
CertManager |
сертификат, выписанный cert-manager | certManager.clusterIssuerName; если опустить, платформа подставит selfsigned |
CustomCertificate |
сертификат, который у вас уже есть | customCertificate.secretName, обязателен и непуст |
Disabled |
TLS нет, точка входа отдаётся открыто | ничего |
OnlyInURI |
TLS на точке входа нет, а опубликованный адрес читается как https |
ничего |
certManager и customCertificate взаимно исключаются: класс, назвавший оба,
отвергается, и CustomCertificate без непустого secretName — тоже.
Чтобы принести свой сертификат, положите его в Secret того namespace, который
обслуживает класс, и назовите этот секрет:
spec:
exposurePolicy:
type: External
authentication: Token
https:
mode: CustomCertificate
customCertificate:
secretName: llm-chat-tlsПри type: ClusterLocal блок https присутствовать не должен.
Выдача прав
Модуль поставляет роли уровня кластера; связывание за вами.
| Уровень или роль | Ресурсы | Действия |
|---|---|---|
Пользователь (d8:user-authz:ai-inference:user, rbacv2 use/view) |
inferenceservices |
get, list, watch |
Пользователь / use/view |
inferenceserviceclasses/placement-preview |
create |
Пользователь / use/view |
inferenceserviceclasses/cluster-view |
get |
Редактор (:editor, rbacv2 use/edit) |
inferenceservices |
create, update, patch, delete, deletecollection |
Редактор кластера (:cluster-editor) |
inferenceserviceclasses |
get, list, watch, create, update, patch, delete, deletecollection |
Редактор кластера / manage |
подресурсы интерфейса выше | create / get |
manage/view |
moduleconfigs/ai-inference, классы |
get, list, watch и подресурсы интерфейса |
manage/edit |
moduleconfigs/ai-inference, классы |
правка и подресурсы интерфейса |
Людям намеренно не даётся: inferenceserviceclasses/planner, любой подресурс */status, Secret, а также
bind, escalate и impersonate. Прибавки уровней PrivilegedUser, Admin и ClusterAdmin пусты — здесь эти
уровни не получают ни одного лишнего действия.
Обратите внимание, чего пользователь namespace не может: прочитать класс. Именно поэтому у заказа есть
status.model.endpointType — это единственное окно пользователя в решение, которое принял класс.
Присмотр за модулем
С модулем поставляются два предупреждения, оба про переход вместимости между заказами:
D8AIInferenceCapacityPreempted— вместимость вытеснена в пользу заказа-получателя на классе устройств. Что-то выросло за счёт другого заказа;D8AIInferenceDonorHeldAtLoweredBound— заказ-даритель удерживается на понижённой границе числа копий. Он отдал вместимость и не получил её назад.
Поставляются и два табло Grafana, оба про среду исполнения запросов: статистика работы и статистика запросов.
Заказ в состоянии Degraded — то, что стоит замечать: его точка доступа отвечает, но вырасти он не может.
Причина обычно ScaleUpBlocked — вытеснение исчерпало вместимость, нужную автоматике роста.
Проверки при присмотре
# какие классы есть и прошли ли они проверку
kubectl get inferenceserviceclass
# заказы по всему кластеру и класс, которым пользуется каждый
kubectl get inferenceservice -A
# почему класс не Ready
kubectl get inferenceserviceclass llm-chat-external \
-o jsonpath='{range .status.conditions[*]}{.type}{"="}{.status}{" "}{.reason}{"\n"}{end}'Класс публикует два условия: Validated и Ready. Заказ, сообщающий PlacementUndecidable, указывает на ваш
класс, а не на вместимость кластера: размещение не удалось рассудить вовсе, значит выражению отбора класса нужен
взгляд. Сравните с ResourceClaimExhausted, который значит, что поиск был и все разрешённые классы устройств
перебраны, — этот про вместимость.
Обновление модуля
Обновление модуля может принести новый выпуск среды исполнения запросов, и стоит знать заранее, что станет с заказами, которые уже живут в кластере.
Копии, которые служат, продолжают служить — на том выпуске, с которым были подняты. Описание их нагрузки обновляется первым же согласованием после обновления, поэтому копия, поднятая с этого момента — потому что автоматика роста расширила набор или потому что копию заменили после утраты узла, — поднимается на поставляемом выпуске. Уже работающие остаются на месте: иначе это перезапуск — повторная загрузка модели, минуты на каждую копию.
Заказ сам говорит, где он. Пока выпуски расходятся, заказ несёт условие RuntimeCurrent=False с причиной
ReplicasHeldOnPreviousRuntime, и его сообщение называет оба выпуска и число копий, дошедших до поставляемого:
kubectl get inferenceservice support-llm \
-o jsonpath='{range .status.conditions[?(@.type=="RuntimeCurrent")]}{.reason}: {.message}{"\n"}{end}'Два способа довести переход до конца. Правка заказа переводит все копии так, как просит класс, — это перекат с тем простоем, который он подразумевает. Удаление копии переводит только её:
kubectl delete pod support-llm-0 -n my-namespaceПока набор смешанный, копии отвечают по-разному. Параметры запуска приходят из рецепта того выпуска, с которым копия поднята, а рецепт задаёт длину контекста и предел одновременных последовательностей — поэтому запрос у этих пределов одна копия обслужит, а другая отвергнет, пока переход не завершён. Если для вашей нагрузки это важно, правьте заказ сразу после обновления и берите простой в удобное время.
Когда переход не может быть постепенным. Если пересчитанный план сдвинул размещение заказа — другой класс
устройства, другая доля устройства, больший том модели, — копии сохранить нельзя: меняться должны как раз те
объекты, что держат устройства. Тогда заказ сообщает RuntimeCurrent=False с причиной WorkloadRebuilding,
проходит через Pending и возвращается на новом выпуске.
Отключение модуля
Отключение спрашивает подтверждение, и предупреждение стоит прочитать, а не пролистать. Отключение останавливает контроллер и планировщик ресурсов, а вместе с ними — каждую рабочую нагрузку заказа, которую они держат: среду исполнения запросов, её службу и её опубликованную точку доступа.
Сначала уберите, в таком порядке:
- удалите каждый
InferenceServiceво всех namespace; - удалите объекты
InferenceServiceClass, на которые эти namespace ссылались.
Отключение без этой уборки оставляет объекты заказов в кластере, и согласовывать их будет некому.
Куда смотреть дальше
- Конфигурация — справочник настроек;
- Пользовательские ресурсы — полный справочник полей обоих видов;
- Примеры — манифесты классов и заказов, готовые к копированию;
- Вопросы и ответы — ответы, разложенные по причинам отказа.