Skip to content
Merged
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
9e368cf
feat(global-router): add a crate for types the Global Router componen…
atchernych Oct 26, 2026
008d8f2
refactor(global-router): drop doc comments that restate their items
atchernych Oct 26, 2026
71f89c9
fix(global-router): check a registration's model and correct three do…
atchernych Oct 27, 2026
8e30f3c
fix(global-router): let a client follow any lease the service grants
atchernych Oct 27, 2026
8b6c23e
feat(global-router): name a pool's PoolRelay by every address of its pod
atchernych Oct 27, 2026
c7ee161
test(global-router): drop checks the JSON round trips already make
atchernych Oct 27, 2026
48637b7
feat(global-router): add the pool discovery contract and its stores
atchernych Oct 27, 2026
03eae14
fix(global-router): mark a stale MCS view incomplete and log empty ones
atchernych Oct 27, 2026
36125d9
docs(global-router): say that an MCS watch stream waits for its first…
atchernych Oct 27, 2026
c410173
perf(global-router): read EndpointSlice lists from the API server's c…
atchernych Oct 27, 2026
959e2d5
perf(global-router): list EndpointSlices unpaged, so the cache serves…
atchernych Oct 27, 2026
a962b76
docs(global-router): say that pool Services need a ServiceExport, not…
atchernych Oct 27, 2026
1a1372a
fix(global-router): address the standards review of the discovery stores
atchernych Oct 27, 2026
ce637fc
fix(global-router): address the second review of the discovery stores
atchernych Oct 27, 2026
130aaef
fix(global-router): address the third review of the discovery stores
atchernych Oct 27, 2026
0d55a3c
feat(http-server): copy the reusable HTTP server core from #17178
atchernych Oct 26, 2026
8ae83b1
feat(global-router): add the Global Discovery HTTP service
atchernych Oct 27, 2026
4a70569
feat(global-router): add a container image and manifest for the Globa…
atchernych Oct 27, 2026
a38eb3d
fix(global-router): accept hyphenated pool suffixes and state the man…
atchernych Oct 27, 2026
d0dc8fb
fix(global-router): bound the lease the Global Discovery Service grants
atchernych Oct 27, 2026
f8d3d54
refactor(global-router): pass registrations and listings straight thr…
atchernych Oct 27, 2026
6db71b3
fix(global-router): bound how long the Kubernetes client waits
atchernych Oct 27, 2026
c6efecb
Merge branch 'main' into atchernych/global-common
atchernych Oct 28, 2026
4bb3f61
Merge branch 'atchernych/global-common' into atchernych/global-discov…
atchernych Oct 28, 2026
5d30317
Merge branch 'atchernych/global-discovery-service' into atchernych/gl…
atchernych Oct 28, 2026
7919ee9
Merge branch 'main' into atchernych/global-common
atchernych Oct 29, 2026
4323f2a
refactor(global-router): rename the shared types as reviewers asked
atchernych Oct 29, 2026
5aadafa
refactor(global-router): build the discovery client on the shared types
atchernych Oct 29, 2026
0a33716
Merge branch 'atchernych/global-common' into atchernych/global-discov…
atchernych Oct 29, 2026
69d703e
refactor(global-router): follow the shared types' renames
atchernych Oct 29, 2026
8428c92
Merge branch 'atchernych/global-discovery-service' into atchernych/gl…
atchernych Oct 29, 2026
e3f8acd
refactor(global-router): follow the shared types' renames
atchernych Oct 29, 2026
547d554
Merge branch 'main' into atchernych/global-discovery-service
atchernych Oct 29, 2026
2a80836
docs(global-router): document the stores' watch streams, as the bots …
atchernych Oct 29, 2026
459c03f
fix(global-router): use Karmada's real Service name limit, 55
atchernych Oct 29, 2026
c09f797
Merge branch 'atchernych/global-discovery-service' into atchernych/gl…
atchernych Oct 29, 2026
f5c0508
docs(global-router): word the Karmada name check for any exported Ser…
atchernych Oct 29, 2026
044d6b5
Merge branch 'atchernych/global-discovery-service' into atchernych/gl…
atchernych Oct 29, 2026
fb5ba6c
refactor(global-router): split --store into --pool-discovery and --re…
atchernych Oct 29, 2026
d829bc7
refactor(global-router): tighten the new flags after an adversarial r…
atchernych Oct 29, 2026
06b4f63
docs(global-router): call the discovery implementations backends, not…
atchernych Oct 29, 2026
4bd3635
Merge branch 'atchernych/global-discovery-service' into atchernych/gl…
atchernych Oct 29, 2026
952f1ff
ci(global-router): cover the Global Discovery Service image and manifest
atchernych Oct 29, 2026
c5760a3
fix(global-router): check the Global Discovery Service flags, and ans…
atchernych Oct 29, 2026
8e0fc02
fix(global-router): run the example manifest under restricted Pod Sec…
atchernych Oct 29, 2026
fad5749
fix(global-router): make every flag mistake a usage error, and warn w…
atchernych Oct 29, 2026
822175e
docs(global-router): say what the watch stream waits for
atchernych Oct 29, 2026
260d5e6
feat(global-router): reserve POST /routers until router registration …
atchernych Oct 29, 2026
39a56e0
docs(global-router): say how callers treat a partial list
atchernych Oct 29, 2026
bc1eeed
Merge branch 'atchernych/global-discovery-service' into atchernych/gl…
atchernych Oct 30, 2026
0917613
Merge branch 'main' into atchernych/global-discovery-service
atchernych Oct 30, 2026
00bec2a
Merge branch 'atchernych/global-discovery-service' into atchernych/gl…
atchernych Oct 30, 2026
7332626
fix(global-router): list every Ready PoolRelay pod of a pool
atchernych Oct 30, 2026
b844440
Merge branch 'atchernych/global-discovery-service' into atchernych/gl…
atchernych Oct 30, 2026
98c2a63
fix(global-router): cap request bodies at 64 KiB
atchernych Oct 30, 2026
88c07e4
Merge branch 'main' into atchernych/global-discovery-http
atchernych Nov 2, 2026
7f61b37
docs(global-router): give the manifest's path from the repository root
atchernych Nov 2, 2026
c0264e1
Merge branch 'main' into atchernych/global-discovery-http
atchernych Nov 2, 2026
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
10 changes: 10 additions & 0 deletions .github/codeowners/areas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ areas:
- lib/llm/src/grpc/protos/
- lib/llm/src/grpc/service.rs
- lib/llm/src/grpc/service/
- lib/http-server/
- lib/llm/src/http.rs
- lib/llm/src/http/
- lib/llm/src/http/service.rs
Expand Down Expand Up @@ -73,6 +74,7 @@ areas:
path_globs:
- lib/global-view-broadcaster/
- components/src/dynamo/pool_relay/
- container/global-discovery-service.yaml
- lib/global-view-receiver/
- docs/fern/pages/developer-guide/knowledge-base/modular-components/router/multi-dc-kv-routing.md
- lib/global-*/**
Expand Down Expand Up @@ -1089,6 +1091,10 @@ shared:
owners:
- frontend
- observability
- glob: lib/http-server/src/metrics.rs
owners:
- frontend
- observability
- glob: lib/llm/src/kv_router/shared_cache.rs
owners:
- router
Expand Down Expand Up @@ -1288,6 +1294,10 @@ shared:
owners:
- operator
- backend-vllm
- glob: container/Dockerfile.global-discovery-service
owners:
- global-router
- ops
- glob: deploy/operator/Dockerfile
owners:
- operator
Expand Down
4 changes: 4 additions & 0 deletions .github/filters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ ignore:
- '.github/allurerc.mjs'
- '.github/actionlint.yaml'
- 'container/Dockerfile.docs'
# The Global Discovery Service's image and example manifest have no CI
# image-build or deploy job yet.
- 'container/Dockerfile.global-discovery-service'
- 'container/global-discovery-service.yaml'
- 'container/run.sh'
- 'container/use-sccache.sh'
- 'container/dev/**'
Expand Down
4 changes: 4 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
* @ai-dynamo/dynamo-runtime-codeowners

# === frontend (@ai-dynamo/dynamo-frontend-codeowners) ===
/lib/http-server/ @ai-dynamo/dynamo-frontend-codeowners
/examples/chat_templates/ @ai-dynamo/dynamo-frontend-codeowners
/components/src/dynamo/frontend/ @ai-dynamo/dynamo-frontend-codeowners
/components/src/dynamo/frontend/tests/ @ai-dynamo/dynamo-frontend-codeowners
Expand Down Expand Up @@ -94,6 +95,7 @@
/lib/global-view-receiver/ @ai-dynamo/dynamo-global-router-codeowners
/lib/global-view-broadcaster/ @ai-dynamo/dynamo-global-router-codeowners
/components/src/dynamo/pool_relay/ @ai-dynamo/dynamo-global-router-codeowners
/container/global-discovery-service.yaml @ai-dynamo/dynamo-global-router-codeowners

# === backend-vllm (@ai-dynamo/dynamo-backend-vllm-codeowners) ===
/lib/sidecar/vllm/ @ai-dynamo/dynamo-backend-vllm-codeowners
Expand Down Expand Up @@ -663,6 +665,7 @@ requirements.txt
/examples/diffusers/Dockerfile @ai-dynamo/dynamo-diffusion-codeowners @ai-dynamo/dynamo-ops-codeowners
/lib/kvbm-logical/src/metrics/ @ai-dynamo/dynamo-kv-memory-codeowners @ai-dynamo/dynamo-observability-codeowners
/tests/serve/test_vllm_omni.py @ai-dynamo/dynamo-runtime-codeowners @ai-dynamo/dynamo-backend-vllm-codeowners
/lib/http-server/src/metrics.rs @ai-dynamo/dynamo-frontend-codeowners @ai-dynamo/dynamo-observability-codeowners
/tests/test_kustomize_matrix.py @ai-dynamo/dynamo-runtime-codeowners @ai-dynamo/dynamo-ops-codeowners
/.github/workflows/fern-docs.yml @ai-dynamo/dynamo-ops-codeowners @ai-dynamo/dynamo-docs-codeowners
/examples/deployments/**/vllm/** @ai-dynamo/dynamo-operator-codeowners @ai-dynamo/dynamo-backend-vllm-codeowners
Expand Down Expand Up @@ -758,6 +761,7 @@ requirements.txt
/components/src/dynamo/vllm/multimodal_utils/ @ai-dynamo/dynamo-backend-vllm-codeowners @ai-dynamo/dynamo-multimodal-codeowners
/deploy/inference-gateway/ext-proc/Dockerfile @ai-dynamo/dynamo-epp-codeowners @ai-dynamo/dynamo-router-codeowners @ai-dynamo/dynamo-ops-codeowners
/components/src/dynamo/trtllm/encode_helper.py @ai-dynamo/dynamo-backend-trtllm-codeowners @ai-dynamo/dynamo-multimodal-codeowners
/container/Dockerfile.global-discovery-service @ai-dynamo/dynamo-global-router-codeowners @ai-dynamo/dynamo-ops-codeowners
/container/templates/sglang_runtime.Dockerfile @ai-dynamo/dynamo-ops-codeowners @ai-dynamo/dynamo-backend-sglang-codeowners
/container/templates/trtllm_runtime.Dockerfile @ai-dynamo/dynamo-ops-codeowners @ai-dynamo/dynamo-backend-trtllm-codeowners
/tests/router/test_router_e2e_with_vllm_xpu.py @ai-dynamo/dynamo-router-codeowners @ai-dynamo/dynamo-xpu-codeowners @ai-dynamo/dynamo-backend-vllm-codeowners
Expand Down
43 changes: 43 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ members = [
"lib/kv-hashing",
"lib/mocker",
"lib/kv-router",
"lib/http-server",
"lib/global-common",
"lib/global-discovery-client",
"lib/global-discovery-service",
Expand Down Expand Up @@ -74,6 +75,7 @@ dynamo-data-gen = { path = "lib/data-gen", version = "1.7.0" }
dynamo-kv-hashing = { path = "lib/kv-hashing", version = "1.7.0" }
dynamo-global-common = { path = "lib/global-common", version = "1.7.0" }
dynamo-global-router = { path = "lib/global-router", version = "1.7.0" }
dynamo-http-server = { path = "lib/http-server", version = "1.7.0" }
dynamo-memory = { path = "lib/memory", version = "1.7.0" }
dynamo-mocker = { path = "lib/mocker", version = "1.7.0" }
dynamo-kv-router = { path = "lib/kv-router", version = "1.7.0" }
Expand Down
26 changes: 26 additions & 0 deletions container/Dockerfile.global-discovery-service
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# syntax=docker/dockerfile:1.10.0
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# CPU-only image for the Global Discovery Service, which serves pool discovery
# for the Global Router over HTTP; see global-discovery-service.yaml. Build it
# from the repository root:
# docker build -f container/Dockerfile.global-discovery-service -t global-discovery-service .
FROM rust:1.96.1-trixie AS builder
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
build-essential cmake pkg-config protobuf-compiler libclang-dev libssl-dev curl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /workspace
COPY . .
RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,target=/usr/local/cargo/git,sharing=locked \
cargo build --locked --release -p dynamo-global-discovery-service --bin global-discovery-service

FROM debian:trixie-slim AS global-discovery-service
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
ca-certificates libstdc++6 \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /workspace/target/release/global-discovery-service /usr/local/bin/global-discovery-service
USER 10001:0
EXPOSE 8080
ENTRYPOINT ["/usr/local/bin/global-discovery-service"]
120 changes: 120 additions & 0 deletions container/global-discovery-service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Global Discovery Service with pools from Karmada, applied directly to the hub
# cluster (not through Karmada). Replace IMAGE with the pushed image, for example:
# sed "s#IMAGE#<registry>/global-discovery-service:<tag>#" global-discovery-service.yaml | kubectl apply -f -
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
# It needs only read access to EndpointSlices. It lists the pools whose
# `<DGD name>-pool-relay` Services Karmada imports, and the replicas of the
# Global Router Service named by the --router-* arguments: set those to the
# router's namespace, its Service and the port PoolRelays connect to.
#
# It reads the imported slices with the pod's credentials, so this cluster
# needs a ServiceImport for each pool's PoolRelay Service and for the router
# Service. To read the Karmada control plane instead, which holds every
# exported Service's slices, mount its kubeconfig from a Secret, point
# KUBECONFIG at it, and grant the same read access there.
apiVersion: v1
kind: Namespace
metadata:
name: global-discovery
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: global-discovery-service
namespace: global-discovery
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: global-discovery-service
rules:
- apiGroups: ["discovery.k8s.io"]
resources: ["endpointslices"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: global-discovery-service
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: global-discovery-service
subjects:
- kind: ServiceAccount
name: global-discovery-service
namespace: global-discovery
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: global-discovery-service
namespace: global-discovery
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: global-discovery-service
template:
metadata:
labels:
app.kubernetes.io/name: global-discovery-service
spec:
serviceAccountName: global-discovery-service
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: global-discovery
image: IMAGE
args:
# Takes IPv4 and IPv6. On nodes with IPv6 turned off, use
# 0.0.0.0:8080.
- --listen=[::]:8080
- --pool-discovery=karmada
- --router-namespace=global-discovery
- --router-service=global-router
- --router-port-name=http
env:
- name: DYN_LOG
value: info
ports:
- name: http
containerPort: 8080
readinessProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 5
lifecycle:
# Keep serving while the Pod leaves the Service's endpoints.
preStop:
exec:
command: ["sleep", "5"]
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 256Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
---
apiVersion: v1
kind: Service
metadata:
name: global-discovery-service
namespace: global-discovery
spec:
selector:
app.kubernetes.io/name: global-discovery-service
ports:
- name: http
port: 8080
targetPort: http
10 changes: 6 additions & 4 deletions lib/global-common/src/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,12 @@ pub struct PoolListing {
pub frontends: Vec<Url>,
#[serde(default)]
pub capacity: Vec<Capacity>,
/// The only PoolRelay allowed to stream this pool's state, by each
/// identity it can connect with. On a trusted network these are its pod's
/// addresses, as [`address_identity`] writes them: two on a dual-stack
/// cluster. Empty when the service does not know the PoolRelay.
/// The PoolRelays allowed to stream this pool's state, by each identity
/// they can connect with. A pool runs active-active PoolRelay replicas,
/// each reporting to every router, and routers dedupe the overlapping
/// reports. On a trusted network these are the addresses of every Ready
/// replica's pod, as [`address_identity`] writes them: two per pod on a
/// dual-stack cluster. Empty when the service does not know the PoolRelays.
#[serde(default)]
pub relay_identities: Vec<String>,
}
Expand Down
30 changes: 30 additions & 0 deletions lib/global-discovery-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,33 @@ edition.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true

[[bin]]
name = "global-discovery-service"
path = "src/main.rs"

[dependencies]
dynamo-global-common = { workspace = true }
dynamo-http-server = { workspace = true }
dynamo-runtime = { workspace = true }

anyhow = { workspace = true }
async-stream = { workspace = true }
async-trait = { workspace = true }
axum = { workspace = true }
clap = { version = "4.5.49", features = ["derive"] }
futures = { workspace = true }
k8s-openapi = { workspace = true }
kube = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
tokio-util = { workspace = true }
tracing = { workspace = true }
url = { workspace = true }

[dev-dependencies]
dynamo-global-discovery-client = { path = "../global-discovery-client" }

reqwest = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true, features = ["test-util"] }
9 changes: 9 additions & 0 deletions lib/global-discovery-service/src/backends.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

//! Implementations of [`crate::pool_discovery`], one module per
//! backend.

pub mod kubernetes;
pub mod memory;
mod stream;
Loading
Loading