How OME works
How the OME manager, the model agent and OME's custom resources turn a model and a runtime into serving pods.
OME serves large language models on Kubernetes. You describe what to serve and how in OME's custom resources. OME can bring the weights to your nodes or leave them to your runtime. It picks a runtime when you don't name one, and creates and manages the pods that serve the model. Since v1.3, OME can run any component on OMENative, its own workload engine, which manages each replica as one unit. Components that span several nodes use it by default.
Components¶
The manager is OME's core, and OMENative runs inside it. Two optional, alpha components work with OMENative: the OME scheduler places its gangs of pods, and Alfred recommends when to migrate its Instances.
| Component | What it does | Where it runs |
|---|---|---|
| Manager | OME's control plane. Its controllers turn OME's resources into serving workloads, and its webhooks check the resources and add containers and settings to serving pods. | The Deployment ome-controller-manager, with three replicas by default, from the ome-resources chart |
| OMENative | Since v1.3. OME's workload engine. It creates a component's pods itself and manages each replica as one Instance. Multi-node components use it by default, and any component can opt in. | Inside the manager, on by default |
| Model agent | Downloads the models that select its node, and labels the node when a model is ready there. | The DaemonSet ome-model-agent-daemonset, from the ome-resources chart when you turn it on |
| ome-agent | Downloads fine-tuned weights into the pods that serve them, and reads the config files of pvc:// models. |
An init container in serving pods, and Jobs |
| OME scheduler | Since v1.3. Alpha. A second scheduler that places each gang of pods together in one accelerator domain. Pods opt in with schedulerName. It runs only on Kubernetes 1.35. |
A Deployment, ome-scheduler by default, from the ome-scheduler chart |
| Alfred | Since v1.3. Alpha. The GPU cluster caretaker. It watches for fragmented GPU capacity and unhealthy nodes and, by default, only recommends Instance migrations. | The Deployment ome-alfred, from the ome-alfred chart |
| Quota manager | Since v1.3. Keeps the status of AcceleratorQuotas up to date, and can turn the quota tree into Kueue queues. | A Deployment, ome-quota-manager by default, from the ome-quota-manager chart |
| kubectl ome | Since v1.3. The OME CLI. It inspects OME's resources and runs alpha actions on them, with your Kubernetes identity. | Your machine, as a kubectl plugin |
Resources¶
All of OME's kinds have the API version ome.io/v1beta1. You write them all except TrafficMaps, which OME writes itself, and most InferenceReplicas: OME writes one for each OMENative component of an InferenceService, and you can also run a standalone one yourself.
| Kind | Scope | Purpose |
|---|---|---|
| InferenceService | Namespaced | Serves a model. It names a model, a runtime or both, and describes the engine, decoder and router. |
| BaseModel, ClusterBaseModel | Namespaced, Cluster | A model: where its weights live and which nodes keep a copy. |
| FineTunedWeight | Cluster | Weights fine-tuned from a base model, which an InferenceService serves on top of that model. |
| ServingRuntime, ClusterServingRuntime | Namespaced, Cluster | How to serve a family of models: the models it supports, and the containers and settings of the engine, decoder and router. |
| AcceleratorClass | Cluster | A kind of GPU or other accelerator, and how to find the nodes that have it. |
| BenchmarkJob | Namespaced | A genai-bench run against an InferenceService or a URL. |
| AutoscalerPolicy | Namespaced | Since v1.3. Alpha, and off by default. An autoscaling template that InferenceService components attach by name. |
| RolloutPolicy | Namespaced | Since v1.3. Alpha, and off by default. A rollout progression that InferenceService rollout groups refer to. |
| InferenceReplica | Namespaced | Since v1.3. One pod set, run as Instances: the workload OME writes for one OMENative component, or a standalone pod set you create directly. |
| AcceleratorQuota | Cluster | Since v1.3. One entry in a tree of quotas that divides accelerators among teams. |
| WorkloadCluster | Cluster | Since v1.3. Alpha, and in development. A cluster that OME can place workloads onto, for multi-cluster serving, which is off by default. |
| TrafficMap | Namespaced | Since v1.3. Alpha, and in development. How the traffic of a multi-cluster InferenceService splits across clusters. |
An InferenceService can use the BaseModels and ServingRuntimes in its own namespace, and every ClusterBaseModel and ClusterServingRuntime.
How weights reach the nodes¶
On each node that a BaseModel or ClusterBaseModel selects, the model agent:
- Downloads an
hf://oroci://model fromspec.storage.storageUriinto the directory thatspec.storage.pathnames, pulls amodelpack://model into it through anllmman servedaemon, or finds alocal://model already there. - Reads the model's config files. From them, OME fills in the model's architecture, size and other fields that runtime selection uses.
- Labels its node with the model's state there:
Updating, thenReadyorFailed. The label key names the model, such asmodels.ome.io/clusterbasemodel.qwen2-5-7b-instruct.
OME gives the engine and decoder pods a node selector on that label with the value Ready, so they run only on nodes that have the model. Until a node does, they stay Pending. Each pod mounts the model's directory read-only, and OME sets the MODEL_PATH environment variable to it. See Base models.
Since v1.3, the ome-resources chart installs the model agent only when you set modelAgent.enabled: true, as Install OME does. Without it, every model except a pvc:// model stays In_Transit, and the pods that serve it stay Pending.
A pvc:// model stays on its PersistentVolumeClaim, and the serving pods mount the claim. See Serve models from a PVC.
A fine-tuned weight downloads into the engine and decoder pods, through an init container that runs ome-agent.
Since v1.3, an InferenceService can skip this path and name only a runtime, which loads the weights itself.
From InferenceService to pods¶
An InferenceService serves one model with up to three components:
- the engine, which runs the model;
- optionally, a decoder, for prefill-decode disaggregation: the engine's pods run the prefill phase, and the decoder's pods the decode phase;
- optionally, a router in front of them, which sends requests to their pods.
When you create or change an InferenceService, OME works through these steps:
- Find the model that
spec.modelnames. The kind defaults to ClusterBaseModel, so an InferenceService that uses a BaseModel setsspec.model.kind: BaseModel. - Choose the runtime. When
spec.runtimenames a runtime, OME uses it. Otherwise, it picks the best match among the runtimes that support the model and allow automatic selection. See How runtimes match the model. By default, the InferenceService follows changes to its runtime. To pin it to one revision, see Runtime revisions and pinning. - Build the components. OME starts from the runtime's
engineConfig,decoderConfigandrouterConfig, and the InferenceService's own settings win. When the runtime lists accelerator classes and the InferenceService names a class or a selection policy, OME picks an AcceleratorClass for the engine and decoder. Their pods get the class's node selector. See Select accelerators. - Create each component's workload for its deployment mode, and a Service for it. Both are named after the InferenceService and the component, such as
llama-enginefor the engine ofllama. - Expose the InferenceService. By default, OME creates a ClusterIP Service named after the InferenceService, which sends requests to the router's pods, or to the engine's when there's no router. When you turn on ingress creation, OME creates a Kubernetes Ingress or Gateway API HTTPRoutes instead. See Ingress and external access.
- Report status. OME records the InferenceService's URL, the state of its components, and conditions such as
Readyin its status. Since v1.3,kubectl ome statusshows in one report why the InferenceService is or isn't ready.
If OME can't find a runtime, for example because the runtime was deleted, it records a RuntimeNotFound warning event and leaves the serving workload as it is.
Deployment modes and OMENative¶
Since v1.3, an engine or decoder with a leader or worker block runs on OMENative by default. The block can come from the InferenceService or its runtime. Any other component runs as a RawDeployment. To run all of an InferenceService's components on OMENative, the router included, set spec.deploymentMode: OMENative.
OMENative: InferenceService -> InferenceReplica -> pods, grouped into Instances
RawDeployment: InferenceService -> Deployment -> ReplicaSet -> podsOn OMENative, each replica of a component is an Instance: one pod, or a leader pod and its worker pods for a model that spans nodes. By default, when a pod of a multi-pod Instance fails, OME recreates all of its pods. When the scheduler-plugins PodGroup CRD is installed, OME also creates a PodGroup for each multi-pod Instance, so that a gang-aware scheduler, such as the OME scheduler, places its pods together. See Gang scheduling.
A component's ome.io/deploymentMode annotation wins over all of this. Since v1.3, it's the only way to run a component in the deprecated MultiNode mode, on a LeaderWorkerSet. On v1.2.2, a leader or worker block makes a component MultiNode, and there's no OMENative mode. See How OME resolves the mode.
Next steps¶
- OMENative overview: how OMENative creates, updates, repairs and migrates Instances.
- Deployment modes and OMENative: how OME picks a component's mode, and how to change it.
- The OME scheduler and Alfred: alpha gang scheduling and migration advice for OMENative's Instances.
- Serving runtimes: how a runtime declares the models it supports and the pods it runs.
- InferenceService: the fields of an InferenceService and its components.