-
Notifications
You must be signed in to change notification settings - Fork 41
feat(llm): add multi-replica worker routing #585
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,17 +4,26 @@ This repository contains the Helm chart for deploying NVCF ingress routes via th | |
|
|
||
| ## Overview | ||
|
|
||
| The chart deploys `HTTPRoute`, `TCPRoute`, and `ReferenceGrant` resources that attach to an existing Gateway provisioned separately by the cluster operator (e.g. Envoy Gateway, Istio, Traefik, Kong). It also includes optional `PodMonitor` resources for scraping Envoy Gateway proxy metrics with Prometheus. | ||
|
|
||
| The chart deploys routing configuration only. It does not include any container images. Backend services referenced by the routes (`api`, `nvct-api`, `api-keys`, `invocation`, `llm-api-gateway`, `vanity-gateway`, `reval`, `sis`, `grpc`, `nats`) must already be deployed separately. | ||
| The chart deploys `HTTPRoute`, `GRPCRoute`, `TCPRoute`, `UDPRoute`, and | ||
| `ReferenceGrant` resources that attach to an existing Gateway provisioned | ||
| separately by the cluster operator, such as Envoy Gateway, Istio, Traefik, or | ||
| Kong. It also includes optional `PodMonitor` resources for scraping Envoy | ||
| Gateway proxy metrics with Prometheus. | ||
|
|
||
| The chart deploys routing configuration only. It does not include any | ||
| container images. Backend services referenced by the routes (`api`, | ||
| `nvct-api`, `api-keys`, `invocation`, `llm-api-gateway`, | ||
| `llm-request-router-backend-router`, `vanity-gateway`, `reval`, `sis`, `grpc`, | ||
| `nats`) must already be deployed separately. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Kubernetes cluster | ||
| - Helm 3.x | ||
| - `kubectl` | ||
| - A Gateway API compatible controller installed in the cluster | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Hyphenate the compound adjective. Change 🧰 Tools🪛 LanguageTool[grammar] ~24-~24: Use a hyphen to join words. (QB_NEW_EN_HYPHEN) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
| - An existing `Gateway` resource with an HTTP listener (and TCP listeners if the gRPC or NATS routes are enabled) | ||
| - Existing `Gateway` resources with the listeners required by each enabled route | ||
| - A Gateway controller with `UDPRoute` support when LLM worker routing is enabled | ||
| - The backend services that the routes target, deployed in their respective namespaces | ||
|
|
||
| ## Getting Started | ||
|
|
@@ -57,6 +66,8 @@ Important settings to review before deployment: | |
| - `nvcfGatewayRoutes.gateways.shared.*` for the HTTP Gateway name, namespace, and listener | ||
| - `nvcfGatewayRoutes.gateways.grpc.*` for the TCP Gateway name, namespace, and listener | ||
| - `nvcfGatewayRoutes.gateways.nats.*` for the NATS TCP Gateway name, namespace, and listener | ||
| - `nvcfGatewayRoutes.gateways.llmGrpc.*` for the LLM worker gRPC TCP listener | ||
| - `nvcfGatewayRoutes.gateways.llmQuic.*` for the LLM reverse-tunnel UDP listener | ||
| - `nvcfGatewayRoutes.routes.<route>.enabled` to toggle individual routes | ||
| - `nvcfGatewayRoutes.routes.nvcfApi.grpc.enabled` and | ||
| `nvcfGatewayRoutes.routes.nvctApi.grpc.enabled` to expose API gRPC routes | ||
|
|
@@ -87,6 +98,7 @@ Enabled `HTTPRoute` entries must not share a resolved hostname because each `HTT | |
| | `grpc` | TCPRoute | Not rendered | `grpc.nvcf:10081` | | ||
| | `grpcWorker` | TCPRoute (disabled by default) | Not rendered | `grpc.nvcf:10086` | | ||
| | `nats` | TCPRoute (disabled by default) | Not rendered | `nats.nats-system:4222` | | ||
| | `llmWorker` | TCPRoute and UDPRoute (disabled by default) | Not rendered | `llm-request-router-backend-router.<backend namespace>:50071/TCP,50072/UDP` | | ||
|
|
||
| Cross-namespace routing is supported via `ReferenceGrant` resources rendered into each backend namespace. | ||
|
|
||
|
|
@@ -97,3 +109,9 @@ Cross-namespace routing is supported via `ReferenceGrant` resources rendered int | |
| - The `grpc` TCPRoute does not enforce HTTP hostname matching at the Gateway layer. Configure DNS or TCP load balancer routing outside this chart. | ||
| - The `grpcWorker` TCPRoute is beta support for split or multi-cluster gRPC worker callbacks. It carries HTTP/1 CONNECT callback traffic only. Enable it only when the control-plane grpc-proxy runs one replica with HPA disabled. Multi-replica grpc-proxy requires pod-specific callback routing and is not supported by this shared TCPRoute. | ||
| - Enabling the `nats` route requires a reachable TCP listener for NATS on the referenced Gateway. The HTTP Gateway address does not imply NATS reachability unless that same Gateway also has the NATS TCP listener configured. | ||
| - The `llmWorker` routes target Stargate's authority/SNI-aware backend router. | ||
| Set `nvcfGatewayRoutes.routes.llmWorker.backend.namespace` to the effective | ||
| namespace of the `llm-request-router` release. The gateway chart cannot | ||
| derive the namespace of a separate Helm release. | ||
| Keep the TCP and UDP Gateways separate when the infrastructure requires | ||
| separate load balancers for each protocol. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| {{- if and .Values.nvcfGatewayRoutes.enabled .Values.nvcfGatewayRoutes.routes.llmWorker.enabled }} | ||
| {{- $backendNamespace := include "nvcf-gateway.llmWorkerBackendNamespace" . }} | ||
| apiVersion: gateway.networking.k8s.io/v1beta1 | ||
| kind: ReferenceGrant | ||
| metadata: | ||
| name: allow-llm-worker-routes | ||
| namespace: {{ $backendNamespace }} | ||
| labels: | ||
| {{- include "nvcf-gateway.labels" . | nindent 4 }} | ||
| spec: | ||
| from: | ||
| - group: gateway.networking.k8s.io | ||
| kind: TCPRoute | ||
| namespace: {{ .Values.nvcfGatewayRoutes.gateways.llmGrpc.namespace }} | ||
| - group: gateway.networking.k8s.io | ||
| kind: UDPRoute | ||
| namespace: {{ .Values.nvcfGatewayRoutes.gateways.llmQuic.namespace }} | ||
| to: | ||
| - group: "" | ||
| kind: Service | ||
| name: {{ .Values.nvcfGatewayRoutes.routes.llmWorker.backend.name }} | ||
| {{- end }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| {{- if and .Values.nvcfGatewayRoutes.enabled .Values.nvcfGatewayRoutes.routes.llmWorker.enabled }} | ||
| {{- $backendNamespace := include "nvcf-gateway.llmWorkerBackendNamespace" . }} | ||
| apiVersion: gateway.networking.k8s.io/v1alpha2 | ||
| kind: TCPRoute | ||
| metadata: | ||
| name: {{ .Values.nvcfGatewayRoutes.routes.llmWorker.name }}-grpc | ||
| namespace: {{ .Values.nvcfGatewayRoutes.gateways.llmGrpc.namespace }} | ||
| labels: | ||
| {{- include "nvcf-gateway.labels" . | nindent 4 }} | ||
| app.kubernetes.io/component: llm-worker-grpc-route | ||
| {{- with .Values.nvcfGatewayRoutes.routes.llmWorker.routeAnnotations }} | ||
| annotations: | ||
| {{- toYaml . | nindent 4 }} | ||
| {{- end }} | ||
| spec: | ||
| parentRefs: | ||
| - name: {{ .Values.nvcfGatewayRoutes.gateways.llmGrpc.name }} | ||
| namespace: {{ .Values.nvcfGatewayRoutes.gateways.llmGrpc.namespace }} | ||
| sectionName: {{ .Values.nvcfGatewayRoutes.gateways.llmGrpc.listenerName }} | ||
| rules: | ||
| - backendRefs: | ||
| - name: {{ .Values.nvcfGatewayRoutes.routes.llmWorker.backend.name }} | ||
| namespace: {{ $backendNamespace }} | ||
| port: {{ .Values.nvcfGatewayRoutes.routes.llmWorker.backend.grpcPort }} | ||
| {{- end }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| {{- if and .Values.nvcfGatewayRoutes.enabled .Values.nvcfGatewayRoutes.routes.llmWorker.enabled }} | ||
| {{- $backendNamespace := include "nvcf-gateway.llmWorkerBackendNamespace" . }} | ||
| apiVersion: gateway.networking.k8s.io/v1alpha2 | ||
| kind: UDPRoute | ||
| metadata: | ||
| name: {{ .Values.nvcfGatewayRoutes.routes.llmWorker.name }}-quic | ||
| namespace: {{ .Values.nvcfGatewayRoutes.gateways.llmQuic.namespace }} | ||
| labels: | ||
| {{- include "nvcf-gateway.labels" . | nindent 4 }} | ||
| app.kubernetes.io/component: llm-worker-quic-route | ||
| {{- with .Values.nvcfGatewayRoutes.routes.llmWorker.routeAnnotations }} | ||
| annotations: | ||
| {{- toYaml . | nindent 4 }} | ||
| {{- end }} | ||
| spec: | ||
| parentRefs: | ||
| - name: {{ .Values.nvcfGatewayRoutes.gateways.llmQuic.name }} | ||
| namespace: {{ .Values.nvcfGatewayRoutes.gateways.llmQuic.namespace }} | ||
| sectionName: {{ .Values.nvcfGatewayRoutes.gateways.llmQuic.listenerName }} | ||
| rules: | ||
| - backendRefs: | ||
| - name: {{ .Values.nvcfGatewayRoutes.routes.llmWorker.backend.name }} | ||
| namespace: {{ $backendNamespace }} | ||
| port: {{ .Values.nvcfGatewayRoutes.routes.llmWorker.backend.quicPort }} | ||
| {{- end }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| #!/usr/bin/env bash | ||
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| chart_dir="${script_dir}/../chart" | ||
| rendered="$(mktemp)" | ||
| disabled="$(mktemp)" | ||
| trap 'rm -f "$rendered" "$disabled"' EXIT | ||
|
|
||
| helm template nvcf-gateway-routes "$chart_dir" \ | ||
| --namespace gateway \ | ||
| --set nvcfGatewayRoutes.routes.llmWorker.enabled=true \ | ||
| --set nvcfGatewayRoutes.gateways.llmGrpc.name=llm-grpc-gateway \ | ||
| --set nvcfGatewayRoutes.gateways.llmGrpc.namespace=gateway \ | ||
| --set nvcfGatewayRoutes.gateways.llmQuic.name=llm-quic-gateway \ | ||
| --set nvcfGatewayRoutes.gateways.llmQuic.namespace=gateway \ | ||
| --set nvcfGatewayRoutes.routes.llmWorker.backend.namespace=router-system \ | ||
| >"$rendered" | ||
|
|
||
| assert_contains() { | ||
| local pattern="$1" | ||
| local message="$2" | ||
| if ! grep -Fq -- "$pattern" "$rendered"; then | ||
| echo "FAIL: ${message}" >&2 | ||
| exit 1 | ||
| fi | ||
| } | ||
|
|
||
| assert_contains "kind: TCPRoute" \ | ||
| "LLM worker routing must expose gRPC registration over TCP" | ||
| assert_contains "kind: UDPRoute" \ | ||
| "LLM worker routing must expose reverse tunnels over UDP" | ||
| assert_contains "name: llm-request-router-backend-router" \ | ||
| "LLM worker routes must target the authority/SNI-aware backend router" | ||
| assert_contains "name: allow-llm-worker-routes" \ | ||
| "ReferenceGrant must permit cross-namespace LLM worker routes" | ||
| assert_contains "sectionName: llm-grpc" \ | ||
| "TCPRoute must attach to the configured LLM gRPC listener" | ||
| assert_contains "sectionName: llm-quic" \ | ||
| "UDPRoute must attach to the configured LLM QUIC listener" | ||
|
|
||
| reference_grant_service_name="$(awk ' | ||
| $0 == "kind: ReferenceGrant" { in_grant = 1; target_grant = 0; in_to = 0 } | ||
| in_grant && !target_grant && $1 == "name:" && $2 == "allow-llm-worker-routes" { target_grant = 1 } | ||
| target_grant && $0 == " to:" { in_to = 1 } | ||
| target_grant && in_to && $1 == "name:" { print $2; exit } | ||
| ' "$rendered")" | ||
| if [[ "$reference_grant_service_name" != "llm-request-router-backend-router" ]]; then | ||
| echo "FAIL: LLM worker ReferenceGrant must stay scoped to the configured backend Service" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| backend_namespace_references="$(grep -Fc -- "namespace: router-system" "$rendered")" | ||
| if [[ "$backend_namespace_references" != "3" ]]; then | ||
| echo "FAIL: LLM worker routes and ReferenceGrant must use the configured backend namespace" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| if helm template nvcf-gateway-routes "$chart_dir" \ | ||
| --namespace gateway \ | ||
| --set nvcfGatewayRoutes.routes.llmWorker.enabled=true \ | ||
| --set-string nvcfGatewayRoutes.routes.llmWorker.backend.namespace= \ | ||
| >/dev/null 2>&1; then | ||
| echo "FAIL: enabled LLM worker routing must require an explicit backend namespace" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| helm template nvcf-gateway-routes "$chart_dir" \ | ||
| --namespace gateway \ | ||
| --set nvcfGatewayRoutes.routes.llmWorker.enabled=false \ | ||
| >"$disabled" | ||
|
|
||
| if grep -Eq '^ name: (llm-worker-(grpc|quic)|allow-llm-worker-routes)$' "$disabled"; then | ||
| echo "FAIL: disabled LLM worker routing must not render route resources" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "PASS: LLM worker Gateway routes render correctly" |
Uh oh!
There was an error while loading. Please reload this page.