写点什么

kubernetes/k8s CNI 分析 - 容器网络接口分析

用户头像
良凯尔
关注
发布于: 2 小时前
kubernetes/k8s CNI 分析 - 容器网络接口分析

关联博客:kubernetes/k8s CSI 分析-容器存储接口分析https://xie.infoq.cn/article/5cd26c1b24c5665820411bb5a

kubernetes/k8s CRI 分析-容器运行时接口分析https://xie.infoq.cn/article/0d2bc16c22af9fc710b9f9c30

概述

kubernetes 的设计初衷是支持可插拔架构,从而利于扩展 kubernetes 的功能。在此架构思想下,kubernetes 提供了 3 个特定功能的接口,分别是容器网络接口 CNI、容器运行时接口 CRI 和容器存储接口 CSI。kubernetes 通过调用这几个接口,来完成相应的功能。


下面我们来对容器运行时接口 CNI 来做一下介绍与分析。

CNI 是什么

CNI,全称是 Container Network Interface,即容器网络接口。


CNI 是 K8s 中标准的调用网络实现的接口。Kubelet 通过这个标准的接口来调用不同的网络插件以实现不同的网络配置方式。


CNI 网络插件是一个可执行文件,是遵守容器网络接口(CNI)规范的网络插件。常见的 CNI 网络插件包括 Calico、flannel、Terway、Weave Net 等。


当 kubelet 选择使用 CNI 类型的网络插件时(通过 kubelet 启动参数指定),kubelet 在创建 pod、删除 pod 的时候,会调用 CNI 网络插件来做 pod 的构建网络和销毁网络等操作。

kubelet 的网络插件

kubelet 的网络插件有以下 3 种类型:


(1)CNI;


(2)kubenet;


(3)Noop,代表不配置网络插件。


这里主要对 kubelet 中 CNI 相关的源码进行分析。

CNI 架构

kubelet 创建/删除 pod 时,会调用 CRI,然后 CRI 会调用 CNI 来进行 pod 网络的构建/删除。



kubelet 构建 pod 网络的大致过程

(1)kubelet 先通过 CRI 创建 pause 容器(pod sandbox),生成 network namespace;


(2)kubelet 根据启动参数配置调用具体的网络插件如 CNI 网络插件;


(3)网络插件给 pause 容器(pod sandbox)配置网络;


(4)pod 中其他的容器都与 pause 容器(pod sandbox)共享网络。

kubelet 中 cni 相关的源码分析

kubelet 的 cni 源码分析包括如下几部分:


(1)cni 相关启动参数分析;


(2)关键 struct/interface 分析;


(3)cni 初始化分析;


(4)cni 构建 pod 网络分析;


(5)cni 销毁 pod 网络分析。

基于 tag v1.17.4

https://github.com/kubernetes/kubernetes/releases/tag/v1.17.4

1.kubelet 组件 cni 相关启动参数分析

kubelet 组件 cni 相关启动参数相关代码如下:


// pkg/kubelet/config/flags.gofunc (s *ContainerRuntimeOptions) AddFlags(fs *pflag.FlagSet) {    ...    // Network plugin settings for Docker.  fs.StringVar(&s.NetworkPluginName, "network-plugin", s.NetworkPluginName, fmt.Sprintf("<Warning: Alpha feature> The name of the network plugin to be invoked for various events in kubelet/pod lifecycle. %s", dockerOnlyWarning))  fs.StringVar(&s.CNIConfDir, "cni-conf-dir", s.CNIConfDir, fmt.Sprintf("<Warning: Alpha feature> The full path of the directory in which to search for CNI config files. %s", dockerOnlyWarning))  fs.StringVar(&s.CNIBinDir, "cni-bin-dir", s.CNIBinDir, fmt.Sprintf("<Warning: Alpha feature> A comma-separated list of full paths of directories in which to search for CNI plugin binaries. %s", dockerOnlyWarning))  fs.StringVar(&s.CNICacheDir, "cni-cache-dir", s.CNICacheDir, fmt.Sprintf("<Warning: Alpha feature> The full path of the directory in which CNI should store cache files. %s", dockerOnlyWarning))  fs.Int32Var(&s.NetworkPluginMTU, "network-plugin-mtu", s.NetworkPluginMTU, fmt.Sprintf("<Warning: Alpha feature> The MTU to be passed to the network plugin, to override the default. Set to 0 to use the default 1460 MTU. %s", dockerOnlyWarning))    ...}
复制代码


cni 相关启动参数的默认值在NewContainerRuntimeOptions函数中设置。


