Deployment modes and OMENative

OME runs InferenceService components as Kubernetes Deployments or on OMENative, which creates, updates, repairs and moves each replica as one unit. Opt in to OMENative, see how OME picks a component's mode, and read what OMENative creates and reports.

An InferenceService component, the engine, decoder or router, runs in a deployment mode, which decides what creates and manages its pods: a Kubernetes Deployment, or OMENative. On OMENative, the component's replicas run as Instances. An Instance is one pod, or a leader and its workers, and OME creates, updates, repairs and moves it as one unit. You can put a whole InferenceService on OMENative, or one component.

The deployment modes

Mode What runs the pods How a component gets it
RawDeployment A Deployment named {isvc}-{component} spec.deploymentMode: RawDeployment or the component's ome.io/deploymentMode annotation. The default when nothing sets a mode.
OMENative (since v1.3) An InferenceReplica named {isvc}-{component}, which runs the replicas as Instances. See OMENative. spec.deploymentMode: OMENative or the component's annotation. The default for an engine or decoder with a leader or worker.
MultiNode (deprecated) A LeaderWorkerSet named lws-{isvc}-{component}. See MultiNode. The component's annotation, on the engine or the decoder only.

All three modes put a Service named {isvc}-{component} in front of the component. On RawDeployment, a change is a plain Deployment update, with no rollout group, canary or held revision. A RawDeployment or OMENative component also gets a PodDisruptionBudget, from its minAvailable or maxUnavailable or the ome-resources chart's default of maxUnavailable: 1: see Configure pod disruption budgets. Autoscaler policy explains when it gets an autoscaler.

An InferenceService with both an engine and a decoder is prefill-decode disaggregated, a shape rather than a mode: the engine runs prefill and the decoder runs decode. On OMENative, each gets its own InferenceReplica, and the two can roll out as one rollout group.

Opt in to OMENativeUnreleased: coming in v1.3

Opting in gives any component, even one with a single pod per replica, what OMENative does for its Instances:

  • By default, an update brings the new pod up and serving before the old one drains.
  • With the chart's settings, an update that fails because of the new revision itself, as with an image that can't be pulled, is retried with backoff. After the last attempt, OME holds the revision until a newer revision replaces it or you release it.
  • You can migrate an Instance off its node, and the replacement starts before the original is removed.
  • Status has a row for each Instance, with its phase, revisions and pod counts.

Rollout groups, canaries included, need OMENative declared in the InferenceService, with spec.deploymentMode or the component's own annotation, or the webhook rejects them with CoordinationRequiresOMENative or CanaryRequiresOMENative. A leader, a worker or the runtime's annotation doesn't count.

For the whole InferenceService

Set spec.deploymentMode: OMENative to run all the components on OMENative, the router included. The field takes OMENative or RawDeployment. A component's ome.io/deploymentMode annotation, from the InferenceService or the runtime, wins over it, so you can keep the router on a Deployment with ome.io/deploymentMode: RawDeployment in spec.router.annotations.

This InferenceService serves llama-3-2-3b-instruct from Pre-configured models and runtimes with one engine Instance:

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

Serve a model on OMENative applies an InferenceService like this one and checks its InferenceReplica, Instance and pod.

OMENative ignores a component's deploymentStrategy and paces rollouts with its lifecycle.updateStrategy instead: see OMENative update strategies. The webhook can warn about the unused field. For the engine under spec.deploymentMode: OMENative, the warning is:

Text
engine.deploymentStrategy is set but deploymentMode is "OMENative"; deploymentStrategy applies only to RawDeployment and is ignored here — use engine.lifecycle.updateStrategy.rollingUpdate for rollout pacing

For one component

To choose one component's mode, set its ome.io/deploymentMode annotation. This version annotates the engine and adds a router, which runs as a Deployment because nothing sets its mode:

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

With both an engine and a decoder, declare both OMENative or neither. The webhook compares their annotations in the InferenceService, or spec.deploymentMode, and rejects a mismatch. For an engine annotated OMENative next to a decoder with no mode set, the message is:

Text
InvalidDeploymentModeCombination: engine deploymentMode="OMENative" does not match decoder deploymentMode=""; when either is OMENative, both must use the same value

Change a component's mode

