kubernetes网络和CNI简介

kubernetes网络和CNI简介

kubernetes提供了出色的容器编排能力,同时开放了网络、存储、运行时的接口,打造了云原生生态的同时,也顺手一统了容器云的天下。网络是云服务的基础和核心之一,就网络插件而言,市面上就有很多,kubernetes提供了CNI为所有网络插件提供接口。下面简单介绍一下kubernetes的容器网络接口。

Pod网络

对于任何kubernetes网络方案,都需要满足以下需求

1. 每个Pod都拥有一个独立的IP地址,而且假定所有的pod都在一个可以直接连通的、扁平的网络空间中;

2. 用户不需要额外考虑如何建立Pod之间的连接,也不需要考虑将容器端口映射到主机端口等问题;

3. 网络要求:

  • 所有的容器都可以在不用NAT的方式下同别的容器通讯

  • 所有节点都可在不用NAT的方式下同所有容器通讯

  • 容器的地址和其他宿主机和容器看到的地址是同一个地址

Pod网络基本原理

容器通过使用linux内核提供的Cgroups和Namespaces技术实现了相互之间的网络、存储等资源的隔离与限制。对于网络,kubernetes项目中的Pod则通过让一组用户容器和pause容器/infra容器共享一个network namespace的方式,使一个Pod内的容器都使用了一个网络栈。而一个 Network Namespace 的网络栈包括:网卡(Network Interface)、回环设备(Loopback Device)、路由表(Routing Table)和 iptables 规则。所以,不难想象,Pod的网络和一台虚拟机的网络栈配置起来实际上是类似的,比如同样需要虚拟网卡,IP、MAC地址等,并且每个Pod都有自己唯一的网络栈。当所有的Pod都有了自己的网络栈后,如果想要连接两个Pod进行通信,则类似于其他任何网络架构,需要配置交换机、路由器等,并为其规划IP,路由等信息。如果是对于物理机,我们可以使用网线、交换机、路由器这些设备进行连接,但在Pod中显然需要其他方式。

刚开始接触容器的时候,觉得这种设计好奇怪,本来进程间通信挺容易的,如今用namespace做隔离,再想办法让隔离的进程进行网络通信…~_~!!

Pod网络类型

kubernetes Pod的网络方案有很多,最典型的就是Flannel的三种后端实现方式了(UDP、VxLan、host-gw),讨论这些则主要是在关注容器跨主机通信的问题。而这里主要讨论的则是Pod的内部的网卡如何创建,又如何将网络数据包在宿主机和容器之间传递。

图片来自这里

1. 虚拟网桥:创建一个虚拟网卡对(veth pair),一头在容器内,一头在宿主机的root namespace内,并且使用Linux bridge(网桥)或者OpenvSwitch(OVS)来连接两个不同namespace内的网卡对。这样一来,容器内发出的网络数据包,可以通过网桥进入宿主机网络栈,而发往容器的网络数据包也可以经过网桥进入容器。例如,docker项目中的docker0、kubernetes项目中的cni0都是网桥设备。

veth pair有个很好的特性,两张虚拟网卡总是成对的出现,并且,从其中一个\\”网卡\\”发出的数据包可以直接出现在与它对应的另一张\\”网卡\\”上,有点像物理的\\”虫洞\\”嚯

2. 多路复用:如图所示,使用一个中间网络设备,暴露多个虚拟网卡接口,容器网卡都可以接入这个中间设备,并通过mac地址/IP地址来区分packet应该转发给哪一个容器设备。

多路复用:物理上,一根光纤内,可以同时跑很多很多不同频率的光波,这就是多路复用的其中一种实现方式。

3. 硬件交换:还有个“比较直接”的方法就是为每个Pod分配一个虚拟网卡,这样一来,Pod与Pod之间的连接关系就会变的非常清晰,因为近乎物理机之间的通信基础。如今大多数网卡都支持SR-IOV功能,该功能将单一的物理网卡虚拟成多个VF接口,每个VF接口都有单独的虚拟PCIe通道,这些虚拟的PCIe通道共用物理网卡的PCIe通道。

kubernetes CNI

工作原理简介