// cmd/kubelet/app/options/container_runtime.go// NewContainerRuntimeOptions will create a new ContainerRuntimeOptions with// default values.func NewContainerRuntimeOptions() *config.ContainerRuntimeOptions {  dockerEndpoint := ""  if runtime.GOOS != "windows" {    dockerEndpoint = "unix:///var/run/docker.sock"  }
return &config.ContainerRuntimeOptions{ ContainerRuntime: kubetypes.DockerContainerRuntime, RedirectContainerStreaming: false, DockerEndpoint: dockerEndpoint, DockershimRootDirectory: "/var/lib/dockershim", PodSandboxImage: defaultPodSandboxImage, ImagePullProgressDeadline: metav1.Duration{Duration: 1 * time.Minute}, ExperimentalDockershim: false,
//Alpha feature CNIBinDir: "/opt/cni/bin", CNIConfDir: "/etc/cni/net.d", CNICacheDir: "/var/lib/cni/cache", }}
复制代码


下面来简单分析几个比较重要的 cni 相关启动参数:


(1)--network-plugin:指定要使用的网络插件类型,可选值cnikubenet"",默认为空串,代表 Noop,即不配置网络插件(不构建 pod 网络)。此处配置值为cni时,即指定 kubelet 使用的网络插件类型为cni


(2)--cni-conf-dir:CNI 配置文件所在路径。默认值:/etc/cni/net.d


(3)--cni-bin-dir:CNI 插件的可执行文件所在路径,kubelet 将在此路径中查找 CNI 插件的可执行文件来执行 pod 的网络操作。默认值:/opt/cni/bin

2.关键 struct/interface 分析

interface NetworkPlugin

先来看下关键的 interface:NetworkPlugin


NetworkPlugin interface 声明了 kubelet 网络插件的一些操作方法,不同类型的网络插件只需要实现这些方法即可,其中最关键的就是SetUpPodTearDownPod方法,作用分别是构建 pod 网络与销毁 pod 网络,cniNetworkPlugin实现了该 interface。


// pkg/kubelet/dockershim/network/plugins.go// NetworkPlugin is an interface to network plugins for the kubelettype NetworkPlugin interface {  // Init initializes the plugin.  This will be called exactly once  // before any other methods are called.  Init(host Host, hairpinMode kubeletconfig.HairpinMode, nonMasqueradeCIDR string, mtu int) error
// Called on various events like: // NET_PLUGIN_EVENT_POD_CIDR_CHANGE Event(name string, details map[string]interface{})
// Name returns the plugin's name. This will be used when searching // for a plugin by name, e.g. Name() string
// Returns a set of NET_PLUGIN_CAPABILITY_* Capabilities() utilsets.Int
// SetUpPod is the method called after the infra container of // the pod has been created but before the other containers of the // pod are launched. SetUpPod(namespace string, name string, podSandboxID kubecontainer.ContainerID, annotations, options map[string]string) error
// TearDownPod is the method called before a pod's infra container will be deleted TearDownPod(namespace string, name string, podSandboxID kubecontainer.ContainerID) error
// GetPodNetworkStatus is the method called to obtain the ipv4 or ipv6 addresses of the container GetPodNetworkStatus(namespace string, name string, podSandboxID kubecontainer.ContainerID) (*PodNetworkStatus, error)
// Status returns error if the network plugin is in error state Status() error}
复制代码

struct cniNetworkPlugin

cniNetworkPlugin struct 实现了NetworkPlugin interface,实现了SetUpPodTearDownPod等方法。


// pkg/kubelet/dockershim/network/cni/cni.gotype cniNetworkPlugin struct {  network.NoopNetworkPlugin
loNetwork *cniNetwork
sync.RWMutex defaultNetwork *cniNetwork
host network.Host execer utilexec.Interface nsenterPath string confDir string binDirs []string cacheDir string podCidr string}
复制代码

struct PluginManager

struct PluginManager 中的plugin属性是interface NetworkPlugin类型,可以传入具体的网络插件实现,如cniNetworkPlugin struct


// pkg/kubelet/dockershim/network/plugins.go// The PluginManager wraps a kubelet network plugin and provides synchronization// for a given pod's network operations.  Each pod's setup/teardown/status operations// are synchronized against each other, but network operations of other pods can// proceed in parallel.type PluginManager struct {  // Network plugin being wrapped  plugin NetworkPlugin
// Pod list and lock podsLock sync.Mutex pods map[string]*podLock}
复制代码

struct dockerService

struct dockerService 其实在 CRI 分析的博文部分有做过详细分析,可以去回顾一下,下面再简单做一下介绍。


struct dockerService 实现了 CRI shim 服务端的容器运行时接口以及容器镜像接口,所以其代表了 dockershim(kubelet 内置的 CRI shim)的服务端。


struct dockerService 中的network属性是struct PluginManager类型,在该结构体初始化时会将具体的网络插件结构体如struct cniNetworkPlugin存储进该属性。


创建 pod、删除 pod 时会根据dockerService结构体的network属性里面存储的具体的网络插件结构体,去调用某个具体网络插件(如cniNetworkPlugin)的SetUpPodTearDownPod方法来构建 pod 的网络、销毁 pod 的网络。


// pkg/kubelet/dockershim/docker_service.gotype dockerService struct {  client           libdocker.Interface  os               kubecontainer.OSInterface  podSandboxImage  string  streamingRuntime *streamingRuntime  streamingServer  streaming.Server
network *network.PluginManager // Map of podSandboxID :: network-is-ready networkReady map[string]bool networkReadyLock sync.Mutex
containerManager cm.ContainerManager // cgroup driver used by Docker runtime. cgroupDriver string checkpointManager checkpointmanager.CheckpointManager // caches the version of the runtime. // To be compatible with multiple docker versions, we need to perform // version checking for some operations. Use this cache to avoid querying // the docker daemon every time we need to do such checks. versionCache *cache.ObjectCache // startLocalStreamingServer indicates whether dockershim should start a // streaming server on localhost. startLocalStreamingServer bool
// containerCleanupInfos maps container IDs to the `containerCleanupInfo` structs // needed to clean up after containers have been removed. // (see `applyPlatformSpecificDockerConfig` and `performPlatformSpecificContainerCleanup` // methods for more info). containerCleanupInfos map[string]*containerCleanupInfo}
复制代码

3.cni 初始化分析

Kubelet 启动过程中针对网络主要做以下步骤,分别是探针获取当前环境的网络插件以及初始化网络插件(只有当容器运行时选择为内置 dockershim 时,才会做 CNI 的初始化操作,将 CNI 初始化完成后交给 dockershim 使用)。


cni 初始化的调用链:


main (cmd/kubelet/kubelet.go)-> NewKubeletCommand (cmd/kubelet/app/server.go)-> Run (cmd/kubelet/app/server.go)


-> run (cmd/kubelet/app/server.go)


-> RunKubelet (cmd/kubelet/app/server.go)-> CreateAndInitKubelet(cmd/kubelet/app/server.go)-> kubelet.NewMainKubelet(pkg/kubelet/kubelet.go)-> cni.ProbeNetworkPlugins & network.InitNetworkPlugin(pkg/kubelet/network/plugins.go)


调用链很长,这里直接进入关键的函数NewMainKubelet进行分析。

NewMainKubelet

NewMainKubelet 函数中主要看到dockershim.NewDockerService调用。


// pkg/kubelet/kubelet.go// NewMainKubelet instantiates a new Kubelet object along with all the required internal modules.// No initialization of Kubelet and its modules should happen here.func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,...) {    ...    switch containerRuntime {  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)    ...}
复制代码