When a component's mode changes, OME creates the new workload and leaves the old Deployment, InferenceReplica or LeaderWorkerSet running. The old pods keep their GPUs, so the new pods can stay Pending until you delete the old workload. When the component moves to OMENative, its Service {isvc}-{component} switches to the new pods at once, so it serves nothing until they're ready.

While ingress creation is off, as it is by default, the external Service {isvc} fronts the router, or the engine when there's no router, unless the InferenceService is cluster-local. It sends requests to that component's old and new pods alike until you delete the old workload.

For example, after you move the engine of llama-3-2-3b-instruct from a Deployment to OMENative, delete the engine's Deployment:

Shell
kubectl delete deployment llama-3-2-3b-instruct-engine -n llama-demo
Output
deployment.apps "llama-3-2-3b-instruct-engine" deleted from llama-demo namespace

How OME resolves the mode

For a component the InferenceService declares, OME merges in the engineConfig, decoderConfig or routerConfig of its serving runtime, and the InferenceService's values win. It takes the component's mode from the first of these that sets one:

  1. The component's ome.io/deploymentMode annotation, in the InferenceService or the runtime: spec.engine.annotations or engineConfig.annotations for the engine. OME accepts RawDeployment, OMENative, MultiNode (deprecated) and VirtualDeployment, and skips any other value.
  2. The InferenceService's spec.deploymentMode.
  3. A leader or worker on the engine or the decoder, from the InferenceService or the runtime, which makes the component OMENative. The router has no leader or worker.
  4. RawDeployment.

So a runtime's annotation wins over spec.deploymentMode, unless the InferenceService sets the annotation on the component itself. In the InferenceService's own metadata.annotations, the annotation sets no component's mode: only VirtualDeployment works there, as Other mode values shows.

kubectl ome runtime effective shows the components' modes and the rules that set them, as in RawDeployment (Default).

When OME picks the runtime for you, it skips runtimes whose engineConfig or decoderConfig annotation declares a different mode from the InferenceService's for that component: see Runtime deployment-mode matching.

OMENativeUnreleased: coming in v1.3

For an OMENative component, OME writes an InferenceReplica named {isvc}-{component} from the InferenceService, and the InferenceReplica controller creates, updates and replaces the component's Instances. Make your changes in the InferenceService, and get or describe the InferenceReplica, short name irep, to see what's happening.

Instances

An OMENative component runs as Instances. An Instance is one pod, or, with a leader and a worker, a leader pod and worker.size worker pods that OME starts, stops and replaces together. The autoscaler, minReplicas and maxReplicas count Instances, not pods.

OME names the pods {isvc}-{component}-{index}-{runner}-{ordinal}, as in llama-3-2-3b-instruct-engine-0-default-0 for the engine above: see What an Instance is.

What OME creates

Besides the pods, OME creates these objects for an OMENative component:

Object Name What it's for
Service {isvc}-{component} Takes the component's traffic, as in the other modes
Headless Service {isvc}-{component}-headless Gives the pods DNS names, {pod}.{isvc}-{component}-headless, that resolve before the pods are ready, so a leader and its workers can find one another
Revision Services {isvc}-{component}-rev-{hash}, and the same with -headless One pair per revision, used to drain pods, connect components of the same revision and analyze canaries
ControllerRevision {isvc}-{component}-{hash} One per version of the pod template, which the InferenceReplica rolls Instances between
PodGroup {isvc}-{component}-{index} One per multi-pod Instance, for gang scheduling, when the scheduler-plugins PodGroup CRD is installed

OME also creates the PodDisruptionBudget and autoscaler from The deployment modes. It adds a PodMonitor that scrapes all the pods, workers included, when the Prometheus operator's PodMonitor CRD was installed before the manager started.

For a multi-pod Instance, the Service selects only leaders when the InferenceService itself sets worker on the component, and workers too when only the runtime does. To select only leaders, add leader: {} and worker: {} to the component in the InferenceService, which keep the runtime's settings. Without a router, the external Service {isvc} sends requests to the workers too.

Pod labels and environment

OME adds these labels to the pods, on top of those in the component's pod template. Where a key is in both, OME's value wins.

Label Value
ome.io/inferenceservice The InferenceService's name
component engine, decoder or router
ome.io/managed-by OMENative
ome.io/instance-index The Instance's index
ome.io/instance-incarnation The Instance's incarnation: 1 when new, and up by one for each restart and each update that recreates its pods
ome.io/runner default, leader or worker
ome.io/pod-ordinal The pod's ordinal within its runner
ome.io/revision-hash The hash of the pod's revision
ome.io/pairing-protocol The revision's prefill-decode pairing protocol, when it has one
scheduling.x-k8s.io/pod-group The Instance's PodGroup, on the pods of a multi-pod Instance

