Merge pull request #85850 from danwinship/kubelet-ipv6-node-ip

Allow "kubelet --node-ip ::" to mean prefer IPv6
This commit is contained in:
Kubernetes Prow Robot 2020-01-13 17:41:08 -08:00 committed by GitHub
commit 61d36e4a43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 122 additions and 12 deletions

View File

@ -365,7 +365,7 @@ func (f *KubeletFlags) AddFlags(mainfs *pflag.FlagSet) {
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).") 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).")
fs.StringVar(&f.NodeIP, "node-ip", f.NodeIP, "IP address of the node. If set, kubelet will use this IP address for the node") 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.")
fs.StringVar(&f.ProviderID, "provider-id", f.ProviderID, "Unique identifier for identifying the node in a machine database, i.e cloudprovider") fs.StringVar(&f.ProviderID, "provider-id", f.ProviderID, "Unique identifier for identifying the node in a machine database, i.e cloudprovider")

View File

@ -65,8 +65,12 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
cloud cloudprovider.Interface, // typically Kubelet.cloud cloud cloudprovider.Interface, // typically Kubelet.cloud
nodeAddressesFunc func() ([]v1.NodeAddress, error), // typically Kubelet.cloudResourceSyncManager.NodeAddresses nodeAddressesFunc func() ([]v1.NodeAddress, error), // typically Kubelet.cloudResourceSyncManager.NodeAddresses
) Setter { ) Setter {
preferIPv4 := nodeIP == nil || nodeIP.To4() != nil
isPreferredIPFamily := func(ip net.IP) bool { return (ip.To4() != nil) == preferIPv4 }
nodeIPSpecified := nodeIP != nil && !nodeIP.IsUnspecified()
return func(node *v1.Node) error { return func(node *v1.Node) error {
if nodeIP != nil { if nodeIPSpecified {
if err := validateNodeIPFunc(nodeIP); err != nil { if err := validateNodeIPFunc(nodeIP); err != nil {
return fmt.Errorf("failed to validate nodeIP: %v", err) return fmt.Errorf("failed to validate nodeIP: %v", err)
} }
@ -74,7 +78,7 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
} }
if externalCloudProvider { if externalCloudProvider {
if nodeIP != nil { if nodeIPSpecified {
if node.ObjectMeta.Annotations == nil { if node.ObjectMeta.Annotations == nil {
node.ObjectMeta.Annotations = make(map[string]string) node.ObjectMeta.Annotations = make(map[string]string)
} }
@ -101,7 +105,7 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
// that address Type (like InternalIP and ExternalIP), meaning other addresses of the same Type are discarded. // that address Type (like InternalIP and ExternalIP), meaning other addresses of the same Type are discarded.
// See #61921 for more information: some cloud providers may supply secondary IPs, so nodeIP serves as a way to // See #61921 for more information: some cloud providers may supply secondary IPs, so nodeIP serves as a way to
// ensure that the correct IPs show up on a Node object. // ensure that the correct IPs show up on a Node object.
if nodeIP != nil { if nodeIPSpecified {
enforcedNodeAddresses := []v1.NodeAddress{} enforcedNodeAddresses := []v1.NodeAddress{}
nodeIPTypes := make(map[v1.NodeAddressType]bool) nodeIPTypes := make(map[v1.NodeAddressType]bool)
@ -125,6 +129,23 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
} }
nodeAddresses = enforcedNodeAddresses nodeAddresses = enforcedNodeAddresses
} else if nodeIP != nil {
// nodeIP is "0.0.0.0" or "::"; sort cloudNodeAddresses to
// prefer addresses of the matching family
sortedAddresses := make([]v1.NodeAddress, 0, len(cloudNodeAddresses))
for _, nodeAddress := range cloudNodeAddresses {
ip := net.ParseIP(nodeAddress.Address)
if ip == nil || isPreferredIPFamily(ip) {
sortedAddresses = append(sortedAddresses, nodeAddress)
}
}
for _, nodeAddress := range cloudNodeAddresses {
ip := net.ParseIP(nodeAddress.Address)
if ip != nil && !isPreferredIPFamily(ip) {
sortedAddresses = append(sortedAddresses, nodeAddress)
}
}
nodeAddresses = sortedAddresses
} else { } else {
// If nodeIP is unset, just use the addresses provided by the cloud provider as-is // If nodeIP is unset, just use the addresses provided by the cloud provider as-is
nodeAddresses = cloudNodeAddresses nodeAddresses = cloudNodeAddresses
@ -168,12 +189,14 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
var ipAddr net.IP var ipAddr net.IP
var err error var err error
// 1) Use nodeIP if set // 1) Use nodeIP if set (and not "0.0.0.0"/"::")
// 2) If the user has specified an IP to HostnameOverride, use it // 2) If the user has specified an IP to HostnameOverride, use it
// 3) Lookup the IP from node name by DNS and use the first valid IPv4 address. // 3) Lookup the IP from node name by DNS
// If the node does not have a valid IPv4 address, use the first valid IPv6 address.
// 4) Try to get the IP from the network interface used as default gateway // 4) Try to get the IP from the network interface used as default gateway
if nodeIP != nil { //
// For steps 3 and 4, IPv4 addresses are preferred to IPv6 addresses
// unless nodeIP is "::", in which case it is reversed.
if nodeIPSpecified {
ipAddr = nodeIP ipAddr = nodeIP
} else if addr := net.ParseIP(hostname); addr != nil { } else if addr := net.ParseIP(hostname); addr != nil {
ipAddr = addr ipAddr = addr
@ -182,18 +205,17 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
addrs, _ = net.LookupIP(node.Name) addrs, _ = net.LookupIP(node.Name)
for _, addr := range addrs { for _, addr := range addrs {
if err = validateNodeIPFunc(addr); err == nil { if err = validateNodeIPFunc(addr); err == nil {
if addr.To4() != nil { if isPreferredIPFamily(addr) {
ipAddr = addr ipAddr = addr
break break
} } else if ipAddr == nil {
if addr.To16() != nil && ipAddr == nil {
ipAddr = addr ipAddr = addr
} }
} }
} }
if ipAddr == nil { if ipAddr == nil {
ipAddr, err = utilnet.ChooseHostInterface() ipAddr, err = utilnet.ResolveBindAddress(nodeIP)
} }
} }

View File

@ -293,6 +293,94 @@ func TestNodeAddress(t *testing.T) {
hostnameOverride: true, hostnameOverride: true,
shouldError: false, shouldError: false,
}, },
{
name: "Dual-stack cloud, IPv4 first, no nodeIP",
nodeAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
{Type: v1.NodeHostName, Address: testKubeletHostname},
},
expectedAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
{Type: v1.NodeHostName, Address: testKubeletHostname},
},
shouldError: false,
},
{
name: "Dual-stack cloud, IPv6 first, no nodeIP",
nodeAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeHostName, Address: testKubeletHostname},
},
expectedAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeHostName, Address: testKubeletHostname},
},
shouldError: false,
},
{
name: "Dual-stack cloud, IPv4 first, request IPv4",
nodeIP: net.ParseIP("0.0.0.0"),
nodeAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
{Type: v1.NodeHostName, Address: testKubeletHostname},
},
expectedAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeHostName, Address: testKubeletHostname},
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
},
shouldError: false,
},
{
name: "Dual-stack cloud, IPv6 first, request IPv4",
nodeIP: net.ParseIP("0.0.0.0"),
nodeAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeHostName, Address: testKubeletHostname},
},
expectedAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeHostName, Address: testKubeletHostname},
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
},
shouldError: false,
},
{
name: "Dual-stack cloud, IPv4 first, request IPv6",
nodeIP: net.ParseIP("::"),
nodeAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
{Type: v1.NodeHostName, Address: testKubeletHostname},
},
expectedAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
{Type: v1.NodeHostName, Address: testKubeletHostname},
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
},
shouldError: false,
},
{
name: "Dual-stack cloud, IPv6 first, request IPv6",
nodeIP: net.ParseIP("::"),
nodeAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeHostName, Address: testKubeletHostname},
},
expectedAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
{Type: v1.NodeHostName, Address: testKubeletHostname},
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
},
shouldError: false,
},
} }
for _, testCase := range cases { for _, testCase := range cases {
t.Run(testCase.name, func(t *testing.T) { t.Run(testCase.name, func(t *testing.T) {