feat(atenet): graceful termination of atenet-router - #774
feat(atenet): graceful termination of atenet-router#774shrutiyam-glitch wants to merge 4 commits into
Conversation
|
/retest |
Lior Lieberman (LiorLieberman)
left a comment
There was a problem hiding this comment.
Thanks shrutiyam-glitch !
| case <-time.After(p.timeout): | ||
| slog.WarnContext(ctx, "ext_proc drain deadline exceeded; forcing stop") | ||
| p.extproc.Stop() |
There was a problem hiding this comment.
lets add a cmoment and open an issue to add metrics here. Would be good to have a sense of how many unclean shutdowns we had
| // drainEnvoy gracefully drains the dataplane sidecar; nil when there is | ||
| // none to drive (agentgateway mode). It is handed a context bounded by | ||
| // envoyWindow and must return when it expires. | ||
| drainEnvoy func(context.Context) error |
There was a problem hiding this comment.
what is "none to drive"
suggest rewording to be more clear.
Also a function as in the parmas is not great. can we have something like "withDrainFunc" that decorates it or something?
| // delay is the route-drain window: after the readiness flip, how long to | ||
| // keep serving while the Service endpoints drop this pod. | ||
| delay time.Duration | ||
| // drainEnvoy gracefully drains the dataplane sidecar; nil when there is |
There was a problem hiding this comment.
Can we remove Envoy and agentgateway from the names here and everywhere in the PR? You can have drainDataplaneFunc (or drainDataplane) or something and just say that when set to nil, it simply means the dataplane doesn't require active graceful draining (or manages its own lifecycle). This keeps the generic router orchestrator cleanly decoupled from the underlying proxy implementation.
Which issue(s) this PR is related to:
Fixes #721
Required for System Upgrade flow (#473)
What this PR does / why we need it:
This PR implements graceful termination and zero-outage rolling updates for
atenet-router, building on the readiness probe and graceful drain patterns established in #719 (atelet).Draining a two-container networking pod (
atenet-routerGo control-plane +envoyC++ proxy dataplane) introduces complex lifecycle interdependencies. This PR resolves the dual-container SIGTERM race, respects Envoy'sfailClosedext_procfilter dependency, preserves parked requests riding out worker pool saturation, and accelerates idle deployments via an event-driven file handshake.1. Event-Driven Dataplane Synchronization (
emptyDirMarker File)Envoy fast-exits by default on SIGTERM. To keep Envoy alive while the Go control-plane orchestrates the drain, we configured an IPC handshake between containers via a pod-shared
emptyDirvolume mounted at/var/run/atenet:/var/run/atenet/drain-completewhen its shutdown sequence finishes.preStopHook: Runswhile [ ! -f /var/run/atenet/drain-complete ]; do sleep 0.5; done.Outcome: Envoy exits as soon as — the drain is done, rather than wasting time in a fixed worst-case sleep. If the router crashes, Kubelet terminates the
preStophook atterminationGracePeriodSeconds: 60—slower cleanup, never a wedge.2. The Multi-Container Shutdown Sequence (
drain.go)Because Kubernetes issues SIGTERM to both containers at once, a coordination state machine ensures Envoy never drops a connection and
ext_procis never stopped prematurely:(
• Flips
/readyz503.• Starts 13s
drain-delay.lifecycle.preStophook:while [ ! -f .../drain-complete ]; do sleep 0.5; done• Kubelet holds SIGTERM back.
EndpointSlice controller begins dropping Old Pod IP.
(
•
ext_proccontinues unparking/routing requests.preStoploop.• Serves active TCP connections.
No new connections arrive at Old Pod.
(
•
/healthcheck/fail•
/drain_listeners?graceful&skip_exit• Polls
/statsfor active downstream connections.• GOAWAY /
Connection: closeon established connections.• In-flight requests keep running.
(
• Calls
extproc.GracefulStop().• Parked request streams finish.
• Still waiting in the
preStoploop.(
ext_procdrain finishes.• Writes
drain-completemarker.• Hard-stops xDS (
Stop()) & exits.preStoploop detects marker file!•
preStopexits 0.• Kubelet sends SIGTERM to Envoy.
• Envoy exits immediately.
Envoy ref: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/operations/draining
Testing & Verification
drain_test.go): Added comprehensive unit tests covering:ext_procstreams within deadline.--drain-timeout.Test script:
hack/verify-atenet-drain.sh/readyz=200,/healthz=200; the instant the pod turned Terminating:/readyz=503while/healthz=200—NotReadybut alive, for the whole drain.preserved file counter: 1 — park → resume → route, served by the Terminating pod during its drain-delay window.
SIGKILL).