From 4d0ff88ffc6bd34afdfe0dcf84a7fba24a673590 Mon Sep 17 00:00:00 2001 From: along Date: Fri, 18 Sep 2026 19:08:59 -0700 Subject: [PATCH 1/9] fix(self-managed): route Pylon discovery through the backend router Use backend-router discovery when backend routing is enabled so advertised pod identities match registration routing. Preserve explicit worker endpoints and direct discovery when backend routing is disabled. Keep the bootstrap port at 50071 independently of the main router port; custom ports use an explicit worker address. Cover disabled and automatic backend modes and update the deployment documentation. Signed-off-by: along --- .../self-managed/environments/base.yaml | 5 ++- deploy/stacks/self-managed/global.yaml.gotmpl | 13 +++++-- .../self-managed/tests/api-env-wiring.sh | 2 +- .../tests/llm-router-worker-address.sh | 28 ++++++++++++-- .../csp-end-to-end-example-installation.md | 5 ++- docs/user/helmfile-installation.md | 7 +++- docs/user/llm-function-enablement.md | 38 +++++++++++-------- 7 files changed, 68 insertions(+), 30 deletions(-) diff --git a/deploy/stacks/self-managed/environments/base.yaml b/deploy/stacks/self-managed/environments/base.yaml index e6f97c36f1..eaa7f01c0a 100644 --- a/deploy/stacks/self-managed/environments/base.yaml +++ b/deploy/stacks/self-managed/environments/base.yaml @@ -79,8 +79,9 @@ global: invocationServiceURL: "" # gRPC proxy worker CONNECT endpoint advertised by the proxy. grpcProxyWorkerConnectURL: "" - # Optional LLM request-router address override advertised to LLM worker - # sidecars. Empty uses llm-request-router.nvcf.svc.cluster.local:50071. + # Pylon discovery endpoint. Empty uses the backend-router Service when + # backend routing is enabled, otherwise the main request-router Service, + # on port 50071. Set the full address for a custom port or remote workers. llmRequestRouterAddress: "" nodeSelectors: diff --git a/deploy/stacks/self-managed/global.yaml.gotmpl b/deploy/stacks/self-managed/global.yaml.gotmpl index 29417f8a40..c9f2cc3ab9 100644 --- a/deploy/stacks/self-managed/global.yaml.gotmpl +++ b/deploy/stacks/self-managed/global.yaml.gotmpl @@ -362,7 +362,16 @@ natsAuthCalloutService: {{- $grpcProxyWorkerConnectURL := dig "grpcProxyWorkerConnectURL" "" $workerEndpoints }} {{- $grpcProxyWorkerConnectBaseURL := dig "grpcproxy" "workerConnectBaseURL" "" .Values | default $grpcProxyWorkerConnectURL }} {{- $llmRequestRouterGrpcPort := dig "addons" "llm" "requestRouter" "service" "grpcPort" 50071 .Values }} -{{- $llmRequestRouterDefaultAddress := printf "llm-request-router.nvcf.svc.cluster.local:%v" $llmRequestRouterGrpcPort }} +{{- $llmEnabled := dig "addons" "llm" "enabled" false .Values }} +{{- $backendRouterEnabled := dig "addons" "llm" "requestRouter" "backendRouter" "enabled" $llmEnabled .Values }} +{{- $llmRequestRouterUsesBackend := $backendRouterEnabled }} +{{- if not (kindIs "bool" $llmRequestRouterUsesBackend) }} +{{- /* Match the chart's backendRouterEnabled helper when enabled is null. */}} +{{- $llmRequestRouterUsesBackend = and + (eq (dig "addons" "llm" "requestRouter" "workload" "kind" "Deployment" .Values) "Deployment") + (gt (dig "addons" "llm" "requestRouter" "replicaCount" 3 .Values | int) 1) }} +{{- end }} +{{- $llmRequestRouterDefaultAddress := ternary "http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071" "llm-request-router.nvcf.svc.cluster.local:50071" $llmRequestRouterUsesBackend }} {{- $llmRequestRouterWorkerAddress := dig "llmRequestRouterAddress" "" $workerEndpoints | trim | default $llmRequestRouterDefaultAddress }} {{- $llmRequestRouterWorkerAuthority := $llmRequestRouterWorkerAddress }} {{- $llmRequestRouterDNSAddressPattern := `^([A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)(\.([A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?))*:[0-9]{1,5}$` }} @@ -371,7 +380,6 @@ natsAuthCalloutService: {{- else if hasPrefix "http://" $llmRequestRouterWorkerAuthority }} {{- $llmRequestRouterWorkerAuthority = trimPrefix "http://" $llmRequestRouterWorkerAuthority }} {{- end }} -{{- $llmEnabled := dig "addons" "llm" "enabled" false .Values }} {{- if $llmEnabled }} {{- $llmRequestRouterWorkerAddressError := "global.workerEndpoints.llmRequestRouterAddress must use optional http:// or https:// followed by DNS-or-IPv4:port or [IPv6]:port with port 1-65535" }} {{- $llmRequestRouterBracketedIPv6Pattern := `^\[[0-9A-Fa-f:.]+\]:[0-9]{1,5}$` }} @@ -1045,7 +1053,6 @@ llmApiGateway: {{- $pylonGrpcDialAddress := dig "addons" "llm" "requestRouter" "backendRouter" "pylonGrpcDialAddress" "" .Values | default "" | toString | trim }} {{- $pylonReverseTunnelDialAddress := dig "addons" "llm" "requestRouter" "backendRouter" "pylonReverseTunnelDialAddress" "" .Values | default "" | toString | trim }} -{{- $backendRouterEnabled := dig "addons" "llm" "requestRouter" "backendRouter" "enabled" $llmEnabled .Values }} {{- $llmWorkerRouteEnabled := dig "ingress" "gatewayApi" "routes" "llmWorker" "enabled" false .Values }} {{- $grpcTlsEnabled := dig "addons" "llm" "requestRouter" "grpcTls" "enabled" false .Values }} {{- $grpcTlsAllowInsecureHttp := dig "addons" "llm" "requestRouter" "grpcTls" "allowInsecureHttp" false .Values }} diff --git a/deploy/stacks/self-managed/tests/api-env-wiring.sh b/deploy/stacks/self-managed/tests/api-env-wiring.sh index 0562fe3886..e223d4d1ec 100755 --- a/deploy/stacks/self-managed/tests/api-env-wiring.sh +++ b/deploy/stacks/self-managed/tests/api-env-wiring.sh @@ -120,7 +120,7 @@ assert_yaml_value "$explicit_values" '.api.remoteConfig.configData.custom.retain assert_yaml_value "$explicit_values" '.api.remoteConfig.configData.nvcf.sidecars.retained-setting' \ keep-inside-sidecars "nested sidecar remote config" assert_yaml_value "$explicit_values" "$remote_worker_address_expression" \ - llm-request-router.nvcf.svc.cluster.local:50071 "stack-owned worker address" + http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071 "stack-owned worker address" assert_yaml_value "$explicit_values" '.api.env.CUSTOM_API_ENV' \ configured "generic API env" assert_yaml_value "$explicit_values" '.api.env.LITERAL_TEMPLATE_VALUE' \ diff --git a/deploy/stacks/self-managed/tests/llm-router-worker-address.sh b/deploy/stacks/self-managed/tests/llm-router-worker-address.sh index 32b8ae95c5..3661812fb7 100755 --- a/deploy/stacks/self-managed/tests/llm-router-worker-address.sh +++ b/deploy/stacks/self-managed/tests/llm-router-worker-address.sh @@ -213,7 +213,7 @@ if assert_remote_config_address "$work_dir/wrong-owner-values.yaml" \ fail "remote-config assertion accepted a worker address outside the API values" fi -local_worker_address='llm-request-router.nvcf.svc.cluster.local:50071' +local_worker_address='http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071' printf '%s\n' \ 'addons:' \ ' llm:' \ @@ -228,8 +228,28 @@ if grep -Eq 'NVCF_(LLM_REQUEST_ROUTER_WORKER_ADDRESS|STARGATE_ADDRESS)' \ fail "enabled LLM rendered the worker address through the legacy API env path" fi +direct_worker_address='llm-request-router.nvcf.svc.cluster.local:50071' +backend_mode_cases=( + "disabled|false|Deployment|1|$direct_worker_address" + "auto-multiple|null|Deployment|3|$local_worker_address" + "auto-single|null|Deployment|1|$direct_worker_address" + "auto-stateful|null|StatefulSet|3|$direct_worker_address" +) +for backend_mode_case in "${backend_mode_cases[@]}"; do + IFS='|' read -r case_name backend_enabled workload replicas expected_address <<<"$backend_mode_case" + write_environment true '' + { + printf ' requestRouter:\n workload:\n kind: %s\n' "$workload" + printf ' replicaCount: %s\n' "$replicas" + printf ' backendRouter:\n enabled: %s\n' "$backend_enabled" + } >>"$environment_file" + values_file="$work_dir/$case_name-values.yaml" + render_api_values "$values_file" >/dev/null + assert_remote_config_address "$values_file" "$expected_address" || + fail "$case_name: expected bootstrap address $expected_address" +done + custom_router_grpc_port='51071' -custom_port_worker_address="llm-request-router.nvcf.svc.cluster.local:$custom_router_grpc_port" printf '%s\n' \ 'addons:' \ ' llm:' \ @@ -241,8 +261,8 @@ render_api_values \ "addons.llm.requestRouter.service.grpcPort=$custom_router_grpc_port" \ >/dev/null assert_remote_config_address "$work_dir/custom-port-api-values.yaml" \ - "$custom_port_worker_address" || - fail "enabled local LLM did not use the configured request-router gRPC port" + "$local_worker_address" || + fail "request-router gRPC port changed the backend-router bootstrap address" assert_llm_request_router_grpc_port "$work_dir/custom-port-api-values.yaml" \ "$custom_router_grpc_port" || fail "enabled LLM did not pass the configured gRPC port to the request-router chart" diff --git a/docs/user/csp-end-to-end-example-installation.md b/docs/user/csp-end-to-end-example-installation.md index fa25199ffb..5c51af144f 100644 --- a/docs/user/csp-end-to-end-example-installation.md +++ b/docs/user/csp-end-to-end-example-installation.md @@ -206,8 +206,9 @@ global: nvctServiceURL: "" # CHANGE (multi-cluster): "http://tasks.${GATEWAY_ADDR}". Worker env NVCT_FQDN. nvctGrpcServiceURL: "" # CHANGE (multi-cluster): "http://worker-tasks.${GATEWAY_ADDR}". Worker env NVCT_FQDN_GRPC. invocationServiceURL: "" # Empty = in-cluster default. Workers use this for the invocation stream address. - # CHANGE (multi-cluster): worker-reachable request-router host:port. Empty - # uses llm-request-router.nvcf.svc.cluster.local:50071. + # CHANGE (multi-cluster): worker-reachable request-router endpoint. Empty + # uses the backend-router Service on port 50071 when backend routing is + # enabled, otherwise the main request-router Service on port 50071. llmRequestRouterAddress: "" nodeSelectors: diff --git a/docs/user/helmfile-installation.md b/docs/user/helmfile-installation.md index 66d861367e..c2bd37b873 100644 --- a/docs/user/helmfile-installation.md +++ b/docs/user/helmfile-installation.md @@ -356,8 +356,11 @@ ingress: When `addons.llm` is enabled, the stack defaults `global.workerEndpoints.llmRequestRouterAddress` to -`llm-request-router.nvcf.svc.cluster.local:50071`. Colocated workers require no -additional configuration. For a split deployment, this address alone is not +`http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071` when +backend routing is enabled. With backend routing disabled, it uses +`llm-request-router.nvcf.svc.cluster.local:50071`. Set an explicit address for +a custom port. Colocated workers using the defaults require no additional +configuration. For a split deployment, this address alone is not enough. Configure the paired backend-router gRPC and reverse QUIC dial addresses, Gateway routes, DNS, and trust described in [Remote compute clusters and regions](./llm-function-enablement.md#remote-compute-clusters-and-regions). diff --git a/docs/user/llm-function-enablement.md b/docs/user/llm-function-enablement.md index d093323441..a06640af0c 100644 --- a/docs/user/llm-function-enablement.md +++ b/docs/user/llm-function-enablement.md @@ -64,8 +64,8 @@ chains, and hostname mismatches prevent the gRPC watch and registration connections. Replace or rotate the bundle with a rolling restart of the worker pods. The `pylonGrpcDialAddress` override must be an explicit `https://` URI when a custom CA is configured. The separate -`global.workerEndpoints.llmRequestRouterAddress` input remains a scheme-less -`host:port` initial address. +`global.workerEndpoints.llmRequestRouterAddress` input accepts `host:port` or +an explicit HTTP(S) URI for the initial connection. For gRPC, TLS SNI and hostname verification always use the external HTTPS dial hostname. After discovery, Pylon separately sends the concrete request-router @@ -76,11 +76,11 @@ pod SANs. The QUIC identity remains separate. The request router presents a certificate issued by cert-manager, or one you issue and supply in a pre-created Secret. -For a single-cluster deployment, workers dial -`llm-request-router.nvcf.svc.cluster.local:50071`. For remote workers, the -backend router preserves the advertised request-router pod hostname as the -QUIC SNI while it sends traffic through an external UDP endpoint. The QUIC -certificate must cover that advertised hostname. The default wildcard SAN is +For a single-cluster deployment, initial gRPC discovery defaults to +`http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071`. For +remote workers, the backend router preserves the advertised request-router pod +hostname as the QUIC SNI while it sends traffic through an external UDP +endpoint. The QUIC certificate must cover that advertised hostname. The default wildcard SAN is `*.llm-request-router-headless.nvcf.svc.cluster.local`. It does not need the external UDP load-balancer hostname. Do not reuse the gRPC NLB leaf certificate as the QUIC leaf certificate. @@ -182,10 +182,16 @@ constraint in the external issuer's own configuration. When `addons.llm.enabled` is `true`, the stack defaults `global.workerEndpoints.llmRequestRouterAddress` to -`llm-request-router.nvcf.svc.cluster.local:50071`. Colocated workers require no -additional configuration. For a split control-plane and compute-plane -deployment, override this value with a host and port that worker pods can -reach. +`http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071` when +backend routing is enabled, which is the stack default. This lets discovery +return the same pod identities that the backend router uses for registration. +When backend routing is disabled, the default is +`llm-request-router.nvcf.svc.cluster.local:50071`. + +An explicit worker address takes precedence. Both defaults use port `50071` +independently of `addons.llm.requestRouter.service.grpcPort`. For a custom port +or a split control-plane and compute-plane deployment, set the full address +to an endpoint that worker pods can reach. The stack maps the configured or default address to `api.remoteConfig.configData.nvcf.llm-request-router.worker-address`. The NVCF @@ -709,11 +715,11 @@ kubectl -n nvcf-backend get pod \ ``` The worker args must contain -`--stargate-address=llm-request-router.nvcf.svc.cluster.local:50071`, or the -configured routable DNS name, and must not contain `--quic-insecure`. The -external address is the initial gRPC dial endpoint. The reverse tunnel verifies -the advertised request-router pod hostname instead. The environment must -contain: +`--stargate-address=http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071` +with default backend routing, or the configured routable endpoint, and must +not contain `--quic-insecure`. The external address is the initial gRPC dial +endpoint. The reverse tunnel verifies the advertised request-router pod +hostname instead. The environment must contain: ```text STARGATE_TLS_CERT_PATH=/etc/ssl/certs/ca-certificates.crt From 82c7763421def525145c80f90c84be044c1d16a7 Mon Sep 17 00:00:00 2001 From: along Date: Fri, 18 Sep 2026 19:37:55 -0700 Subject: [PATCH 2/9] refactor(self-managed): simplify LLM bootstrap address selection Select the bootstrap endpoint using the existing backend-routing flag. Leave explicit-null chart automatic mode outside this fix and remove its workload/replica resolution and test cases. Retain the explicit-disabled regression check and explicit worker-address overrides. Signed-off-by: along --- deploy/stacks/self-managed/global.yaml.gotmpl | 10 +++---- .../tests/llm-router-worker-address.sh | 26 +++++-------------- 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/deploy/stacks/self-managed/global.yaml.gotmpl b/deploy/stacks/self-managed/global.yaml.gotmpl index c9f2cc3ab9..bf65d3f7ce 100644 --- a/deploy/stacks/self-managed/global.yaml.gotmpl +++ b/deploy/stacks/self-managed/global.yaml.gotmpl @@ -364,14 +364,10 @@ natsAuthCalloutService: {{- $llmRequestRouterGrpcPort := dig "addons" "llm" "requestRouter" "service" "grpcPort" 50071 .Values }} {{- $llmEnabled := dig "addons" "llm" "enabled" false .Values }} {{- $backendRouterEnabled := dig "addons" "llm" "requestRouter" "backendRouter" "enabled" $llmEnabled .Values }} -{{- $llmRequestRouterUsesBackend := $backendRouterEnabled }} -{{- if not (kindIs "bool" $llmRequestRouterUsesBackend) }} -{{- /* Match the chart's backendRouterEnabled helper when enabled is null. */}} -{{- $llmRequestRouterUsesBackend = and - (eq (dig "addons" "llm" "requestRouter" "workload" "kind" "Deployment" .Values) "Deployment") - (gt (dig "addons" "llm" "requestRouter" "replicaCount" 3 .Values | int) 1) }} +{{- $llmRequestRouterDefaultAddress := "llm-request-router.nvcf.svc.cluster.local:50071" }} +{{- if $backendRouterEnabled }} +{{- $llmRequestRouterDefaultAddress = "http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071" }} {{- end }} -{{- $llmRequestRouterDefaultAddress := ternary "http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071" "llm-request-router.nvcf.svc.cluster.local:50071" $llmRequestRouterUsesBackend }} {{- $llmRequestRouterWorkerAddress := dig "llmRequestRouterAddress" "" $workerEndpoints | trim | default $llmRequestRouterDefaultAddress }} {{- $llmRequestRouterWorkerAuthority := $llmRequestRouterWorkerAddress }} {{- $llmRequestRouterDNSAddressPattern := `^([A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)(\.([A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?))*:[0-9]{1,5}$` }} diff --git a/deploy/stacks/self-managed/tests/llm-router-worker-address.sh b/deploy/stacks/self-managed/tests/llm-router-worker-address.sh index 3661812fb7..69c5f9d931 100755 --- a/deploy/stacks/self-managed/tests/llm-router-worker-address.sh +++ b/deploy/stacks/self-managed/tests/llm-router-worker-address.sh @@ -229,25 +229,13 @@ if grep -Eq 'NVCF_(LLM_REQUEST_ROUTER_WORKER_ADDRESS|STARGATE_ADDRESS)' \ fi direct_worker_address='llm-request-router.nvcf.svc.cluster.local:50071' -backend_mode_cases=( - "disabled|false|Deployment|1|$direct_worker_address" - "auto-multiple|null|Deployment|3|$local_worker_address" - "auto-single|null|Deployment|1|$direct_worker_address" - "auto-stateful|null|StatefulSet|3|$direct_worker_address" -) -for backend_mode_case in "${backend_mode_cases[@]}"; do - IFS='|' read -r case_name backend_enabled workload replicas expected_address <<<"$backend_mode_case" - write_environment true '' - { - printf ' requestRouter:\n workload:\n kind: %s\n' "$workload" - printf ' replicaCount: %s\n' "$replicas" - printf ' backendRouter:\n enabled: %s\n' "$backend_enabled" - } >>"$environment_file" - values_file="$work_dir/$case_name-values.yaml" - render_api_values "$values_file" >/dev/null - assert_remote_config_address "$values_file" "$expected_address" || - fail "$case_name: expected bootstrap address $expected_address" -done +render_api_values "$work_dir/backend-disabled-api-values.yaml" \ + --state-values-set addons.llm.requestRouter.backendRouter.enabled=false \ + --state-values-set addons.llm.requestRouter.replicaCount=1 \ + >/dev/null +assert_remote_config_address "$work_dir/backend-disabled-api-values.yaml" \ + "$direct_worker_address" || + fail "disabled backend routing did not use the main request-router address" custom_router_grpc_port='51071' printf '%s\n' \ From 03d772ca71485e5a6ab74a5ecd30d478a3a50338 Mon Sep 17 00:00:00 2001 From: along Date: Fri, 18 Sep 2026 20:05:49 -0700 Subject: [PATCH 3/9] fix(self-managed): preserve direct router discovery port Keep the configured main request-router gRPC port in the discovery address when backend routing is disabled. Retain the independent backend-router default and explicit worker-address overrides. Cover default and custom direct-service ports and align deployment documentation. Refs #1990 Signed-off-by: along --- .../self-managed/environments/base.yaml | 6 +++--- deploy/stacks/self-managed/global.yaml.gotmpl | 2 +- .../tests/llm-router-worker-address.sh | 19 +++++++++++-------- .../csp-end-to-end-example-installation.md | 2 +- docs/user/helmfile-installation.md | 7 ++++--- docs/user/llm-function-enablement.md | 11 ++++++----- 6 files changed, 26 insertions(+), 21 deletions(-) diff --git a/deploy/stacks/self-managed/environments/base.yaml b/deploy/stacks/self-managed/environments/base.yaml index eaa7f01c0a..05f10bca58 100644 --- a/deploy/stacks/self-managed/environments/base.yaml +++ b/deploy/stacks/self-managed/environments/base.yaml @@ -79,9 +79,9 @@ global: invocationServiceURL: "" # gRPC proxy worker CONNECT endpoint advertised by the proxy. grpcProxyWorkerConnectURL: "" - # Pylon discovery endpoint. Empty uses the backend-router Service when - # backend routing is enabled, otherwise the main request-router Service, - # on port 50071. Set the full address for a custom port or remote workers. + # Pylon discovery endpoint. Empty uses the backend-router Service on port + # 50071 when enabled, otherwise the main Service's configured gRPC port. + # Set the full address for a custom backend-router port or remote workers. llmRequestRouterAddress: "" nodeSelectors: diff --git a/deploy/stacks/self-managed/global.yaml.gotmpl b/deploy/stacks/self-managed/global.yaml.gotmpl index bf65d3f7ce..02f41648d1 100644 --- a/deploy/stacks/self-managed/global.yaml.gotmpl +++ b/deploy/stacks/self-managed/global.yaml.gotmpl @@ -364,7 +364,7 @@ natsAuthCalloutService: {{- $llmRequestRouterGrpcPort := dig "addons" "llm" "requestRouter" "service" "grpcPort" 50071 .Values }} {{- $llmEnabled := dig "addons" "llm" "enabled" false .Values }} {{- $backendRouterEnabled := dig "addons" "llm" "requestRouter" "backendRouter" "enabled" $llmEnabled .Values }} -{{- $llmRequestRouterDefaultAddress := "llm-request-router.nvcf.svc.cluster.local:50071" }} +{{- $llmRequestRouterDefaultAddress := printf "llm-request-router.nvcf.svc.cluster.local:%v" $llmRequestRouterGrpcPort }} {{- if $backendRouterEnabled }} {{- $llmRequestRouterDefaultAddress = "http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071" }} {{- end }} diff --git a/deploy/stacks/self-managed/tests/llm-router-worker-address.sh b/deploy/stacks/self-managed/tests/llm-router-worker-address.sh index 69c5f9d931..be7c705a33 100755 --- a/deploy/stacks/self-managed/tests/llm-router-worker-address.sh +++ b/deploy/stacks/self-managed/tests/llm-router-worker-address.sh @@ -228,14 +228,17 @@ if grep -Eq 'NVCF_(LLM_REQUEST_ROUTER_WORKER_ADDRESS|STARGATE_ADDRESS)' \ fail "enabled LLM rendered the worker address through the legacy API env path" fi -direct_worker_address='llm-request-router.nvcf.svc.cluster.local:50071' -render_api_values "$work_dir/backend-disabled-api-values.yaml" \ - --state-values-set addons.llm.requestRouter.backendRouter.enabled=false \ - --state-values-set addons.llm.requestRouter.replicaCount=1 \ - >/dev/null -assert_remote_config_address "$work_dir/backend-disabled-api-values.yaml" \ - "$direct_worker_address" || - fail "disabled backend routing did not use the main request-router address" +for direct_grpc_port in 50071 51071; do + direct_worker_address="llm-request-router.nvcf.svc.cluster.local:$direct_grpc_port" + direct_values_file="$work_dir/backend-disabled-$direct_grpc_port-api-values.yaml" + render_api_values "$direct_values_file" \ + --state-values-set addons.llm.requestRouter.backendRouter.enabled=false \ + --state-values-set addons.llm.requestRouter.replicaCount=1 \ + --state-values-set "addons.llm.requestRouter.service.grpcPort=$direct_grpc_port" \ + >/dev/null + assert_remote_config_address "$direct_values_file" "$direct_worker_address" || + fail "disabled backend routing did not use the main request-router port $direct_grpc_port" +done custom_router_grpc_port='51071' printf '%s\n' \ diff --git a/docs/user/csp-end-to-end-example-installation.md b/docs/user/csp-end-to-end-example-installation.md index 5c51af144f..71b475203e 100644 --- a/docs/user/csp-end-to-end-example-installation.md +++ b/docs/user/csp-end-to-end-example-installation.md @@ -208,7 +208,7 @@ global: invocationServiceURL: "" # Empty = in-cluster default. Workers use this for the invocation stream address. # CHANGE (multi-cluster): worker-reachable request-router endpoint. Empty # uses the backend-router Service on port 50071 when backend routing is - # enabled, otherwise the main request-router Service on port 50071. + # enabled, otherwise the main Service on its configured gRPC port. llmRequestRouterAddress: "" nodeSelectors: diff --git a/docs/user/helmfile-installation.md b/docs/user/helmfile-installation.md index c2bd37b873..2e76db8f34 100644 --- a/docs/user/helmfile-installation.md +++ b/docs/user/helmfile-installation.md @@ -358,9 +358,10 @@ When `addons.llm` is enabled, the stack defaults `global.workerEndpoints.llmRequestRouterAddress` to `http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071` when backend routing is enabled. With backend routing disabled, it uses -`llm-request-router.nvcf.svc.cluster.local:50071`. Set an explicit address for -a custom port. Colocated workers using the defaults require no additional -configuration. For a split deployment, this address alone is not +`llm-request-router.nvcf.svc.cluster.local` with +`addons.llm.requestRouter.service.grpcPort` (default `50071`). Set an explicit +address for a custom backend-router port. Colocated workers using the defaults +require no additional configuration. For a split deployment, this address alone is not enough. Configure the paired backend-router gRPC and reverse QUIC dial addresses, Gateway routes, DNS, and trust described in [Remote compute clusters and regions](./llm-function-enablement.md#remote-compute-clusters-and-regions). diff --git a/docs/user/llm-function-enablement.md b/docs/user/llm-function-enablement.md index a06640af0c..17780a3ca5 100644 --- a/docs/user/llm-function-enablement.md +++ b/docs/user/llm-function-enablement.md @@ -186,12 +186,13 @@ When `addons.llm.enabled` is `true`, the stack defaults backend routing is enabled, which is the stack default. This lets discovery return the same pod identities that the backend router uses for registration. When backend routing is disabled, the default is -`llm-request-router.nvcf.svc.cluster.local:50071`. +`llm-request-router.nvcf.svc.cluster.local` with the port from +`addons.llm.requestRouter.service.grpcPort` (default `50071`). -An explicit worker address takes precedence. Both defaults use port `50071` -independently of `addons.llm.requestRouter.service.grpcPort`. For a custom port -or a split control-plane and compute-plane deployment, set the full address -to an endpoint that worker pods can reach. +An explicit worker address takes precedence. The backend-router default uses +port `50071` independently of `addons.llm.requestRouter.service.grpcPort`. +For a custom backend-router port or a split control-plane and compute-plane +deployment, set the full address to an endpoint that worker pods can reach. The stack maps the configured or default address to `api.remoteConfig.configData.nvcf.llm-request-router.worker-address`. The NVCF From 6e72167fe75fa9ccbeb1e290cc16b879803e7519 Mon Sep 17 00:00:00 2001 From: along Date: Sun, 20 Sep 2026 18:24:05 -0700 Subject: [PATCH 4/9] fix(self-managed): use scheme-less backend address Use the same host:port form for both Pylon discovery defaults. Keep only the documentation needed to describe the selected address and update rendered-value expectations. Refs #1990 Signed-off-by: along --- .../self-managed/environments/base.yaml | 6 ++-- deploy/stacks/self-managed/global.yaml.gotmpl | 2 +- .../self-managed/tests/api-env-wiring.sh | 2 +- .../tests/llm-router-worker-address.sh | 2 +- .../csp-end-to-end-example-installation.md | 6 ++-- docs/user/helmfile-installation.md | 9 ++---- docs/user/llm-function-enablement.md | 28 ++++++++----------- 7 files changed, 23 insertions(+), 32 deletions(-) diff --git a/deploy/stacks/self-managed/environments/base.yaml b/deploy/stacks/self-managed/environments/base.yaml index 05f10bca58..0b0be9fab0 100644 --- a/deploy/stacks/self-managed/environments/base.yaml +++ b/deploy/stacks/self-managed/environments/base.yaml @@ -79,9 +79,9 @@ global: invocationServiceURL: "" # gRPC proxy worker CONNECT endpoint advertised by the proxy. grpcProxyWorkerConnectURL: "" - # Pylon discovery endpoint. Empty uses the backend-router Service on port - # 50071 when enabled, otherwise the main Service's configured gRPC port. - # Set the full address for a custom backend-router port or remote workers. + # Pylon discovery endpoint. Empty uses + # llm-request-router-backend-router.nvcf.svc.cluster.local:50071 when backend + # routing is enabled, otherwise llm-request-router.nvcf.svc.cluster.local:50071. llmRequestRouterAddress: "" nodeSelectors: diff --git a/deploy/stacks/self-managed/global.yaml.gotmpl b/deploy/stacks/self-managed/global.yaml.gotmpl index 02f41648d1..8246a65ca2 100644 --- a/deploy/stacks/self-managed/global.yaml.gotmpl +++ b/deploy/stacks/self-managed/global.yaml.gotmpl @@ -366,7 +366,7 @@ natsAuthCalloutService: {{- $backendRouterEnabled := dig "addons" "llm" "requestRouter" "backendRouter" "enabled" $llmEnabled .Values }} {{- $llmRequestRouterDefaultAddress := printf "llm-request-router.nvcf.svc.cluster.local:%v" $llmRequestRouterGrpcPort }} {{- if $backendRouterEnabled }} -{{- $llmRequestRouterDefaultAddress = "http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071" }} +{{- $llmRequestRouterDefaultAddress = "llm-request-router-backend-router.nvcf.svc.cluster.local:50071" }} {{- end }} {{- $llmRequestRouterWorkerAddress := dig "llmRequestRouterAddress" "" $workerEndpoints | trim | default $llmRequestRouterDefaultAddress }} {{- $llmRequestRouterWorkerAuthority := $llmRequestRouterWorkerAddress }} diff --git a/deploy/stacks/self-managed/tests/api-env-wiring.sh b/deploy/stacks/self-managed/tests/api-env-wiring.sh index e223d4d1ec..479eae4654 100755 --- a/deploy/stacks/self-managed/tests/api-env-wiring.sh +++ b/deploy/stacks/self-managed/tests/api-env-wiring.sh @@ -120,7 +120,7 @@ assert_yaml_value "$explicit_values" '.api.remoteConfig.configData.custom.retain assert_yaml_value "$explicit_values" '.api.remoteConfig.configData.nvcf.sidecars.retained-setting' \ keep-inside-sidecars "nested sidecar remote config" assert_yaml_value "$explicit_values" "$remote_worker_address_expression" \ - http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071 "stack-owned worker address" + llm-request-router-backend-router.nvcf.svc.cluster.local:50071 "stack-owned worker address" assert_yaml_value "$explicit_values" '.api.env.CUSTOM_API_ENV' \ configured "generic API env" assert_yaml_value "$explicit_values" '.api.env.LITERAL_TEMPLATE_VALUE' \ diff --git a/deploy/stacks/self-managed/tests/llm-router-worker-address.sh b/deploy/stacks/self-managed/tests/llm-router-worker-address.sh index be7c705a33..b804be6b83 100755 --- a/deploy/stacks/self-managed/tests/llm-router-worker-address.sh +++ b/deploy/stacks/self-managed/tests/llm-router-worker-address.sh @@ -213,7 +213,7 @@ if assert_remote_config_address "$work_dir/wrong-owner-values.yaml" \ fail "remote-config assertion accepted a worker address outside the API values" fi -local_worker_address='http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071' +local_worker_address='llm-request-router-backend-router.nvcf.svc.cluster.local:50071' printf '%s\n' \ 'addons:' \ ' llm:' \ diff --git a/docs/user/csp-end-to-end-example-installation.md b/docs/user/csp-end-to-end-example-installation.md index 71b475203e..62b64e23f7 100644 --- a/docs/user/csp-end-to-end-example-installation.md +++ b/docs/user/csp-end-to-end-example-installation.md @@ -206,9 +206,9 @@ global: nvctServiceURL: "" # CHANGE (multi-cluster): "http://tasks.${GATEWAY_ADDR}". Worker env NVCT_FQDN. nvctGrpcServiceURL: "" # CHANGE (multi-cluster): "http://worker-tasks.${GATEWAY_ADDR}". Worker env NVCT_FQDN_GRPC. invocationServiceURL: "" # Empty = in-cluster default. Workers use this for the invocation stream address. - # CHANGE (multi-cluster): worker-reachable request-router endpoint. Empty - # uses the backend-router Service on port 50071 when backend routing is - # enabled, otherwise the main Service on its configured gRPC port. + # CHANGE (multi-cluster): worker-reachable request-router host:port. Empty uses + # llm-request-router-backend-router.nvcf.svc.cluster.local:50071 when backend routing is enabled, + # otherwise llm-request-router.nvcf.svc.cluster.local:50071. llmRequestRouterAddress: "" nodeSelectors: diff --git a/docs/user/helmfile-installation.md b/docs/user/helmfile-installation.md index 2e76db8f34..5295f7d42d 100644 --- a/docs/user/helmfile-installation.md +++ b/docs/user/helmfile-installation.md @@ -356,12 +356,9 @@ ingress: When `addons.llm` is enabled, the stack defaults `global.workerEndpoints.llmRequestRouterAddress` to -`http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071` when -backend routing is enabled. With backend routing disabled, it uses -`llm-request-router.nvcf.svc.cluster.local` with -`addons.llm.requestRouter.service.grpcPort` (default `50071`). Set an explicit -address for a custom backend-router port. Colocated workers using the defaults -require no additional configuration. For a split deployment, this address alone is not +`llm-request-router-backend-router.nvcf.svc.cluster.local:50071` when backend +routing is enabled, otherwise `llm-request-router.nvcf.svc.cluster.local:50071`. +For a split deployment, this address alone is not enough. Configure the paired backend-router gRPC and reverse QUIC dial addresses, Gateway routes, DNS, and trust described in [Remote compute clusters and regions](./llm-function-enablement.md#remote-compute-clusters-and-regions). diff --git a/docs/user/llm-function-enablement.md b/docs/user/llm-function-enablement.md index 17780a3ca5..3f184391c8 100644 --- a/docs/user/llm-function-enablement.md +++ b/docs/user/llm-function-enablement.md @@ -64,8 +64,8 @@ chains, and hostname mismatches prevent the gRPC watch and registration connections. Replace or rotate the bundle with a rolling restart of the worker pods. The `pylonGrpcDialAddress` override must be an explicit `https://` URI when a custom CA is configured. The separate -`global.workerEndpoints.llmRequestRouterAddress` input accepts `host:port` or -an explicit HTTP(S) URI for the initial connection. +`global.workerEndpoints.llmRequestRouterAddress` input remains a scheme-less +`host:port` initial address. For gRPC, TLS SNI and hostname verification always use the external HTTPS dial hostname. After discovery, Pylon separately sends the concrete request-router @@ -77,10 +77,11 @@ pod SANs. The QUIC identity remains separate. The request router presents a certificate issued by cert-manager, or one you issue and supply in a pre-created Secret. For a single-cluster deployment, initial gRPC discovery defaults to -`http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071`. For -remote workers, the backend router preserves the advertised request-router pod +`llm-request-router-backend-router.nvcf.svc.cluster.local:50071`. For remote +workers, the backend router preserves the advertised request-router pod hostname as the QUIC SNI while it sends traffic through an external UDP -endpoint. The QUIC certificate must cover that advertised hostname. The default wildcard SAN is +endpoint. The QUIC certificate must cover that advertised hostname. The +default wildcard SAN is `*.llm-request-router-headless.nvcf.svc.cluster.local`. It does not need the external UDP load-balancer hostname. Do not reuse the gRPC NLB leaf certificate as the QUIC leaf certificate. @@ -182,17 +183,10 @@ constraint in the external issuer's own configuration. When `addons.llm.enabled` is `true`, the stack defaults `global.workerEndpoints.llmRequestRouterAddress` to -`http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071` when -backend routing is enabled, which is the stack default. This lets discovery -return the same pod identities that the backend router uses for registration. -When backend routing is disabled, the default is -`llm-request-router.nvcf.svc.cluster.local` with the port from -`addons.llm.requestRouter.service.grpcPort` (default `50071`). - -An explicit worker address takes precedence. The backend-router default uses -port `50071` independently of `addons.llm.requestRouter.service.grpcPort`. -For a custom backend-router port or a split control-plane and compute-plane -deployment, set the full address to an endpoint that worker pods can reach. +`llm-request-router-backend-router.nvcf.svc.cluster.local:50071` when backend +routing is enabled, otherwise `llm-request-router.nvcf.svc.cluster.local:50071`. +For a split control-plane and compute-plane deployment, override this value +with a host and port that worker pods can reach. The stack maps the configured or default address to `api.remoteConfig.configData.nvcf.llm-request-router.worker-address`. The NVCF @@ -716,7 +710,7 @@ kubectl -n nvcf-backend get pod \ ``` The worker args must contain -`--stargate-address=http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071` +`--stargate-address=llm-request-router-backend-router.nvcf.svc.cluster.local:50071` with default backend routing, or the configured routable endpoint, and must not contain `--quic-insecure`. The external address is the initial gRPC dial endpoint. The reverse tunnel verifies the advertised request-router pod From 59afb58a87c47957e9a8397c31bffa1798028a5c Mon Sep 17 00:00:00 2001 From: along Date: Sun, 20 Sep 2026 18:36:27 -0700 Subject: [PATCH 5/9] test(self-managed): separate router address modes Preserve the direct-router address and custom-port assertions with backend routing disabled. Add separate default and custom-port coverage for the enabled backend router. Refs #1990 Signed-off-by: along --- .../tests/llm-router-worker-address.sh | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/deploy/stacks/self-managed/tests/llm-router-worker-address.sh b/deploy/stacks/self-managed/tests/llm-router-worker-address.sh index b804be6b83..d7bb0df1af 100755 --- a/deploy/stacks/self-managed/tests/llm-router-worker-address.sh +++ b/deploy/stacks/self-managed/tests/llm-router-worker-address.sh @@ -213,11 +213,14 @@ if assert_remote_config_address "$work_dir/wrong-owner-values.yaml" \ fail "remote-config assertion accepted a worker address outside the API values" fi -local_worker_address='llm-request-router-backend-router.nvcf.svc.cluster.local:50071' +local_worker_address='llm-request-router.nvcf.svc.cluster.local:50071' printf '%s\n' \ 'addons:' \ ' llm:' \ ' enabled: true' \ + ' requestRouter:' \ + ' backendRouter:' \ + ' enabled: false' \ >"$environment_file" render_api_values "$work_dir/local-api-values.yaml" >/dev/null assert_remote_config_address "$work_dir/local-api-values.yaml" \ @@ -228,33 +231,40 @@ if grep -Eq 'NVCF_(LLM_REQUEST_ROUTER_WORKER_ADDRESS|STARGATE_ADDRESS)' \ fail "enabled LLM rendered the worker address through the legacy API env path" fi -for direct_grpc_port in 50071 51071; do - direct_worker_address="llm-request-router.nvcf.svc.cluster.local:$direct_grpc_port" - direct_values_file="$work_dir/backend-disabled-$direct_grpc_port-api-values.yaml" - render_api_values "$direct_values_file" \ - --state-values-set addons.llm.requestRouter.backendRouter.enabled=false \ - --state-values-set addons.llm.requestRouter.replicaCount=1 \ - --state-values-set "addons.llm.requestRouter.service.grpcPort=$direct_grpc_port" \ - >/dev/null - assert_remote_config_address "$direct_values_file" "$direct_worker_address" || - fail "disabled backend routing did not use the main request-router port $direct_grpc_port" -done - custom_router_grpc_port='51071' +custom_port_worker_address="llm-request-router.nvcf.svc.cluster.local:$custom_router_grpc_port" +render_api_values \ + "$work_dir/custom-port-api-values.yaml" \ + --state-values-set \ + "addons.llm.requestRouter.service.grpcPort=$custom_router_grpc_port" \ + >/dev/null +assert_remote_config_address "$work_dir/custom-port-api-values.yaml" \ + "$custom_port_worker_address" || + fail "disabled backend routing did not use the configured request-router gRPC port" +assert_llm_request_router_grpc_port "$work_dir/custom-port-api-values.yaml" \ + "$custom_router_grpc_port" || + fail "enabled LLM did not pass the configured gRPC port to the request-router chart" + +backend_worker_address='llm-request-router-backend-router.nvcf.svc.cluster.local:50071' printf '%s\n' \ 'addons:' \ ' llm:' \ ' enabled: true' \ >"$environment_file" +render_api_values "$work_dir/backend-api-values.yaml" >/dev/null +assert_remote_config_address "$work_dir/backend-api-values.yaml" \ + "$backend_worker_address" || + fail "enabled backend routing did not use the backend-router address" + render_api_values \ - "$work_dir/custom-port-api-values.yaml" \ + "$work_dir/backend-custom-port-api-values.yaml" \ --state-values-set \ "addons.llm.requestRouter.service.grpcPort=$custom_router_grpc_port" \ >/dev/null -assert_remote_config_address "$work_dir/custom-port-api-values.yaml" \ - "$local_worker_address" || +assert_remote_config_address "$work_dir/backend-custom-port-api-values.yaml" \ + "$backend_worker_address" || fail "request-router gRPC port changed the backend-router bootstrap address" -assert_llm_request_router_grpc_port "$work_dir/custom-port-api-values.yaml" \ +assert_llm_request_router_grpc_port "$work_dir/backend-custom-port-api-values.yaml" \ "$custom_router_grpc_port" || fail "enabled LLM did not pass the configured gRPC port to the request-router chart" @@ -328,8 +338,8 @@ assert_remote_config_address "$work_dir/maximum-port-api-values.yaml" \ write_environment true '' render_api_values "$work_dir/default-api-values.yaml" >/dev/null assert_remote_config_address "$work_dir/default-api-values.yaml" \ - "$local_worker_address" || - fail "enabled LLM did not default the worker address to the cluster-local service" + "$backend_worker_address" || + fail "enabled LLM did not default the worker address to the backend-router service" invalid_address_cases=( 'missing-port|router' From 51f2ed88fbe982613e6f3c137e439dfefa98c6a0 Mon Sep 17 00:00:00 2001 From: along Date: Sun, 20 Sep 2026 19:03:21 -0700 Subject: [PATCH 6/9] test(self-managed): clarify backend router address name Name the backend discovery expectation after the backend router it targets. Refs #1990 Signed-off-by: along --- .../self-managed/tests/llm-router-worker-address.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deploy/stacks/self-managed/tests/llm-router-worker-address.sh b/deploy/stacks/self-managed/tests/llm-router-worker-address.sh index d7bb0df1af..ad88883137 100755 --- a/deploy/stacks/self-managed/tests/llm-router-worker-address.sh +++ b/deploy/stacks/self-managed/tests/llm-router-worker-address.sh @@ -245,7 +245,7 @@ assert_llm_request_router_grpc_port "$work_dir/custom-port-api-values.yaml" \ "$custom_router_grpc_port" || fail "enabled LLM did not pass the configured gRPC port to the request-router chart" -backend_worker_address='llm-request-router-backend-router.nvcf.svc.cluster.local:50071' +backend_router_address='llm-request-router-backend-router.nvcf.svc.cluster.local:50071' printf '%s\n' \ 'addons:' \ ' llm:' \ @@ -253,7 +253,7 @@ printf '%s\n' \ >"$environment_file" render_api_values "$work_dir/backend-api-values.yaml" >/dev/null assert_remote_config_address "$work_dir/backend-api-values.yaml" \ - "$backend_worker_address" || + "$backend_router_address" || fail "enabled backend routing did not use the backend-router address" render_api_values \ @@ -262,7 +262,7 @@ render_api_values \ "addons.llm.requestRouter.service.grpcPort=$custom_router_grpc_port" \ >/dev/null assert_remote_config_address "$work_dir/backend-custom-port-api-values.yaml" \ - "$backend_worker_address" || + "$backend_router_address" || fail "request-router gRPC port changed the backend-router bootstrap address" assert_llm_request_router_grpc_port "$work_dir/backend-custom-port-api-values.yaml" \ "$custom_router_grpc_port" || @@ -338,7 +338,7 @@ assert_remote_config_address "$work_dir/maximum-port-api-values.yaml" \ write_environment true '' render_api_values "$work_dir/default-api-values.yaml" >/dev/null assert_remote_config_address "$work_dir/default-api-values.yaml" \ - "$backend_worker_address" || + "$backend_router_address" || fail "enabled LLM did not default the worker address to the backend-router service" invalid_address_cases=( From 103becaaad18df46a132e7a3bb821e7c6bc41bef Mon Sep 17 00:00:00 2001 From: along Date: Sun, 20 Sep 2026 19:45:58 -0700 Subject: [PATCH 7/9] fix(self-managed): configure backend router grpc port Forward the backend-router gRPC port independently from the main request router and use it for the default worker bootstrap address. Preserve the direct-router tests and add backend default and custom-port coverage. Refs #1990 Signed-off-by: along --- .../self-managed/environments/base.yaml | 2 ++ deploy/stacks/self-managed/global.yaml.gotmpl | 5 +++- .../tests/llm-router-worker-address.sh | 24 ++++++++++++++----- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/deploy/stacks/self-managed/environments/base.yaml b/deploy/stacks/self-managed/environments/base.yaml index 0b0be9fab0..75aeb8b23c 100644 --- a/deploy/stacks/self-managed/environments/base.yaml +++ b/deploy/stacks/self-managed/environments/base.yaml @@ -402,6 +402,8 @@ addons: # enabled: defaults to addons.llm.enabled # replicaCount defaults to the request-router chart value. # replicaCount: 2 + # service: + # grpcPort: 50071 # Addresses workers dial to reach the router. Both default to the # backend-router Service in-cluster, which is correct when workers run # alongside the control plane. Set both to externally reachable diff --git a/deploy/stacks/self-managed/global.yaml.gotmpl b/deploy/stacks/self-managed/global.yaml.gotmpl index 8246a65ca2..041f1ce054 100644 --- a/deploy/stacks/self-managed/global.yaml.gotmpl +++ b/deploy/stacks/self-managed/global.yaml.gotmpl @@ -362,11 +362,12 @@ natsAuthCalloutService: {{- $grpcProxyWorkerConnectURL := dig "grpcProxyWorkerConnectURL" "" $workerEndpoints }} {{- $grpcProxyWorkerConnectBaseURL := dig "grpcproxy" "workerConnectBaseURL" "" .Values | default $grpcProxyWorkerConnectURL }} {{- $llmRequestRouterGrpcPort := dig "addons" "llm" "requestRouter" "service" "grpcPort" 50071 .Values }} +{{- $llmRequestRouterBackendGrpcPort := dig "addons" "llm" "requestRouter" "backendRouter" "service" "grpcPort" 50071 .Values }} {{- $llmEnabled := dig "addons" "llm" "enabled" false .Values }} {{- $backendRouterEnabled := dig "addons" "llm" "requestRouter" "backendRouter" "enabled" $llmEnabled .Values }} {{- $llmRequestRouterDefaultAddress := printf "llm-request-router.nvcf.svc.cluster.local:%v" $llmRequestRouterGrpcPort }} {{- if $backendRouterEnabled }} -{{- $llmRequestRouterDefaultAddress = "llm-request-router-backend-router.nvcf.svc.cluster.local:50071" }} +{{- $llmRequestRouterDefaultAddress = printf "llm-request-router-backend-router.nvcf.svc.cluster.local:%v" $llmRequestRouterBackendGrpcPort }} {{- end }} {{- $llmRequestRouterWorkerAddress := dig "llmRequestRouterAddress" "" $workerEndpoints | trim | default $llmRequestRouterDefaultAddress }} {{- $llmRequestRouterWorkerAuthority := $llmRequestRouterWorkerAddress }} @@ -1132,6 +1133,8 @@ llmRequestRouter: */}} backendRouter: enabled: {{ $backendRouterEnabled }} + service: + grpcPort: {{ $llmRequestRouterBackendGrpcPort }} {{- $backendRouterValues := dig "addons" "llm" "requestRouter" "backendRouter" dict .Values }} {{- if hasKey $backendRouterValues "replicaCount" }} replicaCount: {{ index $backendRouterValues "replicaCount" }} diff --git a/deploy/stacks/self-managed/tests/llm-router-worker-address.sh b/deploy/stacks/self-managed/tests/llm-router-worker-address.sh index ad88883137..78dc27217f 100755 --- a/deploy/stacks/self-managed/tests/llm-router-worker-address.sh +++ b/deploy/stacks/self-managed/tests/llm-router-worker-address.sh @@ -160,6 +160,14 @@ assert_llm_request_router_grpc_port() { test "$actual_port" = "$expected_port" } +assert_backend_router_grpc_port() { + local values_file="$1" + local expected_port="$2" + + test "$(yq -r '.llmRequestRouter.backendRouter.service.grpcPort' "$values_file")" = \ + "$expected_port" +} + invalid_worker_address_error='global.workerEndpoints.llmRequestRouterAddress must use optional http:// or https:// followed by DNS-or-IPv4:port or [IPv6]:port with port 1-65535' assert_worker_address_rejected() { @@ -255,18 +263,22 @@ render_api_values "$work_dir/backend-api-values.yaml" >/dev/null assert_remote_config_address "$work_dir/backend-api-values.yaml" \ "$backend_router_address" || fail "enabled backend routing did not use the backend-router address" +assert_backend_router_grpc_port "$work_dir/backend-api-values.yaml" 50071 || + fail "enabled backend routing did not pass the default gRPC port to the chart" +custom_backend_router_grpc_port='51072' +custom_backend_router_address="llm-request-router-backend-router.nvcf.svc.cluster.local:$custom_backend_router_grpc_port" render_api_values \ "$work_dir/backend-custom-port-api-values.yaml" \ --state-values-set \ - "addons.llm.requestRouter.service.grpcPort=$custom_router_grpc_port" \ + "addons.llm.requestRouter.backendRouter.service.grpcPort=$custom_backend_router_grpc_port" \ >/dev/null assert_remote_config_address "$work_dir/backend-custom-port-api-values.yaml" \ - "$backend_router_address" || - fail "request-router gRPC port changed the backend-router bootstrap address" -assert_llm_request_router_grpc_port "$work_dir/backend-custom-port-api-values.yaml" \ - "$custom_router_grpc_port" || - fail "enabled LLM did not pass the configured gRPC port to the request-router chart" + "$custom_backend_router_address" || + fail "enabled backend routing did not use the configured backend-router gRPC port" +assert_backend_router_grpc_port "$work_dir/backend-custom-port-api-values.yaml" \ + "$custom_backend_router_grpc_port" || + fail "enabled LLM did not pass the configured backend-router gRPC port to the chart" external_worker_address='router.example.com:443' render_api_values \ From 60ce9fad7760ed7980b68b13db6ce1ec7fed02af Mon Sep 17 00:00:00 2001 From: along Date: Sun, 20 Sep 2026 19:49:44 -0700 Subject: [PATCH 8/9] refactor(self-managed): rename backend router port variable Signed-off-by: along --- deploy/stacks/self-managed/global.yaml.gotmpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deploy/stacks/self-managed/global.yaml.gotmpl b/deploy/stacks/self-managed/global.yaml.gotmpl index 041f1ce054..0640092542 100644 --- a/deploy/stacks/self-managed/global.yaml.gotmpl +++ b/deploy/stacks/self-managed/global.yaml.gotmpl @@ -362,12 +362,12 @@ natsAuthCalloutService: {{- $grpcProxyWorkerConnectURL := dig "grpcProxyWorkerConnectURL" "" $workerEndpoints }} {{- $grpcProxyWorkerConnectBaseURL := dig "grpcproxy" "workerConnectBaseURL" "" .Values | default $grpcProxyWorkerConnectURL }} {{- $llmRequestRouterGrpcPort := dig "addons" "llm" "requestRouter" "service" "grpcPort" 50071 .Values }} -{{- $llmRequestRouterBackendGrpcPort := dig "addons" "llm" "requestRouter" "backendRouter" "service" "grpcPort" 50071 .Values }} +{{- $backendRouterGrpcPort := dig "addons" "llm" "requestRouter" "backendRouter" "service" "grpcPort" 50071 .Values }} {{- $llmEnabled := dig "addons" "llm" "enabled" false .Values }} {{- $backendRouterEnabled := dig "addons" "llm" "requestRouter" "backendRouter" "enabled" $llmEnabled .Values }} {{- $llmRequestRouterDefaultAddress := printf "llm-request-router.nvcf.svc.cluster.local:%v" $llmRequestRouterGrpcPort }} {{- if $backendRouterEnabled }} -{{- $llmRequestRouterDefaultAddress = printf "llm-request-router-backend-router.nvcf.svc.cluster.local:%v" $llmRequestRouterBackendGrpcPort }} +{{- $llmRequestRouterDefaultAddress = printf "llm-request-router-backend-router.nvcf.svc.cluster.local:%v" $backendRouterGrpcPort }} {{- end }} {{- $llmRequestRouterWorkerAddress := dig "llmRequestRouterAddress" "" $workerEndpoints | trim | default $llmRequestRouterDefaultAddress }} {{- $llmRequestRouterWorkerAuthority := $llmRequestRouterWorkerAddress }} @@ -1134,7 +1134,7 @@ llmRequestRouter: backendRouter: enabled: {{ $backendRouterEnabled }} service: - grpcPort: {{ $llmRequestRouterBackendGrpcPort }} + grpcPort: {{ $backendRouterGrpcPort }} {{- $backendRouterValues := dig "addons" "llm" "requestRouter" "backendRouter" dict .Values }} {{- if hasKey $backendRouterValues "replicaCount" }} replicaCount: {{ index $backendRouterValues "replicaCount" }} From fda247d8e25f5b9ca210a067cfbfdaba23ab9575 Mon Sep 17 00:00:00 2001 From: along Date: Sun, 20 Sep 2026 21:48:46 -0700 Subject: [PATCH 9/9] fix: update bdd test --- tests/bdd/features/single-cluster-helmfile-llm-pki.feature | 5 +++-- tests/bdd/godog_test.go | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/bdd/features/single-cluster-helmfile-llm-pki.feature b/tests/bdd/features/single-cluster-helmfile-llm-pki.feature index 0ec78e7ac4..9398eecc4c 100644 --- a/tests/bdd/features/single-cluster-helmfile-llm-pki.feature +++ b/tests/bdd/features/single-cluster-helmfile-llm-pki.feature @@ -71,10 +71,11 @@ Feature: Install a local single-cluster NVCF stack with PKI-secured LLM transpor | name: ADDONS_LLM_ENABLED | | value: "true" | | llm-request-router.nvcf.svc.cluster.local | + | worker-address: llm-request-router-backend-router.nvcf.svc.cluster.local:50071 | | name: NVCF_SERVICE_PKI_ALLOWED_DOMAINS | | value: "nvcf.svc.cluster.local" | | nvcf-openbao-migrations: | - # A colocated worker uses the in-cluster h2c Service directly. The + # A colocated worker uses the in-cluster backend-router Service directly. The # dedicated HTTPS identity and route belong only to an explicitly # enabled remote-worker ingress. And the rendered manifests in "deploy/stacks/self-managed/out" should not contain: @@ -113,7 +114,7 @@ Feature: Install a local single-cluster NVCF stack with PKI-secured LLM transpor When I run command "kubectl --context k3d-ncp-local get configmap/nvcf-api-remote-config -n nvcf -o yaml" Then the command exit code should be 0 - And the command output should contain "worker-address: llm-request-router.nvcf.svc.cluster.local:50071" + And the command output should contain "worker-address: llm-request-router-backend-router.nvcf.svc.cluster.local:50071" And the command output should contain "llm-router-client-image: nvcr.io/${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM}/pylon:" Then these Kubernetes resources should not exist in namespace "envoy-gateway-system" using context "k3d-ncp-local": diff --git a/tests/bdd/godog_test.go b/tests/bdd/godog_test.go index 0460bdb671..0114fea7f8 100644 --- a/tests/bdd/godog_test.go +++ b/tests/bdd/godog_test.go @@ -682,7 +682,8 @@ func TestSingleClusterHelmfileLLMPKIFeatureFileWiresToSteps(t *testing.T) { "kubectl --context k3d-ncp-local get configmap/nvcf-api-remote-config -n nvcf -o yaml": { ExitCode: 0, Stdout: "data:\n nvcf-api.yaml: |\n nvcf:\n" + - " llm-request-router:\n worker-address: llm-request-router.nvcf.svc.cluster.local:50071\n" + + " llm-request-router:\n" + + " worker-address: llm-request-router-backend-router.nvcf.svc.cluster.local:50071\n" + " sidecars:\n llm-router-client-image: nvcr.io/test-org/test-team/pylon:test\n", }, "helm get values nvca-operator --namespace nvca-operator --kube-context k3d-ncp-local -o yaml": { @@ -2099,6 +2100,7 @@ metadata: spec: dnsNames: - llm-request-router.nvcf.svc.cluster.local +worker-address: llm-request-router-backend-router.nvcf.svc.cluster.local:50071 env: - name: ADDONS_LLM_ENABLED value: "true"