这里对变量containerRuntime值等于docker时做分析,即 kubelet 启动参数--container-runtime值为docker,这时 kubelet 会使用内置的CRI shimdockershim作为容器运行时,初始化并启动dockershim


其中,调用dockershim.NewDockerService的作用是:新建并初始化dockershim服务端,包括初始化 docker client、初始化 cni 网络配置等操作。


而其中 CNI 部分的主要逻辑为:


(1)调用cni.ProbeNetworkPlugins:根据 kubelet 启动参数 cni 相关配置,获取 cni 配置文件、cni 网络插件可执行文件等信息,根据这些 cni 的相关信息来初始化cniNetworkPlugin结构体并返回;


(2)调用network.InitNetworkPlugin:根据 networkPluginName 的值(对应 kubelet 启动参数--network-plugin),选择相应的网络插件,调用其Init()方法,做网络插件的初始化操作(初始化操作主要是起了一个 goroutine,定时探测 cni 的配置文件以及可执行文件,让其可以热更新);


(3)将上面步骤中获取到的cniNetworkPlugin结构体,赋值给dockerService structnetwork属性,待后续创建 pod、删除 pod 时可以调用cniNetworkPluginSetUpPodTearDownPod方法来构建 pod 的网络、销毁 pod 的网络。


kubelet 对 CNI 的实现的主要代码:pkg/kubelet/network/cni/cni.go-SetUpPod/TearDownPod(构建 Pod 网络和销毁 Pod 网络)


其中函数入参pluginSettings *NetworkPluginSettings的参数值,其实是从 kubelet 启动参数配置而来,kubelet cni 相关启动参数在前面已经做了分析了,忘记的可以回头看一下。


// pkg/kubelet/dockershim/docker_service.go// NewDockerService creates a new `DockerService` struct.// NOTE: Anything passed to DockerService should be eventually handled in another way when we switch to running the shim as a different process.func NewDockerService(config *ClientConfig, podSandboxImage string, streamingConfig *streaming.Config, pluginSettings *NetworkPluginSettings,  cgroupsName string, kubeCgroupDriver string, dockershimRootDir string, startLocalStreamingServer bool, noJsonLogPath string) (DockerService, error) {    ...    ds := &dockerService{    client:          c,    os:              kubecontainer.RealOS{},    podSandboxImage: podSandboxImage,    streamingRuntime: &streamingRuntime{      client:      client,      execHandler: &NativeExecHandler{},    },    containerManager:          cm.NewContainerManager(cgroupsName, client),    checkpointManager:         checkpointManager,    startLocalStreamingServer: startLocalStreamingServer,    networkReady:              make(map[string]bool),    containerCleanupInfos:     make(map[string]*containerCleanupInfo),    noJsonLogPath:             noJsonLogPath,  }  ...    // dockershim currently only supports CNI plugins.  pluginSettings.PluginBinDirs = cni.SplitDirs(pluginSettings.PluginBinDirString)  // (1)根据kubelet启动参数cni相关配置,获取cni配置文件、cni网络插件可执行文件等信息,根据这些cni的相关信息来初始化```cniNetworkPlugin```结构体并返回  cniPlugins := cni.ProbeNetworkPlugins(pluginSettings.PluginConfDir, pluginSettings.PluginCacheDir, pluginSettings.PluginBinDirs)  cniPlugins = append(cniPlugins, kubenet.NewPlugin(pluginSettings.PluginBinDirs, pluginSettings.PluginCacheDir))  netHost := &dockerNetworkHost{    &namespaceGetter{ds},    &portMappingGetter{ds},  }  // (2)根据networkPluginName的值(对应kubelet启动参数```--network-plugin```),选择相应的网络插件,调用其```Init()```方法,做网络插件的初始化操作(初始化操作主要是起了一个goroutine,定时探测cni的配置文件以及可执行文件,让其可以热更新)  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)  }  // (3)将上面步骤中获取到的```cniNetworkPlugin```结构体,赋值给```dockerService struct```的```network```属性,待后续创建pod、删除pod时可以调用```cniNetworkPlugin```的```SetUpPod```、```TearDownPod```方法来构建pod的网络、销毁pod的网络。    ds.network = network.NewPluginManager(plug)  klog.Infof("Docker cri networking managed by %v", plug.Name())    ...}
复制代码


先来看下pluginSettings长什么样,其实是struct NetworkPluginSettings,包含了网络插件名称、网络插件可执行文件所在目录、网络插件配置文件所在目录等属性,代码如下:


// pkg/kubelet/dockershim/docker_service.gotype NetworkPluginSettings struct {  // HairpinMode is best described by comments surrounding the kubelet arg  HairpinMode kubeletconfig.HairpinMode  // NonMasqueradeCIDR is the range of ips which should *not* be included  // in any MASQUERADE rules applied by the plugin  NonMasqueradeCIDR string  // PluginName is the name of the plugin, runtime shim probes for  PluginName string  // PluginBinDirString is a list of directiores delimited by commas, in  // which the binaries for the plugin with PluginName may be found.  PluginBinDirString string  // PluginBinDirs is an array of directories in which the binaries for  // the plugin with PluginName may be found. The admin is responsible for  // provisioning these binaries before-hand.  PluginBinDirs []string  // PluginConfDir is the directory in which the admin places a CNI conf.  // Depending on the plugin, this may be an optional field, eg: kubenet  // generates its own plugin conf.  PluginConfDir string  // PluginCacheDir is the directory in which CNI should store cache files.  PluginCacheDir string  // MTU is the desired MTU for network devices created by the plugin.  MTU int}
复制代码

3.1 cni.ProbeNetworkPlugins

cni.ProbeNetworkPlugins 中主要作用为:根据 kubelet 启动参数 cni 相关配置,获取 cni 配置文件、cni 网络插件可执行文件等信息,根据这些 cni 的相关信息来初始化cniNetworkPlugin结构体并返回。


其中看到plugin.syncNetworkConfig()调用,主要作用是给cniNetworkPlugin结构体的defaultNetwork属性赋值。


