手工体验docker 是怎么管理网络的-kubelet 如何创建网络(3)

11 篇文章 0 订阅
3 篇文章 0 订阅

我们已经从浅入深的总结了两篇文章,来分析docker 如何利用cni 创建网络。今天我们来到终结篇,分析下kubelet 是如何利用cni 创建网络。

1. kubelet 创建容器的过程

在这里插入图片描述
我们把这张图放在这里。kubelet 是一个本地的服务,会定期watch 分配到自己头上的pod,当发现自己需要处理一个addpod 的动作的时候。会通过一个kubecontainer.Runtime去执行pod 的生命周期管理,这个Runtime 的一个实现就是kuberuntime.NewKubeGenericRuntimeManager,代码位于/kubelet/kuberuntime/kuberuntime_manager.go。我们看下SyncPod 这个方法。它的注释说的很清楚:

//  1. Compute sandbox and container changes.
//  2. Kill pod sandbox if necessary.
//  3. Kill any containers that should not be running.
//  4. Create sandbox if necessary.
//  5. Create ephemeral containers.
//  6. Create init containers.
//  7. Create normal containers.

代码中注释的也非常清楚。我们重点看下NewKubeGenericRuntimeManager.runtimeService 这个字段。因为这个字段,是真正去访问运行时服务的client。我们看到图中,cri 会发起一个grpc请求,请求的对象是一个被称之为dockershim(垫钱)的grpc server。这个server是在kubelet启动时run起来的,它全权代理了针对docker.sock 的本地请求,至于请求发给了docker 以后,数据怎么走,我们并不关心。 参见我之前的文章,dockerd,containerd,runc 和 container-shim 的关系。其中我们所关心的pod 的网络设置,也就是设置在pod 的沙箱中的网络设置,发生在步骤4。我们下面重点跟踪。

2. 网络的创建

其创建pod 的第一步 就是创建pod 的沙箱环境也就是pause 容器,我们重点看下创建沙箱的过程。

Step 1: Pull the image for the sandbox.
Step 2: Create the sandbox container.
Step 3: Create Sandbox Checkpoint.
Step 4: Start the sandbox container.
Step 5: Setup networking for the sandbox.

实现在:runtimeService.RunPodSandbox,dockershim 就会先调用Docker API创建并启动infra容器。最终会来到:r.runtimeClient.RunPodSandbox,这是一个grpc 请求,请求的server 真实响应在代码在
kubernetes\pkg\kubelet\dockershim\docker_sandbox.go: RunPodSandbox中实现的。

第5步的时候就会给pod设置网络,CNI插件在pod启动的时候设置pod网络,分配IP地址,设置路由关系,创建网卡等。这个实现在这里:ds.network.SetUpPod。这个方法会调用kubelet 在启动时曾经初始化过的cni插件的相关接口方法,这个接口就是CNI接口,

plugin.addToNetwork

type CNI interface {
	AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
	CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
	DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
	GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)

	AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
	CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
	DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
	GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)

	ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error)
	ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error)
}

CNI的插件有一段很简单的代码,调用了CNI插件的相关代码处理。

// AddNetworkList executes a sequence of plugins with the ADD command
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)
}

我们看到 CNI driver 会根据配置调用具体的 cni 插件,cni 插件将给 pause 容器配置正确的网络,其处理就像我们在前一篇文章中所写的一样,这里只是用代码的方式调用而已。

3. 其他容器的绑定

这一步很简单了,因为pod 是容器组,所以当pause 容器的网络设置好了以后,只要把sandboxid 传递给接下来的容器创建接口就好了。
很简单,这里的接口在创建容器时只是传入的上一步的sandboxID。

containerID, err := m.runtimeService.CreateContainer(podSandboxID, containerConfig, podSandboxConfig)

4. 多节点的容器互通

我们忽略了一步,当我们安装插件的逻辑,设置好了当前容器的network namespace后,多节点的互通由谁负责呢?这一步的解决,我们以flannel 为例。

我们在k8s环境上用命令安装的flannel,又是干什么用的呢?

https://raw.githubusercontent.com/coreos/flannel/a70459be0084506e4ec919aa1c114638878db11b/Documentation/kube-flannel.yml

从yaml文件上看有两个容器,分别为install-cni和 kube-flannel

install-cni:为初始化容器,执行后就消失了,用于从etcd 获取当前节点的网段信息,生成该主机/etc/cni/net.d/的flannel配置,我们看到它也的确做了mount。k8s创建、删除容器的时候就可以按照CNI流程挂载和删除网卡。

kube-flannel:主要启动flanneld,flanneld启动的时候会传入两个参数ip-masq和kube-subnet-mgr。
第一个参数–ip-masq,是为容器配置snat,容器默认都是可以直接访问外网的。MASQUERADE和SNAT都是用于将内网地址出局时转变为外部地址的方式,但MASQ本质上还是SNAT,只不过特殊一点。网络出口为动态分配IP时,MASQ是唯一的选择,它先判断当前系统的默认网关,再根据默认网关找到本机对应的出口IP地址,再做SNAT。
第二个参数–kube-subnet-mgr代表其使用kube类型的subnet-manager。该类型有别于使用etcd的local-subnet-mgr类型,使用kube类型后,flannel上各Node的IP子网分配均基于K8S Node的spec.podCIDR属性。
flanneld初始化创建基本的网络接口flannel,打通不同节点上的网络。CNI就负责将各个pod挂载到这个网络上。 flanneld 就是来去封装和解包vxlan的软件组件。数据到了vxlan 以后,容器就可以通过这种overlay 的方式,达到了集群内容器与容器的直接可达。

5. 总结

这里的过程总结可以看到,kubelet 做了大量的解耦的工作,调用CNI插件也是通过二进制直接操作的方式,以后当我们编写新的CNI插件时可以直接学习flannel的做法了。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值