Skip to content

[dynamo] Add NVIDIA Dynamo project for Forge#115

Draft
thameem-abbas wants to merge 11 commits into
openshift-psap:mainfrom
thameem-abbas:dynamo-support
Draft

[dynamo] Add NVIDIA Dynamo project for Forge#115
thameem-abbas wants to merge 11 commits into
openshift-psap:mainfrom
thameem-abbas:dynamo-support

Conversation

@thameem-abbas

Copy link
Copy Markdown
Member

Add projects/dynamo/ — a Forge project for deploying and benchmarking NVIDIA Dynamo inference workloads on OpenShift. Follows the llm_d project pattern but targets Dynamo's own operator stack (DynamoGraphDeployment CRD, etcd/NATS, EPP-based routing) instead of RHOAI/KServe.

Includes:

  • CI/CLI entrypoints with standard Forge phases
  • Config schema for Dynamo Helm deploy, deployment profiles (agg/disagg), workloads, and model cache
  • DynamoGraphDeployment manifest renderer (aggregated + disaggregated)
  • Toolbox commands: deploy_dynamo_platform, capture_dynamo_state, cleanup_dynamo_resources
  • Runtime config with RunSpec matrix expansion
  • Presets (smoke, disagg-smoke, full)
  • Unit tests for manifest rendering and config utilities

Reuses GPU operator, NFD, model cache, guidellm benchmarks, and DSL framework from existing Forge projects.

Add projects/dynamo/ — a Forge project for deploying and benchmarking
NVIDIA Dynamo inference workloads on OpenShift. Follows the llm_d
project pattern but targets Dynamo's own operator stack
(DynamoGraphDeployment CRD, etcd/NATS, EPP-based routing) instead of
RHOAI/KServe.

Includes:
- CI/CLI entrypoints with standard Forge phases
- Config schema for Dynamo Helm deploy, deployment profiles (agg/disagg),
  workloads, and model cache
- DynamoGraphDeployment manifest renderer (aggregated + disaggregated)
- Toolbox commands: deploy_dynamo_platform, capture_dynamo_state,
  cleanup_dynamo_resources
- Runtime config with RunSpec matrix expansion
- Presets (smoke, disagg-smoke, full)
- Unit tests for manifest rendering and config utilities

Reuses GPU operator, NFD, model cache, guidellm benchmarks, and DSL
framework from existing Forge projects.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 9, 2026
@openshift-ci

openshift-ci Bot commented Jul 9, 2026

Copy link
Copy Markdown

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2dcc556e-d5c6-4844-84fc-2667b76aeb34

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@thameem-abbas

thameem-abbas commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

Design: Dynamo Project for Forge

Why a Separate Project

Dynamo uses a fundamentally different deployment stack from llm_d:

llm_d dynamo
Operator RHOAI + KServe Dynamo operator (Helm chart)
CRD LLMInferenceService DynamoGraphDeployment
Infrastructure Gateway API (DSG) etcd + NATS (optional) + EPP
Routing KServe gateway EPP (Endpoint Picker Plugin) → Inference Gateway
Serving modes Single pool Aggregated + Disaggregated (prefill/decode split)

Shared code accessed via cross-project imports — no duplication.

Architecture

prepare phase (cluster-wide, run once)
  ├── verify_oc_access / verify_cluster_version
  ├── prepare_nfd()                          ← reuse projects.cluster
  ├── prepare_gpu_operator()                 ← reuse projects.gpu_operator  
  ├── deploy_dynamo_platform()               ← NEW: helm install (operator + etcd + optional NATS)
  └── wait_for_dynamo_crds()                 ← NEW

prepare phase (per RunSpec)
  ├── ensure_test_namespace()
  ├── prepare_model_cache()                  ← reuse projects.kserve
  └── verify_gpu_nodes()

