Repository navigation
Keep an AI Gateway route attached - #538
mikemikimike wants to merge 6 commits into
Conversation
Refs modelplaneai#531 Signed-off-by: mikemikimike <13286568797@163.com>
dennis-upbound
left a comment
There was a problem hiding this comment.
Thanks for picking this up. I applied the anchor route from this PR to a live GKE gateway running Envoy AI Gateway v1.1.0, the version the serving stack pins.
The approach works. With the anchor in place, I removed the gateway's last model route, and the Envoy Deployment kept the same ReplicaSet and pods. Without the anchor, the same step rolled it every time.
-
The anchor shows up in
/v1/models. AI Gateway lists every exact match onx-ai-eg-modelas a model (gateway.go at v1.1.0). The option to exclude a model from the list came after v1.1.0. With this route applied,GET /v1/modelsreturned["ml-team/qwen3-8b-uni", "modelplane-system/__gateway_anchor__"], and a request naming the anchor got HTTP 500. Matching on a header that isn't the model header, such asx-modelplane-gateway-anchor: "true", avoids this. AnyAIGatewayRoutecounts toward keeping ext-proc injected, whatever it matches. After that change, the route was still accepted,/v1/modelslisted only the real model, and the real model kept serving. -
GatewayReadystill turns True before ext-proc runs. AI Gateway accepts the route within a second, but the sidecar arrives with the Envoy Deployment's rollout, about 30 seconds later in #531's timeline. With the anchor in place, that window happens once, when the gateway is created, instead of with every first model, which is the main fix. A model deployed together with a new gateway can still get 500s whileGatewayReadyis True. Either say so in the description, or haveGatewayReadywait for the rollout. An observe-onlyObjecton the Envoy Deployment, whose readiness checks the rollout finished, would do it. -
The description is missing the template's checklist, so
checklist-completedfails. CONTRIBUTING.md also asks the description to open with the problem beforeFixes #531.
The permanent anchor can be accepted before Envoy rolls out its ext-proc sidecar, so the first ModelRoute can report RoutingReady while the proxy returns 500. Observe the Envoy Deployment rollout in GatewayReady and hold route composition until GatewayReady is true. Match the anchor on a non-model header so it stays out of /v1/models. Fixes modelplaneai#531. Signed-off-by: mikemikimike <13286568797@163.com>
dennis-upbound
left a comment
There was a problem hiding this comment.
Thanks, the header change fixes /v1/models, and the description reads well. Waiting for the Envoy rollout is the right idea. One blocking problem, and a few smaller ones:
- Gating
compose-model-routeonGatewayReadydeletes every model's routes whenever the gateway isn't Ready.resolve_inputs()returning False makes the function compose nothing, and Crossplane deletes composed resources the pipeline stops desiring: theAIGatewayRoute, backends and client certificate.GatewayReadynow goes False wheneverreadyReplicas != replicason the Envoy Deployment, so one Envoy pod restarting after a node drain, an Envoy Gateway upgrade or an eviction takes every model on the gateway offline. That happens while the other replica could still serve. I'd keep composing and gate onlyRoutingReady: report the route not ready untilGatewayReadyis True, but don't drop it. Or apply the gate only before the route first exists. - The rollout check should also require
status.replicas == spec.replicas. During a rolling update,updatedReplicas,readyReplicasandavailableReplicascan all equalspec.replicaswhile old pods, without the ext-proc sidecar, are still running and taking traffic.status.replicascounts those. - Set
spec.watch: trueon the observed Deployment. provider-kubernetes re-reads a not-readyObjectevery 30s, but a Ready one only on its hourly resync (#497). Without the watch, the gateway can report Ready for up to an hour after a later rollout starts. - Renaming the Envoy Deployment to
modelplane-inference-gateway-envoychanges it on existing gateways. Does Envoy Gateway create the new Deployment before deleting the old one? If not, upgrading Modelplane briefly drops traffic on every gateway, and the description should say so.
| (condition for condition in gateway_conditions or [] if condition.type == "GatewayReady"), | ||
| None, | ||
| ) | ||
| if not gateway_ready or gateway_ready.status != "True": |
There was a problem hiding this comment.
Returning False here composes nothing, so Crossplane deletes this route's AIGatewayRoute, backends and certificate whenever GatewayReady is False, including when one Envoy pod restarts. I'd let composition continue and gate only RoutingReady on GatewayReady.
There was a problem hiding this comment.
Fixed in e0ccd09: when GatewayReady is false, an already observed route remains composed and RoutingReady stays false. Before a route exists, composition still waits. The GatewayRollout case covers this behavior.
Signed-off-by: mikemikimike <13286568797@163.com>
|
Addressed in e0ccd09: the rollout query requires status.replicas to equal spec.replicas, and the observed Deployment enables spec.watch. I removed the PR-added Envoy Deployment name override and observe the existing generated name, so this change does not rename deployed resources. All 630 composition-function tests passed; Ruff lint and formatting passed. The kind integration suite was not run. |
dennis-upbound
left a comment
There was a problem hiding this comment.
Thanks. e0ccd09 fixes three of the four:
- An existing route keeps composing during a gateway rollout, with
RoutingReadyFalse. - The rollout check now requires
status.replicas == spec.replicas. - The observed Deployment is watched.
One blocking problem with the Deployment name. Envoy Gateway names it envoy-<namespace>-<gateway>-<hash>. On two GKE clusters running this stack, the inference gateway's Deployment was envoy-modelplane-system-inference-gateway-5dbfd6c0. _ENVOY_DEPLOYMENT_NAME is modelplane-system-inference-gateway-5dbfd6c0, without the envoy- prefix. So the observe-only Object looks for a Deployment that doesn't exist, stays not Ready, and holds GatewayReady False. Every route that doesn't exist yet then waits forever, so no new model gets a route. The test fixtures use the same name, so they pass. Adding the prefix fixes it. A test that builds the name the way Envoy Gateway does would keep it from drifting.
| _ENVOY_GATEWAY_NAMESPACE = "envoy-gateway-system" | ||
| # Envoy Gateway's default for modelplane-system/inference-gateway. Keep this | ||
| # name so observing the Deployment does not replace the one already serving. | ||
| _ENVOY_DEPLOYMENT_NAME = "modelplane-system-inference-gateway-5dbfd6c0" |
There was a problem hiding this comment.
Envoy Gateway prefixes this with envoy-: the live Deployment is envoy-modelplane-system-inference-gateway-5dbfd6c0. Without the prefix the observed Object never finds it, so GatewayReady stays False.
There was a problem hiding this comment.
Addressed in 824e861a:
- Pin
envoyDeployment.nametoenvoy-modelplane-system-inference-gateway, so readiness observes the fixed Deployment instead of deriving a generated hash. - Keep composing model routes while
GatewayReadyis false;RoutingReadyremains false until the gateway can serve. - Require the observed generation and a non-empty AI Gateway ext-proc config marker. Readiness compares
updatedReplicaswith the replicas currently reported by the Deployment, and requires at least one available replica. This allows one updated replica to serve while its peer is temporarily unavailable, but keeps readiness false while an old-template replica remains. - Add regression coverage for rollout states and TLS plus auth reaching
GatewayReady.
All 631 composition-function tests passed. Ruff formatting and lint, the repository's ty checks, function-test structure checks, and CEL readiness cases passed. The Nix flake check and kind-based integration suite were not run on this Windows host.
| _ENVOY_GATEWAY_NAMESPACE = "envoy-gateway-system" | ||
| # Envoy Gateway's default for modelplane-system/inference-gateway. Keep this | ||
| # name so observing the Deployment does not replace the one already serving. | ||
| _ENVOY_DEPLOYMENT_NAME = "modelplane-system-inference-gateway-5dbfd6c0" |
There was a problem hiding this comment.
Doesn't Envoy Gateway prefix this with envoy-? At v1.8.4 it builds the name in ExpectedResourceHashedName, which I think gives envoy-modelplane-system-inference-gateway-5dbfd6c0.
Rather than tracking Envoy Gateway's generated name, how about going back to pinning envoyDeployment.name on the EnvoyProxy, as an earlier revision did? Envoy Gateway uses the name when it's set, and a fixed name doesn't need the hash. I think renaming existing gateways is fine before 1.0.
| "has(object.status.readyReplicas) && object.status.readyReplicas == object.spec.replicas && " | ||
| "has(object.status.availableReplicas) && object.status.availableReplicas == object.spec.replicas && " |
There was a problem hiding this comment.
With two replicas and envoyPDB.maxUnavailable: 1, won't this turn GatewayReady False whenever one Envoy pod is drained or evicted? Now that compose-model-route follows GatewayReady, that would set RoutingReady False on every model on the gateway, and hold new routes, while the other pod keeps serving.
If I'm reading AI Gateway right, the sidecar is added by a pod webhook, so a replacement pod gets it anyway. The 500s would only come from old pods that are still running. Would checking observedGeneration, updatedReplicas == replicas == spec.replicas and Available be enough?
| "has(object.status.readyReplicas) && object.status.readyReplicas == object.spec.replicas && " | |
| "has(object.status.availableReplicas) && object.status.availableReplicas == object.spec.replicas && " |
There was a problem hiding this comment.
Fixed in 824e861a. Readiness now compares updatedReplicas with the Deployment's currently reported replicas and requires at least one available replica. If the desired count is two but only one updated replica is currently reported and available, GatewayReady stays true while the peer is temporarily unavailable. If an old-template replica is still included in replicas, the counts differ and readiness stays false. The CEL acceptance cases cover both states.
| ) | ||
| # Do not create a route before its gateway is serving, but keep an | ||
| # existing route in desired state during a gateway rollout. | ||
| if "route" not in self.req.observed.resources: |
There was a problem hiding this comment.
Do we need to hold composition until the gateway is Ready? With the anchor in place, I'd expect a route created during a rollout to be harmless. If so, always composing and gating only RoutingReady would let us drop this special case.
| "A programmed gateway with TLS and auth serves authenticated HTTPS, copies its Secrets to the " | ||
| "cluster, and publishes its address but no URLs." | ||
| ), | ||
| name="AnchorRouteNotAccepted", |
There was a problem hiding this comment.
Nit: with TLSAndAuth repurposed, does anything still cover a TLS+auth gateway reaching GatewayReady=True? Maybe worth keeping both cases.
GatewayReady observes the Deployment emitted by Envoy Gateway. Its generated name includes an envoy- prefix, so the previous name never resolved and readiness stayed false. Match the generated name while keeping the existing Deployment. Signed-off-by: mikemikimike <13286568797@163.com>
Gateway readiness can fall while one Envoy pod is unavailable, and waiting for readiness before composing a model route delays its creation. Keep composing routes while reporting their readiness separately. Pin the Envoy Deployment name for observation. Existing gateways will replace their generated Deployment. Wait for the AI Gateway's ext-proc template annotation and for all replicas to be updated, while allowing one available replica to serve traffic. Towards modelplaneai#531. Signed-off-by: mikemikimike <13286568797@163.com>
Signed-off-by: mikemikimike <13286568797@163.com>
With no model routes on an InferenceGateway, Envoy AI Gateway removes its ext-proc sidecar and rolls the Envoy Deployment. A new ModelRoute can report
RoutingReady=Truebefore the rollout completes, so requests return HTTP 500.Fixes #531 (#531)
This keeps a backend-free AIGatewayRoute attached with an exact match on
x-modelplane-gateway-anchor: "true", not the model header, so/v1/modelslists only real models.GatewayReadywaits for the anchor route, the AI Gateway ext-proc template marker, and the Envoy Deployment rollout. The EnvoyProxy pins the Deployment name so readiness observes the fixed Deployment; existing gateways replace the generated Deployment during the upgrade. While the gateway is unready,compose-model-routecontinues composing the route and keepsRoutingReadyfalse.Validation: all 631 composition-function tests passed. Ruff formatting and lint checks, the repository's
tychecks, function-test structure checks, and docs manifest validation passed. The Nix flake check and kind-based integration suite have not been run on this Windows host.I have:
nix flake check(or./nix.sh flake check) and made sure it passes.git commit -s.