CNI是Container Network Interface的缩写,它是一个通用的容器网络插件的k8s网络接口,开源社区里已经有了很多实现容器网络的方案,不同的网络实现方案在k8s内都是以插件调用的形式工作,所以这里需要一个统一的标准接口。如果将k8s的Pod视为一台“虚拟机”,那么网络插件的工作就是管理这台虚拟机的网络栈,包括给这台虚拟机插入网卡、配置路由、IP等;而CNI的工作则是对接网络插件kubelet容器运行时管理工具(对于docker容器运行时来说实际上是dockershim),主要体现在Pod的创建和删除过程:

  • CNI加载目录 /etc/cni/net.d/下的配置文件,比如:10-calico.conflist

  • Pod Create

    • 使用macvlan二进制文件创建网卡

    • 调用dhcp二进制文件获取IP

    • 将网卡放入pod network namespace

  • Pod Delete

    • 调用dhcp二进制文件释放IP

    • 调用macvlan二进制文件删除网卡

    • 结束容器

CNI 配置文件,给CRI使用的,比如dockershim

  1. root@master8088:~# cat /etc/cni/net.d/10-calico.conflist

  2. {

  3. \\\"name\\\": \\\"k8s-pod-network\\\",

  4. \\\"cniVersion\\\": \\\"0.3.0\\\",

  5. \\\"plugins\\\": [

  6. {

  7. \\\"type\\\": \\\"calico\\\",

  8. \\\"log_level\\\": \\\"info\\\",

  9. \\\"datastore_type\\\": \\\"kubernetes\\\",

  10. \\\"nodename\\\": \\\"master8088\\\",

  11. \\\"mtu\\\": 1500,

  12. \\\"ipam\\\": {

  13. \\\"type\\\": \\\"host-local\\\",

  14. \\\"subnet\\\": \\\"usePodCidr\\\"

  15. },

  16. \\\"policy\\\": {

  17. \\\"type\\\": \\\"k8s\\\"

  18. },

  19. \\\"kubernetes\\\": {

  20. \\\"kubeconfig\\\": \\\"/etc/cni/net.d/calico-kubeconfig\\\"

  21. }

  22. },

  23. {

  24. \\\"type\\\": \\\"portmap\\\",

  25. \\\"snat\\\": true,

  26. \\\"capabilities\\\": {\\\"portMappings\\\": true}

  27. }

  28. ]

  29. }

可执行文件

CNI官方维护的插件包括以下几个,对于已经搭建好的k8s,cni插件可以在 /opt/cni/bin/文件夹下查看:CNI的基础可执行文件按照功能可以划分为三类:

Main插件:创建具体网络设备 

bridge:网桥设备,连接container和host 

ipvlan:为容器增加ipvlan网卡 

loopback:lo设备

macvlan:为容器创建一个MAC地址 

ptp:创建一对Veth Pair 

vlan:分配一个vlan设备 

host-device:将已存在的设备移入容器内

IPAM插件:负责分配IP地址 

dhcp:容器向DHCP服务器发起请求,给Pod发放或回收IP地址 

host-local:使用预先配置的IP地址段来进行分配

static:为容器分配一个静态IPv4/IPv6地址,主要用于debug

meta插件:并非单独使用 

bandwidth:使用Token Bucket Filter(TBF)来限流的插件

flannel:flannel网络方案的CNI插件,对应于flannel配置文件

portmap:通过iptables配置端口映射 

sbr:为网卡设置source based routing 

tuning:通过sysctl调整网络设备参数

firewall:通过iptables给容器网络的进出流量进行一系列限制

目前kubernetes项目中的Pod只能加载一个CNI的配置,如果需要一个POD使用多网卡多网络方案是不可以的。

实验

实验内容:go安装社区维护的CNI相关插件,在linux主机上创建一个network namespace,再利用安装的网络插件给这个linux network namespace配置好网络。实验方法教程参考的是这里

安装cni插件:

  1. mkdir -p go/src/github.com/containernetworking/

  2. cd go/src/github.com/containernetworking/

  3. git clone https://github.com/containernetworking/plugins.git

  4. git clone https://github.com/containernetworking/cni.git

  5. cd plugins

  6. ./build_linux.sh

  7. cd ../cni/cnitool

  8. go build cnitool.go

linux上创建一个network namespace:

  1. sudo ip netns add Mytest

