二進制部署kubernetes時,大多數使用的是推薦的flannel網絡插件,但是部署後我們會發現一個問題,kubelet啓動沒有指定flannel ,我們也沒有配置network-plugin,那麼kubelet在創建pod時,究竟flannel如何參與容器的網絡創建的。從配置中能看到的是flannel啓動時,將本節點的網段傳遞了docker啓動參數。docker啓動後會以此設置docker0網橋的網段。
網絡上很多沒有說明具體的原因,有一篇文章倒是有提到。可以參考,但是沒有本質解決我的疑問。
其實我們有以下幾個疑問:
- flannel這個插件的工作原理,爲什麼network-plugin沒有體現flannel, flannel最終實現跨節點的pod通信?
- 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。