test phase (per RunSpec)
  ├── render DynamoGraphDeployment manifest  ← NEW: agg or disagg topology
  ├── oc apply DynamoGraphDeployment         ← NEW
  ├── wait_for_dynamo_ready()                ← NEW: poll pods
  ├── discover_endpoint()                    ← NEW: from HTTPRoute/Service
  ├── run_smoke_request()                    ← reuse projects.guidellm
  └── run_guidellm_benchmark()               ← reuse projects.guidellm

Infrastructure Notes

  • etcd — required for service discovery and configuration in K8s deployments
  • NATS — optional. Default event plane transport for distributed (etcd/kubernetes) backends since Dynamo 1.1.0, but can be replaced with ZMQ (DYN_EVENT_PLANE=zmq) for single-node setups. Helm chart controls via global.nats.install
  • EPP — required for production routing. Implements Gateway API InferencePool extension for KV-cache-aware request routing

Reused Code

Component Source
GPU operator / NFD setup projects.gpu_operator, projects.cluster
Model cache PVC projects.kserve.toolbox.prepare_hf_model_cache
Smoke request + benchmarks projects.guidellm
DSL framework, config system projects.core
RunSpec matrix expansion Adapted from llm_d/runtime_config.py
CI/CLI entrypoints, Fournos Standard Forge patterns

New Components

  • render_graph_deployment.py — builds DynamoGraphDeployment manifests from config. Handles aggregated (single worker pool) and disaggregated (separate prefill/decode workers + EPP) topologies. Modeled after llm_d's render_inference_service.py.
  • deploy_dynamo_platform/ toolbox — Helm install/upgrade for the Dynamo platform chart (operator + etcd + optional NATS).
  • capture_dynamo_state/ toolbox — captures DynamoGraphDeployments, DynamoComponentDeployments, operator/worker/EPP logs, etcd/NATS status.
  • Deployment profilesaggregated and disaggregated with configurable backend framework (vllm/sglang/trtllm), replicas, tensor parallelism, and vllm args.
  • Dynamo-specific SCCsdynamo-frontend-scc (fsGroup RunAsAny) and dynamo-backend-scc (fsGroup RunAsAny + IPC_LOCK + SYS_RAWIO) for OpenShift.

Presets

Preset Model Mode Benchmark
smoke Qwen3-0.6B aggregated short (2min)
disagg-smoke Qwen3-0.6B disaggregated short
full Llama-3-70B, Qwen3-32B agg + disagg concurrent-1k-1k, multi-turn

Testing Status

  • ✅ Unit tests pass (5/5 manifest rendering tests)
  • ✅ Project auto-discovered by run_cli
  • ✅ DynamoGraphDeployment deployed on Janus (OCP 4.21, H200s) — worker 2/2 + EPP 1/1 running
  • ⏳ End-to-end HTTP smoke test blocked on Istio CA mismatch (cluster-level issue, not project code)

Next Steps

  1. Fix Istio CA on test cluster → complete end-to-end smoke test
  2. Add Caliper postprocess plugin for Dynamo-specific metrics
  3. Onboard to Fournos hub for CI automation (TBD if needed)

thameem-abbas and others added 8 commits July 9, 2026 09:08
Remove unused imports, fix import sorting, remove unused variable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6-step automated health check for the Dynamo gateway stack:
  1. Gateway proxy pod running (SCC, fd exhaustion, Istio CA)
  2. Gateway programmed (GatewayClass exists)
  3. HTTPRoute accepted (namespace restriction, pool not found)
  4. EPP running (SCC, startup probe vs worker readiness)
  5. ext_proc connectivity (502, Istio sidecar interception)
  6. Inference smoke test (routing headers, RoutingFailed)

Each step prints the fix when it fails. Stops at first failure.

Includes standalone.py (no Forge deps, just python3+oc) and
main.py (Forge DSL toolbox command).

Tested on psap-dra-ocp2 (all pass) and poseidon (correctly
identifies missing InferencePool after DGD cleanup).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Port key capabilities from benchflow's Dynamo integration:

