kubernetes apiserver源码: advertise address选择网卡

404 阅读4分钟

kubernetes version: v1.21.14

源码分析

// Complete set default ServerRunOptions.
// Should be called after kube-apiserver flags parsed.
func Complete(s *options.ServerRunOptions) (completedServerRunOptions, error) {
	var options completedServerRunOptions
	// 设置默认网卡
	if err := s.GenericServerRunOptions.DefaultAdvertiseAddress(s.SecureServing.SecureServingOptions); err != nil {
		return options, err
	}
        // ...
}        
  • k8s.io/apiserver/pkg/server/options/server_run_options.go
// DefaultAdvertiseAddress sets the field AdvertiseAddress if unset. The field will be set based on the SecureServingOptions.
func (s *ServerRunOptions) DefaultAdvertiseAddress(secure *SecureServingOptions) error {
	if secure == nil {
		return nil
	}

	if s.AdvertiseAddress == nil || s.AdvertiseAddress.IsUnspecified() {
                // 获取外部网卡地址
		hostIP, err := secure.DefaultExternalAddress()
		if err != nil {
			return fmt.Errorf("Unable to find suitable network address.error='%v'. "+
				"Try to set the AdvertiseAddress directly or provide a valid BindAddress to fix this.", err)
		}
		s.AdvertiseAddress = hostIP
	}

	return nil
}

  • k8s.io/apiserver/pkg/server/options/serving.go
func (s *SecureServingOptions) DefaultExternalAddress() (net.IP, error) {
	if s.ExternalAddress != nil && !s.ExternalAddress.IsUnspecified() {
		return s.ExternalAddress, nil
	}
        // 获取外部网卡,s.BindAddress可以为nil
	return utilnet.ResolveBindAddress(s.BindAddress)
}
  • k8s.io/apimachinery/pkg/util/net/interface.go
// ResolveBindAddress returns the IP address of a daemon, based on the given bindAddress:
// If bindAddress is unset, it returns the host's default IP, as with ChooseHostInterface().
// If bindAddress is unspecified or loopback, it returns the default IP of the same
// address family as bindAddress.
// Otherwise, it just returns bindAddress.
func ResolveBindAddress(bindAddress net.IP) (net.IP, error) {
	addressFamilies := preferIPv4
	if bindAddress != nil && memberOf(bindAddress, familyIPv6) {
		addressFamilies = preferIPv6
	}
        // 任何回环地址或者未指定地址,都将采用默认策略
	if bindAddress == nil || bindAddress.IsUnspecified() || bindAddress.IsLoopback() {
		// 根据偏好选择ipv4
                hostIP, err := chooseHostInterface(addressFamilies)
		if err != nil {
			return nil, err
		}
		bindAddress = hostIP
	}
	return bindAddress, nil
}
func chooseHostInterface(addressFamilies AddressFamilyPreference) (net.IP, error) {
	var nw networkInterfacer = networkInterface{}
        // 查看/proc/net/route文件
	if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) {
		return chooseIPFromHostInterfaces(nw, addressFamilies)
	}
        // 获取路由
	routes, err := getAllDefaultRoutes()
	if err != nil {
		return nil, err
	}
        // 从默认路由网卡中选择地址
	return chooseHostInterfaceFromRoute(routes, nw, addressFamilies)
}

/proc/net/route文件的内容如下, 其中Destination全0的是默认网关:

$ cat /proc/net/route
Iface   Destination     Gateway         Flags   RefCnt  Use     Metric  Mask            MTU     Window  IRTT                                                       
ens33   00000000        020AA8C0        0003    0       0       100     00000000        0       0       0                                                                            
docker0 000011AC        00000000        0001    0       0       0       0000FFFF        0       0       0                                                                            
ens33   000AA8C0        00000000        0001    0       0       100     00FFFFFF        0       0       0                                                                            
virbr0  007AA8C0        00000000        0001    0       0       0       00FFFFFF        0       0       0    
  • chooseHostInterface -> getAllDefaultRoutes
// getAllDefaultRoutes obtains IPv4 and IPv6 default routes on the node. If unable
// to read the IPv4 routing info file, we return an error. If unable to read the IPv6
// routing info file (which is optional), we'll just use the IPv4 route information.
// Using all the routing info, if no default routes are found, an error is returned.
func getAllDefaultRoutes() ([]Route, error) {
        // 提取ipv4地址
	routes, err := v4File.extract()
	if err != nil {
		return nil, err
	}
	v6Routes, _ := v6File.extract()
	routes = append(routes, v6Routes...)
	if len(routes) == 0 {
		return nil, noRoutesError{
			message: fmt.Sprintf("no default routes found in %q or %q", v4File.name, v6File.name),
		}
	}
	return routes, nil
}

这里的extract实际上是一个跳板函数,实际调用的是getIPv4DefaultRoutes

var (
	v4File = RouteFile{name: ipv4RouteFile, parse: getIPv4DefaultRoutes} 
	v6File = RouteFile{name: ipv6RouteFile, parse: getIPv6DefaultRoutes}
)
// v4File.extract()
func (rf RouteFile) extract() ([]Route, error) {
        // 打开/proc/net/route文件
	file, err := os.Open(rf.name)
	if err != nil {
		return nil, err
	}
	defer file.Close()
        // 调用getIPv4DefaultRoutes
	return rf.parse(file)
}

// ...

