Triton on Ray: Ray Serve's gRPC proxy in front of Triton - #212
Merged
Conversation
A minimal Ray deployment for GPU inference that scales to more GPUs on demand: Ray Serve + ONNX Runtime behind the KServe v2 HTTP API, on KubeRay, beside the supersonic release and on its model repository claim. One Serve deployment, SonicServer, loads every onnxruntime_onnx model of the repository onto one GPU — a replica is a server, and a pod (one GPU) is a replica. Ray Serve adds replicas from in-flight requests; the Ray autoscaler adds a GPU pod for each replica with nowhere to run. No KEDA, no Prometheus in the loop. The TensorFlow models (deepmet, deeptau) are listed UNAVAILABLE with the reason rather than served. The protocol includes the binary tensor extension, so tritonclient.http works unchanged; gRPC (what CMSSW speaks) is not here yet. - docker/sonic-ray: rayproject/ray 2.52.0 cu128 + onnxruntime-gpu 1.26.0 (last CUDA 12 build, held by Renovate) + the sonic_ray package; built and published by ci.yml like the other aux images, build fails if the CUDA provider's libraries do not resolve on the image - apps/ray: kuberay-operator 1.7.0 (namespaced), the sonic-ray chart (RayService + metrics Service) and the AF release; the chart refuses values that cannot work (Serve replicas > GPU pods, pods with != 1 GPU, grace period <= Serve drain) - validate-manifests.sh renders charts sourced from this repository - tests/sonic_ray (server, CPU onnxruntime, models built in-test) and tests/manifests/test_ray.py (rendered chart, parity with supersonic) Not run on the cluster: GPU inference, autoscaling and the LoadBalancer allocation are for the first deploy to confirm. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…e_env Dmitry's call: serving some of the CMS models without maintaining a Ray image beats serving them all with one. So docker/sonic-ray is gone, along with its CI matrix entry, image badge and Renovate pin. The pods now run rayproject/ray:2.52.0-py312-cu128 as published (ray[all], CUDA 12.8, cuDNN 9) through the geddes Docker Hub proxy cache. The sonic_ray package lives in the chart's files/ and is rendered into a ConfigMap mounted on PYTHONPATH on head and workers, its hash annotated onto both pod templates so a code change rolls the cluster. onnxruntime-gpu==1.26.0 (the last CUDA 12 build) goes into the Serve application's runtime_env; Ray installs it once per pod when the first replica starts there. The chart refuses to render without the pin. The price is a minute or two per scale-up and PyPI reachability from the GPU nodes; the README says so. Tests follow the code to its new home and check the ConfigMap, the mount, the image tag derived from ray.version, and the pin. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Dmitry's call: start without the Triton protocol and add it later. That
removes the KServe v2 wire format (JSON + binary tensor extension) and the
config.pbtxt parser — about two thirds of the server.
What is left is models.py (find every <model>/<version>/model.onnx in the
repository, load it with ONNX Runtime, run it on named arrays, report the
tensors ORT itself declares) and serve_app.py (the Ray Serve deployment with
GET /healthz, GET /models, GET /models/{name}, POST /models/{name}). Inputs
are cast to the model's dtypes; a bad name, rank or shape is a 400 with the
reason; a directory without an ONNX model is listed under "skipped" with the
reason instead of 404'd.
No existing SONIC client speaks this yet — the README says so and names the
path back: KServe v2 HTTP first, then gRPC for CMSSW. The model allowlist
went with the rest; the chart, the tests and the docs follow.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Dmitry's spec: Triton as the actual server, gRPC through Ray's plumbing so Serve balances and autoscales it, and the least new code that gets there. Every worker pod now runs supersonic's Triton (same image, arguments, resources, model repository claim — the parity tests from the first attempt return) beside a Ray container. Serve's gRPC proxy is handed Triton's own generated servicer (tritonclient.grpc.service_pb2_grpc), so it accepts exactly Triton's RPCs; our code is a ~100-line forwarder that passes each unary RPC to the Triton on localhost and returns its answer, becomes ready only when Triton answers ServerReady, and polls ServerLive as its health check. ModelStreamInfer, the one bidirectional stream, is the only RPC not carried. Nothing of ours parses a request; the ONNX Runtime server and its tests are gone. One replica per pod: workers advertise a `triton` resource, replicas claim it. The serve Service exposes the gRPC proxy on Triton's conventional 8001, so CMSSW clients point at it as they point at supersonic's Envoy. Still no custom image: official Ray (CPU flavour — no Ray process touches a GPU) and official Triton. tritonclient 2.48.0 — the last release whose protobuf-4 stubs match the Ray image — is pip-installed --no-deps into an emptyDir on PYTHONPATH by an init container on every pod, because Serve's proxies import the servicer outside any runtime_env. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The server in every worker pod is NVIDIA's Triton Inference Server, configured as in the AF's supersonic release; SuperSONIC is a deployment of Triton, not an owner of it. Docs, comments and test docstrings said "SuperSONIC's Triton" — they now say what is meant. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Chart, forwarder, values comments and tests no longer describe this release in terms of the supersonic one. The parity tests that compared the Triton block against apps/sonic/supersonic/values.yaml are gone with them — the two releases are independent deployments of Triton, and one should not turn the other's CI red. What remains is the CMS model repository claim name, which is the existing PVC's. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Dmitry: no model manager uploads; the models CMSSW ships, as the existing CVMFS-backed SONIC deployment serves them. The AF values now mount the cluster's read-only cvmfs claim (HostToContainer, the repositories are autofs mounts on the node) and point Triton at four --model-repository directories inside CMSSW_17_0_0_pre2 with an explicit load list of ten models, the same image, arguments and resources that deployment uses. The chart grew one optional knob, modelRepository.mountPropagation. A new CMSSW release is a path change in the values. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…both
Dmitry asked why the values carried two min/max pairs. They were Serve's
(1–4 replicas) and the worker group's (0–4 pods), coupled one to one by the
triton resource and policed by a validation rule. Now `replicas: {min, max}`
is the only pair: it becomes Serve's autoscaling bounds and the group's
ceiling; the group starts with `min` pods and its own floor is always 0,
since a pod with a replica on it is never idle and Serve's minimum therefore
keeps pods alive. The cross-check rule and its test go away.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Sep 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Triton on Ray, with Ray Serve's gRPC proxy carrying Triton's protocol: every worker pod runs NVIDIA's Triton Inference Server, pointed at the models CMSSW ships on CVMFS (four
--model-repositorydirectories insideCMSSW_17_0_0_pre2, an explicit list of ten models, the cluster's read-onlycvmfsclaim), beside a Ray container; Serve's gRPC proxy is handed Triton's own generated servicer, so it accepts exactly Triton's RPCs, counts each one, load-balances it, and forwards it to the Triton in the chosen pod. Ray Serve sizes the deployment from those requests and the Ray autoscaler adds a GPU pod for each replica with nowhere to go. Triton does all of the inference.deepmet,deeptau_2018v2p5, six ParticleNet variants,particlenet_PT,unifiedparticletransformer_AK4_V01) straight from CVMFS — nothing uploaded, no model manager; a new CMSSW release is a path change in the values. Every backend,config.pbtxtsemantics, dynamic batching, repository index — it is Triton. CMSSW'sTritonClientandtritonclient.grpcpoint atsonic-ray-serve:8001like at any Triton endpoint. The one RPC not forwarded isModelStreamInfer(bidirectional; Serve's proxy carries unary and server-streaming only; CMSSW uses unaryModelInfer).apps/ray/sonic-ray/chart/files/sonic_ray/serve_app.py: a Serve deployment that, for every unary RPC ofGRPCInferenceService(derived from the generated servicer at import), forwards the protobuf message to Triton on localhost and returns Triton's answer. It blocks onServerReadybefore becoming ready and pollsServerLiveas its health check. Nothing in it parses a request.triton: 1resource; each replica claims one. That pins a replica next to its Triton, leaves a pod without a replica idle (reclaimable), and makes a replica without a pod the pending request that grows the group. One setting,replicas: {min: 1, max: 4}, bounds Serve and the worker group alike (target_ongoing_requests: 16; the group's own floor is 0 since Serve's minimum keeps pods alive); the chart refuses to render if a pod has other than one GPU or if the grace period would cut Triton's drain short.2.52.0-py312-cpu, no Ray process touches a GPU) via the Docker Hub proxy cache, official Triton (26.04-py3, the tag supersonic runs). Triton's Python stubs (tritonclient==2.48.0, the last release whose protobuf-4 stubs match the Ray image,--no-deps) are pip-installed into an emptyDir onPYTHONPATHby an init container on every pod — Serve's proxies import the servicer outside anyruntime_env, so replicas-only installation would not do.Socket '0.0.0.0:8000' already in use. Triton's gRPC keeps 8001 (what the forwarder dials). Both are chart values, and the chart refuses to render if they collide with Ray's ports or disagree withtriton.args.nv_*andray_serve_*metrics are exposed for the AF Prometheus underrelease="sonic-ray".Layout
apps/ray/operator/kuberay-operator1.7.0, namespaced tocmsapps/ray/sonic-ray/chart/apps/ray/sonic-ray/values.yamltests/sonic_ray/tests/manifests/test_ray.pycvmfsclaim with host-to-container propagation, one replica per pod, Serve ≤ worker bounds, stubs installed where the proxies run, chart refusalsAlso:
validate-manifests.shnowhelm templates charts sourced from this repository (the RayService kind has no kubeconform schema), README badges for the two new components,mypy.iniscope.Verified
pytest testsgreen;validate-manifests.shgreen; ruff/mypy/prettier clean.tritonclient's servicer against the image's grpcio 1.74 / protobuf 4.25, a CMSSW client end to end, autoscaling, and the LoadBalancer allocation. Nothing needs to be built first — merging is enough for Flux to reconcile it.History of this PR
Three earlier shapes were tried and dropped in review: a custom Ray image with ONNX Runtime and a KServe v2 HTTP server; the same on the stock image; then plain-JSON endpoints. Each served fewer models or fewer clients than Triton already does. This shape keeps Triton and reduces our part to the forwarder.
Follow-ups (deliberately out of scope)
ray_serve_*next to the SuperSONICnv_*dashboards.target_ongoing_requestsagainst real CMSSW load once it is measured.🤖 Generated with Claude Code