Skip to content

feat: add healthCheckFailureDelay to shutdown config - #9363

Open
consideRatio wants to merge 2 commits into
envoyproxy:mainfrom
consideRatio:readiness-failure-delay
Open

feat: add healthCheckFailureDelay to shutdown config#9363
consideRatio wants to merge 2 commits into
envoyproxy:mainfrom
consideRatio:readiness-failure-delay

Conversation

@consideRatio

@consideRatio consideRatio commented Jun 28, 2026

Copy link
Copy Markdown

What

Adds healthCheckFailureDelay to ShutdownConfig.

When configured, the shutdown manager starts Envoy listener drain immediately with /drain_listeners?graceful&skip_exit, then delays /healthcheck/fail until the configured duration has elapsed. The default remains 0s, preserving the existing behavior where /healthcheck/fail starts listener drain immediately.

This is useful for environments where failing health checks (and thereby readiness) immediately can leave a node without ready local endpoints before upstream load balancers have stopped sending traffic to it.


In practice, I experienced the need for this on GKE with Cilium / (they call it Dataplane v2), with a envoy gateway helm chart deployed as Gateway API controller. The envoy gateway Service of type: LoadBalancer had externalTrafficPolicy: Local by default, and the GCP provided LoadBalancer for the Service resource takes many seconds to realize that no non-terminating pods on the node are available - so it kept sending traffic to the node. The node receiving the traffic then didn't pass it forward to other pods on other node (because of externalTrafficPolicy: Local), and since the pod on the node wasn't just terminating, but non-ready, new connection was refused.

Validation

I did a e2e test in the GKE cluster where I observed issues before, and confirmed that the issue was resolved using an image built from this PR branch.

go test ./internal/cmd/envoy ./internal/infrastructure/kubernetes/proxy ./api/v1alpha1/validation
git diff --check

@netlify

netlify Bot commented Jun 28, 2026

Copy link
Copy Markdown

Deploy Preview for cerulean-figolla-1f9435 ready!

Name Link
🔨 Latest commit 118e833
🔍 Latest deploy log https://app.netlify.com/projects/cerulean-figolla-1f9435/deploys/6a7034297f06150008c4c1e2
😎 Deploy Preview https://deploy-preview-9363--cerulean-figolla-1f9435.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@consideRatio
consideRatio marked this pull request as ready for review June 28, 2026 14:42
@consideRatio
consideRatio requested a review from a team as a code owner June 28, 2026 14:42
1. Kubernetes sends SIGTERM to the pod
2. Shutdown manager fails health checks via `/healthcheck/fail`
- This causes Kubernetes readiness probes to fail
- External load balancers and services stop routing new traffic to the pod

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They stop routing traffic, but it can take many seconds - for example for an load balancers like provided for GCP for a service of type: LoadBalancer.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7b759222e4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/cmd/envoy/shutdown_manager.go Outdated
@consideRatio

Copy link
Copy Markdown
Author

@zirain could you approve the CI to run?

- `readinessFailureDelay`: 0 seconds - Optional delay before failing readiness after drain starts

Configure `readinessFailureDelay` when failing readiness immediately would leave
the node without ready local endpoints before upstream load balancers have

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this section be rephrased so it highlights that this field may need to be set only when CNIs eagerly rm endpoints even when terminating ? relates to cilium/cilium#40969 (comment)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm my observation was that i had trouble with non-ready and terminating endpoints, not with terminating endpoints - the pod will still be terminating just not become non-ready - and this is being addressed in the issue.

It sounds like it is the reasonable behavior to not route traffic to a non-ready pod, no matter what.