// pkg/kubelet/dockershim/network/cni/cni.go// ProbeNetworkPlugins : get the network plugin based on cni conf file and bin filefunc ProbeNetworkPlugins(confDir, cacheDir string, binDirs []string) []network.NetworkPlugin {  old := binDirs  binDirs = make([]string, 0, len(binDirs))  for _, dir := range old {    if dir != "" {      binDirs = append(binDirs, dir)    }  }
plugin := &cniNetworkPlugin{ defaultNetwork: nil, loNetwork: getLoNetwork(binDirs), execer: utilexec.New(), confDir: confDir, binDirs: binDirs, cacheDir: cacheDir, }
// sync NetworkConfig in best effort during probing. plugin.syncNetworkConfig() return []network.NetworkPlugin{plugin}}
复制代码


plugin.syncNetworkConfig()


主要逻辑:


(1)getDefaultCNINetwork():根据 kubelet 启动参数配置,去对应的 cni conf 文件夹下寻找 cni 配置文件,返回包含 cni 信息的 cniNetwork 结构体;


(2)plugin.setDefaultNetwork():根据上一步获取到的 cniNetwork 结构体,赋值给cniNetworkPlugin结构体的defaultNetwork属性。


// pkg/kubelet/dockershim/network/cni/cni.gofunc (plugin *cniNetworkPlugin) syncNetworkConfig() {  network, err := getDefaultCNINetwork(plugin.confDir, plugin.binDirs)  if err != nil {    klog.Warningf("Unable to update cni config: %s", err)    return  }  plugin.setDefaultNetwork(network)}
复制代码


getDefaultCNINetwork()


主要逻辑:


(1)在 cni 配置文件所在目录下,可以识别 3 种 cni 配置文件,分别是.conf, .conflist, .json


(2)调用sort.Strings()将 cni 配置文件所在目录下的所有 cni 配置文件按照字典顺序升序排序。


(3)只取第一个读取到的 cni 配置文件,然后直接 return。所以就算在 cni 配置文件目录下配置了多个 cni 配置文件,也只会有其中一个最终生效。


(4)调用cniConfig.ValidateNetworkList(),校验 cni 可执行文件目录下是否存在对应的可执行文件。


// pkg/kubelet/dockershim/network/cni/cni.gofunc getDefaultCNINetwork(confDir string, binDirs []string) (*cniNetwork, error) {  files, err := libcni.ConfFiles(confDir, []string{".conf", ".conflist", ".json"})  switch {  case err != nil:    return nil, err  case len(files) == 0:    return nil, fmt.Errorf("no networks found in %s", confDir)  }
cniConfig := &libcni.CNIConfig{Path: binDirs}
sort.Strings(files) for _, confFile := range files { var confList *libcni.NetworkConfigList if strings.HasSuffix(confFile, ".conflist") { confList, err = libcni.ConfListFromFile(confFile) if err != nil { klog.Warningf("Error loading CNI config list file %s: %v", confFile, err) continue } } else { conf, err := libcni.ConfFromFile(confFile) if err != nil { klog.Warningf("Error loading CNI config file %s: %v", confFile, err) continue } // Ensure the config has a "type" so we know what plugin to run. // Also catches the case where somebody put a conflist into a conf file. if conf.Network.Type == "" { klog.Warningf("Error loading CNI config file %s: no 'type'; perhaps this is a .conflist?", confFile) continue }
confList, err = libcni.ConfListFromConf(conf) if err != nil { klog.Warningf("Error converting CNI config file %s to list: %v", confFile, err) continue } } if len(confList.Plugins) == 0 { klog.Warningf("CNI config list %s has no networks, skipping", string(confList.Bytes[:maxStringLengthInLog(len(confList.Bytes))])) continue }
// Before using this CNI config, we have to validate it to make sure that // all plugins of this config exist on disk caps, err := cniConfig.ValidateNetworkList(context.TODO(), confList) if err != nil { klog.Warningf("Error validating CNI config list %s: %v", string(confList.Bytes[:maxStringLengthInLog(len(confList.Bytes))]), err) continue }
klog.V(4).Infof("Using CNI configuration file %s", confFile)
return &cniNetwork{ name: confList.Name, NetworkConfig: confList, CNIConfig: cniConfig, Capabilities: caps, }, nil } return nil, fmt.Errorf("no valid networks found in %s", confDir)}
复制代码


plugin.setDefaultNetwork


将上面获取到的cniNetwork结构体赋值给cniNetworkPlugin结构体的defaultNetwork属性。


// pkg/kubelet/dockershim/network/cni/cni.gofunc (plugin *cniNetworkPlugin) setDefaultNetwork(n *cniNetwork) {  plugin.Lock()  defer plugin.Unlock()  plugin.defaultNetwork = n}
复制代码

3.2 network.InitNetworkPlugin

network.InitNetworkPlugin()主要作用:根据 networkPluginName 的值(对应 kubelet 启动参数--network-plugin),选择相应的网络插件,调用其Init()方法,做网络插件的初始化操作。


// pkg/kubelet/dockershim/network/plugins.go// 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{}
allErrs := []error{} for _, plugin := range plugins { name := plugin.Name() if errs := validation.IsQualifiedName(name); len(errs) != 0 { allErrs = append(allErrs, fmt.Errorf("network plugin has invalid name: %q: %s", name, strings.Join(errs, ";"))) continue }
if _, found := pluginMap[name]; found { allErrs = append(allErrs, fmt.Errorf("network plugin %q was registered more than once", name)) continue } pluginMap[name] = plugin }
chosenPlugin := pluginMap[networkPluginName] if chosenPlugin != nil { err := chosenPlugin.Init(host, hairpinMode, nonMasqueradeCIDR, mtu) if err != nil { allErrs = append(allErrs, fmt.Errorf("network plugin %q failed init: %v", networkPluginName, err)) } else { klog.V(1).Infof("Loaded network plugin %q", networkPluginName) } } else { allErrs = append(allErrs, fmt.Errorf("network plugin %q not found", networkPluginName)) }
return chosenPlugin, utilerrors.NewAggregate(allErrs)}
复制代码


chosenPlugin.Init()