It also sets these environment variables:

Variable Value
OME_INFERENCESERVICE_NAME The InferenceService's name
OME_COMPONENT engine, decoder or router
OME_COMPONENT_REPLICAS The component's number of Instances
OME_INSTANCE_INDEX The Instance's index
OME_RUNNER default, leader or worker
OME_RUNNER_SIZE The number of pods in the pod's runner: 1 for default and leader, the worker size for worker
OME_RUNNER_INDEX The pod's position in its runner, from 0. Always 0 for default and leader, even when the pod's ordinal is 1.
OME_INSTANCE_SUBDOMAIN {isvc}-{component}-{index}. It isn't a DNS name, so reach the leader through OME_LEADER_ADDRESS.
OME_LEADER_ADDRESS Multi-pod Instances only. The leader's DNS name, {isvc}-{component}-{index}-leader-0.{isvc}-{component}-headless
OME_INSTANCE_POD_RANK Multi-pod Instances only. The pod's rank in the Instance: 0 for the leader, and k + 1 for worker k
OME_INSTANCE_POD_COUNT Multi-pod Instances only. The number of pods in the Instance
OME_<PEER>_ENDPOINT With spec.rollout.groups only. One per other component, as in OME_DECODER_ENDPOINT: {isvc}-{peer}.{namespace}.svc.cluster.local
OME_<PEER>_REVISION_ENDPOINT With spec.rollout.groups only, when the pod pairs with a revision of the peer. That revision's Service, {isvc}-{peer}-rev-{hash}.{namespace}.svc.cluster.local

OME sets them in all containers but the init containers, over any variable with the same name. The pods of a multi-pod Instance that request google.com/tpu also get TPU_WORKER_HOSTNAMES, TPU_WORKER_ID and the other TPU_* variables that multi-host JAX reads. A runtime written for a LeaderWorkerSet uses these instead of the LWS_* variables and labels: Move from LeaderWorkerSet to OMENative has the mapping.

OMENative pods also have the readiness gate ome.io/serving. OME sets it to False to take the pod out of rotation without stopping it, as it does during a drain or before an in-place update.

Observe OMENative in statusUnreleased: coming in v1.3

An OMENative component reports conditions, Instance counts and a row per Instance.

Conditions

The InferenceReplica's Ready condition says whether the component serves, so you can wait on it:

Shell
kubectl wait --for=condition=Ready inferencereplica/llama-3-2-3b-instruct-engine -n llama-demo --timeout=15m
Output
inferencereplica.ome.io/llama-3-2-3b-instruct-engine condition met

A rollout in flight makes the condition Unknown, so kubectl wait keeps waiting until it finishes. The reason is the first row in this table that applies:

Reason Status Message
InstanceFailed False At least one Instance has Phase=Failed
Staged True Staged at partition {partition}: {updated}/{total} Instances on {revision}, {held} held on the prior revision
RolloutInProgress Unknown Rolling out {revision} ({updated}/{total} Instances on target)
NoReplicas False InferenceReplica has no desired Instances
AllInstancesReady True {ready}/{total} Instances Ready
MinimumAvailable True {serving}/{total} Instances serving (min {minimum})
ReplicaCountMismatch False {serving}/{total} Instances serving, need {minimum} (no rollout in flight)

The minimum is the desired number of Instances less the maxUnavailable of the component's update strategy, or all of them when it isn't set. Staged means the rollout holds at a partition on purpose: see OMENative update strategies.

Other conditions flag problems without changing Ready:

Condition True when Reasons when True, False
RolloutStalled Mid-rollout, an Instance not yet on the new revision has failed, as with a new pod in CrashLoopBackOff. The old pods can keep EngineReady, DecoderReady or RouterReady True. InstancesFailing, Progressing
DrainOverdue An Instance being removed is past the deadline of the operation that drains it. DrainsOverdue, DrainsWithinDeadline
GangSchedulingUnavailable The scheduler-plugins PodGroup CRD isn't installed, so a multi-pod Instance's pods may schedule partially. PodGroupCRDNotInstalled, GangSchedulingAvailable
MigrationPolicyUnconfigured A migration request waits because ome.controller.lifecycle.audit isn't set. The chart sets it. MigrationCapacityUnconfigured, MigrationCapacityConfigured
InstanceReadyTimeoutUnconfigured Both the component's lifecycle.instanceReadyTimeout and the operator's are unset, so an Instance that stays unready waits for you instead of failing. The chart sets ome.controller.lifecycle.instanceReadyTimeout: 30m. InstanceReadyTimeoutUnconfigured, InstanceReadyTimeoutConfigured

