二進制部署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。

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