当 kubelet 启动参数--network-plugin的值配置为cni时,会调用到cniNetworkPluginInit()方法,代码如下。


启动一个 goroutine,每隔 5 秒,调用一次plugin.syncNetworkConfig。再来回忆一下plugin.syncNetworkConfig()的作用:根据 kubelet 启动参数配置,去对应的 cni conf 文件夹下寻找 cni 配置文件,返回包含 cni 信息的 cniNetwork 结构体,赋值给cniNetworkPlugin结构体的defaultNetwork属性,从而达到 cni conf 以及 bin 更新后,kubelet 也能感知并更新cniNetworkPlugin结构体的效果。


此处也可以看出该 goroutine 存在的意义,让 cni 的配置文件以及可执行文件等可以热更新,而无需重启 kubelet。


// pkg/kubelet/dockershim/network/cni/cni.gofunc (plugin *cniNetworkPlugin) Init(host network.Host, hairpinMode kubeletconfig.HairpinMode, nonMasqueradeCIDR string, mtu int) error {  err := plugin.platformInit()  if err != nil {    return err  }
plugin.host = host
plugin.syncNetworkConfig()
// start a goroutine to sync network config from confDir periodically to detect network config updates in every 5 seconds go wait.Forever(plugin.syncNetworkConfig, defaultSyncConfigPeriod)
return nil}
复制代码


plugin.platformInit()只是检查了下是否有nsenter,没有做其他操作。


// pkg/kubelet/dockershim/network/cni/cni_others.gofunc (plugin *cniNetworkPlugin) platformInit() error {  var err error  plugin.nsenterPath, err = plugin.execer.LookPath("nsenter")  if err != nil {    return err  }  return nil}
复制代码

4.CNI 构建 pod 网络分析

kubelet 创建 pod 时,通过 CRI 创建并启动 pod sandbox,然后 CRI 会调用 CNI 网络插件构建 pod 网络。


kubelet 中 CNI 构建 pod 网络的方法是:pkg/kubelet/network/cni/cni.go-SetUpPod


其中SetUpPod方法的调用链如下(只列出了关键部分):


main (cmd/kubelet/kubelet.go)...-> klet.syncPod(pkg/kubelet/kubelet.go)-> kl.containerRuntime.SyncPod(pkg/kubelet/kubelet.go)-> m.createPodSandbox(pkg/kubelet/kuberuntime/kuberuntime_manager.go)-> m.runtimeService.RunPodSandbox (pkg/kubelet/kuberuntime/kuberuntime_sandbox.go)-> ds.network.SetUpPod(pkg/kubelet/dockershim/docker_sandbox.go)-> pm.plugin.SetUpPod(pkg/kubelet/dockershim/network/plugins.go)-> SetUpPod(pkg/kubelet/dockershim/network/cni/cni.go)


下面的代码只是列出来看一下关键方法cniNetworkPlugin.SetUpPod()的调用链,不做具体分析。


// pkg/kubelet/kuberuntime/kuberuntime_manager.gofunc (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) (result kubecontainer.PodSyncResult) {  ...  podSandboxID, msg, err = m.createPodSandbox(pod, podContainerChanges.Attempt)  ...}
复制代码


// pkg/kubelet/kuberuntime/kuberuntime_sandbox.go// createPodSandbox creates a pod sandbox and returns (podSandBoxID, message, error).func (m *kubeGenericRuntimeManager) createPodSandbox(pod *v1.Pod, attempt uint32) (string, string, error) {    ...    podSandBoxID, err := m.runtimeService.RunPodSandbox(podSandboxConfig, runtimeHandler)    ...}
复制代码


RunPodSandbox方法中可以看到,是先创建 pod sandbox,然后启动 pod sandbox,然后才是给该 pod sandbox 构建网络。


// pkg/kubelet/dockershim/docker_sandbox.gofunc (ds *dockerService) RunPodSandbox(ctx context.Context, r *runtimeapi.RunPodSandboxRequest) (*runtimeapi.RunPodSandboxResponse, error) {    ...    createResp, err := ds.client.CreateContainer(*createConfig)    ...    err = ds.client.StartContainer(createResp.ID)    ...    err = ds.network.SetUpPod(config.GetMetadata().Namespace, config.GetMetadata().Name, cID, config.Annotations, networkOptions)    ...}
复制代码


PluginManager.SetUpPod方法中可以看到,调用了pm.plugin.SetUpPod,前面介绍 cni 初始化的时候讲过相关赋值初始化操作,这里会调用到cniNetworkPluginSetUpPod方法。


// pkg/kubelet/dockershim/network/plugins.gofunc (pm *PluginManager) SetUpPod(podNamespace, podName string, id kubecontainer.ContainerID, annotations, options map[string]string) error {  defer recordOperation("set_up_pod", time.Now())  fullPodName := kubecontainer.BuildPodFullName(podName, podNamespace)  pm.podLock(fullPodName).Lock()  defer pm.podUnlock(fullPodName)
klog.V(3).Infof("Calling network plugin %s to set up pod %q", pm.plugin.Name(), fullPodName) if err := pm.plugin.SetUpPod(podNamespace, podName, id, annotations, options); err != nil { return fmt.Errorf("networkPlugin %s failed to set up pod %q network: %v", pm.plugin.Name(), fullPodName, err) }
return nil}
复制代码

cniNetworkPlugin.SetUpPod

cniNetworkPlugin.SetUpPod 方法作用 cni 网络插件构建 pod 网络的调用入口。其主要逻辑为:


(1)调用plugin.checkInitialized():检查网络插件是否已经初始化完成;


(2)调用plugin.host.GetNetNS():获取容器网络命名空间路径,格式/proc/${容器PID}/ns/net


(3)调用context.WithTimeout():设置调用 cni 网络插件的超时时间;


(3)调用plugin.addToNetwork():如果是 linux 环境,则调用 cni 网络插件,给 pod 构建回环网络;


(4)调用plugin.addToNetwork():调用 cni 网络插件,给 pod 构建默认网络。