// getIPv4DefaultRoutes obtains the IPv4 routes, and filters out non-default routes.
func getIPv4DefaultRoutes(input io.Reader) ([]Route, error) {
	routes := []Route{}
	scanner := bufio.NewReader(input)
	for {
		line, err := scanner.ReadString('\n')
		if err == io.EOF {
			break
		}
		//ignore the headers in the route info
		if strings.HasPrefix(line, "Iface") {
			continue
		}
		fields := strings.Fields(line)
		// Interested in fields:
		//  0 - interface name
		//  1 - destination address
		//  2 - gateway
		dest, err := parseIP(fields[1], familyIPv4)
		if err != nil {
			return nil, err
		}
		gw, err := parseIP(fields[2], familyIPv4)
		if err != nil {
			return nil, err
		}
                // destination 为全0的是默认网卡
		if !dest.Equal(net.IPv4zero) {
			continue
		}
		routes = append(routes, Route{
			Interface:   fields[0],
			Destination: dest,
			Gateway:     gw,
			Family:      familyIPv4,
		})
	}
	return routes, nil
}
  • chooseHostInterface -> chooseHostInterfaceFromRoute
// chooseHostInterfaceFromRoute cycles through each default route provided, looking for a
// global IP address from the interface for the route. If there are routes but no global
// address is obtained from the interfaces, it checks if the loopback interface has a global address.
// addressFamilies determines whether it prefers IPv4 or IPv6
func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
	for _, family := range addressFamilies {
		klog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
		for _, route := range routes {
			if route.Family != family {
				continue
			}
			klog.V(4).Infof("Default route transits interface %q", route.Interface)
                        // 获取第一个合法的IPv4地址
			finalIP, err := getIPFromInterface(route.Interface, family, nw)
			if err != nil {
				return nil, err
			}
			if finalIP != nil {
				klog.V(4).Infof("Found active IP %v ", finalIP)
				return finalIP, nil
			}
			// In case of network setups where default routes are present, but network
			// interfaces use only link-local addresses (e.g. as described in RFC5549).
			// the global IP is assigned to the loopback interface, and we should use it
			loopbackIP, err := getIPFromLoopbackInterface(family, nw)
			if err != nil {
				return nil, err
			}
			if loopbackIP != nil {
				klog.V(4).Infof("Found active IP %v on Loopback interface", loopbackIP)
				return loopbackIP, nil
			}
		}
	}
	klog.V(4).Infof("No active IP found by looking at default routes")
	return nil, fmt.Errorf("unable to select an IP from default routes.")
}
  • chooseHostInterface -> chooseHostInterfaceFromRoute -> getIPFromInterface
// getIPFromInterface gets the IPs on an interface and returns a global unicast address, if any. The
// interface must be up, the IP must in the family requested, and the IP must be a global unicast address.
func getIPFromInterface(intfName string, forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
	intf, err := nw.InterfaceByName(intfName)
	if err != nil {
		return nil, err
	}
        // 判断网卡是否启动
	if isInterfaceUp(intf) {
                // 返回网卡的ip数组,一个网卡可以绑定多个IP
		addrs, err := nw.Addrs(intf)
		if err != nil {
			return nil, err
		}
		klog.V(4).Infof("Interface %q has %d addresses :%v.", intfName, len(addrs), addrs)
                // 选择合法的IP,过滤掉广播ip
		matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
		if err != nil {
			return nil, err
		}
		if matchingIP != nil {
			klog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intfName)
			return matchingIP, nil
		}
	}
	return nil, nil
}

  • chooseHostInterface -> chooseHostInterfaceFromRoute -> getIPFromInterface -> getMatchingGlobalIP
// getMatchingGlobalIP returns the first valid global unicast address of the given
// 'family' from the list of 'addrs'.
func getMatchingGlobalIP(addrs []net.Addr, family AddressFamily) (net.IP, error) {
	if len(addrs) > 0 {
		for i := range addrs {
			klog.V(4).Infof("Checking addr  %s.", addrs[i].String())
			ip, _, err := net.ParseCIDR(addrs[i].String())
			if err != nil {
				return nil, err
			}
			if memberOf(ip, family) {
				if ip.IsGlobalUnicast() {
					klog.V(4).Infof("IP found %v", ip)
					return ip, nil
				} else {
					klog.V(4).Infof("Non-global unicast address found %v", ip)
				}
			} else {
				klog.V(4).Infof("%v is not an IPv%d address", ip, int(family))
			}

		}
	}
	return nil, nil
}

概念验证Demo

获取网卡

package main

import (
	"encoding/json"
	"fmt"
	"net"
)

func main(){
	inf,err:=net.InterfaceByName("ens33")
	if err != nil {
		fmt.Println(err.Error())
	}
	content,_:=json.Marshal(inf)
	fmt.Println(string(content))
	fmt.Println(inf.Addrs())
}

输出:

{"Index":2,"MTU":1500,"Name":"ens33","HardwareAddr":"AAwp6PU2","Flags":19}
[192.168.10.xx/24 fe80::42e4:89b:26ae:xxxx/64] <nil>

判断网卡是否启动

package main

import (	
	"fmt"
	"net"
	"os"
)

func main(){
	inf,err:=net.InterfaceByName("ens33")
	if err != nil {
		fmt.Println(err.Error())
	}
	if isInterfaceUp(inf){
		fmt.Fprintf(os.Stdout, "%s is up\n", inf.Name)
	}else{
		fmt.Fprintf(os.Stdout, "%s is down\n", inf.Name)
	}
}

func isInterfaceUp(intf *net.Interface) bool {
	if intf == nil {
		return false
	}
	if intf.Flags&net.FlagUp != 0 {
		return true
	}
	return false
}