Skip to content

feat(dns): pod-network cluster-DNS probe via overlay-test pods (task 242) - #28

Merged
mattmattox merged 1 commit into
mainfrom
feat/pod-network-cluster-dns-242
Jul 4, 2026
Merged

feat(dns): pod-network cluster-DNS probe via overlay-test pods (task 242)#28
mattmattox merged 1 commit into
mainfrom
feat/pod-network-cluster-dns-242

Conversation

@mattmattox

Copy link
Copy Markdown
Contributor

Summary

Implements the correct way to monitor cluster DNS on this fleet — the real fix behind the gated re-enable.

Why the in-agent check can't work

node-doctor runs hostNetwork. Verified live on a1-ops-prd: from the host netns, kubernetes.default.svc.a1-ops-prd.local (the correct domain) → NXDOMAIN via kube-dns ClusterIP, while the same name resolves from a pod and external domains resolve from the host. Cilium doesn't route host-netns→ClusterIP for cluster records. So no domain (derived or explicit) lets node-doctor resolve cluster DNS — the check stays disabled (clusterDomains: []).

The fix

  • pkg/clusterdns — shared cluster-domain derivation (moved out of dns.go) + Probe() (resolves kubernetes.default.svc.<derived>).
  • overlay-test-server /clusterdns — runs Probe() in the pod's pod-network context, where cluster records DO resolve.
  • new monitor network-cluster-dns-pod — discovers overlay-test pods, HTTP-probes their /clusterdns (node-doctor can reach pod IPs from host netns — that's how the CNI overlay test works — just not the ClusterIP), and drives ClusterDNSDown from pod-sourced truth with DNSMonitor-style consecutive-failure latching. No-peers cycle is a no-op (no false positive).

Safety / rollout

  • Gated: clusterDnsPodProbe.enabled defaults false → ships inert.
  • Deploy ordering: requires the overlay-test image serving /clusterdns (this release). Enabling against an older overlay-test image 404s every probe → false ClusterDNSDown. So the re-enable = roll v1.8.3 (new overlay-test image) then flip the toggle, in a watched window with canary.

Tests

  • pkg/clusterdns: derivation (standard/custom/fallback) + hermetic Probe.
  • Monitor: all-resolved→False, all-fail-past-threshold→True, partial≥minSuccess→False, no-peers→no condition. Fake clientset + httptest, deterministic.

Task: #19561 (242). Extends the hostnetwork-agent-cannot-resolve-clusterip-dns-cilium-probe-from-pod finding.

…242)

node-doctor runs hostNetwork; from the host netns the kube-dns ClusterIP does NOT
resolve CLUSTER records (Cilium host-netns->ClusterIP NXDOMAINs any cluster domain,
even the correct one — verified on a1-ops-prd), so the in-agent cluster-DNS check
cannot work and stays disabled (clusterDomains: []). This adds the correct path:

- pkg/clusterdns: shared cluster-domain derivation (moved out of dns.go) + Probe()
  that resolves kubernetes.default.svc.<derived> via net.Resolver.
- overlay-test-server: new /clusterdns endpoint runs Probe() in the pod's own
  (pod-network) context, where cluster records DO resolve, returning JSON.
- new monitor network-cluster-dns-pod: discovers overlay-test pods, HTTP-probes their
  /clusterdns (node-doctor CAN reach pod IPs from host netns, just not the ClusterIP),
  and drives ClusterDNSDown from pod-sourced truth with DNSMonitor-style consecutive-
  failure latching. No-peers cycle leaves the counter untouched (no false positive).
- chart: gated behind clusterDnsPodProbe.enabled (default FALSE) so it ships inert.
  Requires an overlay-test image serving /clusterdns (this release); enabling against
  an older overlay-test image would 404 every probe -> false ClusterDNSDown.

Tests: clusterdns derivation/probe (hermetic), monitor all-resolved/all-fail-threshold/
partial-min-success/no-peers via fake clientset + httptest. Task #19561 (242).
Re-enable (flip clusterDnsPodProbe.enabled + roll new overlay-test image) is a watched-
window decision.
// cluster domain from its `search` line. Returns ("", false) if the file can't be read
// or no cluster domain can be identified.
func DeriveClusterDomainFromResolver(resolverPath string) (string, bool) {
file, err := os.Open(resolverPath)
result := clusterdns.Probe(r.Context(), "/etc/resolv.conf")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(result) //nolint:errcheck // best-effort response

@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: 6888c6c260

ℹ️ 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 on lines +391 to +392
status.AddCondition(types.NewCondition(
"ClusterDNSDown",

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 Stop dns-health from clearing pod DNS failures

When this opt-in monitor reports ClusterDNSDown=True, the chart still also runs dns-health with clusterDomains: []; DNSMonitor.checkDNSDomains treats an empty cluster list as success and updateFailureTracking emits ClusterDNSDown=False every cycle. The Kubernetes condition manager keys updates only by condition type, so the next dns-health status can overwrite this pod-sourced failure and make the new alert depend on monitor ordering instead of the actual pod probe result. Suppress the old cluster condition when cluster DNS is disabled, or have this monitor use a separate condition.

Useful? React with 👍 / 👎.

checkNameservers: true
failureCountThreshold: 3
enableNameserverChecks: true
{{- if .Values.clusterDnsPodProbe.enabled }}

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 Guard the new Helm value before dereferencing it

The checked-in chart values file (helm/node-doctor/values.yaml) still has no clusterDnsPodProbe block, so rendering/installing helm/node-doctor directly from the repo evaluates .Values.clusterDnsPodProbe as nil here and fails before the intended disabled default can apply. The defaults were added only to values.yaml.template, which helps the release-generation path but not local chart users or CI that renders the committed chart; add the block to values.yaml as well or use a nil-safe guard/default.

Useful? React with 👍 / 👎.

@mattmattox
mattmattox merged commit c69c3a8 into main Jul 4, 2026
9 of 10 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.

2 participants