设置网络参数: vi/etc/cni/net.d/10-myptp.conf

  1. {

  2. \\\"cniVersion\\\": \\\"0.3.1\\\",

  3. \\\"name\\\": \\\"myptp\\\",

  4. \\\"type\\\": \\\"ptp\\\",

  5. \\\"ipMasq\\\": true,

  6. \\\"ipam\\\": {

  7. \\\"type\\\": \\\"host-local\\\",

  8. \\\"subnet\\\": \\\"172.16.29.0/24\\\",

  9. \\\"routes\\\": [

  10. {

  11. \\\"dst\\\": \\\"0.0.0.0/0\\\"

  12. }

  13. ]

  14. }

  15. }

模拟给容器配置网络:

  1. sudo CNI_PATH=../../plugins/bin ./cnitool add myptp /var/run/netns/Mytest

返回值

  1. {

  2. \\\"cniVersion\\\": \\\"0.3.1\\\",

  3. \\\"interfaces\\\": [

  4. {

  5. \\\"name\\\": \\\"vethe6180c75\\\",

  6. \\\"mac\\\": \\\"ea:d0:00:22:ce:33\\\"

  7. },

  8. {

  9. \\\"name\\\": \\\"eth0\\\",

  10. \\\"mac\\\": \\\"56:20:72:2b:5a:7e\\\",

  11. \\\"sandbox\\\": \\\"/var/run/netns/Mytest\\\"

  12. }

  13. ],

  14. \\\"ips\\\": [

  15. {

  16. \\\"version\\\": \\\"4\\\",

  17. \\\"interface\\\": 1,

  18. \\\"address\\\": \\\"172.16.29.4/24\\\",

  19. \\\"gateway\\\": \\\"172.16.29.1\\\"

  20. }

  21. ],

  22. \\\"routes\\\": [

  23. {

  24. \\\"dst\\\": \\\"0.0.0.0/0\\\"

  25. }

  26. ],

  27. \\\"dns\\\": {}

  28. }

测试网络是否可用:

  1. $ sudo ip -n Mytest addr

  2. 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000

  3. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

  4. 3: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default

  5. link/ether 56:20:72:2b:5a:7e brd ff:ff:ff:ff:ff:ff link-netnsid 0

  6. inet 172.16.29.4/24 brd 172.16.29.255 scope global eth0

  7. valid_lft forever preferred_lft forever

  8. inet6 fe80::5420:72ff:fe2b:5a7e/64 scope link

  9. valid_lft forever preferred_lft forever

进入namespace,ping一下百度的DNS

  1. $ sudo ip netns exec Mytest ping -c 2 180.76.76.76

  2. PING 180.76.76.76 (180.76.76.76) 56(84) bytes of data.

  3. 64 bytes from 180.76.76.76: icmp_seq=1 ttl=49 time=27.4 ms

  4. 64 bytes from 180.76.76.76: icmp_seq=2 ttl=49 time=26.3 ms


  5. --- 180.76.76.76 ping statistics ---

  6. 2 packets transmitted, 2 received, 0% packet loss, time 1001ms

  7. rtt min/avg/max/mdev = 26.374/26.916/27.458/0.542 ms

清除:

  1. sudo CNI_PATH=../../plugins/bin ./cnitool del myptp /var/run/netns/Mytest`

  2. sudo ip netns del Mytest

总结

1. 容器网络实现了容器间、容器和宿主机间、容器和服务间的通信;

2. CNI主要提供了容器运行时和网络插件之间的协作;

3. CNI主要以插件调用的方式工作。

4. 自己实现一个网络方案,除了需要实现网络本身(例如:flanneld),还需要该网络对应的CNI插件(例如:flannel)。

最后多说一句,所谓的云服务其实就是基于网络的服务,好好规划网络可以充分利用数据中心的资源,只有充分利用数据中心的资源,才能称之为云计算。

参考资料

  1. https://github.com/containernetworking/cni/tree/master/cnitool

  2. https://thenewstack.io/hackers-guide-kubernetes-networking/

  3. http://www.cnblogs.com/YaoDD/p/7419383.html

  4. https://github.com/containernetworking/plugins

  5. https://time.geekbang.org/column/article/64948

  6. https://www.zhihu.com/question/24950671

原创文章,作者:EBCloud,如若转载,请注明出处:https://www.sudun.com/ask/33499.html

(0)
EBCloud's avatarEBCloud
上一篇 2024年4月2日 下午3:28
下一篇 2024年4月2日 下午3:28

相关推荐

发表回复

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