OME also sets the InferenceService's EngineReady, DecoderReady or RouterReady condition for an OMENative component, with the same minimum:

Reason Status Message
Ready True All Instances are Ready
MinimumAvailable True {serving}/{total} Instances serving (min {minimum})
InsufficientAvailable False {serving}/{total} Instances serving, need {minimum}
NoReplicas False Component has no desired Instances

Lifecycle status

The InferenceService reports an OMENative component's Instance counts and revisions under status.components.{component}.lifecycle, a LifecycleStatus. Show the engine's:

Shell
kubectl get inferenceservice llama-3-2-3b-instruct -n llama-demo \
  -o 'custom-columns=REPLICAS:.status.components.engine.lifecycle.replicas,READY:.status.components.engine.lifecycle.readyReplicas,SERVING:.status.components.engine.lifecycle.servingReplicas,REVISION:.status.components.engine.lifecycle.currentRevision'
Output
REPLICAS   READY   SERVING   REVISION
1          1       1         llama-3-2-3b-instruct-engine-5d8f9c7a

readyReplicas, servingReplicas and availableReplicas count Instances as Readiness and availability defines them. The other fields:

Field What it reports
currentRevision The ControllerRevision that serves traffic
updateRevision The ControllerRevision being rolled out
replicas Instances, in any phase
updatedReplicas Instances on updateRevision
updatedReadyReplicas Instances on updateRevision whose pods are all ready
rolloutHold The gate that last held the rollout, why, and since when. Cleared once the rollout advances or completes.

rolloutHold names one of these gates:

  • Plan waits for the rollout group to settle on a valid plan.
  • Pairing keeps a compatible engine and decoder pair serving while the pairing protocol changes.
  • Ratio keeps the ratio between the components within tolerance.
  • Sequential waits for another component's turn or soak.
  • Budget keeps to the surge and unavailability budgets.
  • RetryBlock backs off before it retries a failed update.
  • Held stops updates to a revision whose retries ran out, until a newer revision replaces it or you release it.

Per-Instance status

OME keeps a row for each Instance on the InferenceReplica only, in status.instanceStatuses, or in status.instanceStatusColumns in the compact ColumnarV2 encoding. The chart turns that encoding on, and OME then uses it whenever it's smaller. Where Instance status lives explains the fields.

kubectl ome instance reads the rows in either encoding. For an InferenceService named chat in the namespace prod, with one ready engine Instance:

Shell
kubectl ome instance list chat -n prod
Output
COMP     IDX/INC   PHASE   PODS    REVS          AOF   EVIDENCE
engine   0/1       Ready   1/1/1   chat...ne-a   A--   OK

MultiNode (deprecated)

A MultiNode component runs as a LeaderWorkerSet named lws-{isvc}-{component}, with a leader pod and its worker pods per replica. It needs LeaderWorkerSet installed in the cluster, and gets no PodDisruptionBudget or autoscaler. Its Service {isvc}-{component} sends traffic to the leader pods of every LeaderWorkerSet in the namespace, so keep the component's LeaderWorkerSet the only one there.

Other mode values

Value Where What happens
VirtualDeployment The InferenceService's own metadata.annotations Legacy. OME creates or updates no workload, Service or ingress, marks the InferenceService Ready with the reason VirtualDeployment, and sets its URL to {isvc}.{namespace}.svc.{cluster domain}.
VirtualDeployment, or MultiNode on the router A component's annotation The manager logs Failed to reconcile component and retries, with no event. It creates or updates no workload for that component or any after it: engine, then decoder, then router.
Any other value, such as v1.2.2's MultiNodeRayVLLM A component's annotation OME skips it, and the next rule in How OME resolves the mode decides.
Anything but RawDeployment ome.controller.deploymentMode in the ome-resources chart The manager logs Failed to initialize deployment configuration and exits at startup.

Next steps