// pkg/kubelet/dockershim/network/cni/cni.gofunc (plugin *cniNetworkPlugin) SetUpPod(namespace string, name string, id kubecontainer.ContainerID, annotations, options map[string]string) error {  if err := plugin.checkInitialized(); err != nil {    return err  }  netnsPath, err := plugin.host.GetNetNS(id.ID)  if err != nil {    return fmt.Errorf("CNI failed to retrieve network namespace path: %v", err)  }
// Todo get the timeout from parent ctx cniTimeoutCtx, cancelFunc := context.WithTimeout(context.Background(), network.CNITimeoutSec*time.Second) defer cancelFunc() // Windows doesn't have loNetwork. It comes only with Linux if plugin.loNetwork != nil { if _, err = plugin.addToNetwork(cniTimeoutCtx, plugin.loNetwork, name, namespace, id, netnsPath, annotations, options); err != nil { return err } }
_, err = plugin.addToNetwork(cniTimeoutCtx, plugin.getDefaultNetwork(), name, namespace, id, netnsPath, annotations, options) return err}
复制代码


plugin.addToNetwork


plugin.addToNetwork 方法的作用就是调用 cni 网络插件,给 pod 构建指定类型的网络,其主要逻辑为:


(1)调用 plugin.buildCNIRuntimeConf():构建调用 cni 网络插件的配置;


(2)调用 cniNet.AddNetworkList():调用 cni 网络插件,进行网络构建。


// pkg/kubelet/dockershim/network/cni/cni.gofunc (plugin *cniNetworkPlugin) addToNetwork(ctx context.Context, network *cniNetwork, podName string, podNamespace string, podSandboxID kubecontainer.ContainerID, podNetnsPath string, annotations, options map[string]string) (cnitypes.Result, error) {  rt, err := plugin.buildCNIRuntimeConf(podName, podNamespace, podSandboxID, podNetnsPath, annotations, options)  if err != nil {    klog.Errorf("Error adding network when building cni runtime conf: %v", err)    return nil, err  }
pdesc := podDesc(podNamespace, podName, podSandboxID) netConf, cniNet := network.NetworkConfig, network.CNIConfig klog.V(4).Infof("Adding %s to network %s/%s netns %q", pdesc, netConf.Plugins[0].Network.Type, netConf.Name, podNetnsPath) res, err := cniNet.AddNetworkList(ctx, netConf, rt) if err != nil { klog.Errorf("Error adding %s to network %s/%s: %v", pdesc, netConf.Plugins[0].Network.Type, netConf.Name, err) return nil, err } klog.V(4).Infof("Added %s to network %s: %v", pdesc, netConf.Name, res) return res, nil}
复制代码


cniNet.AddNetworkList


AddNetworkList 方法中主要是调用了 addNetwork 方法,所以来看下 addNetwork 方法的逻辑:


(1)调用c.exec.FindInPath():拼接出 cni 网络插件可执行文件的绝对路径;


(2)调用buildOneConfig():构建配置;


(3)调用c.args():构建调用 cni 网络插件的参数;(4)调用invoke.ExecPluginWithResult():调用 cni 网络插件进行 pod 网络的构建操作。


// vendor/github.com/containernetworking/cni/libcni/api.go func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {  var err error  var result types.Result  for _, net := range list.Plugins {    result, err = c.addNetwork(ctx, list.Name, list.CNIVersion, net, result, rt)    if err != nil {      return nil, err    }  }
if err = setCachedResult(result, list.Name, rt); err != nil { return nil, fmt.Errorf("failed to set network %q cached result: %v", list.Name, err) }
return result, nil}
func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) { c.ensureExec() pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path) if err != nil { return nil, err }
newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt) if err != nil { return nil, err }
return invoke.ExecPluginWithResult(ctx, pluginPath, newConf.Bytes, c.args("ADD", rt), c.exec)}
复制代码


c.args


c.args 方法作用是构建调用 cni 网络插件可执行文件时的参数。


从代码中可以看出,参数有Command(命令,Add代表构建网络,Del代表销毁网络)、ContainerID(容器 ID)、NetNS(容器网络命名空间路径)、IfName(Interface Name 即网络接口名称)、PluginArgs(其他参数如 pod 名称、pod 命名空间等)等。


// vendor/github.com/containernetworking/cni/libcni/api.gofunc (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args {  return &invoke.Args{    Command:     action,    ContainerID: rt.ContainerID,    NetNS:       rt.NetNS,    PluginArgs:  rt.Args,    IfName:      rt.IfName,    Path:        strings.Join(c.Path, string(os.PathListSeparator)),  }}
复制代码


invoke.ExecPluginWithResult


invoke.ExecPluginWithResult 主要是将调用参数变成 env,然后调用 cni 网络插件可执行文件,并获取返回结果。


func ExecPluginWithResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) (types.Result, error) {  if exec == nil {    exec = defaultExec  }
stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv()) if err != nil { return nil, err }
// Plugin must return result in same version as specified in netconf versionDecoder := &version.ConfigDecoder{} confVersion, err := versionDecoder.Decode(netconf) if err != nil { return nil, err }
return version.NewResult(confVersion, stdoutBytes)}
复制代码

5.CNI 销毁 pod 网络分析

kubelet 删除 pod 时,CRI 会调用 CNI 网络插件销毁 pod 网络。


kubelet 中 CNI 销毁 pod 网络的方法是:pkg/kubelet/network/cni/cni.go-TearDownPod


其中TearDownPod方法的调用链如下(只列出了关键部分):


main (cmd/kubelet/kubelet.go)...-> m.runtimeService.StopPodSandbox (pkg/kubelet/kuberuntime/kuberuntime_sandbox.go)-> ds.network.TearDownPod(pkg/kubelet/dockershim/docker_sandbox.go)-> pm.plugin.TearDownPod(pkg/kubelet/dockershim/network/plugins.go)-> TearDownPod(pkg/kubelet/dockershim/network/cni/cni.go)


下面的代码只是列出来看一下关键方法cniNetworkPlugin.TearDownPod()的调用链,不做具体分析。


