Deploy an InferenceService

Write the smallest InferenceService that serves a model, with only a model, a model and a runtime, or only a runtime, then wait for it, send it a request and see what OME created.

An InferenceService is the one resource you have to write to serve a model. At its smallest it names a model. OME picks a runtime that supports the model, creates the serving pods and a Service, and reports when the service is ready. This guide writes the three smallest shapes an InferenceService takes, then follows one of them from kubectl apply to a request. The other guides in this section, accelerators, storage and a runtime you name, are variations on it.

Step 1: Choose the shape

An InferenceService names a model, a runtime, or both. Pick the shape that fits, and save it as isvc.yaml.

OME picks the runtime: the one that best supports the model among the runtimes that allow automatic selection. engine: {} declares the engine, the component that runs the model, and lets the runtime fill it in.

isvc.yaml
apiVersion: ome.io/v1beta1
kind: InferenceService
metadata:
  name: llama-3-2-1b-instruct
  namespace: llama-demo
spec:
  model:
    name: llama-3-2-1b-instruct
  engine: {}

model.kind defaults to ClusterBaseModel. To serve a BaseModel from llama-demo instead, add kind: BaseModel under model.

Name the runtime when OME wouldn't pick it on its own, when several runtimes match the model and you want a particular one, or when you want the choice to stay fixed. Reference a runtime explicitly covers the details.

isvc.yaml
apiVersion: ome.io/v1beta1
kind: InferenceService
metadata:
  name: llama-3-2-1b-instruct
  namespace: llama-demo
spec:
  model:
    name: llama-3-2-1b-instruct
  runtime:
    name: vllm-llama-3-2-1b-instruct
  engine:
    minReplicas: 1
    maxReplicas: 1

minReplicas and maxReplicas keep the engine at one pod. Without them, the range comes from the runtime, then from the chart's replica defaults, 1 to 3 for the engine.

Since v1.3, you can leave out the model when the runtime loads the weights itself. OME then skips all model work: no runtime selection, no MODEL_PATH, no model volume, and no node selector for the model. This runtime passes $(MODEL_PATH) to vLLM, so the engine sets the variable to the Hugging Face repository and gives vLLM the token from a Secret hf-token in llama-demo, as Credentials creates it.

isvc.yaml
apiVersion: ome.io/v1beta1
kind: InferenceService
metadata:
  name: llama-runtime-only
  namespace: llama-demo
spec:
  runtime:
    name: vllm-llama-3-2-1b-instruct
  engine:
    runner:
      env:
        - name: MODEL_PATH
          value: meta-llama/Llama-3.2-1B-Instruct
        - name: HF_TOKEN
          valueFrom:
            secretKeyRef:
              name: hf-token
              key: token

This shape needs no BaseModel and no model agent. On v1.2.2, every InferenceService needs spec.model.

The webhook rejects an InferenceService that names neither: at least one of spec.model or spec.runtime must be set.

The rest of this guide uses the first shape. The commands work for the second one unchanged. For the third, use the name llama-runtime-only in place of llama-3-2-1b-instruct.

Step 2: Apply it

Shell
kubectl apply -f isvc.yaml
Output
Warning: Runtime vllm-llama-3-2-1b-instruct will be auto-selected for model llama-3-2-1b-instruct
inferenceservice.ome.io/llama-3-2-1b-instruct created

The warning names the runtime OME picked. When you name a runtime that declares support for the model, there's no warning. If no runtime that allows automatic selection supports the model, the webhook rejects the InferenceService with a message that starts with no supporting runtime found for model llama-3-2-1b-instruct: see Troubleshoot runtime selection.

Step 3: Wait until it's ready

The first start pulls the runtime's image and loads the model, which can take several minutes:

Shell
kubectl wait --for=condition=Ready inferenceservice/llama-3-2-1b-instruct -n llama-demo --timeout=30m
Output
inferenceservice.ome.io/llama-3-2-1b-instruct condition met

status.url is where to send requests. With ingress creation off, the default, it's the address of the Service that OME puts in front of the model:

Shell
kubectl get inferenceservice llama-3-2-1b-instruct -n llama-demo -o jsonpath='{.status.url}'
Output
http://llama-3-2-1b-instruct.llama-demo.svc.cluster.local:8080

Step 4: See what OME created

With the chart's default settings, OME creates:

  • A Deployment and a Service named llama-3-2-1b-instruct-engine. The pods run only on nodes where the model is Ready, as Node labels and status describes.
  • A HorizontalPodAutoscaler and a PodDisruptionBudget of the same name. The autoscaler keeps CPU use at 80% with 1 to 3 replicas, and the budget lets one pod be unavailable at a time.
  • The Service llama-3-2-1b-instruct in front of the engine, whose address is the URL above. See Ingress and external access.

The RUNTIME column of kubectl get inferenceservice shows spec.runtime.name, so it's empty when OME picks the runtime. The engine's workload always names the runtime OME used, in its serving-runtime label:

Shell
kubectl get deployment llama-3-2-1b-instruct-engine -n llama-demo -o jsonpath='{.metadata.labels.serving-runtime}'
Output
vllm-llama-3-2-1b-instruct

The engine runs as a Deployment because nothing chose another deployment mode. A multi-node engine runs on OMENative by default, and any component can opt in.

Step 5: Send a request

The Service is a ClusterIP Service, so forward a local port to it, and leave the command running:

Shell
kubectl port-forward svc/llama-3-2-1b-instruct 8080:8080 -n llama-demo
Output
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080

In a second terminal, ask the runtime which model names it serves:

Shell
curl -s http://localhost:8080/v1/models

The response lists them under data. Send a chat completion to /v1/chat/completions with one of those names as model, as Step 7 of Serve your first model does. The response is an OpenAI chat completion, with the answer in choices[0].message.content.

Troubleshooting

Troubleshoot an InferenceService covers more symptoms.

The webhook rejects the InferenceService

kubectl prints admission webhook "inferenceservice.ome-webhook-server.validator" denied the request:, then a message:

The message includes Fix
referenced model "llama-3-2-1b-instruct" not found in namespace "llama-demo" Create the ClusterBaseModel, or add kind: BaseModel if the model is a BaseModel in llama-demo.
model format name is required Wait for the model to become Ready: OME fills in its format when the model agent has read it.
no supporting runtime found for model llama-3-2-1b-instruct No runtime with autoSelect: true supports the model. See Troubleshoot runtime selection, or name a runtime.
at least one of spec.model or spec.runtime must be set Add a model or a runtime.

The engine pod stays Pending

Look at the pod's events with kubectl describe pod -n llama-demo -l ome.io/inferenceservice=llama-3-2-1b-instruct. Insufficient nvidia.com/gpu means no node has a free GPU for the runtime's request. didn't match Pod's node affinity/selector means the model isn't Ready on any node with a free GPU: list the nodes that have it with kubectl get nodes -l models.ome.io/clusterbasemodel.llama-3-2-1b-instruct=Ready.

The engine pod runs, but the InferenceService doesn't become ReadyUnreleased: coming in v1.3

Engines that run as a Deployment need the Prometheus Operator's PodMonitor CRD. Install it as in The PodMonitor CRD, then restart the controller with kubectl rollout restart deployment/ome-controller-manager -n ome, because it looks for the CRD only when it starts.

Clean up

Delete the InferenceService. The model and the runtime stay, for other InferenceServices to use:

Shell
kubectl delete inferenceservice llama-3-2-1b-instruct -n llama-demo
Output
inferenceservice.ome.io "llama-3-2-1b-instruct" deleted from llama-demo namespace

Next steps