Skip to content

fix(infra): set explicit host on envoy PreStop httpGet to fix hostNetwork drain - #9861

Merged
zirain merged 5 commits into
envoyproxy:mainfrom
pujitha24:auto/issue-9853
Sep 11, 2026
Merged

zirain merged 5 commits into
envoyproxy:mainfrom
pujitha24:auto/issue-9853

Conversation

@pujitha24

Copy link
Copy Markdown
Contributor

What this PR does / why we need it:
In hostNetwork: true deployments, the Envoy container's PreStop lifecycle
hook (an httpGet call to the shutdown-manager sidecar's /shutdown/ready
endpoint on port 19002) fails with a kubelet error: "failed to find
networking container". Because the PreStop hook never completes, the
shutdown-manager never gets a chance to drain connections and fail active
health checks before Envoy exits, so in-flight connections can be dropped
on pod termination.

This is caused by a confirmed upstream kubelet bug
(kubernetes/kubernetes#134285): for hostNetwork
pods, the PodSandboxStatus reported by the CRI has an empty pod IP, so
kubelet cannot resolve an implicit target address for the PreStop httpGet
action.

Approach:
Set Host: "127.0.0.1" explicitly on the envoy container's PreStop
httpGet action in internal/infrastructure/kubernetes/proxy/resource.go,
instead of leaving it unset (which triggers kubelet's buggy pod-IP
resolution path). Golden testdata files for the affected deployments and
daemonsets are regenerated to include host: 127.0.0.1 under the PreStop
httpGet spec.

Which issue(s) this PR fixes:
Fixes #

Assisted-by: claude-sonnet-5 (via Claude Code)


PR Checklist

  • Authorship & ownership: Coding agents / AI assistants are welcome, but I have reviewed every change, understand how and why it works, can explain and maintain it, and take full responsibility for this PR. I have not submitted generated output I do not understand.
  • DCO: All commits are signed off (git commit -s).
  • API agreed first: N/A this PR does not contain API changes.
  • Required checks pass: go build ./internal/infrastructure/kubernetes/proxy/..., go test ./internal/infrastructure/kubernetes/proxy/..., and golangci-lint run ./internal/infrastructure/kubernetes/proxy/... (0 issues) all pass locally. make generate gen-check N/A (no API/CRD/helm changes).
  • Tests added/updated: Existing golden-file tests (TestDeployment, TestDaemonSet, TestGatewayNamespaceModeMultipleResources) cover this container spec; regenerated via the repo's own -override-testdata flag after confirming they failed against the old golden data (Host: "" -> "127.0.0.1") and pass against the new golden data.
  • Docs: N/A, no user-facing docs changes; this only changes an internal container spec.
  • Release notes: Added release-notes/current/bug_fixes/9853-envoy-prestop-hostnetwork.md.
  • Generated files committed: N/A, no API/helm chart changes.
  • Scope & compatibility: Change is limited to one Go file plus mechanically regenerated golden testdata; backward compatible (no user-facing config or API changes).
  • Codex review: Requested a Codex review and addressed all of its comments.
  • Copilot review: Requested a Copilot review and addressed all of its comments.

Validation: I do not have access to a live hostNetwork Kubernetes cluster
in this environment, so I could not reproduce the exact end-to-end kubelet
failure. Instead: (1) go test ./internal/infrastructure/kubernetes/proxy/...
failed against the pre-fix golden data with a diff showing
Host: (string) "" -> Host: (string) "127.0.0.1", confirming the code
change takes effect; (2) after regenerating golden data with
-override-testdata, the full package test suite passes; (3)
golangci-lint run ./internal/infrastructure/kubernetes/proxy/... reports
0 issues. The root cause is a documented, confirmed upstream kubelet bug
referenced directly in the linked issue's comments.

Report: #9853
Signed-off-by: Pujitha Paladugu 10557236+pujitha24@users.noreply.github.com


AI assistance: this change was drafted with Claude Code.

@pujitha24
pujitha24 requested a review from a team as a code owner August 27, 2026 04:12
@netlify

netlify Bot commented Aug 27, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for cerulean-figolla-1f9435 ready!

Name Link
🔨 Latest commit f773b38
🔍 Latest deploy log https://app.netlify.com/projects/cerulean-figolla-1f9435/deploys/6a9e4f682702b800084e8d9d
😎 Deploy Preview https://deploy-preview-9861--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.

@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: 3e84c3ae56

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

// pod IP for the PreStop hook in hostNetwork mode, causing the
// hook to fail with "failed to find networking container"
// (see https://github.com/kubernetes/kubernetes/issues/134285).
Host: "127.0.0.1",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid loopback for non-hostNetwork lifecycle hooks

When the generated pod does not use hostNetwork—including the default Deployment and DaemonSet configurations—the kubelet sends this HTTP lifecycle request from the node network namespace, so 127.0.0.1:19002 refers to the node rather than the pod where the shutdown-manager listens. The PreStop hook will therefore fail for ordinary pods, disabling graceful draining for the majority of installations; set this host only for host-networked pods or use a hook executed inside the pod's network namespace.

Useful? React with 👍 / 👎.

@codecov

codecov Bot commented Aug 27, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.32%. Comparing base (f330f3a) to head (b3dfb8f).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9861      +/-   ##
==========================================
+ Coverage   76.29%   76.32%   +0.02%     
==========================================
  Files         261      261              
  Lines       44607    44623      +16     
==========================================
+ Hits        34034    34059      +25     
+ Misses       8318     8311       -7     
+ Partials     2255     2253       -2     

☔ 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.

// This must not be set for non-hostNetwork pods: the kubelet runs
// HTTPGet lifecycle hooks from the node's network namespace, so
// "127.0.0.1" would target the node itself rather than the pod.
preStopHTTPGet.Host = "127.0.0.1"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

what about IPv6?
TBH, I don't think we should do this in code, mentioned it in somewhere should be enough.

@pujitha24

Copy link
Copy Markdown
Contributor Author

Good point on IPv6 — hardcoding 127.0.0.1 only works for IPv4/dual-stack pods; a hostNetwork pod running IPv6-only would need ::1 instead, and this function doesn't currently have the pod's IP family threaded through to pick correctly, so as written this only fixes the IPv4 case.

On whether this belongs in code at all: that's a fair call to make either way, and not one I want to push back on. If you'd rather we just document the kubelet bug and the workaround (e.g. a note in the hostNetwork docs) instead of carrying IP-family branching logic here for it, I'm fine closing this out in favor of that — happy to send that doc note if that's the preferred direction, just let me know.

@arkodg

arkodg commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

-1 to adding code that adds complexity for the patch case to solve an upstream bug, this could be worked around with another patch for httpGet

@pujitha24

Copy link
Copy Markdown
Contributor Author

Fair point on IPv6 — 127.0.0.1 only works for IPv4 hostNetwork pods, and for dual-stack it's ambiguous which family the kubelet would even route through, so as written this only covers one case, not "IPv6" generically.

On doing it in code at all: I don't have a strong argument against just documenting it as a known kubelet limitation (with a link to kubernetes/kubernetes#134285) instead. Would you rather I drop this code change and turn it into a docs note, or is it worth extending to use spec.ipFamily (which the proxy container spec already threads through elsewhere) to pick the right loopback address? I don't want to guess at which you'd prefer.

@arkodg

arkodg commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fair point on IPv6 — 127.0.0.1 only works for IPv4 hostNetwork pods, and for dual-stack it's ambiguous which family the kubelet would even route through, so as written this only covers one case, not "IPv6" generically.

On doing it in code at all: I don't have a strong argument against just documenting it as a known kubelet limitation (with a link to kubernetes/kubernetes#134285) instead. Would you rather I drop this code change and turn it into a docs note, or is it worth extending to use spec.ipFamily (which the proxy container spec already threads through elsewhere) to pick the right loopback address? I don't want to guess at which you'd prefer.

+1 to adding gotchas + workarounds in docs

pujitha24 added a commit to pujitha24/gateway that referenced this pull request Aug 31, 2026
…code workaround

Per maintainer feedback on PR envoyproxy#9861 (zirain: IPv6 isn't handled and this
shouldn't live in code; arkodg: prefer documenting the workaround), revert
the Host override on the envoy PreStop httpGet hook and instead document
the kubelet hostNetwork bug and the patch-based workaround.

Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
@pujitha24

Copy link
Copy Markdown
Contributor Author

Made this a docs-only note per the discussion above (dropped the code path, added the kubelet bug + workaround to the hostNetwork docs) — let me know if the wording needs adjusting or anything else would help move this along.

@arkodg
arkodg requested a review from zirain September 7, 2026 05:37
…work drain

**What this PR does / why we need it**:
In `hostNetwork: true` deployments, the Envoy container's PreStop lifecycle
hook (an httpGet call to the shutdown-manager sidecar's `/shutdown/ready`
endpoint on port 19002) fails with a kubelet error: "failed to find
networking container". Because the PreStop hook never completes, the
shutdown-manager never gets a chance to drain connections and fail active
health checks before Envoy exits, so in-flight connections can be dropped
on pod termination.

This is caused by a confirmed upstream kubelet bug
(kubernetes/kubernetes#134285): for hostNetwork
pods, the PodSandboxStatus reported by the CRI has an empty pod IP, so
kubelet cannot resolve an implicit target address for the PreStop httpGet
action.

**Approach**:
Set `Host: "127.0.0.1"` explicitly on the envoy container's PreStop
httpGet action in `internal/infrastructure/kubernetes/proxy/resource.go`,
instead of leaving it unset (which triggers kubelet's buggy pod-IP
resolution path). Golden testdata files for the affected deployments and
daemonsets are regenerated to include `host: 127.0.0.1` under the PreStop
httpGet spec.

**Which issue(s) this PR fixes**:
Fixes #

Assisted-by: claude-sonnet-5 (via Claude Code)

---

**PR Checklist**

- [x] **Authorship & ownership**: Coding agents / AI assistants are welcome, but I have reviewed every change, understand how and why it works, can explain and maintain it, and take full responsibility for this PR. I have not submitted generated output I do not understand.
- [x] **DCO**: All commits are signed off (`git commit -s`).
- [x] **API agreed first**: N/A this PR does not contain API changes.
- [x] **Required checks pass**: `go build ./internal/infrastructure/kubernetes/proxy/...`, `go test ./internal/infrastructure/kubernetes/proxy/...`, and `golangci-lint run ./internal/infrastructure/kubernetes/proxy/...` (0 issues) all pass locally. `make generate gen-check` N/A (no API/CRD/helm changes).
- [x] **Tests added/updated**: Existing golden-file tests (TestDeployment, TestDaemonSet, TestGatewayNamespaceModeMultipleResources) cover this container spec; regenerated via the repo's own `-override-testdata` flag after confirming they failed against the old golden data (Host: "" -> "127.0.0.1") and pass against the new golden data.
- [x] **Docs**: N/A, no user-facing docs changes; this only changes an internal container spec.
- [x] **Release notes**: Added `release-notes/current/bug_fixes/9853-envoy-prestop-hostnetwork.md`.
- [x] **Generated files committed**: N/A, no API/helm chart changes.
- [x] **Scope & compatibility**: Change is limited to one Go file plus mechanically regenerated golden testdata; backward compatible (no user-facing config or API changes).
- [ ] **Codex review**: Requested a Codex review and addressed all of its comments.
- [ ] **Copilot review**: Requested a Copilot review and addressed all of its comments.

**Validation**: I do not have access to a live hostNetwork Kubernetes cluster
in this environment, so I could not reproduce the exact end-to-end kubelet
failure. Instead: (1) `go test ./internal/infrastructure/kubernetes/proxy/...`
failed against the pre-fix golden data with a diff showing
`Host: (string) "" -> Host: (string) "127.0.0.1"`, confirming the code
change takes effect; (2) after regenerating golden data with
`-override-testdata`, the full package test suite passes; (3)
`golangci-lint run ./internal/infrastructure/kubernetes/proxy/...` reports
0 issues. The root cause is a documented, confirmed upstream kubelet bug
referenced directly in the linked issue's comments.

Report: envoyproxy#9853
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Setting Host: 127.0.0.1 unconditionally on the envoy PreStop hook broke
graceful shutdown for the default (non-hostNetwork) Deployment/DaemonSet
case, since kubelet runs HTTPGet lifecycle hooks from the node's network
namespace: hardcoding the host made the request target the node instead
of the pod. This was caught by all 6 e2e-test jobs failing on
TestEGUpgrade/EnvoyShutdown, and matches a concern raised by an automated
review bot on the PR.

hostNetwork is only ever enabled here via a user-supplied Deployment/
DaemonSet patch (there's no first-class API field for it), so detect it
by inspecting that patch and only apply the Host override when it's set,
restoring the original working behavior otherwise.

Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
…k pods only"

This reverts commit b3dfb8f.

Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
… hostNetwork drain"

This reverts commit 3e84c3a.

Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
…code workaround

Per maintainer feedback on PR envoyproxy#9861 (zirain: IPv6 isn't handled and this
shouldn't live in code; arkodg: prefer documenting the workaround), revert
the Host override on the envoy PreStop httpGet hook and instead document
the kubelet hostNetwork bug and the patch-based workaround.

Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
@pujitha24

Copy link
Copy Markdown
Contributor Author

Fixed the failing DCO check — the two revert commits were missing Signed-off-by (plain git revert doesn't add it automatically). Rebased with --signoff to add it to just those two; no file content changed, only commit metadata, so this shouldn't affect the review.

@zirain
zirain merged commit af6ec0b into envoyproxy:main Sep 11, 2026
29 checks passed
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.

3 participants