Metrics:
- Add 20 Dynamo-specific PromQL queries to caliper queries.yaml
  (frontend TTFT/ITL/throughput, component KV cache, operator reconcile)
- Add metrics.yaml config selecting Dynamo + infra query keys

Benchmarks:
- Add run_aiperf_benchmark toolbox command (dataset download, aiperf
  CLI invocation, result parsing)
- Add 3 aiperf benchmark profiles: mooncake-trace-2k, mooncake-full,
  mooncake-toolagent
- Wire aiperf into test_phase via runtime.aiperf_benchmark_key

DGD renderer:
- Support router_mode: kv (standalone Frontend) vs direct (EPP)
- Support KVBM: kv_transfer_config arg + DYN_KVBM_CPU_CACHE_GB env
- Support DRA resources: use_dra_resources + configurable resource name
- Add deployment profiles: aggregated-kv-router, aggregated-kvbm,
  aggregated-dra
- Configurable hf_home, kv_block_size, frontend_cpu, router_args

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
render_graph_deployment.py: 342 → 214 lines (-37%)
- Unified _build_worker() for all topologies (agg/disagg/prefill/decode)
- Unified _build_routing_service() for EPP vs KV-router frontend
- _BuildCtx carries resolved config instead of 12 kwargs per function

check_gateway_health/standalone.py: deleted (-329 lines)
- Was 90% duplication of main.py without Forge DSL
- main.py is the canonical implementation using Forge toolbox pattern

Net: -457 lines removed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Matches the guidellm toolbox pattern:
- Renders and oc-applies a K8s Job (pip installs aiperf at runtime)
- Results written to forge-dynamo-results PVC (persists across runs)
- Polls job completion via @Retry
- Creates copy pod to extract profile_export_aiperf.json
- Parses summary metrics to aiperf_summary.json in artifact dir
- Cleans up job + copy pod, keeps PVC

Tested end-to-end on poseidon (H100): 100 Mooncake trace entries,
Qwen3-0.6B, full pipeline in 1m49s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wire up caliper-export for the Dynamo project:
- MLflow experiment: forge-dynamo
- Uses same psap-forge-mlflow-export vault as llm_d
- Uploads all artifact dir contents (aiperf results, guidellm
  results, DGD manifests, job logs) to MLflow tracking server

Caliper-export is already registered as a ci.py subcommand.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…/metrics

- Extract ALL aiperf output files from PVC (7 files: json, csv, jsonl,
  logs, inputs, server_metrics) — was only extracting summary JSON
- Build mlflow_run_metadata.json with params (model, dataset, endpoint),
  metrics (TTFT, ITL, throughput, latency with p95), and tags
- Auto-export to MLflow via Caliper when MLFLOW_TRACKING_URI is set
- Fix missing `import os`

Validated on poseidon: 8 artifacts + 8 params + 12 metrics uploaded to
MLflow experiment forge-dynamo. Matches benchflow artifact structure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Separate guidellm and aiperf into distinct config maps to prevent
schema cross-contamination:

  runtime:
    benchmark_tool: guidellm | aiperf | null
    benchmark_key: short | mooncake-trace-2k | ...

  workloads:
    guidellm_benchmarks:    # guidellm-only fields
      short: { args: ... }
    aiperf_benchmarks:      # aiperf-only fields
      mooncake-trace-2k: { dataset_url: ..., streaming: ... }

Dispatcher validates key exists in the matching map — wrong tool+key
combo fails immediately instead of silently ignoring unknown fields.

Also:
- Configurable PVC name in model_cache config + renderer
- aggregated-rr-4r-tp2 profile for Qwen3-32B reproduction
- Improved endpoint discovery (frontend → gateway → any:8000)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@openshift-ci

openshift-ci Bot commented Jul 15, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign harshith-umesh for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant