Skip to content

Commit 61d36e4

Browse files
authored
Merge pull request kubernetes#85850 from danwinship/kubelet-ipv6-node-ip
Allow "kubelet --node-ip ::" to mean prefer IPv6
2 parents b008eda + ce68edf commit 61d36e4

File tree

3 files changed

+122
-12
lines changed

3 files changed

+122
-12
lines changed

cmd/kubelet/app/options/options.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ func (f *KubeletFlags) AddFlags(mainfs *pflag.FlagSet) {
365365

366366
fs.StringVar(&f.HostnameOverride, "hostname-override", f.HostnameOverride, "If non-empty, will use this string as identification instead of the actual hostname. If --cloud-provider is set, the cloud provider determines the name of the node (consult cloud provider documentation to determine if and how the hostname is used).")
367367

368-
fs.StringVar(&f.NodeIP, "node-ip", f.NodeIP, "IP address of the node. If set, kubelet will use this IP address for the node")
368+
fs.StringVar(&f.NodeIP, "node-ip", f.NodeIP, "IP address of the node. If set, kubelet will use this IP address for the node. If unset, kubelet will use the node's default IPv4 address, if any, or its default IPv6 address if it has no IPv4 addresses. You can pass `::` to make it prefer the default IPv6 address rather than the default IPv4 address.")
369369

370370
fs.StringVar(&f.ProviderID, "provider-id", f.ProviderID, "Unique identifier for identifying the node in a machine database, i.e cloudprovider")
371371

pkg/kubelet/nodestatus/setters.go

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,20 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
6565
cloud cloudprovider.Interface, // typically Kubelet.cloud
6666
nodeAddressesFunc func() ([]v1.NodeAddress, error), // typically Kubelet.cloudResourceSyncManager.NodeAddresses
6767
) Setter {
68+
preferIPv4 := nodeIP == nil || nodeIP.To4() != nil
69+
isPreferredIPFamily := func(ip net.IP) bool { return (ip.To4() != nil) == preferIPv4 }
70+
nodeIPSpecified := nodeIP != nil && !nodeIP.IsUnspecified()
71+
6872
return func(node *v1.Node) error {
69-
if nodeIP != nil {
73+
if nodeIPSpecified {
7074
if err := validateNodeIPFunc(nodeIP); err != nil {
7175
return fmt.Errorf("failed to validate nodeIP: %v", err)
7276
}
7377
klog.V(2).Infof("Using node IP: %q", nodeIP.String())
7478
}
7579

7680
if externalCloudProvider {
77-
if nodeIP != nil {
81+
if nodeIPSpecified {
7882
if node.ObjectMeta.Annotations == nil {
7983
node.ObjectMeta.Annotations = make(map[string]string)
8084
}
@@ -101,7 +105,7 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
101105
// that address Type (like InternalIP and ExternalIP), meaning other addresses of the same Type are discarded.
102106
// See #61921 for more information: some cloud providers may supply secondary IPs, so nodeIP serves as a way to
103107
// ensure that the correct IPs show up on a Node object.
104-
if nodeIP != nil {
108+
if nodeIPSpecified {
105109
enforcedNodeAddresses := []v1.NodeAddress{}
106110

107111
nodeIPTypes := make(map[v1.NodeAddressType]bool)
@@ -125,6 +129,23 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
125129
}
126130

127131
nodeAddresses = enforcedNodeAddresses
132+
} else if nodeIP != nil {
133+
// nodeIP is "0.0.0.0" or "::"; sort cloudNodeAddresses to
134+
// prefer addresses of the matching family
135+
sortedAddresses := make([]v1.NodeAddress, 0, len(cloudNodeAddresses))
136+
for _, nodeAddress := range cloudNodeAddresses {
137+
ip := net.ParseIP(nodeAddress.Address)
138+
if ip == nil || isPreferredIPFamily(ip) {
139+
sortedAddresses = append(sortedAddresses, nodeAddress)
140+
}
141+
}
142+
for _, nodeAddress := range cloudNodeAddresses {
143+
ip := net.ParseIP(nodeAddress.Address)
144+
if ip != nil && !isPreferredIPFamily(ip) {
145+
sortedAddresses = append(sortedAddresses, nodeAddress)
146+
}
147+
}
148+
nodeAddresses = sortedAddresses
128149
} else {
129150
// If nodeIP is unset, just use the addresses provided by the cloud provider as-is
130151
nodeAddresses = cloudNodeAddresses
@@ -168,12 +189,14 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
168189
var ipAddr net.IP
169190
var err error
170191

171-
// 1) Use nodeIP if set
192+
// 1) Use nodeIP if set (and not "0.0.0.0"/"::")
172193
// 2) If the user has specified an IP to HostnameOverride, use it
173-
// 3) Lookup the IP from node name by DNS and use the first valid IPv4 address.
174-
// If the node does not have a valid IPv4 address, use the first valid IPv6 address.
194+
// 3) Lookup the IP from node name by DNS
175195
// 4) Try to get the IP from the network interface used as default gateway
176-
if nodeIP != nil {
196+
//
197+
// For steps 3 and 4, IPv4 addresses are preferred to IPv6 addresses
198+
// unless nodeIP is "::", in which case it is reversed.
199+
if nodeIPSpecified {
177200
ipAddr = nodeIP
178201
} else if addr := net.ParseIP(hostname); addr != nil {
179202
ipAddr = addr
@@ -182,18 +205,17 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
182205
addrs, _ = net.LookupIP(node.Name)
183206
for _, addr := range addrs {
184207
if err = validateNodeIPFunc(addr); err == nil {
185-
if addr.To4() != nil {
208+
if isPreferredIPFamily(addr) {
186209
ipAddr = addr
187210
break
188-
}
189-
if addr.To16() != nil && ipAddr == nil {
211+
} else if ipAddr == nil {
190212
ipAddr = addr
191213
}
192214
}
193215
}
194216

195217
if ipAddr == nil {
196-
ipAddr, err = utilnet.ChooseHostInterface()
218+
ipAddr, err = utilnet.ResolveBindAddress(nodeIP)
197219
}
198220
}
199221

pkg/kubelet/nodestatus/setters_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,94 @@ func TestNodeAddress(t *testing.T) {
293293
hostnameOverride: true,
294294
shouldError: false,
295295
},
296+
{
297+
name: "Dual-stack cloud, IPv4 first, no nodeIP",
298+
nodeAddresses: []v1.NodeAddress{
299+
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
300+
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
301+
{Type: v1.NodeHostName, Address: testKubeletHostname},
302+
},
303+
expectedAddresses: []v1.NodeAddress{
304+
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
305+
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
306+
{Type: v1.NodeHostName, Address: testKubeletHostname},
307+
},
308+
shouldError: false,
309+
},
310+
{
311+
name: "Dual-stack cloud, IPv6 first, no nodeIP",
312+
nodeAddresses: []v1.NodeAddress{
313+
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
314+
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
315+
{Type: v1.NodeHostName, Address: testKubeletHostname},
316+
},
317+
expectedAddresses: []v1.NodeAddress{
318+
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
319+
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
320+
{Type: v1.NodeHostName, Address: testKubeletHostname},
321+
},
322+
shouldError: false,
323+
},
324+
{
325+
name: "Dual-stack cloud, IPv4 first, request IPv4",
326+
nodeIP: net.ParseIP("0.0.0.0"),
327+
nodeAddresses: []v1.NodeAddress{
328+
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
329+
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
330+
{Type: v1.NodeHostName, Address: testKubeletHostname},
331+
},
332+
expectedAddresses: []v1.NodeAddress{
333+
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
334+
{Type: v1.NodeHostName, Address: testKubeletHostname},
335+
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
336+
},
337+
shouldError: false,
338+
},
339+
{
340+
name: "Dual-stack cloud, IPv6 first, request IPv4",
341+
nodeIP: net.ParseIP("0.0.0.0"),
342+
nodeAddresses: []v1.NodeAddress{
343+
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
344+
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
345+
{Type: v1.NodeHostName, Address: testKubeletHostname},
346+
},
347+
expectedAddresses: []v1.NodeAddress{
348+
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
349+
{Type: v1.NodeHostName, Address: testKubeletHostname},
350+
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
351+
},
352+
shouldError: false,
353+
},
354+
{
355+
name: "Dual-stack cloud, IPv4 first, request IPv6",
356+
nodeIP: net.ParseIP("::"),
357+
nodeAddresses: []v1.NodeAddress{
358+
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
359+
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
360+
{Type: v1.NodeHostName, Address: testKubeletHostname},
361+
},
362+
expectedAddresses: []v1.NodeAddress{
363+
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
364+
{Type: v1.NodeHostName, Address: testKubeletHostname},
365+
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
366+
},
367+
shouldError: false,
368+
},
369+
{
370+
name: "Dual-stack cloud, IPv6 first, request IPv6",
371+
nodeIP: net.ParseIP("::"),
372+
nodeAddresses: []v1.NodeAddress{
373+
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
374+
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
375+
{Type: v1.NodeHostName, Address: testKubeletHostname},
376+
},
377+
expectedAddresses: []v1.NodeAddress{
378+
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
379+
{Type: v1.NodeHostName, Address: testKubeletHostname},
380+
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
381+
},
382+
shouldError: false,
383+
},
296384
}
297385
for _, testCase := range cases {
298386
t.Run(testCase.name, func(t *testing.T) {

0 commit comments

Comments
 (0)