Теперь запускаем модель при помощи KServe на GPU

Совсем недавно мы писали статью про то, как запустить в кластере k8s сервис Kserve и продемонстрировали запуск в нём инференса модели Qwen3. Но запуск мы осуществляли на CPU и ждать ответа нам приходилось больше минуты.

Теперь давайте немного дополним прошлую статью и покажем, как запускать инференс в Kserve при помощи GPU (в данном случае Nvidia). И сравнив производительность с запуском на CPU.

В этот раз для тестирования нам достался сервер Huawei 2288H V5, имеющий следующие характеристики:

  • 2 CPU Intel(R) Xeon(R) Gold 5118 CPU @ 2.30GHz
  • 256 Гб ОП
  • 2 SAS диска ST600MM0009
  • видеокарта NVIDIA Corporation GV100GL [Tesla V100 PCIe 16GB] (rev a1)
  • ОС: openScaler 24.03 LTS SP2
  • ядро: 6.6.0-108.0.0.114.os2403sp2.x86_64

У нас уже развёрнут на этом сервере k8s (один узел — он же мастер, он же рабочий), поэтому мы не будем в 2025 году утомлять вас рассказом «как ставить k8s».

Для начала, мы установим в k8s GPU-оператор https://github.com/NVIDIA/gpu-operator , который позволит использовать внутри кластера k8s драйвера nvidia.

Установку будем производить при помощи helm:

helm repo add NVIDIA https://helm.ngc.nvidia.com/nvidia
helm repo update
helm install --wait --generate-name -n gpu-operator --create-namespace NVIDIA/gpu-operator

Проверяем что драйвер установлен корректно:

kubectl -n gpu-operator exec -it gpu-feature-discovery-tckgl -- nvidia-smi

Defaulted container “gpu-feature-discovery” out of: gpu-feature-discovery, toolkit-validation (init)

Wed Oct 15 10:41:16 2025

+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.82.07 Driver Version: 580.82.07 CUDA Version: 13.0 |
+-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 Tesla V100-PCIE-16GB Off | 00000000:3B:00.0 Off | 0 |
| N/A 42C P0 29W / 250W | 0MiB / 16384MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
| 1 Tesla V100-PCIE-16GB Off | 00000000:86:00.0 Off | 0 |
| N/A 40C P0 27W / 250W | 0MiB / 16384MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| No running processes found |
+-----------------------------------------------------------------------------------------+

Теперь подготовим 2 yaml манифеста для запуска инференсов.

Один для CPU:

apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: huggingface-qwen3-cpu
spec:
  predictor:
    model:
      modelFormat:
        name: huggingface
      args:
      - --model_name=qwen3
      - --model_id=Qwen/Qwen3-1.7B
      - --max-model-len=16384
      resources:
        limits:
          cpu: "6"
          memory: 24Gi
        requests:
          cpu: "6"
          memory: 24Gi

 

И манифест для запуска на nvidia:

apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: huggingface-qwen3-nvidia
spec:
  predictor:
    model:
      modelFormat:
        name: huggingface
      args:
      - --model_name=qwen3
      - --model_id=Qwen/Qwen3-1.7B
      - --max-model-len=16384
      - --dtype=half
      resources:
        limits:
          cpu: "6"
          memory: 24Gi
          nvidia.com/gpu: "1"
        requests:
          cpu: "6"
          memory: 24Gi
          nvidia.com/gpu: "1"

 

Как вы видите, мы выделили 1 ядро nvidia для работы нашего инференса.

Для внешнего доступа (мы же ленивые) просто сделаем 2 службы типа NodePort и будем получать доступ к инференсам просто меняя в команде curl номер порта.

Служба для доступа к инференсу на CPU:

apiVersion: v1
kind: Service
metadata:
  name: huggingface-qwen3-predictor-np-cpu
  namespace: default
spec:
  ports:
  - name: http
    port: 80
     protocol: TCP
     nodePort: 30123
     targetPort: 8012
   selector:
     serving.kserve.io/inferenceservice: huggingface-qwen3-cpu
   sessionAffinity: None
   type: NodePort

 

