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.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")

View File

@ -65,8 +65,12 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
cloud cloudprovider.Interface, // typically Kubelet.cloud
nodeAddressesFunc func() ([]v1.NodeAddress, error), // typically Kubelet.cloudResourceSyncManager.NodeAddresses
) 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 {
if nodeIP != nil {
if nodeIPSpecified {
if err := validateNodeIPFunc(nodeIP); err != nil {
return fmt.Errorf("failed to validate nodeIP: %v", err)
}
@ -74,7 +78,7 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
}
if externalCloudProvider {
if nodeIP != nil {
if nodeIPSpecified {
if node.ObjectMeta.Annotations == nil {
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.
// 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.
if nodeIP != nil {
if nodeIPSpecified {
enforcedNodeAddresses := []v1.NodeAddress{}
nodeIPTypes := make(map[v1.NodeAddressType]bool)
@ -125,6 +129,23 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
}
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 {
// If nodeIP is unset, just use the addresses provided by the cloud provider as-is
nodeAddresses = cloudNodeAddresses
@ -168,12 +189,14 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
var ipAddr net.IP
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
// 3) Lookup the IP from node name by DNS and use the first valid IPv4 address.
// If the node does not have a valid IPv4 address, use the first valid IPv6 address.
// 3) Lookup the IP from node name by DNS
// 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
} else if addr := net.ParseIP(hostname); addr != nil {
ipAddr = addr
@ -182,18 +205,17 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
addrs, _ = net.LookupIP(node.Name)
for _, addr := range addrs {
if err = validateNodeIPFunc(addr); err == nil {
if addr.To4() != nil {
if isPreferredIPFamily(addr) {
ipAddr = addr
break
}
if addr.To16() != nil && ipAddr == nil {
} else if ipAddr == nil {
ipAddr = addr
}
}
}
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,
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 {
t.Run(testCase.name, func(t *testing.T) {