I may figure out in more detail what you mean, and i'll think extensively about it during the weekend, but i can't yet clearly see when this ought/ought not to be configured. If you have a sharp description on when already, it could help me out a lot!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Key findings

  1. Cilium 1.18.3 introduced a fix (loadbalancer: Keep backends around that are terminating and not serving cilium/cilium#40969) for ensuring existing connections traffic arrive, and I've experienced issues with Cilium 1.18.7 related to new connections traffic
  2. The envoy gateway controller (not the envoy gateway helm chart itself) creates k8s Service resources with type: LoadBalancer and externalTrafficPolicy: Local by default
  3. A provisioned LoadBalancer for the Service resource will likely not itself be pod aware, and route to nodes. A controller will make it route to relevant nodes, but it may be slow to update its node targets, and slower than terminating pods become non-ready.
  4. The readiness probe is by default configured to be probed every 5 seconds with a failure threshold of 1, so a terminating pod will become non-ready in 0-5 seconds.
  5. A new connection arriving to a node, having only non-ready terminating pods, will fail to route to a pod.

My conclusions

Feature relevance

There is relevance for this feature whenever a provisioned Load Balancer routes to nodes, which I think it often will be, unless the readiness probe is adjusted to fail slower than the node targets are updated by the load balancer's controller.

I think something like this is an essential feature!

Related but off topic thinking

  • I doubt having the configured k8s health probe fail at all is desirable to signal something, and assume its an accepted side consequence of wanting to fail the readiness probe to signal something and using the same endpoint.
  • I'm not aware when it makes sense to fail the readiness probe to signal something, but for GKE's load balancers, its not relevant. They will route traffic elsewhere just by observing the only pods on a node are terminating - ready or not.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really struggle! Should the feature instead be a toggle of if the healthcheck/fail call should be made at all?

When is there value to calling healthcheck/fail as part of termination and make k8s health and readiness probes fail? When is there value in delaying that rather than toggling doing it or not?

I only know a value to not do it at all (solves my issue fully), and think it could be a better default even!

@consideRatio consideRatio Aug 2, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arkodg I transitioned this into draft, as I now question if a delay or toggle should be developed, and struggled so hard with docs on when to configure a specific delay - I however think it needs your or other maintainers input on this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey LGTM with the way it currently reads

Comment thread api/v1alpha1/envoyproxy_types.go Outdated
// If unspecified, defaults to 0 seconds.
//
// +optional
ReadinessFailureDelay *gwapiv1.Duration `json:"readinessFailureDelay,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we call this HealthCheckFailureDelay ? since it also impacts https://gateway.envoyproxy.io/docs/api/extension_types/#healthchecksettings

@arkodg arkodg added this to the v1.9.0-rc.1 Release milestone Jul 27, 2026
@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 25.00000% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.04%. Comparing base (8dcac65) to head (89d8e2e).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
internal/cmd/envoy/shutdown_manager.go 0.00% 15 Missing ⚠️
...ternal/infrastructure/kubernetes/proxy/resource.go 60.00% 1 Missing and 1 partial ⚠️
internal/cmd/envoy.go 75.00% 1 Missing ⚠️

❌ Your patch check has failed because the patch coverage (25.00%) is below the target coverage (60.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9363      +/-   ##
==========================================
- Coverage   76.04%   76.04%   -0.01%     
==========================================
  Files         259      259              
  Lines       43258    43278      +20     
==========================================
+ Hits        32896    32911      +15     
- Misses       8174     8180       +6     
+ Partials     2188     2187       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@arkodg

arkodg commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

hey @consideRatio does this PR fix the issue on GKE ? could you test this for http/1.1 and h2 with a moderate rps

@consideRatio

Copy link
Copy Markdown
Author

Thank you for investing time to this @arkodg!!

hey @consideRatio does this PR fix the issue on GKE

Yes fully, I'm very happy with the outcome! I'm using this (pre-rebase) branch build currently, making my service successfully respond to all requests even when spamming requests (http/1.1) via new and existing connection while a node with just one envoy pod is terminating (the previous failure mode).

could you test this for http/1.1 and h2 with a moderate rps

I aim to do this during the coming weekend. I'm currently vacationing away from my computer. To clarify, there isnt tooling or docs to run such test in the project already right?

@consideRatio
consideRatio force-pushed the readiness-failure-delay branch from 8c81595 to e6be2a9 Compare August 2, 2026 20:39
@consideRatio consideRatio changed the title feat: add readinessFailureDelay to shutdown config feat: add healthCheckFailureDelay to shutdown config Aug 2, 2026
@consideRatio
consideRatio force-pushed the readiness-failure-delay branch 7 times, most recently from f5c2235 to 300a264 Compare August 2, 2026 22:33
Allow Envoy listener drain to start immediately while delaying
`/healthcheck/fail` during pod termination. This helps deployments that
need the terminating pod to remain a ready local endpoint while
upstream load balancers stop sending traffic to the node.

The default remains 0s, preserving the existing behavior where
`/healthcheck/fail` starts listener drain immediately.

Signed-off-by: Erik Sundell <erik@sundellopensource.se>
@consideRatio
consideRatio force-pushed the readiness-failure-delay branch from 300a264 to 89d8e2e Compare August 2, 2026 22:46
@consideRatio
consideRatio marked this pull request as draft August 2, 2026 22:53
arkodg
arkodg previously approved these changes Aug 3, 2026

@arkodg arkodg left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM thanks

@arkodg

arkodg commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

hey @consideRatio does this PR fix the issue on GKE ? could you test this for http/1.1 and h2 with a moderate rps

any update on this ?

@consideRatio

consideRatio commented Aug 3, 2026

Copy link
Copy Markdown
Author

hey @consideRatio does this PR fix the issue on GKE ? could you test this for http/1.1 and h2 with a moderate rps

any update on this ?

I didnt have a http 2 workload, and ran out of time to spend =\ it is http 1.1 tested before and the functionality triggered as expected etc, but didnt have a existing http 2 workload

I'll rebuild an image on the current main and reverify every keeps working as expected with a after http 1.1 load and such, thr new config name, and now that its based on an updated main branch

@consideRatio
consideRatio marked this pull request as ready for review August 3, 2026 05:10

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 89d8e2ef71

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if err != nil {
return nil
}
command = append(command, fmt.Sprintf("--health-check-failure-delay=%.0fs", d.Seconds()))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve subsecond health-check delays

When healthCheckFailureDelay is configured with a valid subsecond value such as 400ms, formatting d.Seconds() with %.0f produces --health-check-failure-delay=0s. Shutdown consequently takes the immediate-failure branch, so the configured delay is lost entirely. The generated CRD explicitly accepts ms durations, so preserve the original duration or use d.String() rather than rounding to whole seconds.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed

@consideRatio
consideRatio force-pushed the readiness-failure-delay branch from 4f879fc to 118e833 Compare August 3, 2026 06:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a delay to the shutdown-manager before failing healthchecks

2 participants