Repository navigation
feat(model-serving): add preStop-based lossless upgrade for ModelServing - #1964
chenhuiluo wants to merge 2 commits into
Conversation
Rolling updates delete old-revision Pods immediately, SIGKILLing in-flight LLM requests (client-visible 502/connection resets). This adds a Pod-boundary drain so an old Pod finishes its in-flight requests before termination: - controller: unchanged delete path. A ModelServing annotation (modelserving.volcano.sh/drain-timeout, opt-in, >= 60s) enables the drain; at Pod creation the controller injects a preStop hook and the hook env vars (nothing is copied into user templates). - router: a Pod with a deletionTimestamp stays tracked in the store (in-flight metrics keep being scraped) and is excluded from all scheduling-candidate queries; the store is cleared on the object-delete event. The router never writes to Pods. - preStop hook: polls the engine metrics and exits as soon as running/waiting read zero; the effective wait is min(DRAIN_TIMEOUT, 70% * GRACE_SECONDS). A role-template annotation (drain-kv-buffer, <= 30s) optionally holds a KV buffer for PD prefill Pods so paired decodes finish pulling the last KV. - examples: aggregated and PD disaggregated ModelServing samples. The design proposal is tracked separately on the chenhui/lossless-upgrade-doc branch (docs/proposal/modelserving-lossless- upgrade.md). Co-Authored-By: Kscc <noreply@owtffssent.com> Signed-off-by: luochenhui1 <luochenhui1@kingsoft.com>
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
| sys.exit(0) | ||
| pattern = re.compile( | ||
| r"^(?:vllm|sglang):(?:num_requests_running|num_running_reqs|" | ||
| r"num_requests_waiting|num_queue_reqs)\s+(\S+)", re.M) |
There was a problem hiding this comment.
Regular expressions require that metric names be immediately followed by a space, but the names output by vllm/sglang are followed by “{”.
There was a problem hiding this comment.
Regular expressions require that metric names be immediately followed by a space, but the names output by vllm/sglang are followed by “{”.
Good catch. Fixed: the metric name now accepts an optional label block ((?:{[^}]*})?\s+(\S+)), so vllm:num_requests_running{engine="0",...} 0.0 parses; an empty match now keeps polling instead of exiting immediately. Verified against real vLLM output and covered by tests.
… hook
The drain hook's metric regex required a whitespace right after the metric
name, but vLLM and SGLang emit labeled metrics:
vllm:num_requests_running{engine="0",model_name="..."} 0.0
so every scrape returned zero matches. That did not just skip the drain:
an empty match made all(...) true, so the hook reported "in-flight
drained" and exited immediately -- the drain was silently disabled.
- Accept an optional label block after the metric name.
- Treat an empty match as "not drained": keep polling until the budget
expires and leave the residual to SIGTERM + grace, and log once when no
drain metrics are recognized.
Tests:
- TestDrainHookMetricPattern pins the labeled/unlabeled/suffixed cases
(pure Go, guards against regressing the pattern).
- TestDrainHookScriptBehavior runs the real hook against a mocked metrics
endpoint (zero -> exit, in-flight -> poll until timeout, unmatched ->
keep polling); it skips when python3 is unavailable.
Co-Authored-By: Kscc <noreply@owtffssent.com>
Signed-off-by: luochenhui1 <luochenhui1@kingsoft.com>
Rolling updates delete old-revision Pods immediately, SIGKILLing in-flight LLM requests (client-visible 502/connection resets). This adds a Pod-boundary drain so an old Pod finishes its in-flight requests before termination:
What type of PR is this?
/kind feature
What this PR does / why we need it:
Rolling updates of a ModelServing delete old-revision Pods via DeleteCollection with empty DeleteOptions. LLM inference requests — especially streaming — often run for tens of seconds to minutes, so the old Pod dies with its requests still generating and clients see broken connections.
This PR drains an old Pod at the Pod boundary before it terminates:
modelserving.volcano.sh/drain-timeout, e.g. "300s"). Invalid or too-small values are rejected with a warning event.paired decodes finish pulling the last KV.
Which issue(s) this PR fixes:
Fixes #1778
Bug evidence (required for bug-related PRs):
N/A
Special notes for your reviewer:
pods/patchpermission is added.RoleRollingUpdate, a prefill Pod draining while its decodes keep serving traffic may wait out the in-hook timeout (the decode counters stay above zero); this iscovered by the best-effort framing in the proposal's PD section — the default
ServingGroupRollingUpdatekeeps the judgment exact.Does this PR introduce a user-facing change?: