kubernetes学习之cni与pod网络

pod网络

kubernetes中每个pod都拥有自己的ip地址,pod内的所有容器实例都共享同一个网络命名空间,因此容器实例之间可以通过localhost地址进行通信,并且他们都可以看到同样的ip地址和mac地址。

kubernetes对于pod的网络定制了下列三个要求,所有网络插件支持这几个要求即可:

  • 所有节点上所有的pod可以互相通信,并且不依赖NAT
  • 节点上的进程可以和该节点上的所有pod通信
  • 运行在主机网络空间下的pod可以和所有节点上的所有pod互相通信,并且不依赖NAT

网络插件

cni代表容器网络接口,是CNCF社区下的一个项目,定义了一系列的接口格式,来规范容器的网络接口的配置。

kubernetes支持两种网络插件配置模式,分别为

  • cni
  • kubenet

kubenet

其中kubenet是一个非常简单的网络插件,并且只支持linux。本身不提供跨节点的网络通信以及网络策略功能。通常是要和云控制器结合起来去使用的。kubenet模式下,kubelet会创建一个叫做cbr0的网桥设备,并未每个pod创建一个veth-pair(虚拟接口对),一端在容器内,一端在容器外,在容器外的接口加入到这个网桥(二层)中。节点上所有的pod都通过主机上的接口连接到网桥,因此针对单个节点上的pod是可以互相通信的。

可以通过以下示例进行简单测试验证:

echo 1 > /proc/sys/net/ipv4/conf/all/accept_local
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 1 > /proc/sys/net/ipv4/conf/default/accept_local
echo 0 > /proc/sys/net/ipv4/conf/default/rp_filter
echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables
ip link add testbr type bridge
ip link set testbr promisc on
ip link set testbr up
ip netns add test1
ip netns add test2
ip link add veth1 type veth peer br-veth1
ip link add veth2 type veth peer br-veth2

ip link set veth1 netns test1
ip link set br-veth1 master testbr
ip link set br-veth1 up
ip link set veth2 netns test2
ip link set br-veth2 master testbr
ip link set br-veth2 up

ip netns exec test1 ip addr add 172.56.1.2/24 dev veth1
ip netns exec test2 ip addr add 172.56.1.3/24 dev veth2
ip netns exec test1 ip link set veth1 up
ip netns exec test2 ip link set veth2 up

ip netns exec test2 ping 172.56.1.2

ip netns exec test1 nc -l -p 9999
ip netns exec test2 nc 172.56.1.2 9999

kubenet模式下,还会依赖几个标准的cni插件实现上述功能。第一个是bridge插件,上面说的cbr0就是通过cni的bridge插件进行创建,bridge插件还会创建veth-pair并将其外部一端加入到bridge中,网卡ip地址的配置是hostlocal插件进行分配的,hostlocal插件需要的网段信息是使用节点的podCidrs字段值,所以这要求我们给kubelet配置了podCidrs或者我们的kube-controller-manager启用了自动分配podCidrs功能。同时还依赖于loopback插件创建lo回环接口。最后就是依赖于云控制器自动配置各个节点上的pod的路由了。

cni

在cni模式下,pod的网络接口和ip地址由cni插件配置和分配的,在创建pod时,kubelet会调用cni插件创建网络接口,分配ip地址以及接口配置。

在linux下,会为每个pod创建lo本地回环接口,这是kubelet内置的一个固定的cni配置,只有一个loopback插件。其他网络接口通过我们定义的网络插件逐个执行,进行创建和配置操作。

kubelet在加载cni配置插件时会默认在/etc/cni/net.d目录下寻找.conf,.conflist,.json结尾的配置文件,并做解析和验证,直到找到一个合法的cni配置并返回。

cni使用0.4.0版本的cni。

cni规范

cni规范主要为容器应用指定了一套标准的网络解决方案。其中包括配置规范,接口规范。

在cni规范中:

  • 容器基本可以和linux的网络命名空间划等号。
  • 网络通常就是一些列可以单独寻址并且可以互联互通的实体,实体可以是不同的容器,主机或者是网络设备等。容器可以添加到网络中,也可从网络中删除。

cni插件要实现为可执行的程序,容器管理系统(如kubernetes)会调用这个可执行文件管理容器实例的网络。创建容器实例后,cni插件需要在容器的网络命名空间添加网络接口,配置接口的ip地址,配置接口的路由,如果必要,也需要在主机上配置相关路由等规则。

cni操作

cni插件必须要实现下面4中操作:

  • ADD 添加容器到网络中
  • DEL 将容器从网络中删除
  • CHECK 检查容器网络与预期一致
  • VERSION 报告版本

我们可以把这几个操作认为是函数调用,每个函数调用接收一些必要参数和可选参数,函数根据我们的网络配置执行必要的操作,并将执行的结果返回给调用者。在调用插件时,不同插件的配置是通过标准输入传递给cni插件,和具体容器和网络相关的参数是通过环境变量传递给cni插件的,容器管理服务可以根据cni插件的退出状态码或标准输出返回的信息得到执行结果。

一些标准环境变量的定义和说明如下,通过标准输入传递的配置信息格式参考后面的cni网络配置:

  • CNI_COMMAND 表明要进行的操作,如ADD,DEL
  • CNI_CONTAINERID 容器ID
  • CNI_NETNS 网络命名空间路径
  • CNI_IFNAME 网络接口名称
  • CNI_ARGS 在调用时额外传递的一些参数
  • CNI_PATH cni可执行文件的搜索路径

下面主要来看下ADD和DEL操作的具体定义。

ADD操作

参数:

  • 容器ID 容器实例独一无二的文本标识,由容器管理系统分配
  • 网络命名空间路径 代表网络命名空间路径,如/proc/[pid]/ns/net,/var/run/docker/netns/2cedefe8d5e7等
  • 网络配置 cni网络插件配置
  • 额外参数 针对单个cni插件或单个容器传递过来的额外的参数配置
  • 网络接口名称 在容器内部的网卡的接口名称

结果:

  • 接口列表
  • ip地址配置
  • dns信息

DEL操作

参数:

  • 容器ID
  • 网络命名空间路径
  • 网络配置
  • 额外参数
  • 网络接口名称

所有参数应该和创建时完全一样,删除操作应该释放所有和这个容器相关的网络资源。

通常情况下,执行DEL操作时,如果一些资源确实,则不应该返回失败信息。如ipam插件释放一个ip地址,但是发现ip地址不存在,那么就已经达到目的了,就不要返回错误信息了。再比如,bridge插件在执行DEL操作时,发现网络命名空间或者网络接口都不存在了,那么也要执行委派(delegate)操作,执行ipam插件的DEL,让ipam插件有释放自己资源的能力。

cni网络配置

cni网络配置格式,下面是这个单个cni网络配置的格式说明,是传递给具体某个cni插件的,配置以json形式表示,配置文件可以保存在本地路径上,具体字段含义如下:

  • cniVersion cni配置规范的版本
  • name 网络名称,在所有的容器网络中唯一,kubernetes里这个名称随意
  • type 可执行程序的文件名
  • args 由容器运行时提供的参数
  • ipMasq 如果插件支持的话,用来表明是否在主机上为该网络设置ip地址伪装。如果主机充当网关使用的话,并且其他主机上没有到该主机的路由规则时,这个是必要的
  • ipam ipam相关配置
    • type 引用的ipam插件的可执行文件的文件名
  • dns dns相关配置
    • nameservers dns服务器
    • domain 本地域,用来做短域名解析
    • search 同样用来做短域名解析,大多数dns解析实现会优先使用domain
    • options dns配置的可选参数

其中值得注意的是ipam字段,这个字段是一个约定俗成的字段,cni插件一般实现两个功能:网络接口设备管理和接口地址相关的管理,通过ipam将接口地址管理相关功能提取出来,以便在接口设备管理程序中也可以通过cni规范调用其他cni插件来管理这个接口的ip地址相关配置。这样把这两类功能抽象出来,可以简化插件的开发,并且可以实现复用。

除了上面的字段之外,每个插件还可以定义自己专属的字段,如logLevel等。

cni网络配置列表

cni网络配置列表定义多个网络插件的配置。针对同一个容器,容器管理系统根据列表中定义的网络插件对容器网络依次进行配置。这个配置通常是存放在磁盘上的固定路径下,也可以是容器管理系统动态生成的。具体字段配置如下:

  • cniVersion cni版本
  • name 网络名称
  • disableCheck 禁用CHECK操作
  • plugins 参考上面的cni网络配置

在容器管理系统执行每个插件的时候,需要用这块定义的cniVersion和name替换。以便所有cni执行是时都使用统一的版本,避免不同版本造成问题。其他具体说明可以参考cni文档。

示例参考:https://github.com/containernetworking/cni/blob/spec-v0.4.0/SPEC.md#example-network-configuration-lists

cni插件

CNI插件

主机网络空间

通过设置pod的spec.hostNetwork为true,使用主机的网络命名空间,则不会调用cni去创建pod的网卡。

	// Do not invoke network plugins if in hostNetwork mode.
	if config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetNetwork() == runtimeapi.NamespaceMode_NODE {
		return resp, nil
	}

此处是决定docker container的网络模式判断,即docker run –rm -d –network host tutum/dnsutils sleep 1d这个命令中的–network参数,docker支持的网络可通过docker network ls查看,default是采用docker的bridge驱动。

// modifyHostOptionsForSandbox applies NetworkMode/UTSMode to sandbox's dockercontainer.HostConfig.
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"
	}
}

参考

网络插件配置

CNI规范

CNI插件

linux虚拟网络设备

https://itectec.com/superuser/linux-why-linux-bridge-doesnt-work/
https://blog.csdn.net/dog250/article/details/78746198

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注