fix: create IPv6 EndpointSlice for kubelet Service on dual-stack clusters#8682
fix: create IPv6 EndpointSlice for kubelet Service on dual-stack clusters#8682ckrause wants to merge 7 commits into
Conversation
…ters getNodeAddresses() was discarding the full address map returned by nodeAddress() and only emitting a single IP per node, which always resolved to IPv4 on dual-stack clusters. This prevented the creation of an IPv6 EndpointSlice for the kubelet Service. With Prometheus 3.11 (prometheus/prometheus#18192), EndpointSlices whose addressType does not match svc.Spec.IPFamilies[0] are skipped to avoid duplicate scraping. On IPv6-primary dual-stack clusters this caused all kubelet targets to be dropped, as only the IPv4 EndpointSlice existed. Fix by iterating all InternalIP (or ExternalIP when priority is "external") addresses per node so that syncEndpointSlice() — which already has the dual-stack infrastructure — creates separate IPv4 and IPv6 EndpointSlices. Also set IPFamilyPolicy: PreferDualStack on the kubelet Service so Kubernetes assigns both ClusterIPs on dual-stack clusters; the policy degrades gracefully to single-stack on clusters that only support one IP family. Fixes prometheus-operator#8681
CreateOrUpdateService always copies IPFamilies and IPFamilyPolicy from the existing Service on update, so setting them in syncService() has no effect after initial creation. On first creation Kubernetes assigns the correct values based on the cluster's network configuration.
|
Update: I removed the redundant |
|
|
||
| for _, n := range nodes { | ||
| address, _, err := c.nodeAddress(n) | ||
| _, addrMap, err := c.nodeAddress(n) |
There was a problem hiding this comment.
should we change nodeAddress() to return the map only? If yes, we can also rename it.
| continue | ||
| // Select addresses by priority: use ExternalIP if priority is "external" | ||
| // and external addresses exist, otherwise fall back to InternalIP. | ||
| addrType := corev1.NodeInternalIP |
There was a problem hiding this comment.
should we use the NodeExternalIP addresses if the NodeInternalIP list is empty?
…p only The single string return value is no longer used after the fix to emit all InternalIP addresses per node. Simplify the signature to return the address map only and rename the function to better reflect its purpose.
…sesByType() Move the priority/fallback logic into a dedicated primaryAddressType() helper so getNodeAddresses() can use it to select the correct address type for dual-stack nodes. nodeAddressesByType() is simplified to just build and return the address map without any error return. Also restores the ExternalIP fallback that was missing after the initial dual-stack fix.
|
Hi @simonpasquier, thanks for your comments. I addressed both of them. Can you please check the new version? |
simonpasquier
left a comment
There was a problem hiding this comment.
Can/should we combine nodeAddressesByType and primaryAddressType in a single function? It would be good to add unit tests too.
| } | ||
|
|
||
| return "", m, fmt.Errorf("host address unknown") | ||
| return "", fmt.Errorf("host address unknown") |
There was a problem hiding this comment.
unreachable since we have a "default:" now.
There was a problem hiding this comment.
In theory, the trailing return is still reachable: if a node has neither NodeInternalIP nor NodeExternalIP addresses, neither if condition in the matching branch will be true, and execution falls through to the return after the switch. This covers both the "external" case and the default: case when no addresses are present. Moving the error into each branch would duplicate code.
sorry I missed that you already added them 👍 |
|
Thanks for the suggestion. I kept |
yes please |
|
Sure, done. |
|
Hi @simonpasquier , is it ready to merge from your side now? |
Description
getNodeAddresses()was discarding the full address map returned bynodeAddress()and only emitting a single IP per node, which always resolved to IPv4 on dual-stack clusters. This prevented the creation of an IPv6EndpointSlicefor the kubelet Service.With Prometheus 3.11 (prometheus/prometheus#18192),
EndpointSliceswhoseaddressTypedoes not matchsvc.Spec.IPFamilies[0]are skipped to avoid duplicate scraping. On IPv6-primary dual-stack clusters this caused all kubelet targets to be dropped, as only the IPv4EndpointSliceexisted.Fix by iterating all
InternalIP(orExternalIPwhen priority is "external") addresses per node so thatsyncEndpointSlice()— which already has the dual-stack infrastructure — creates separate IPv4 and IPv6EndpointSlices.Closes: #8681
Type of change
What type of changes does your code introduce to the Prometheus operator? Put an
xin the box that apply.CHANGE(fix or feature that would cause existing functionality to not work as expected)FEATURE(non-breaking change which adds functionality)BUGFIX(non-breaking change which fixes an issue)ENHANCEMENT(non-breaking change which improves existing functionality)NONE(if none of the other choices apply. Example, tooling, build system, CI, docs, etc.)Verification
Tested manually in a development cluster. Unit tests extended for dual-stack.
Changelog entry