Managed model serving¶
An LLMInferenceService is one object that describes the model you want served. The
platform builds the rest: the model server, an inference-aware router in front of it, and
the routing that publishes it.
This is the alternative to assembling that stack yourself. Both end in a vLLM process answering OpenAI-compatible requests; they differ in how much of the arrangement you own.
Which path to take¶
| Managed - this page | Manual | |
|---|---|---|
| You write | one object | a PVC, a Deployment and a Service |
| Weights | fetched for you from a uri |
you mount a volume and let the engine download |
| Routing | an inference-aware router, built for you | a Service, round-robin |
| Engine version and flags | defaults, overridable | entirely yours |
| Use it when | you want the model served | you are tuning the engine itself |
The managed path is the better default. Reach for the manual one when you need control over an engine flag the managed path does not expose, or when the hardware constraint below applies.
Prerequisites¶
- A project with GPU quota - see Accounts & projects
- The cluster's inference gateway. A cluster that has never served a model may not have one, and the symptom is specific - see The gateway has to exist.
Serve a model¶
apiVersion: serving.kserve.io/v1alpha1
kind: LLMInferenceService
metadata:
name: <name>
namespace: <project>
spec:
replicas: 1
model:
uri: hf://<hf-repo-id> # (1)!
name: <model-name> # (2)!
router:
route: {} # (3)!
gateway: {}
scheduler: {}
template:
containers:
- name: main
resources:
limits:
cpu: "8"
memory: 24Gi
nvidia.com/gpu: 1 # (4)!
requests:
cpu: "4"
memory: 12Gi
nvidia.com/gpu: 1
-
Edit this. Where the weights come from.
hf://fetches from Hugging Face in an init container before the engine starts, so a failure here is visible as a failed init container rather than an engine that will not load.s3://,pvc://andoci://are also accepted. -
Edit this. The name callers ask for. It is decoupled from the repository id, and it becomes an interface the moment anything depends on it.
-
Leave as is. The three empty objects are not placeholders - each asks the platform to build that piece with its defaults.
scheduler: {}is what gets you the inference-aware router; omit it and requests are distributed without regard to which replica already holds the conversation. -
Edit this. One GPU per replica. The same fit rules apply as on the manual path - see what has to fit in GPU memory.
What it builds¶
One object becomes several, and knowing the names makes the failures readable:
| Created | What it is |
|---|---|
<name>-kserve |
the Deployment running the engine |
<name>-kserve-workload-svc |
the Service in front of it, on port 8000 over HTTPS |
<name>-kserve-router-scheduler |
the router that picks which replica serves a request |
<name>-inference-pool |
the pool the router chooses from |
<name>-kserve-route |
the route publishing it through the cluster's inference gateway |
Verify¶
Ready becomes true only when the workload, the router, the pool and the route are all
ready. Read the individual conditions rather than the summary, because they fail for
unrelated reasons:
WorkloadsReady is about the engine. RouterReady and HTTPRoutesReady are about
publishing, and can be false while the model is perfectly healthy and serving inside the
cluster.
The Service speaks HTTPS with a certificate the platform generates, so a check from inside the cluster has to allow it:
The model name in the response is the one callers must use
/v1/models returns the name you set, not the Hugging Face repository id. A request
naming the repository id is rejected even though the weights are correct.
The gateway has to exist¶
The route is published through one gateway shared by the cluster, named in the KServe
configuration. If that gateway has not been created, every LLMInferenceService fails the
same way regardless of how it is written.
Managed HTTPRoute references non-existent Gateway
The condition names the gateway it wanted:
RouterReady=False
Managed HTTPRoute references non-existent Gateway openshift-ingress/openshift-ai-inference
The workload still starts and still serves inside the cluster - only publishing fails.
That is why the model can answer while the object never goes Ready.
Which gateway is expected comes from kserveIngressGateway in the
inferenceservice-config ConfigMap. Creating a Gateway of that name, with
allowedRoutes.namespaces.from: All so tenant projects may attach to it, resolves it
for every project at once. It is a cluster-level fix, not something to work around per
model.
The engine image has to match the GPU¶
The default image is built for recent GPU generations. On an older card the engine fails during device initialisation, before any weight is loaded.
CUDA error: no kernel image is available for execution on the device
The image contains no compiled kernels for that GPU architecture. No amount of configuration fixes it, because nothing in the configuration is wrong.
Either serve the model on a newer card, or override the image with one built for the architecture you have:
template:
containers:
- name: main
image: <vllm-image>
env:
- name: VLLM_ENABLE_CUDA_COMPATIBILITY
value: "0"
- name: LD_LIBRARY_PATH
value: /usr/lib64
The two variables are the same ones the manual path needs, and for the same reason - see the CUDA note. An override is worth removing once the hardware is replaced, since it pins you to an engine version the platform is no longer choosing.
Delete¶
Everything it built goes with it. Weights fetched from a uri are not cached anywhere you
own, so the next start downloads them again.
Next¶
Add a model provider → - put the endpoint behind a model name on the gateway, so callers reach it by name and with a key.