StopPodSandbox方法中可以看到,会先销毁 pod 网络,然后停止 pod sandbox 的运行,但是这两个操作中的任何一个发生错误,kubelet 都会继续进行重试,直到成功为止,所以对这两个操作成功的顺序并没有严格的要求(删除 pod sandbox 的操作由 kubelet gc 去完成)。


// pkg/kubelet/dockershim/docker_sandbox.gofunc (ds *dockerService) StopPodSandbox(ctx context.Context, r *runtimeapi.StopPodSandboxRequest) (*runtimeapi.StopPodSandboxResponse, error) {    ...    // WARNING: The following operations made the following assumption:  // 1. kubelet will retry on any error returned by StopPodSandbox.  // 2. tearing down network and stopping sandbox container can succeed in any sequence.  // This depends on the implementation detail of network plugin and proper error handling.  // For kubenet, if tearing down network failed and sandbox container is stopped, kubelet  // will retry. On retry, kubenet will not be able to retrieve network namespace of the sandbox  // since it is stopped. With empty network namespcae, CNI bridge plugin will conduct best  // effort clean up and will not return error.  errList := []error{}  ready, ok := ds.getNetworkReady(podSandboxID)  if !hostNetwork && (ready || !ok) {    // Only tear down the pod network if we haven't done so already    cID := kubecontainer.BuildContainerID(runtimeName, podSandboxID)    err := ds.network.TearDownPod(namespace, name, cID)    if err == nil {      ds.setNetworkReady(podSandboxID, false)    } else {      errList = append(errList, err)    }  }  if err := ds.client.StopContainer(podSandboxID, defaultSandboxGracePeriod); err != nil {    // Do not return error if the container does not exist    if !libdocker.IsContainerNotFoundError(err) {      klog.Errorf("Failed to stop sandbox %q: %v", podSandboxID, err)      errList = append(errList, err)    } else {      // remove the checkpoint for any sandbox that is not found in the runtime      ds.checkpointManager.RemoveCheckpoint(podSandboxID)    }  }    ...}
复制代码


PluginManager.TearDownPod方法中可以看到,调用了pm.plugin.TearDownPod,前面介绍 cni 初始化的时候讲过相关赋值初始化操作,这里会调用到cniNetworkPluginTearDownPod方法。


// pkg/kubelet/dockershim/network/plugins.gofunc (pm *PluginManager) TearDownPod(podNamespace, podName string, id kubecontainer.ContainerID) error {  defer recordOperation("tear_down_pod", time.Now())  fullPodName := kubecontainer.BuildPodFullName(podName, podNamespace)  pm.podLock(fullPodName).Lock()  defer pm.podUnlock(fullPodName)
klog.V(3).Infof("Calling network plugin %s to tear down pod %q", pm.plugin.Name(), fullPodName) if err := pm.plugin.TearDownPod(podNamespace, podName, id); err != nil { return fmt.Errorf("networkPlugin %s failed to teardown pod %q network: %v", pm.plugin.Name(), fullPodName, err) }
return nil}
复制代码

cniNetworkPlugin.TearDownPod

cniNetworkPlugin.TearDownPod 方法作用 cni 网络插件销毁 pod 网络的调用入口。其主要逻辑为:


(1)调用plugin.checkInitialized():检查网络插件是否已经初始化完成;


(2)调用plugin.host.GetNetNS():获取容器网络命名空间路径,格式/proc/${容器PID}/ns/net


(3)调用context.WithTimeout():设置调用 cni 网络插件的超时时间;


(3)调用plugin.deleteFromNetwork():如果是 linux 环境,则调用 cni 网络插件,销毁 pod 的回环网络;


(4)调用plugin.deleteFromNetwork():调用 cni 网络插件,销毁 pod 的默认网络。


// pkg/kubelet/dockershim/network/cni/cni.gofunc (plugin *cniNetworkPlugin) TearDownPod(namespace string, name string, id kubecontainer.ContainerID) error {  if err := plugin.checkInitialized(); err != nil {    return err  }
// Lack of namespace should not be fatal on teardown netnsPath, err := plugin.host.GetNetNS(id.ID) if err != nil { klog.Warningf("CNI failed to retrieve network namespace path: %v", err) }
// Todo get the timeout from parent ctx cniTimeoutCtx, cancelFunc := context.WithTimeout(context.Background(), network.CNITimeoutSec*time.Second) defer cancelFunc() // Windows doesn't have loNetwork. It comes only with Linux if plugin.loNetwork != nil { // Loopback network deletion failure should not be fatal on teardown if err := plugin.deleteFromNetwork(cniTimeoutCtx, plugin.loNetwork, name, namespace, id, netnsPath, nil); err != nil { klog.Warningf("CNI failed to delete loopback network: %v", err) } }
return plugin.deleteFromNetwork(cniTimeoutCtx, plugin.getDefaultNetwork(), name, namespace, id, netnsPath, nil)}
复制代码


plugin.deleteFromNetwork


plugin.deleteFromNetwork 方法的作用就是调用 cni 网络插件,销毁 pod 指定类型的网络,其主要逻辑为:


(1)调用 plugin.buildCNIRuntimeConf():构建调用 cni 网络插件的配置;


(2)调用 cniNet.DelNetworkList():调用 cni 网络插件,进行 pod 网络销毁。


// pkg/kubelet/dockershim/network/cni/cni.gofunc (plugin *cniNetworkPlugin) deleteFromNetwork(ctx context.Context, network *cniNetwork, podName string, podNamespace string, podSandboxID kubecontainer.ContainerID, podNetnsPath string, annotations map[string]string) error {  rt, err := plugin.buildCNIRuntimeConf(podName, podNamespace, podSandboxID, podNetnsPath, annotations, nil)  if err != nil {    klog.Errorf("Error deleting network when building cni runtime conf: %v", err)    return err  }
pdesc := podDesc(podNamespace, podName, podSandboxID) netConf, cniNet := network.NetworkConfig, network.CNIConfig klog.V(4).Infof("Deleting %s from network %s/%s netns %q", pdesc, netConf.Plugins[0].Network.Type, netConf.Name, podNetnsPath) err = cniNet.DelNetworkList(ctx, netConf, rt) // The pod may not get deleted successfully at the first time. // Ignore "no such file or directory" error in case the network has already been deleted in previous attempts. if err != nil && !strings.Contains(err.Error(), "no such file or directory") { klog.Errorf("Error deleting %s from network %s/%s: %v", pdesc, netConf.Plugins[0].Network.Type, netConf.Name, err) return err } klog.V(4).Infof("Deleted %s from network %s/%s", pdesc, netConf.Plugins[0].Network.Type, netConf.Name) return nil}
复制代码


