kubectl ome placement Preview Unreleased: coming in v1.3
Inspect the alpha multi-cluster placement of an InferenceService: its reported status, the registered workload clusters and its routing table.
kubectl ome placement shows where an InferenceService runs across workload clusters. It shows the placement that the control plane reports, the registered WorkloadClusters and the routing table in its TrafficMap. The command reads multi-cluster placement and routing, which are alpha, still in development and off by default, so what it reports can change between releases. Turn on routing shows the Helm values for the control-plane cluster. Run the command with the control plane as your current context. It reads what the control plane recorded and changes nothing. kubectl ome cluster shows the workload clusters themselves.
kubectl ome placement SUBCOMMAND INFERENCESERVICE [flags]| Subcommand | What it does |
|---|---|
status |
Shows the placement phase and the workload clusters chosen for the InferenceService, with their endpoints. |
explain |
Shows each registered workload cluster with its reported readiness, and sums up its routing settings. |
endpoint |
Shows its routing table: each cluster's endpoint, weight, drains and recorded probe. |
Each subcommand prints a FIELD and VALUE table, one row per fact. The table cuts long field names and values and ends them with ..., so Selector compat... is the Selector compatible row. The -o wide output adds rows, and -o json and -o yaml print the whole report, uncut, as Output formats describes. The problems that the CLI finds are in the Issue rows and in content.issues, and warnings is always empty. Required RBAC lists the permissions that each subcommand needs.
The CLI calls a workload cluster in the InferenceService's status.placement.candidates a home, and an entry of its TrafficMap a route home. These rules hold in all three tables:
- An address shows only its origin: its scheme, host and port. It reads
NotRecordedwhen it's missing, andInvalidEndpointwhen the URL is malformed or uses a scheme other thanhttporhttps. In JSON,pathPresentsays whether the URL had a path. - An unrecognized mode or phase reads
Unknown, and an unrecognized reason readsOtherReportedReason. - Homes, clusters and route homes are sorted by name. With more than four, the table shows a preview row such as Home preview
4/6; use -o wide or json, then the first four.-o wideshows the rest. - The tables end with Hint rows and a View row,
Bounded cells; use -o json for complete identities.
Freshness values say whether a row describes the current version of its object. The CLI compares the generation that the controller recorded with the object's metadata.generation:
| Freshness | Meaning |
|---|---|
Current |
The controller recorded the object's current generation. |
Stale |
The controller recorded an older generation: it hasn't caught up with the latest change. |
Unverifiable |
Nothing records a generation, so the CLI can't tell. |
Invalid |
The record is malformed or contradicts itself, as when a generation is newer than the object's. |
Unavailable |
The CLI couldn't read the object. |
Rows named inspection or inspect show how the CLI read a list, as <state> <kept>/<total>; truncated=<bool>, where the report keeps <kept> of the <total> items:
| State | Meaning |
|---|---|
Validated |
The CLI read the list. |
NotRecorded |
The object records no list. |
MalformedPayload, ConflictingDuplicates, BudgetExceeded or Unavailable |
The list is malformed, has entries that disagree, is too long or couldn't be read. |
status¶
kubectl ome placement status INFERENCESERVICE [flags]status shows the placement that the controller reports in the InferenceService's status.placement: its phase, and each home with its endpoint. It also shows the placement inputs that the InferenceService declares. OME places an InferenceService that sets spec.placement, which requires policy: ClusterAffinity and a mode; its optional clusterAffinity terms restrict which WorkloadClusters match, and without them every registered cluster matches. OME rejects the legacy ome.io/accelerator-requirements and ome.io/cluster-selector annotations on write and no longer places an InferenceService stored with one, but the reports still read such an object's annotations as its declared selectors. Placement reads NotRecorded for an InferenceService that OME hasn't tried to place, and on a cluster that isn't a control plane. When Placement reads Pending, explain shows the registered clusters and whether they're Ready.
Flags¶
| Flag | Default | Description |
|---|---|---|
-o, --output |
table |
Output format: table, wide, json or yaml |
Plus the standard kubeconfig flags, such as -n and --context.
Output fields¶
All three subcommands start with these rows:
| Row | What it shows |
|---|---|
| Mode | The placement mode, Single, All or Split, then (Declared) when spec.placement.mode sets it, or (Defaulted) for the default, Single. The CLI doesn't recognize the mode SplitByCapacity, so an InferenceService that declares it reads Unknown (Declared). |
| Placement | The phase that the controller reports, then (reported): Pending until a matching workload cluster is connected, Admitting until one admits the InferenceService, Placed once at least one has, and Failed when placement failed for good. NotRecorded when the status has no placement. |
| Freshness | Always Unverifiable: status.placement records no generation. |
| Input source | Structured when the InferenceService sets spec.placement, and LegacyAnnotations otherwise, even when it sets neither annotation. |
| Selectors | Whether the legacy annotations' requirements and cluster selector parse as label selectors: Valid, InvalidSelector, or NoRequirements when neither applies. BudgetExceeded when one is too long. Structured placement carries no selectors — the CLI doesn't read clusterAffinity — so an InferenceService that sets spec.placement always reads NoRequirements. |
| Reported cluster | The cluster the controller chose, from status.placement.cluster. Only mode Single chooses one, so the row is empty in the other modes. |
| Endpoint origin | The origin of status.placement.endpoint, which only mode Single records. |
| Service origin | The origin of the InferenceService's status.url. On a control plane, it's the chosen cluster's endpoint in mode Single, and empty in the other modes. |
| Home inspection | How the CLI read status.placement.candidates. |
| Provenance inspect | How the CLI read the AutoscalerPolicies and rollout groups recorded for each home. Unavailable when a home's record is malformed or too large. |
| Issue | A problem that the CLI found in what it read, as <group>: <code> (<count>), for example PlacementPhase: UnknownValue (1). One row per problem. |
The table has no row for a declared spec.placement.split: only the JSON and YAML reports record it, under inputs.split.
Then each home gets these rows:
| Row | What it shows |
|---|---|
| Reported home | The cluster, then its phase: Admitting until the cluster admits the InferenceService, then Admitted. NotRecorded when there's no phase. |
| Home origin | The origin of the home's endpoint. |
-o wide adds these rows to each home:
| Row | What it shows |
|---|---|
| Admitted replicas | The replicas the home's cluster admitted, as in 2 (Reported), or Unknown when the count is 0 or missing. NotApplicable when the Mode row reads Unknown: the CLI doesn't interpret the counts of a mode it doesn't recognize, such as SplitByCapacity. |
| Ready replicas | The home's ready replicas, in the same form. |
| Provenance | Whether the controller recorded the AutoscalerPolicies or rollout groups for the home, Reported or NotRecorded, then freshness unverifiable. |
| Policy inspection | How the CLI read the home's AutoscalerPolicies. |
| Group inspection | How the CLI read the home's active rollout groups. |
The table ends with the hint Placed does not prove replica floor met: a cluster has admitted the InferenceService, but it may not have the replicas it needs yet.
Examples¶
On a control plane, show the placement of chat in prod. It sets spec.placement with policy: ClusterAffinity, mode: All and a clusterAffinity term that requires the label pool=gpu, and OME placed it on the two workload clusters labeled pool=gpu:
kubectl ome placement status chat -n prodFIELD VALUE
Mode All (Declared)
Placement Placed (reported)
Freshness Unverifiable
Input source Structured
Selectors NoRequirements
Reported cluster
Endpoint origin NotRecorded
Service origin NotRecorded
Home inspection Validated 2/2; truncated=false
Provenance inspect Validated 2/2; truncated=false
Reported home worker-a (Admitted)
Home origin https://chat.worker-a.example.com
Reported home worker-b (Admitted)
Home origin https://chat.worker-b.example.com
Hint Placed does not prove replica floor met
View Bounded cells; use -o json for complete identitiesSelectors reads NoRequirements because chat declares its restriction in clusterAffinity, not in the legacy annotations.
With -o wide, each home also gets its replica counts and provenance. For worker-a, these rows follow its Home origin row:
kubectl ome placement status chat -n prod -o wideAdmitted replicas 2 (Reported)
Ready replicas 2 (Reported)
Provenance NotRecorded; freshness unverifiable
Policy inspection NotRecorded 0/0; truncated=false
Group inspection NotRecorded 0/0; truncated=falseProvenance reads NotRecorded because chat references no AutoscalerPolicy or RolloutPolicy.
On a single cluster, for a chat that omits spec.placement, the same command shows that nothing placed it:
kubectl ome placement status chat -n prodFIELD VALUE
Mode Single (Defaulted)
Placement NotRecorded (reported)
Freshness Unverifiable
Input source LegacyAnnotations
Selectors NoRequirements
Reported cluster
Endpoint origin NotRecorded
Service origin http://chat.prod.svc.cluster.local:8080
Home inspection NotRecorded 0/0; truncated=false
Provenance inspect NotRecorded 0/0; truncated=false
Hint Placed does not prove replica floor met
View Bounded cells; use -o json for complete identitiesThe only address is the origin of its status.url.
explain¶
kubectl ome placement explain INFERENCESERVICE [flags]explain lists each registered WorkloadCluster with its reported readiness, and sums up the routing settings in the InferenceService's spec.routing. For an InferenceService stored with the legacy annotations, it also checks their selectors against each cluster's labels. It doesn't evaluate clusterAffinity terms, so for an InferenceService that sets spec.placement, Selector compatible reads NotApplicable on every cluster. A match doesn't make a cluster eligible: explain doesn't say where the controller can place the InferenceService.
Flags¶
| Flag | Default | Description |
|---|---|---|
-o, --output |
table |
Output format: table, wide, json or yaml |
Plus the standard kubeconfig flags, such as -n and --context.
Output fields¶
The table starts with the same rows as status, without the homes. Then come the routing rows:
| Row | What it shows |
|---|---|
| Routing intent | Declared when the InferenceService sets spec.routing, and Absent otherwise. Invalid when a setting fails the routing checks, for example a capacity factor that isn't positive, and BudgetExceeded when there are too many entries. |
| Routing enablement | spec.routing.enabled: OptIn for true, OptOut for false and Inherited when unset. |
| Install routing | Always Gate/defaults unobserved: the CLI doesn't read whether routing is on for the installation or what its defaults are. |
When Routing intent isn't Absent, four rows follow:
| Row | What it shows |
|---|---|
| Capacity factors | Their source, then their count, as in Routing (1): Routing when spec.routing.capacityFactors sets them, and Inherited (0) when spec.routing leaves them out. |
| Routing probe | spec.routing.probe: Inherited when unset, Disabled when it sets disabled: true and Configured otherwise. |
| Capacity poll | spec.routing.capacity, with the same values. |
| Publisher options | spec.routing.publisher: Configured or Inherited, then the number of options it sets. |
With -o wide, a Configured probe adds rows that sum up its settings: Probe method, Probe statuses, Probe thresholds, Probe timing and All-failed policy. A Configured capacity poll adds Capacity method, Capacity window and Capacity timing. A setting that isn't set reads -.
The Fleet rows say how the CLI listed the WorkloadClusters:
| Row | What it shows |
|---|---|
| Fleet state | Observed when the CLI read every WorkloadCluster, Partial when it read only some and Unavailable when it read none. |
| Fleet reason | Why the state isn't Observed, such as Forbidden, Timeout, UnsupportedAPI when the WorkloadCluster API isn't installed, or PageBudgetExceeded above 64 WorkloadClusters. |
Fleet Returned, Fleet Admitted, Fleet Pages, Fleet Complete and Fleet Truncated describe what the CLI read.
Then each cluster gets these rows:
| Row | What it shows |
|---|---|
| Cluster | The WorkloadCluster's name. |
Selector compat... |
Selector compatible: True when the cluster's labels match all the selectors the legacy annotations declare, and False otherwise. A selector can also match the cluster's name, as metadata.name. NotApplicable without a selector — always, when the InferenceService sets spec.placement — and Unknown when a selector is invalid or the cluster's labels are malformed. |
| Reported WLC Ready | The status of the cluster's Ready condition, then its freshness, as in True (Current). Ready means that the control plane can reach the cluster, not that it has capacity. Unknown (Unverifiable) without a Ready condition, and Unknown (Invalid) when it's malformed or its copies disagree. |
| Condition inspect | How the CLI read the cluster's conditions: <kept> counts the Ready condition, the only one it reads, and <total> all of them. |
-o wide adds these rows to each cluster:
| Row | What it shows |
|---|---|
| Reported home | Whether the cluster is one of the InferenceService's homes: True or False. Unknown when there's no placement or the CLI couldn't read the homes. |
| Connection source | How the control plane connects to the cluster: KubeConfigReferenceNotResolved for a kubeconfig Secret, ClusterProfileReferenceNotResolved for a ClusterProfile and Unknown otherwise. The values end in NotResolved because the CLI doesn't read the Secret or the ClusterProfile. |
| Ready reason | The Ready condition's reason when it's ProbeFailedRetrying, ConnectionFailed or ClusterProfileUnsupported, and otherwise OtherReportedReason, even for a reachable cluster. NotRecorded without a Ready condition. |
The table ends with two hints, WLC Ready is reachability, not capacity and Partial fleet: no global eligibility verdict. The second prints even when the CLI read the whole fleet.
Examples¶
On the control plane from the status examples, with a third workload cluster, worker-c, labeled pool=cpu:
kubectl ome placement explain chat -n prodFIELD VALUE
Mode All (Declared)
Placement Placed (reported)
Freshness Unverifiable
Input source Structured
Selectors NoRequirements
Reported cluster
Endpoint origin NotRecorded
Service origin NotRecorded
Home inspection Validated 2/2; truncated=false
Provenance inspect Validated 2/2; truncated=false
Routing intent Absent
Routing enablement Inherited
Install routing Gate/defaults unobserved
Fleet state Observed
Fleet reason
Fleet Returned 3
Fleet Admitted 3
Fleet Pages 1
Fleet Complete true
Fleet Truncated false
Cluster worker-a
Selector compat... NotApplicable
Reported WLC Ready True (Current)
Condition inspect Validated 1/1; truncated=false
Cluster worker-b
Selector compat... NotApplicable
Reported WLC Ready True (Current)
Condition inspect Validated 1/1; truncated=false
Cluster worker-c
Selector compat... NotApplicable
Reported WLC Ready True (Current)
Condition inspect Validated 1/1; truncated=false
Hint WLC Ready is reachability, not capacity
Hint Partial fleet: no global eligibility verdict
View Bounded cells; use -o json for complete identitieschat restricts placement with a clusterAffinity term, which explain doesn't evaluate, so Selector compatible reads NotApplicable on all three clusters, including worker-c, where OME didn't place it. chat sets no spec.routing, so Routing intent is Absent and the table leaves out the four routing detail rows.
With -o wide, these rows follow the Condition inspect row of worker-a:
kubectl ome placement explain chat -n prod -o wideReported home True
Connection source KubeConfigReferenceNotResolved
Ready reason OtherReportedReasonworker-a connects through a kubeconfig Secret, and its Ready reason, Reachable, shows as OtherReportedReason. worker-c shows False for Reported home.
endpoint¶
kubectl ome placement endpoint INFERENCESERVICE [flags]endpoint shows the routing table that the controller computed for the InferenceService, from the TrafficMap with the same name and namespace. It shows the TrafficMap's conditions, whether the publisher acknowledged it, and each route home with its endpoint, weight and recorded probe. These are recorded values: the CLI doesn't probe the endpoints or read the gateway. OME writes the TrafficMap only when routing is on, as Turn on routing shows. Traffic map describes the fields.
Flags¶
| Flag | Default | Description |
|---|---|---|
-o, --output |
table |
Output format: table, wide, json or yaml |
Plus the standard kubeconfig flags, such as -n and --context.
Output fields¶
The table starts with the same rows as status, without the homes. The TrafficMap rows follow:
| Row | What it shows |
|---|---|
| TrafficMap state | Observed when the CLI read a TrafficMap that belongs to this InferenceService, and otherwise Unavailable. |
| TrafficMap reason | Why the state is Unavailable: NotFound when there's no TrafficMap or TrafficMap API, Forbidden, Timeout, Cancelled, Unreadable, or OwnershipUnbound when the TrafficMap doesn't belong to this InferenceService or one of them is being deleted. |
TrafficMap Returned, Admitted, Pages, Complete and Truncated then describe the read: 1, 1, 1, true and false when the report uses the TrafficMap. Then come the rows about its contents:
| Row | What it shows |
|---|---|
| Entry inspection | How the CLI read spec.entries. |
| Probe inspection | How the CLI read the entries' recorded probes. Unavailable when a probe is malformed or dated in the future. |
| Condition inspect | How the CLI read the TrafficMap's conditions: <kept> counts the ones that the rows below show, and <total> all of them. |
| Routing freshness | Whether the routing table describes the current InferenceService: the TrafficMap's spec.observedISVCGeneration compared with the InferenceService's metadata.generation. Staleness checks explains both generations. |
| Traffic override | The OverrideActive condition, as <status>: <reason> (<freshness>), for example True: OverridesApplied (Current). |
| Capacity fallback | The CapacityFallback condition, in the same form. |
| Routable | The Routable condition, as <status>: <reason>. |
| Routable freshness | The Routable condition's freshness and the reason for that freshness, as in Stale: StaleGeneration. A current condition reads Current:. |
| Published | The Published condition, as <status>: <reason> (<freshness>). |
| Publisher | Whether the publisher acknowledged the TrafficMap, from the Published condition or status.published: ReportedTrue or ReportedFalse, NoAcknowledgement before it records anything, Unknown when it records only a generation, and Invalid when the record is malformed, contradicts itself or has an unrecognized reason. |
| Publisher fresh | Whether the acknowledgement is for the TrafficMap's current generation, as a Freshness value. Unverifiable before the publisher records a generation, and Invalid when Publisher is Invalid. |
A condition that the TrafficMap doesn't have reads Unknown: NotRecorded (Unverifiable). Conditions lists each condition's reasons.
While a publish runs, Published reads False: OtherReportedReason and Publisher reads Invalid. Run the command again when the publish finishes. This is a known bug.
Then each route home gets these rows:
| Row | What it shows |
|---|---|
| Route home | The entry's cluster. |
| Endpoint origin | The origin of the entry's endpoint. |
| Final weight | The entry's weight, relative to the other entries, then healthy=true or healthy=false. Why a cluster's weight is zero lists why it can be 0. |
| Recorded probe | The result of the entry's last probe: Passing, Failing or Unknown. NotRecorded without a probe, and Invalid when the probe is malformed or dated in the future. |
| Route evidence | drains=<count>; gate=<gate>; fallback=<code>: how many drains hold the home at weight 0, whether its probe gates it, and its capacity fallback code. |
| Probe policy | The hash of the probe settings behind the entry's probe, or NotRecorded without a probe. |
-o wide adds these rows to each route home. The first three appear only when the entry records its capacity:
| Row | What it shows |
|---|---|
Capacity proven... |
Capacity provenance, the source of the allocated count: ControlPlane, Endpoint when the capacity polled from the home was lower, or UnknownOrDefaultedControlPlane when the entry names no source. |
| Allocated count | The replicas allocated to the home, as in 2 (Reported), or Unknown when it's 0. |
| Ready count | The home's ready replicas, in the same form. |
| Probe freshness | Unverifiable, since probe results record no generation, or Invalid when the probe is malformed or dated in the future. |
| Drain refs | The IDs of the drains that hold the home, separated by commas. Validated when the entry lists none. |
| Probe gate | Whether the probe gates the home: True (Reported) or False (Reported). NotRecorded without a probe. |
| Home cap fallback | A code for why the home falls back to its admitted replicas, such as Unreachable or StaleReport, or NotRecorded when there's none. The entry's capacity.fallbackReason has the full text, and A home falls back to its admitted replicas explains the common reasons. |
The table ends with two hints, Routing generation does not date placement and Recorded probes only; no CLI network probe. The first means that Routing freshness says nothing about how current the placement status is.
Examples¶
On the control plane from the status examples, with routing on, while worker-a is drained for maintenance:
kubectl ome placement endpoint chat -n prodFIELD VALUE
Mode All (Declared)
Placement Placed (reported)
Freshness Unverifiable
Input source Structured
Selectors NoRequirements
Reported cluster
Endpoint origin NotRecorded
Service origin NotRecorded
Home inspection Validated 2/2; truncated=false
Provenance inspect Validated 2/2; truncated=false
TrafficMap state Observed
TrafficMap reason
TrafficMap Retu... 1
TrafficMap Admi... 1
TrafficMap Pages 1
TrafficMap Comp... true
TrafficMap Trun... false
Entry inspection Validated 2/2; truncated=false
Probe inspection Validated 2/2; truncated=false
Condition inspect Validated 4/4; truncated=false
Routing freshness Current
Traffic override True: OverridesApplied (Current)
Capacity fallback False: CapacityPollingDisabled (Current)
Routable True: Routable
Routable freshness Current:
Published True: Published (Current)
Publisher ReportedTrue
Publisher fresh Current
Route home worker-a
Endpoint origin https://chat.worker-a.example.com
Final weight 0; healthy=true
Recorded probe NotRecorded
Route evidence drains=1; gate=NotRecorded; fallback=NotRecorded
Probe policy NotRecorded
Route home worker-b
Endpoint origin https://chat.worker-b.example.com
Final weight 1; healthy=true
Recorded probe NotRecorded
Route evidence drains=0; gate=NotRecorded; fallback=NotRecorded
Probe policy NotRecorded
Hint Routing generation does not date placement
Hint Recorded probes only; no CLI network probe
View Bounded cells; use -o json for complete identitiesOne drain holds worker-a at weight 0, so worker-b gets all the traffic and OverrideActive is True. The TrafficMap reflects the current InferenceService, and the publisher has acknowledged its current generation. The entries record no probe results, so Recorded probe and Probe policy read NotRecorded.
With -o wide, these rows follow the Probe policy row of worker-a:
kubectl ome placement endpoint chat -n prod -o wideCapacity proven... ControlPlane
Allocated count 2 (Reported)
Ready count 2 (Reported)
Probe freshness Unverifiable
Drain refs maintenance-a
Probe gate NotRecorded
Home cap fallback NotRecordedmaintenance-a is the ID of the drain, added with the alpha kubectl ome traffic drain command. Drain a workload cluster shows how to add and withdraw one.
Exit codes¶
| Code | Meaning | Returned by |
|---|---|---|
0 |
Success. The report printed, even when the WorkloadClusters or the TrafficMap were missing or unreadable. | All subcommands |
1 |
General error: an invalid name, namespace, flag or output format, or the CLI couldn't read the InferenceService, as in InferenceService read unavailable: NotFound. |
All subcommands |
Errors print to stderr as error: <message>.