Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions docs/kthena/docs/architecture/model-serving-controller.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,81 @@ The Custom Resource Definition (CRD) of `ModelServing` is primarily divided into
`Entry` Pod serves as the entry point to receive serving requests and distribute tasks, while the `Worker` Pod is responsible for executing the actual serving computations.
- Supports network topology aware scheduling: Enables the co-location scheduling of `Entry` Pods and `Worker` Pods within the same `Role` to the same **HyperNode**.

## Revisions and conservative rolling updates

The controller stores immutable **whole-ModelServing** snapshots in
`ControllerRevision` objects. A Role template hash is derived from the fields
applicable to that Role; it is not a separate Role revision history.

Both ModelServing and Role comparisons use the same hash-plus-semantic comparison.
Hashes are computed from normalized JSON, rather than the Go struct layout. When
hashes or serialized representations differ, decoded Kubernetes semantic equality
decides whether the effective workload changed. Hash drift alone does not trigger
a rollout, and matching hashes do not conceal different data.

| Input | Effect on rolling updates |
| --- | --- |
| Role names, entry templates, worker replica count | Included. The Role name set is immutable after creation; list order is normalized. Templates and worker counts may change. |
| Worker templates | Contents are included when workers exist. Template presence is also included when the applicable headless plugin uses it for entry DNS. Full templates remain in snapshots. |
| Pod-template labels, annotations and PodSpec | Included, except inputs overwritten by the controller as described below. |
| Ordered plugins, their type, config and effective Entry/Worker scope | Stored in snapshots and included in each applicable Role's comparison. Substantive plugin updates are rejected by admission. Historical comparison remains necessary for recovery and older persisted objects. |
| Top-level `schedulerName` | Stored in snapshots, but changing the effective scheduler is rejected by the update webhook. Omitting it defaults to `volcano` at admission; an explicit empty string uses Kubernetes `default-scheduler`. |
| ModelServing/Role `replicas` and rollout controls (`partition`, `maxSurge`, `maxUnavailable`, rollout strategy) | Not template identity; applied as current scaling/rollout controls. |
| `recoveryPolicy`, `revisionHistoryLimit`, ModelServing metadata/status, ServingGroup `restartGracePeriodSeconds`, `gangPolicy`, `networkTopology` | Not template identity. |
| Nested Pod `schedulerName`, controller-owned `GROUP_SIZE` and `WORKER_INDEX` environment entries | Normalized because the controller overwrites them. `ENTRY_ADDRESS` is normalized only where the applicable headless-service plugin overwrites it. |

Plugins are compared as ordered configuration inputs, not by executing hooks and
comparing their output. Kubernetes Pod admission defaults are not broadly removed
from templates: plugins may observe those inputs before Pod admission.

The update webhook rejects adding, removing or changing plugins, including their
order, config and scope, even at zero replicas. Equivalent representations remain
allowed: omitted and empty lists, effective BuiltIn/All defaults, reordered or
duplicated `scope.roles`, and JSON object key order or whitespace. JSON arrays
retain their order; omitted config is not equivalent to an empty object. Changing
plugins together with an image or replica count rejects the entire request. Use a
new ModelServing when a different plugin configuration is required. Existing Role
names may be reordered but cannot be added, removed or renamed, including Roles
with zero replicas.

Older Roles-only revisions do not contain scheduler/plugin snapshots. On the first
upgrade, the controller creates a durable compatibility baseline from historical
Roles and the current scheduler/plugins, retaining running Pods and their revision
identities. The original revision data is not rewritten; a metadata annotation
records its baseline. **Plugin changes persisted before the first migration cannot
be reconstructed**, because the old snapshot lacks the previous plugin
configuration. The immutable update webhook prevents new substantive plugin edits;
historical comparison still detects differences from the stored baseline in older
or admission-bypassed objects, including after restart. It does not repair a mixed
workload that existed before enforcement.

Missing, malformed, foreign-owned or unsupported history pauses affected
template-driven creation/deletion and is retried; it is never evidence that a
running replica should be replaced. A lost established baseline is not silently
rebuilt from current plugins. Explicit replica scale-down remains a scaling action.

Missing ordinals below `partition` are recreated from the historical snapshot,
including historical plugins and worker layout, while current operational replica
counts and rollout controls remain in effect. Revision cleanup preserves observed
ServingGroup and Role references and is deferred while partitions, rollout,
recovery or unresolved history remain, so missing protected ordinals can recover
after controller restart without introducing a new status/CRD field.

During a Role rollout that changes `workerReplicas`, old and new instances can
have different sizes. The shared PodGroup temporarily uses the smallest complete
applied Role template as its scheduling floor, with current replica counts and
explicit gang minima. Losing a Pod does not lower this historical size. Unknown
history leaves the existing PodGroup unchanged until it can be resolved. Once
all instances use the target layout, the PodGroup uses that layout as well.
This scheduling floor permits larger instances to bind partially; readiness and
rollout availability still require every member of each instance's own revision.
It does not express atomic scheduling of arbitrary heterogeneous resource sets.

In-place image updates use the same resolved revision identities and effective
Role comparisons, including scheduler and applicable plugin inputs. Completing
an update requires observing all expected members, so an informer delay cannot
advance the unit's revision while a historical worker is still missing.

## Example

Read the [examples](https://github.com/volcano-sh/kthena/tree/main/examples/model-serving/sample.yaml) to learn more.
Expand Down
Loading
Loading