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
}