Служба для доступа к инференсу на Nvidia:

apiVersion: v1
kind: Service
metadata:
  name: huggingface-qwen3-predictor-np-nvidia
  namespace: default
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    nodePort: 30124
    targetPort: 8012
  selector:
    serving.kserve.io/inferenceservice: huggingface-qwen3-nvidia
  sessionAffinity: None
  type: NodePort

 

Собственно, всё! Запускаем наши инференсы (привычно применяем указанные выше файлы при помощи утилиты kubectl), ждём когда контейнеры запустятся и скачают модель из репозитория. Дождавшись приступаем к проверке.

Сначала зададим (уже набивший оскомину) вопрос про Марка Твена модели запущенной на CPU:

time curl -v http://172.17.3.6:30123/openai/v1/completions 
 -H "content-type: application/json" 
 -H "Host: huggingface-qwen3-qwen-test.example.com" 
 -d '{"model": "qwen3", "prompt": "who is Mark Twen?", "stream":false, "max_tokens": 40}'
* Trying 172.17.3.6:30123...
* Connected to 172.17.3.6 (172.17.3.6) port 30123
* using HTTP/1.x
> POST /openai/v1/completions HTTP/1.1
> Host: huggingface-qwen3-qwen-test.example.com
> User-Agent: curl/8.14.1
> Accept: */*
> content-type: application/json
> Content-Length: 83
> 
* upload completely sent off: 83 bytes
< HTTP/1.1 200 OK
< Content-Length: 466
< Content-Type: application/json
< Date: Wed, 15 Oct 2025 11:21:39 GMT
< Server: uvicorn
< 
* Connection #0 to host 172.17.3.6 left intact
{"id":"cmpl-5f1d21458f374c90bc456be7dbf9ba22","object":"text_completion","created":1760527300,
"model":"qwen3","choices":[{"index":0,"text":" Mark Twain, also known as Samuel Clemens (1835–1910),
 was an American author, humorist, and social critic. He is best known for his novels",
"logprobs":null,"finish_reason":"length","stop_reason":null,"prompt_logprobs":null}],
"usage":{"prompt_tokens":6,"total_tokens":46,"completion_tokens":40,"prompt_tokens_details":null}}
real 0m23,462s
user 0m0,014s
sys 0m0,013s

 

Как вы видите, ответ занял целых 23 секунды.

Что же, теперь спросим у модели на GPU:

time curl -v http://172.17.3.6:30124/openai/v1/completions 
 -H "content-type: application/json" 
 -H "Host: huggingface-qwen3-qwen-test.example.com" 
 -d '{"model": "qwen3", "prompt": "who is Mark Twen?", "stream":false, "max_tokens": 40}'
* Trying 172.17.3.6:30124...
* Connected to 172.17.3.6 (172.17.3.6) port 30124
* using HTTP/1.x
> POST /openai/v1/completions HTTP/1.1
> Host: huggingface-qwen3-qwen-test.example.com
> User-Agent: curl/8.14.1
> Accept: */*
> content-type: application/json
> Content-Length: 83
> 
* upload completely sent off: 83 bytes
< HTTP/1.1 200 OK
< Content-Length: 472
< Content-Type: application/json
< Date: Wed, 15 Oct 2025 11:22:10 GMT
< Server: uvicorn
< 
* Connection #0 to host 172.17.3.6 left intact
{"id":"cmpl-e8f14ea6af1d4e32afe9fc6e7ed894f8","object":"text_completion",
"created":1760527331,"model":"qwen3","choices":[{"index":0,"text":" Mark Twain,
 often referred to as the "Scribe of the South," was an American author, humorist,
 and social critic. He was born in 1835 and died in",
"logprobs":null,"finish_reason":"length","stop_reason":null,"prompt_logprobs":null}],
"usage":{"prompt_tokens":6,"total_tokens":46,"completion_tokens":40,"prompt_tokens_details":null}}
real 0m0,571s
user 0m0,006s
sys 0m0,004s

 

Как говорится, прибавить к этому ответу нечего, всё наглядно и так 🙂