二进制部署kubernetes,flannel网络是如何工作的

二进制部署kubernetes时,大多数使用的是推荐的flannel网络插件,但是部署后我们会发现一个问题,kubelet启动没有指定flannel ,我们也没有配置network-plugin,那么kubelet在创建pod时,究竟flannel如何参与容器的网络创建的。从配置中能看到的是flannel启动时,将本节点的网段传递了docker启动参数。docker启动后会以此设置docker0网桥的网段。

网络上很多没有说明具体的原因,有一篇文章倒是有提到。可以参考,但是没有本质解决我的疑问。

其实我们有以下几个疑问:

  1. flannel这个插件的工作原理,为什么network-plugin没有体现flannel, flannel最终实现跨节点的pod通信?
  2. kubelet对network-plugin参数的默认值是多少?针对该配置,对pod的网络流程的影响?

(一)先回答第一个问题:

      flannel在cni中是一个相对于calico等第三方cni比较特殊的一个插件,特殊在哪呢?https://github.com/containernetworking/plugins中也有说明,flannel属于Meta: other plugins类,不能独立工作,需要有Main类支持才可以。而flannel本质是调用Main类中的bridge,这个和docker 默认的NetworkMode是一致的。因此二进制部署的时候,kubelet没有指定flannel相关的配置,pod的网络也配好了,肯定是docker自己配置的。

     所以二进制部署时,pod的Sandbox的网络不是flannel配置的,即创建infra容器,创建veth pair对,插网桥等动作是docker自己完成的。从docker inspect XX也能看到infra容器的NetworkMode配置为bridge。

    所以这种方式部署的pod,其实跨节点通信部署使用了flannel,单节点内的pod网络,并没有flannel的参与!

     (其实,flannel也可以使用cni的方式来部署,网络参考:https://github.com/containernetworking/cni, 这种方式,就是flannel-cni来调用bridge,而不是docker-engine调用bridge了)

(二)再来看第二个问题:

     既然是docker自己配置的,那么kubelet一定会针对network-plugin这个配置进行区分。

     先来看一个结构:

     参考之前kubelet的文章https://mp.csdn.net/postedit/97369143

 1.    kubelet在启动时会初始化 我们重点关注下network-plugin的配置的作用。

KubeletFlags{        
     ContainerRuntimeOptions {

        ContainerRuntime: "docker"
        DockerEndpoint:  "unix:///var/run/docker.sock"
        PodSandboxImage: pauseXX
        CNIBinDir:  "/opt/cni/bin",
		CNIConfDir: "/etc/cni/net.d",
        NetworkPluginName string //---------------------注意这个就是network-plugin配置参数
     }
     RemoteRuntimeEndpoint   = "unix:///var/run/dockershim.sock"
}

func NewMainKubelet(...crOptions *config.ContainerRuntimeOptions){

    pluginSettings := dockershim.NetworkPluginSettings{
		HairpinMode:        kubeletconfiginternal.HairpinMode(kubeCfg.HairpinMode),
		NonMasqueradeCIDR:  nonMasqueradeCIDR,
		PluginName:         crOptions.NetworkPluginName,//----------这里
		PluginConfDir:      crOptions.CNIConfDir,
		PluginBinDirString: crOptions.CNIBinDir,
		MTU:                int(crOptions.NetworkPluginMTU),
    }
    ...
    //最终传递到了DockerService 即ds中
    case kubetypes.DockerContainerRuntime:
		// Create and start the CRI shim running as a grpc server.
		streamingConfig := getStreamingConfig(kubeCfg, kubeDeps, crOptions)
		ds, err := dockershim.NewDockerService(kubeDeps.DockerClientConfig, crOptions.PodSandboxImage, streamingConfig,
			&pluginSettings, runtimeCgroups, kubeCfg.CgroupDriver, crOptions.DockershimRootDirectory, !crOptions.RedirectContainerStreaming)
    ....
}

//在ds的初始化中,
func NewDockerService(){
   ...
    // dockershim currently only supports CNI plugins.
	pluginSettings.PluginBinDirs = cni.SplitDirs(pluginSettings.PluginBinDirString)
	cniPlugins := cni.ProbeNetworkPlugins(pluginSettings.PluginConfDir, pluginSettings.PluginBinDirs)
	cniPlugins = append(cniPlugins, kubenet.NewPlugin(pluginSettings.PluginBinDirs))
	netHost := &dockerNetworkHost{
		&namespaceGetter{ds},
		&portMappingGetter{ds},
	}
    //划重点,
	plug, err := network.InitNetworkPlugin(cniPlugins, pluginSettings.PluginName, netHost, pluginSettings.HairpinMode, pluginSettings.NonMasqueradeCIDR, pluginSettings.MTU)
	if err != nil {
		return nil, fmt.Errorf("didn't find compatible CNI plugin with given settings %+v: %v", pluginSettings, err)
	}
	ds.network = network.NewPluginManager(plug)
	glog.Infof("Docker cri networking managed by %v", plug.Name()) //在log中能查看的到这句

}

//注意了,这里的NoopNetworkPlugin, 即kubernertes.io/no-op 
// InitNetworkPlugin inits the plugin that matches networkPluginName. Plugins must have unique names.
func InitNetworkPlugin(plugins []NetworkPlugin, networkPluginName string, host Host, hairpinMode kubeletconfig.HairpinMode, nonMasqueradeCIDR string, mtu int) (NetworkPlugin, error) {
	if networkPluginName == "" {
		// default to the no_op plugin
		plug := &NoopNetworkPlugin{}
		plug.Sysctl = utilsysctl.New()
		if err := plug.Init(host, hairpinMode, nonMasqueradeCIDR, mtu); err != nil {
			return nil, err
		}
		return plug, nil
	}

	pluginMap := map[string]NetworkPlugin{}
    ...
}

从上面可以看到 network-plugin参数最终因为我们部署的时候没有配置该配置项,即为空,ds.network 就是 no-op

2 ,如果说上一步是配置最终保存在ds.network中,即如果是没有配置network-plugin的话,就会是一个NoopNetworkManager。那么下面就是ds.network如何发挥作用的。

SyncPod
  CreateSandbox
    RunPodSandbox
      createConfig = ds.makeSandboxDockerConfig
         ds.applySandboxLinuxOptions
            applySandboxSecurityContext
              applySandboxSecurityContext
                modifySandboxNamespaceOptions
                  modifyHostOptionsForSandbox
      ds.client.CreateContainer(*createConfig)

//network就是ds.network,ds.network是什么呢?基于NoopNetworkPlugin,PluginName为“kubernetes.io/no-op”, 即最终hc.NetworkMode = "default"

func modifyHostOptionsForSandbox(nsOpts *runtimeapi.NamespaceOption, network *knetwork.PluginManager, hc *dockercontainer.HostConfig) {
	if nsOpts.GetIpc() == runtimeapi.NamespaceMode_NODE {
		hc.IpcMode = namespaceModeHost
	}
	if nsOpts.GetNetwork() == runtimeapi.NamespaceMode_NODE {
		hc.NetworkMode = namespaceModeHost
		return
	}

	if network == nil {
		hc.NetworkMode = "default"
		return
	}

	switch network.PluginName() {
	case "cni":
		fallthrough
	case "kubenet":
		hc.NetworkMode = "none"
	default:
		hc.NetworkMode = "default"
	}
}

而hc.NetworkMode = "default" 这个参数将会传递给docker-engine,就是NetworkMode参数。
因此我们就有flannel二进制部署的时候,创建的infra的网络模式是bridge。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章