Запуск модели Qwen3 при помощи KServe на OpenScaler
Недавно нашему сообществу попалась на глаза статья от OpenAtom Foundation, где демонстрировался запуск Hugging Face Qwen3 при помощи инструмента KServe в кластере kubernetes на openEuler.
Нас она заинтересовала и сегодня мы попробуем пройтись по ней и проверить её на практике.
Отметим, что целью статьи является демонстрация использования модели Qwen3 от Hugging Face для задач генерации текста путем развертывания InferenceService, запускающего службу Hugging Face.
Среда выполнения Hugging Face от KServe использует vLLM в качестве серверной части для обслуживания Large Language Model (LLM) по умолчанию, что позволяет ускорить время отклика первого токена (TTFT) и повысить производительность генерации токенов по сравнению с официальным API Hugging Face.
Если вы не сталкивались с KServe, то упомянем, что это платформа для обслуживания моделей на базе Kubernetes, которая упрощает развертывание моделей машинного обучения в производственных средах и управление ими. Благодаря стандартизированным интерфейсам и CRD (custom resource definition), KServe поддерживает множество основных серверных систем онлайн-инференса (таких как TensorFlow Serving, TorchServe, Triton Inference Server и Hugging Face Server) для различных моделей глубокого обучения.
Подготовка стенда тестирования
Сервер Huawei 1288H V5:
- 2 CPU Intel(R) Xeon(R) Gold 6230 CPU @ 2.10GHz
- 384 Гб ОП
- 2 SAS диска ST600MM0009
- ОС: openScaler 24.03 LTS SP2
- ядро: 6.6.0-107.0.0.113.oe2403sp2.x86_64
Установим необходимые для тестирования пакеты:
dnf install -y wget curl tar iptables
Теперь установим docker:
curl -sL https://raw.githubusercontent.com/cnrancher/euler-packer/refs/heads/main/scripts/others/install-docker.sh | bash
Установка kind, для лёгкого и быстрого способа развёртывания кластера kubernetes:
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.29.0/kind-linux-amd64
chmod +x ./kind
mv ./kind /usr/local/bin/kind
Устанавливаем утилиту kubectl:
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
Устанавливаем helm:
wget https://get.helm.sh/helm-v3.18.4-linux-amd64.tar.gz
tar -zxvf helm-v3.18.4-linux-amd64.tar.gz
mv linux-amd64/helm /usr/local/bin/helm
Разворачиваем кластер k8s:
kind create cluster
Настраиваем kubectl для управления нашим кластером:
kubectl config use-context kind-kind
Установка KServer на наш кластер k8s:
curl -sL "https://gitee.com/openeuler/openeuler-docker-images/raw/master/AI/kserve/controller/doc/quick_install.sh" | bash -s -- -r
Запуск модели Qwen3 на кластере
Создаём yaml манифест с CRD конфигурацией для сервиса Hugging Face Qwen3 qwen3.yaml:
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
name: huggingface-qwen3
spec:
predictor:
model:
modelFormat:
name: huggingface
args:
- --model_name=qwen3
- --model_id=Qwen/Qwen3-8B
- --max-model-len=16384
resources:
limits:
cpu: "6"
memory: 24Gi
requests:
cpu: "6"
memory: 24Gi
Заметьте, мы не указываем HF_TOKEN, а обращаемся для загрузки модели как anonymous.
и запускаем на кластере:
kubectl apply -f qwen3.yaml
Тут надо заметить, что придётся немного подождать, пока делается pull образу, а после запуска pod, он всё равно не станет сразу ready, потому что потратит минут 10 на скачивание модели.
Спустя какое-то время наш pod наконец-то поменял статус на ready:
kubectl get po
NAME READY STATUS RESTARTS AGE
huggingface-qwen3-predictor-644c59bd85-7qdl4 1/1 Running 0 14m
Проверяем inferenceservices:
kubectl get inferenceservices
NAME URL READY PREV LATEST PREVROLLEDOUTREVISION LATESTREADYREVISION AGE
huggingface-qwen3 http://huggingface-qwen3-default.example.com True 16m
Из этого вывода запоминаем URL.
Если нам нужна более подробная информация, читаем вывод команды:
kubectl describe inferenceservice huggingface-qwen3
Теперь, чтобы проверить работу нашего сервиса, нам надо постучаться в API. Вспоминаем, что мы используем kind и поэтому кластер k8s у нас работает внутри контейнера kind. Из-за этого у нас не получится привычно использовать ip адреса службы (service) k8s и стучаться в него прямо с хоста при помощи curl. Как вариант – можно настроить (KServe устанавливает как зависимость service mesh Istio) gateway, virtualservice. Но в развёрнутом нами варианте kind у нас нет сервиса раздающего LoadBalancer ip адреса, поэтому наш istio не работает как ему положено, а устанавливать и настраивать тот же MetalLB не входит в задачи данной статьи.
Поэтому для демонстрации мы выберем простой путь, а именно будем обращаться к API изнутри контейнера kind.
Для начала узнаём ip-адрес и порт API:
kubectl get svc huggingface-qwen3-predictor
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
huggingface-qwen3-predictor ClusterIP 10.96.202.20 <none> 80/TCP 54m
Из этого вывода запомним ip-адрес 10.96.202.20 и порт 80.
URL inferenceservices у нас уже есть http://huggingface-qwen3-default.example.com
.
Осталось дело за малым, нужно провалиться внутрь системного контейнера kind-control-plane
и оттуда уже запускать curl.
При помощи стандартной команды docker заходим внутрь контейнера. Можем удовлетворить любопытство и посмотреть список запущенных контейнеров (контейнеры внутри контейнера, да, вот такая “матрёшка”):
docker exec -ti kind-control-plane /bin/bash
root@kind-control-plane:/# crictl ps
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID POD NAMESPACE
526cfb43897d9 7b4d2f6e6f0a5 52 minutes ago Running kserve-container 0 52acb8d4eee76 huggingface-qwen3-predictor-644c59bd85-7qdl4 default
81d9933636da3 de5f04897a3d0 59 minutes ago Running manager 0 d49ec12c980a0 kserve-controller-manager-7fc87b466b-gdsnb kserve
41f236c62cf3d 35def84111f7b 59 minutes ago Running kube-rbac-proxy 0 d49ec12c980a0 kserve-controller-manager-7fc87b466b-gdsnb kserve
d129cdbcd7937 94b6cc26be635 About an hour ago Running cert-manager-cainjector 0 a95534c107610 cert-manager-cainjector-58c9d76cb8-76pjd cert-manager
0adbcd90a7758 c5c110afda0f7 About an hour ago Running cert-manager-webhook 0 9fa1d2bd074af cert-manager-webhook-5875b545cf-5bfv5 cert-manager
3040b0479ca9d ef702517cc982 About an hour ago Running cert-manager-controller 0 7a1feaedfcffd cert-manager-74b7f6cbbc-jv9sz cert-manager
1fcb35fc74685 25eeeeca367cf About an hour ago Running istio-proxy 0 c482479fbe453 istio-ingressgateway-74949b4866-xg65c istio-system
ac75a9e7ec19f a342234ebb356 About an hour ago Running discovery 0 a9e95bbd70abb istiod-77bbc7c8bb-9lk68 istio-system
93bc91663a249 bbb6209cc873b About an hour ago Running local-path-provisioner 0 e24bfad2a035c local-path-provisioner-7dc846544d-x7pk8 local-path-storage
b9e8e7e22b595 1cf5f116067c6 About an hour ago Running coredns 0 cdff542b84e58 coredns-674b8bbfcf-lxvmv kube-system
f5385928538ec 1cf5f116067c6 About an hour ago Running coredns 0 71d6829fd6503 coredns-674b8bbfcf-949s6 kube-system
bbc9cea0ee1cd 409467f978b4a About an hour ago Running kindnet-cni 0 eaa6866447dfe kindnet-t7pvq kube-system
35512c227075e b79c189b052cd About an hour ago Running kube-proxy 0 b04f837b6d2a8 kube-proxy-z6tgj kube-system
91422c9a754c1 499038711c081 About an hour ago Running etcd 0 0e741d8897d5e etcd-kind-control-plane kube-system
5baddbea7318c ef43894fa110c About an hour ago Running kube-controller-manager 0 8907f364eb5d4 kube-controller-manager-kind-control-plane kube-system
cb790972a922e c6ab243b29f82 About an hour ago Running kube-apiserver 0 e951d29c6cacc kube-apiserver-kind-control-plane kube-system
2ca9ea97272b4 398c985c0d950 About an hour ago Running kube-scheduler 0 e254ad0e9d270 kube-scheduler-kind-control-plane kube-system
Обращаемся к API при помощи curl. Давайте спросим у Qwen3, кто такой Марк Твен (при этом специально допустим опечатку в фамилии, посмотрим как отреагирует модель).
curl -v http://10.96.202.20/openai/v1/completions -H "content-type: application/json" \
-H "Host: huggingface-qwen3-default.example.com" \
-d '{"model": "qwen3", "prompt": "who is Mark Twen?", "stream":false, "max_tokens": 30}'
* Trying 10.96.202.20:80...
* Connected to 10.96.202.20 (10.96.202.20) port 80 (#0)
> POST /openai/v1/completions HTTP/1.1
> Host: huggingface-qwen3-default.example.com
> User-Agent: curl/7.88.1
> Accept: */*
> content-type: application/json
> Content-Length: 83
>
< HTTP/1.1 200 OK
< date: Fri, 05 Sep 2025 09:24:24 GMT
< server: uvicorn
< content-length: 421
< content-type: application/json
<
* Connection #0 to host 10.96.202.20 left intact
{"id":"cmpl-48237740ebf14f23bbe085c5114adf2b","object":"text_completion","created":1757064265,
"model":"qwen3","choices":[{"index":0,"text":" Mark Twain is the pen name of Samuel Langhorne Clemens (1835–1910), an American writer, humor",
"logprobs":null,"finish_reason":"length","stop_reason":null,"prompt_logprobs":null}],
"usage":{"prompt_tokens":6,"total_tokens":36,"completion_tokens":30,"prompt_tokens_details":null}}
Ответ модели занял довольно продолжительное время, в моём случае целую 1 минуту и 13 секунд . Но это легко объяснимо тем, что мы запустили сервис только на использовании CPU, без GPU ускорителей.
На этом тест считаем выполненным. Как вариант в будущем тесте можем попробовать запустить этот вариант на сервере с GPU ускорителем и проверить на сколько повысится быстродействие.
KServe一доказал, что является мощным и гибким инструментом для развёртывания моделей машинного обучения в Kubernetes, абстрагирующим пользователя от сложностей управления инфраструктурой.