cniNet.DelNetworkList


DelNetworkList 方法中主要是调用了 addNetwork 方法,所以来看下 addNetwork 方法的逻辑:


(1)调用c.exec.FindInPath():拼接出 cni 网络插件可执行文件的绝对路径;


(2)调用buildOneConfig():构建配置;


(3)调用c.args():构建调用 cni 网络插件的参数;(4)调用invoke.ExecPluginWithResult():调用 cni 网络插件进行 pod 网络的销毁操作。


// vendor/github.com/containernetworking/cni/libcni/api.go // DelNetworkList executes a sequence of plugins with the DEL commandfunc (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error {  var cachedResult types.Result
// Cached result on DEL was added in CNI spec version 0.4.0 and higher if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil { return err } else if gtet { cachedResult, err = getCachedResult(list.Name, list.CNIVersion, rt) if err != nil { return fmt.Errorf("failed to get network %q cached result: %v", list.Name, err) } }
for i := len(list.Plugins) - 1; i >= 0; i-- { net := list.Plugins[i] if err := c.delNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil { return err } } _ = delCachedResult(list.Name, rt)
return nil}
func (c *CNIConfig) delNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) error { c.ensureExec() pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path) if err != nil { return err }
newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt) if err != nil { return err }
return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("DEL", rt), c.exec)}
复制代码


c.args


c.args 方法作用是构建调用 cni 网络插件可执行文件时的参数。


从代码中可以看出,参数有Command(命令,Add代表构建网络,Del代表销毁网络)、ContainerID(容器 ID)、NetNS(容器网络命名空间路径)、IfName(Interface Name 即网络接口名称)、PluginArgs(其他参数如 pod 名称、pod 命名空间等)等。


// vendor/github.com/containernetworking/cni/libcni/api.gofunc (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args {  return &invoke.Args{    Command:     action,    ContainerID: rt.ContainerID,    NetNS:       rt.NetNS,    PluginArgs:  rt.Args,    IfName:      rt.IfName,    Path:        strings.Join(c.Path, string(os.PathListSeparator)),  }}
复制代码


invoke.ExecPluginWithResult


invoke.ExecPluginWithResult 主要是将调用参数变成 env,然后调用 cni 网络插件可执行文件,并获取返回结果。


func ExecPluginWithResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) (types.Result, error) {  if exec == nil {    exec = defaultExec  }
stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv()) if err != nil { return nil, err }
// Plugin must return result in same version as specified in netconf versionDecoder := &version.ConfigDecoder{} confVersion, err := versionDecoder.Decode(netconf) if err != nil { return nil, err }
return version.NewResult(confVersion, stdoutBytes)}
复制代码

总结

CNI

CNI,全称是 Container Network Interface,即容器网络接口。


CNI 是 K8s 中标准的调用网络实现的接口。Kubelet 通过这个标准的接口来调用不同的网络插件以实现不同的网络配置方式。


CNI 网络插件是一个可执行文件,是遵守容器网络接口(CNI)规范的网络插件。常见的 CNI 网络插件包括 Calico、flannel、Terway、Weave Net 等。


当 kubelet 选择使用 CNI 类型的网络插件时(通过 kubelet 启动参数指定),kubelet 在创建 pod、删除 pod 的时候,通过 CRI 调用 CNI 网络插件来做 pod 的构建网络和销毁网络等操作。



kubelet 构建 pod 网络的大致过程

(1)kubelet 先通过 CRI 创建 pause 容器(pod sandbox),生成 network namespace;


(2)kubelet 根据启动参数配置调用具体的网络插件如 CNI 网络插件;


(3)网络插件给 pause 容器(pod sandbox)配置网络;


(4)pod 中其他的容器都与 pause 容器(pod sandbox)共享网络。

kubelet 组件 CNI 相关启动参数分析

(1)--network-plugin:指定要使用的网络插件类型,可选值cnikubenet"",默认为空串,代表 Noop,即不配置网络插件(不构建 pod 网络)。此处配置值为cni时,即指定 kubelet 使用的网络插件类型为cni


(2)--cni-conf-dir:CNI 配置文件所在路径。默认值:/etc/cni/net.d


(3)--cni-bin-dir:CNI 插件的可执行文件所在路径,kubelet 将在此路径中查找 CNI 插件的可执行文件来执行 pod 的网络操作。默认值:/opt/cni/bin

kubelet 中的 CNI 初始化

kubelet 启动后,会根据启动参数中 cni 的相关参数,获取 cni 配置文件并初始化 cni 网络插件,待后续创建 pod、删除 pod 时会调用SetUpPodTearDownPod方法来构建 pod 的网络、销毁 pod 的网络。同时,初始化时起了一个 goroutine,定时探测 cni 的配置文件以及可执行文件,让其可以热更新。

CNI 构建 pod 网络

kubelet 创建 pod 时,通过 CRI 创建并启动 pod sandbox,然后 CRI 会调用 CNI 网络插件构建 pod 网络。


kubelet 中 CNI 构建 pod 网络的代码方法是:pkg/kubelet/network/cni/cni.go-SetUpPod

CNI 销毁 pod 网络

kubelet 删除 pod 时,CRI 会调用 CNI 网络插件销毁 pod 网络。


kubelet 中 CNI 销毁 pod 网络的方法是:pkg/kubelet/network/cni/cni.go-TearDownPod

发布于: 2 小时前阅读数: 4
用户头像

良凯尔

关注

热爱的力量 2020.01.10 加入

kubernetes开发者

评论

发布
暂无评论
kubernetes/k8s CNI 分析 - 容器网络接口分析