在Kubernetes中设计了一种网络模型,集群中所有运行的pod资源,都默认会从同一个平面网络得到一个IP地址,无论是否处于同一个节点或者同一个名称空间,各pod彼此之间都可使用各自的地址直接通信。另一方面,pod网络的管理却非kubernetes系统内置的功能,而是由第三方项目以CNI插件方式完成,除pod网络管理,有相当一部分CNI网络插件还实现了网络策略。这些插件赋予管理员和用户通过自定义 NetworkPolicy 资源来管控pod通信的能力。

需要注意的是:在K8S集群中,IP地址分配是以Pod对象为单位,而非容器,同一Pod内的所有容器共享同一网络名称空间。

1 容器网络模型

Network、IPC和UTS名称空间隔离技术是容器能够使用独立网络栈的根本,而操作系统的网络设备虚拟化技术是打通各容器间通信并构建起多样化网络拓扑的至关重要因素,在Linux系统上,这类的虚拟化设备类型有VETH、Bridge、VLAN、MAC VLAN、IPVLAN、VXLAN、MACTV和TAP/IPVTAP等。这些网络虚拟化相关的技术是支撑容器与容器编排系统网络的基础。

1.1 容器网络通信模式

Docker容器的原生网络模型主要有3种:Bridge(桥接)、Host(主机)、none。

  • Bridge:借助虚拟网桥设备为容器建立网络连接。
  • Host:设置容器直接共享当前节点主机的网络名称空间。
  • none:多个容器共享同一个网络名称空间。 ```bash

    使用以下命令查看docker原生的三种网络

    [root@localhost ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 0efec019c899 bridge bridge local 40add8bb5f07 host host local ad94f0b1cca6 none null local

none网络,在该网络下的容器仅有lo网卡,属于封闭式网络,通常用于对安全性要求较高并且不需要联网的应用

[root@localhost ~]# docker run -it —network=none busybox / # ifconfig lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

host网络,共享宿主机的网络名称空间,容器网络配置和host一致,但是存在端口冲突的问题

[root@localhost ~]# docker run -it —network=host busybox / # ip addr 1: lo: mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: mtu 1500 qdisc pfifo_fast qlen 1000 link/ether 00:0c:29:69:a7:23 brd ff:ff:ff:ff:ff:ff inet 192.168.1.4/24 brd 192.168.1.255 scope global dynamic eth0 valid_lft 84129sec preferred_lft 84129sec inet6 fe80::20c:29ff:fe69:a723/64 scope link valid_lft forever preferred_lft forever 3: docker0: mtu 1500 qdisc noqueue link/ether 02:42:29:09:8f:dd brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::42:29ff:fe09:8fdd/64 scope link valid_lft forever preferred_lft forever / # hostname localhost

bridge网络,Docker安装完成时会创建一个名为docker0的linux bridge,不指定网络时,创建的网络默认为桥接网络,都会桥接到docker0上。

[root@localhost ~]# brctl show bridge name bridge id STP enabled interfaces docker0 8000.024229098fdd no

[root@localhost ~]# docker run -d nginx #运行一个nginx容器 c760a1b6c9891c02c992972d10a99639d4816c4160d633f1c5076292855bbf2b

[root@localhost ~]# brctl show
bridge name bridge id STP enabled interfaces docker0 8000.024229098fdd no veth3f1b114

一个新的网络接口veth3f1b114桥接到了docker0上,veth3f1b114就是新创建的容器的虚拟网卡。进入容器查看其网络配置: [root@localhost ~]# docker exec -it c760a1b6c98 bash root@c760a1b6c989:/# apt-get update root@c760a1b6c989:/# apt-get iproute root@c760a1b6c989:/# ip a 1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 38: eth0@if39: mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever

  1. 从上可以看到容器内有一个网卡eth0@if39,实际上eth0@if39veth3f1b114是一对veth pairveth pair是一种成对出现的特殊网络设备,可以想象它们由一根虚拟的网线进行连接的一对网卡,eth0@if39在容器中,veth3f1b114挂在网桥docker0上,最终的效果就是eth0@if39也挂在了docker0上。
  2. 桥接式网络是目前较为流行和默认的解决方案。但是这种方案的弊端是无法跨主机通信的,仅能在宿主机本地进行,而解决该问题的方法就是NAT。所有接入到该桥接设备上的容器都会被NAT隐藏,它们发往Docker主机外部的所有流量都会经过源地址转换后发出,并且默认是无法直接接受节点之外的其他主机发来的请求。当需要接入Docker主机外部流量,就需要进行目标地址转换甚至端口转换将其暴露在外部网络当中。如下图:<br />![](https://cdn.nlark.com/yuque/0/2021/png/2678081/1637856467660-f1302f15-3c9d-4a62-8af0-368187e65f43.png#clientId=u06ca3529-6807-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uba4c7ae4&margin=%5Bobject%20Object%5D&originHeight=419&originWidth=812&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u1eeb9b14-e6cc-4356-9479-7862d949c55&title=)<br />容器内的属于私有地址,需要在左侧的主机上的eth0上进行源地址转换,而右侧的地址需要被访问,就需要将eth0的地址进行NAT转换。SNAT---->DNAT<br />这样的通信方式会比较麻烦,从而需要借助第三方的网络插件实现这样的跨主机通信的网络策略。
  3. <a name="eyMeK"></a>
  4. ### 1.2 Kubernetes的网络模型
  5. Kubernetes集群中主要解决4类通信需求:
  6. - **Pod内容器间通信**:同一个Pod内的多个容器间的通信,它们之间通过lo网卡进行通信。
  7. - **分布式Pod间通信**:通过Pod IP地址进行通信。
  8. - **ServicePod之间的通信**:Pod IP地址和Service IP进行通信,两者并不属于同一网络,实现方式是通过IPVSiptables规则转发。
  9. - **集群外部客户端与Pod对象的通信**,实现方式:IngressNodePortLoadbalance
  10. K8S网络的实现不是集群内部自己实现,而是依赖于第三方网络插件----CNIContainer Network Interface
  11. <a name="tUaxZ"></a>
  12. ### 1.3 CNI网络插件基础
  13. CNIContainer Network Interface)是 CNCF 旗下的一个项目,是容器引擎与遵循改规范网络插件的中间层,专用于为容器配置网络子系统。目前由RKTDocker Kubernetes OpenShift Mesos等相关的容器运行时环境所运行。
  14. 通常,遵循CNI规范的网络插件是一个可执行程序 文件,它们可由容器编排系统(例如Kubernetes等)调用,负责向容器的网络名称空间插入一个网络接口并在宿主机上执行必要的任务以完成虚拟网络配置,因而通常被称为网络管理插件,即NetPlugin。随后,NetPlugin还需要借助IPAM插件为容器的网络接口分配IP地址,这意味着CNI允许将核心网络管理功能与IP地址分配等功能相分离,并通过插件组合的方式堆叠出一个完整的解决方案。简单来说,目前的CNI规范主要由NetPluginIPAM两个插件API组成,如图10-5所示。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/2678081/1637858025000-51d0d2fc-eee7-45e6-8dc1-7064cdd4c7e8.png#clientId=u06ca3529-6807-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=265&id=u13375ee3&margin=%5Bobject%20Object%5D&name=image.png&originHeight=530&originWidth=952&originalType=binary&ratio=1&rotation=0&showTitle=false&size=139141&status=done&style=none&taskId=ucd7126e1-1481-4e2d-a884-1162a63a5ce&title=&width=476)<br />以下是对两个插件的简要说明。
  15. - 网络插件地称Main插件,负责创建/删除网络以及向网络添加/删除容器,它专注于连通容器与容器之间以及容器与宿主机之间的通信,同容器相关的网络设备通 常都由该类插件所创建,例如BridgeIP VLANMAC VLANloopback PTPVETH以及VLAN等虚拟设备。
  16. - IPAM (IP Address Management), 该类插件负责创建I删除地址池以及分配/回收容器的IP地址;目前,该类型插件的实现主要有host-localdhcp两个,前一个基于预置的地址范围进行地址分配,而后一个通过DHCP协议获取地址。
  17. 显然,NetPlugin CNI中最重要的组成部分,它才是执行创建虚拟网络、为Pod生成网络接口设备,以及将Pod接入网络中等核心任务的插件。为了能够满足分布式Pod 信模型中要求的所有Pod必须在同一平面网络中的要求,NetPlugin 目前常用的实现方案有 Overlay网络(Overlay Network)和Underlay网络(Underlay Network)两类。
  18. - Overlay网络借助VXLANUDPIPIPGRE等隧道协议,通过隧道协议报文封 Pod间的通信报文(IP 报文或以太网帧)来构建虚拟网络。
  19. - Underlay网络通常使用direct routing (直接路由)技术在Pod的各子网间路由Pod IP报文,或使用BridgeMAC VLANIP VLAN等技术直接将容器暴露给外部网络。
  20. 其实,Overlay 网络的底层网络也就是承载网络,因此,Underlay 网络的解决方案也就 是一类非借助隧道协议而构建的容器通信网络。相较于承载网络,Overlay 网络由于存在额外的隧道报文封装,会存在一定程度的性能开销。然而,用户在不少场景中可能会希望创建跨越多个L2L3的逻辑网络子网,这就只能借助Overlay封装协议实现。
  21. Pod配置网络接口是NetPlugin的核心功能之一, 但不同的容器虚拟化网络解决方案中,为Pod的网络名称空间创建虚拟接口设备的方式也会有所不同,目前,较为注流的实 现方式有veth (虚拟以太网)设备、多路复用及硬件交换3种,如图10-6所示。
  22. - **veth设备:**创建一个网桥,并为每个容器创建一对 虚拟以太网接口,一个接人容器内部,另一个留置于根名称空间内添加为Linux内核桥接功能或OpenvSwitch(OVS)网桥的从设备。
  23. - **多路复用:**多路复用可以由一个中间网络设备组成,它暴露多个虚拟接口,使用数据包转发规则来控制每个数据包转到的目标接口;MACVLAN技术为每个虚拟接 口配置一个MAC地址并基于此地址完成二层报文收发,IP VLAN则是分配一个IP地址并共享单个MAC,并根据目标IP完成容器报文转发。
  24. - **硬件交换:**现今市面上有相当数量的NIC都支持SR-IOV (单根I/O虚拟化),SR-IOV是创建虚拟设备的一种实现方式,每个虚拟设备自身表现为一个独立的PCI 备,并有着自己的VLAN及硬件强制关联的QoS ; SR-IOV 提供了接近硬件级别的性能。
  25. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/2678081/1637981878482-c584261e-72a0-4396-a45f-410b450898de.png#clientId=u454b9038-b664-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=253&id=u2716d3f4&margin=%5Bobject%20Object%5D&name=image.png&originHeight=505&originWidth=1198&originalType=binary&ratio=1&rotation=0&showTitle=false&size=255439&status=done&style=none&taskId=u657caa4d-98fa-41a1-9f22-d408fc78120&title=&width=599)<br />Kubernetes借助CNI插件体系来组合需要的网络插件完成容器网络编排功能。每次初始化或删除Pod对象时,kubelet 都会调用默认的CNI插件创建一个虚拟设备接口附加到相关的底层网络,为其设置IP地址、路由信息并将其映射到Pod对象的网络名称空间。具 体过程是,kubelet 首先在默认的/etc/cni/net.d/目录中查找JSON格式的CNL配置文件,接着基于该配置文件中各插件的type属性到/opt/cni/bin/中查找相关的插件二进制文件,由该二进制程序基于提供的配置信息完成相应的操作。
  26. kubelet基于包含命令参数CNI ARGSCNI COMMANDCNI IFNAMECNI NETNSCNICONTAINERIDCNIPATH的环境变量调用CNI插件,而被调用的插件同样使用JSON格式的文本信息进行响应,描述操作结果和状态。Pod对象的名称和名称空间将作 CNI ARGS变量的一部分进行传递(例如K8S_ POD_ NAMESPACE=default; K8S_ POD_NAME-myapp-6d9f48c5d9-n77qp;)。它可以定义每个Pod对象或Pod网络名称空间的网络配置(例如,将每个网络名称空间放在不同的子网中)。
  27. 借助插件API,有熟练Go语言编程能力的读者,可以轻松开发出自己的CNI插件, 或扩展现有插件。另外,未来的Kubernetes版本或许会将容器网络视为“一等公民”, 并将网络配置作为Pod对象或名称空间规范的一部分,就像内存、CPU和存储卷一样。目前,还只能使用注解来存储配置或记录Pod网络数据/状态。
  28. <a name="lZOyM"></a>
  29. ### 1.4 Overlay 网络模型
  30. 物理网络模型中,连通多个物理网桥上的主机的一个简单办法是通过媒介直接连接这些网桥设备,各个主机处于同一个局域网(LAN)之中,管理员只需要确保各个网桥上每个主机的IP地址不相互冲突即可。类似地,若能够直接连接宿主机上的虚拟网桥形成一个大的局域网,就能在数据链路层打通各宿主机上的内部网络,让容器可通过自有IP地址直接通信。为避免各容器间的IP地址冲突,一个常见的解决方案是将每个宿主机分配到同一网络中的不同子网,各主机基于自有子网向其容器分配IP地址。
  31. 显然,主机间的网络通信只能经由主机上可对外通信的网络接口进行,跨主机在数据链路层直接连接虚拟网桥的需求必然难以实现,除非借助宿主机间的通信网络构建的通信“隧道”进行数据帧转发。这种于某个通信网络之上构建出的另一个逻辑通信网络通常即10.1.2节提及的Overlay网络或Underlay网络。图10-7Overlay网络功能示意图。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/2678081/1637982567831-e42fb200-1f9b-4a02-ad55-e8fc9e4dbe87.png#clientId=u454b9038-b664-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=366&id=ue6afccd8&margin=%5Bobject%20Object%5D&name=image.png&originHeight=731&originWidth=1501&originalType=binary&ratio=1&rotation=0&showTitle=false&size=285518&status=done&style=none&taskId=u9567345b-36d5-4485-bdbe-afefd0ed103&title=&width=750.5)<br />图10-7 Overlay网络功能示意图
  32. 隧道转发的本质是将容器双方的通信报文分别封装成各自宿主机之间的报文,借助宿主机的网络“隧道”完成数据交换。这种虚拟网络的基本要求是各宿主机只需支持隧道协议即可,对于底层网络没有特殊要求。
  33. **VXLAN协议是目前最流行的Overlay网络隧道协议之一**,它也是由IETF定义的NVO3Network Virtualization over Layer 3)标准技术之一,采用L2 over L4MAC-in-UDP)的报文封装模式,将二层报文用三层协议进行封装,可实现二层网络在三层范围内进行扩展,将“二层域”突破规模限制形成“大二层域”。
  34. 那么,同一大二层域就类似于传统网络中VLAN(虚拟局域网)的概念,只不过在VXLAN网络中,它被称作Bridge-Domain,以下简称为BD。类似于不同的VLAN需要通过VLAN ID进行区分,各BD要通过VNI加以标识。但是,为了确保VXLAN机制通信过程的正确性,涉及VXLAN通信的IP报文一律不能分片,这就要求物理网络的链路层实现中必须提供足够大的MTU值,或修改其MTU值以保证VXLAN报文的顺利传输。不过,降低默认MTU值,以及额外的头部开销,必然会影响到报文传输性能。
  35. VXLAN的显著的优势之一是对**底层网络没有侵入性**,管理员只需要在原有网络之上添加一些额外设备即可构建出虚拟的逻辑网络来。这个额外添加的设备称为VTEPVXLAN Tunnel Endpoints),它工作于VXLAN网络的边缘,负责相关协议报文的封包和解包等操作,从作用来说相当于VXLAN隧道的出入口设备。
  36. VTEP代表着一类支持VXLAN协议的交换机,而支持VXLAN协议的操作系统也可将一台主机模拟为VTEPLinux内核自3.7版本开始通过vxlan内核模块原生支持此协议。于是,各主机上由虚拟网桥构建的LAN便可借助vxlan内核模块模拟的VTEP设备与其他主机上的VTEP设备进行对接,形成隧道网络。同一个二层域内的各VTEP之间都需要建立VXLAN隧道,因此跨主机的容器间直接进行二层通信的VXLAN隧道是各VTEP之间的点对点隧道,如图10-8所示。<br />对于Flannel来说,这个VTEP设备就是各节点上生成flannel.1网络接口,其中的“1”是VXLAN中的BD标识VNI,因而同一Kubernetes集群上所有节点的VTEP设备属于VNI1的同一个BD。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/2678081/1637982485601-7b26e142-f929-4b2a-b441-800a5c489825.png#clientId=u454b9038-b664-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u51d1c1b8&margin=%5Bobject%20Object%5D&name=image.png&originHeight=147&originWidth=287&originalType=url&ratio=1&rotation=0&showTitle=false&size=17032&status=done&style=none&taskId=ud91a8bb1-20e6-4860-941b-4b4a1e528de&title=)<br />图10-8 Linux VTEP<br />类似VLAN的工作机制,相同VXLAN VNI在不同VTEP之间的通信要借助二层网关来完成,而不同VXLAN之间,或者VXLAN同非VXLAN之间的通信则需经由三层网关实现。VXLAN支持使用集中式和分布式两种形式的网关:前者支持流量的集中管理,配置和维护较为简单,但转发效率不高,且容易出现瓶颈和网关可用性问题;后者以各节点为二层或三层网关,消除了瓶颈。
  37. 然而,VXLAN网络中的容器在首次通信之前,源VTEP又如何得知目标服务器在哪一个VTEP,并选择正确的路径传输通信报文呢?常见的解决思路一般有两种:**多播和控制中心**。
  38. **多播**是指同一个BD内的各VTEP加入同一个多播域中,通过多播报文查询目标容器所在的目标VTEP。<br />而**控制中心**则在某个共享的存储服务上保存所有容器子网及相关VTEP的映射信息,各主机上运行着相关的守护进程,并通过与控制中心的通信获取相关的映射信息。Flannel默认的VXLAN后端采用的是后一种方式,它把网络配置信息存储在etcd系统上。
  39. Linux内核自3.7版本开始支持vxlan模块,此前的内核版本可以使用UDPIPIPGRE隧道技术。事实上,考虑到当今公有云底层网络的功能限制,Overlay网络反倒是一种最为可行的容器网络解决方案,仅那些更注重网络性能的场景才会选择Underlay网络。
  40. <a name="daAik"></a>
  41. ### 1.5 Underlay网络模型
  42. **Underlay网络就是传统IT基础设施网络,由交换机和路由器等设备组成,借助以太网协议、路由协议和VLAN协议等驱动**,它还是Overlay网络的底层网络,为Overlay网络提供数据通信服务。容器网络中的Underlay网络是指借助驱动程序将宿主机的底层网络接口直接暴露给容器使用的一种网络构建技术,较为常见的解决方案有**MAC VLANIP VLAN和直接路由等。**
  43. **1.MAC VLAN**<br />MAC VLAN支持在同一个以太网接口上虚拟出多个网络接口,每个虚拟接口都拥有唯一的MAC地址,并可按需配置IP地址。通常这类虚拟接口被网络工程师称作子接口,但在MAC VLAN中更常用上层或下层接口来表述。与Bridge模式相比,MAC VLAN不再依赖虚拟网桥、NAT和端口映射,它允许容器以虚拟接口方式直接连接物理接口。图10-9给出了BridgeMAC VLAN网络对比示意图。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/2678081/1637982767721-9b0e08bb-f77f-499e-a41a-f246fe0d48ca.png#clientId=u454b9038-b664-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u5a705777&margin=%5Bobject%20Object%5D&name=image.png&originHeight=202&originWidth=403&originalType=url&ratio=1&rotation=0&showTitle=false&size=36731&status=done&style=none&taskId=u6f46bbcd-af4e-42a9-8192-114b797eca3&title=)<br />图10-9 Bridge与MAC<br />_VLAN网络对比_MAC VLAN有Private、VEPA、Bridge和Passthru几种工作模式,它们各自的工作特性如下。
  44. - Private:禁止构建在同一物理接口上的多个MAC VLAN实例(容器接口)彼此间的通信,即便外部的物理交换机支持“发夹模式”也不行。
  45. - VPEA:允许构建在同一物理接口上的多个MAC VLAN实例(容器接口)彼此间的通信,但需要外部交换机启用发夹模式,或者存在报文转发功能的路由器设备。
  46. - Bridge:将物理接口配置为网桥,从而允许同一物理接口上的多个MAC VLAN实例基于此网桥直接通信,而无须依赖外部的物理交换机来交换报文;此为最常用的模式,甚至还是Docker容器唯一支持的模式。
  47. - Passthru:允许其中一个MAC VLAN实例直接连接物理接口。
  48. 由上述工作模式可知,**除了Passthru模式外的容器流量将被MAC VLAN过滤而无法与底层主机通信,从而将主机与其运行的容器完全隔离,其隔离级别甚至高于网桥式网络模型,这对于有多租户需求的场景尤为有用。**由于各实例都有专用的MAC地址,因此MAC VLAN允许传输广播和多播流量,但它要求物理接口工作于混杂模式,考虑到很多公有云环境中并不允许使用混杂模式,这意味着MAC VLAN更适用于本地网络环境。
  49. 需要注意的是,MAC VLAN为每个容器使用一个唯一的MAC地址,这可能会导致具有安全策略以防止MAC欺骗的交换机出现问题,因为这类交换机的每个接口只允许连接一个MAC地址。另外,有些物理网卡存在可支撑的MAC地址数量上限。
  50. **2. IP VLAN**<br />IP VLAN类似于MAC VLAN,它同样创建新的虚拟网络接口并为每个接口分配唯一的IP地址,不同之处在于,每个虚拟接口将共享使用物理接口的MAC地址,从而不再违反防止MAC欺骗的交换机的安全策略,且不要求在物理接口上启用混杂模式,如图10-10所示。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/2678081/1637982767693-85cbb651-a840-4b35-8bf0-d7bf66494e27.png#clientId=u454b9038-b664-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u570d4204&margin=%5Bobject%20Object%5D&name=image.png&originHeight=234&originWidth=464&originalType=url&ratio=1&rotation=0&showTitle=false&size=50781&status=done&style=none&taskId=u6e36bfe9-5a35-4446-ad14-b3dd5344077&title=)<br />图10-10 MAC VLAN对比IP VLAN<br />**IP VLAN有L2和L3两种模型**,其中IP VLAN L2的工作模式类似于MAC VLAN Bridge模式,上层接口(物理接口)被用作网桥或交换机,负责为下层接口交换报文;而IP VLAN L3模式中,上层接口扮演路由器的角色,负责为各下层接口路由报文,如图10-11所示。
  51. IP VLAN L2模型与MAC VLAN Bridge模型都支持ARP协议和广播流量,它们拥有直接接入网桥设备的网络接口,能够通过802.1d数据包进行泛洪和MAC地址学习。但IP VLAN L3模式下,网络栈在容器内处理,不支持多播或广播流量,从这个意义上讲,它的运行模式与路由器的报文处理机制相同。
  52. 虽然支持多种网络模型,**但MAC VLANIP VLAN不能同时在同一物理接口上使用。**Linux内核文档中强调,MAC VLANIP VLAN具有较高的相似度,因此,通常仅在必须使用IP VLAN的场景中才不使用MAC VLAN。一般说来,强依赖于IP VLAN的场景有如下几个:
  53. - Linux主机连接到的外部交换机或路由器启用了防止MAC地址欺骗的安全策略;
  54. - 虚拟接口的需求数量超出物理接口能够支撑的容量上限,并且将接口置于混杂模式会给性能带来较大的负面影响;
  55. - 将虚拟接口放入不受信任的网络名称空间中可能会导致恶意的滥用。
  56. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/2678081/1637982768215-c0da0278-d89f-4aa5-98f2-159173d51a36.png#clientId=u454b9038-b664-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uc21475e1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=184&originWidth=359&originalType=url&ratio=1&rotation=0&showTitle=false&size=37554&status=done&style=none&taskId=ucb01f6c5-8277-46df-a414-749681ca77a&title=)<br />图10-11 IP VLAN的L2和L3模型<br />需要注意的是,Linux内核自4.2版本后才支持IP VLAN网络驱动,且在Linux主机上使用ip link命令创建的802.1q配置接口不具有持久性,因此需依赖管理员通过网络启动脚本保持配置。
  57. **3. 直接路由**<br />“直接路由”模型放弃了跨主机容器在L2的连通性,而专注于通过路由协议提供容器在L3的通信方案。**这种解决方案因为更易于集成到现在的数据中心的基础设施之上,便捷地连接容器和主机,并在报文过滤和隔离方面有着更好的扩展能力及更精细的控制模型,因而成为容器化网络较为流行的解决方案之一。**
  58. 一个常用的直接路由解决方案如图10-12所示,每个主机上的各容器在二层通过网桥连通,网关指向当前主机上的网桥接口地址。跨主机的容器间通信,需要依据主机上的路由表指示完成报文路由,因此每个主机的物理接口地址都有可能成为另一个主机路由报文中的“下一跳”,这就要求各主机的物理接口必须位于同一个L2网络中。
  59. 于是,在较大规模的主机集群中,问题的关键便转向如何更好地为每个主机维护路由表信息。常见的解决方案有:<br />①Flannel host-gw使用存储总线etcd和工作在每个节点上的flanneld进程动态维护路由;<br />②Calico使用BGPBorder Gateway Protocol)协议在主机集群中自动分发和学习路由信息。与Flannel不同的是,Calico并不会为容器在主机上使用网桥,而是仅为每个容器生成一对veth设备,留在主机上的那一端会在主机上生成目标地址,作为当前容器的路由条目,如图10-13所示。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/2678081/1637982767996-2d59acfb-14cd-4bac-86a5-db4bd329420d.png#clientId=u454b9038-b664-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u6ac427e0&margin=%5Bobject%20Object%5D&name=image.png&originHeight=201&originWidth=367&originalType=url&ratio=1&rotation=0&showTitle=false&size=48040&status=done&style=none&taskId=u4348cdef-7508-40b0-ac75-c40dac8e8ca&title=)<br />图10-12 直接路由虚拟网络示意图
  60. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/2678081/1637982767748-643a465d-1447-49f4-9142-37d9f28805f3.png#clientId=u454b9038-b664-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u4e504741&margin=%5Bobject%20Object%5D&name=image.png&originHeight=187&originWidth=341&originalType=url&ratio=1&rotation=0&showTitle=false&size=38541&status=done&style=none&taskId=u0847ec99-4a85-499b-9ee0-01230533e83&title=)<br />图10-13 Calico的直接路由模型示意图<br />_图10-13 Calico的直接路由模型示意图_显然,较Overlay来说,无论是MAC VLAN、IP VLAN还是直接路由机制的Underlay网络模型的实现,它们因无须额外的报文开销而通常有着更好的性能表现,但对底层网络有着更多的限制条件。
  61. <a name="fWU9l"></a>
  62. ### 1.6 CNI 插件与选型
  63. 如前所述,CNI 规范负责连接容器管理系统和网络插件两类组件,它们之间通过JSON格式的文件进行通信,以完成容器网络管理。具体的管理操作均由插件来实现,包括创建容器netns(网络名称空间)、关联网络接口到对应的netns,以及给网络接口分配IP等。CNI的基本思想是为容器运行时环境在创建容器时,先创建好netns,然后调用CNI插件为这个netns配置网络,而后启动容器内的进程。
  64. CNI本身只是规范,付诸生产还需要有特定的实现。如前所述,目前CNI提供的插件分为mainipammeta,各类别中都有不少内置实现。另外,可用的第三方实现的CNI插件也有数十种之多,它们多数都是用于提供NetPlugin功能,隶属main插件类型,主要用于配置容器接口和容器网络,这其中,也有不少实现能够支持Kubernetes的网络策略。下面是较为流行的部分网络插件项目。
  65. - **Flannel:**由CoreOS提供的CNI网络插件,也是最简单、最受欢迎的网络插件:它使用VXLANUDP协议封装IP报文来创建Overlay网络,并借助eted维护网络的分配信息,同一节点上的Pod间通信可基于本地虚拟网桥(cni0)进行,而跨节点的Pod间通信则要由flanneld守护进程封装隧道协议报文后,通过查询eted路由 到目的地; Flannel也支持host-gw路由模型。
  66. - **Calico: **同Flannel一样广为流行的CNI网络插件,以灵活、良好的性能和网络策 略所著称。Calico 是路由型CNI网络插件,它在每台机器上运行一个vRouter,并 基于BGP路由协议在节点之闻路由数据包。Calico 支持网络策略,它借助iptables 实现访问控制功能。另外,Calico 也支持IPIP型的Overlay网络。
  67. - **Canal: **由FlannelCalico联合发布的一 款统-网络插件,它试图将二者的功能集成在一起,由前者提供CNI网络插件,由后者提供网络策略。
  68. - **WeaveNet:**由Weaveworks提供的CNI网络插件,支持网络策略。WeaveNet需要在每个节点上部署vRouter路由组件以构建起-一个网格化的TCP连接,并通过Gossip协议来同步控制信息。在数据平面上,WeaveNet通过UDP封装实现L2 道报文,报文封装支持两种模式:一种是运行在用户空间的sleeve (套简)模式, 另一种是运行在内核空间的fastpath (快速路径)模式,当网络拓扑不适合fastpath 模式时,Weave 将自动切换至sleeve 模式。
  69. - **MultusCNI:**多CNI插件,实现了CNI规范的所有参考类插件(例如Flannel MAC VLANIPVLANDHCP等)和第三方插件(例如CalicoWeave Contiv等),也支持Kubernetes中的SR-IOVDPDKOVS-DPDKVPP工作负载,以及Kubernetes中的云原生应用程序和基于NFV的应用程序,是需要为Pod创建多网络接口时的常用选择。
  70. - **Antrea:**一款致力于成为Kubernetes原生网络解决方案的CNI网络插件,它使用OpenvSwitch构建数据平面,基于Overlay网络模型完成Pod间的报文交换,支持 网络策略,支持使用IPSec ESP加密GRE隧道流量。
  71. - **DAMM:**由诺基亚发布的电信级的CNI网络插件,支持具有高级功能的IPVLAN 模式,内置IPAM模块,可管理多个集群范围内的不连续三层网络: 支持通过CNImeta插件将网络管理功能委派给任何其他网络插件。
  72. - **kube-router : **kube-router Kubernetes网络的一体化解决方案,它可取代kube-proxy实现基于ipvsService,能为Pod提供网络,支持网络策略以及拥有完美兼容BGP协议的高级特性。
  73. 尽管人们倾向于把Overlay网络作为解决跨主机容器网络的主要解决方案,但可用的容器网络插件在功能和类型上差别巨大:某些解决方案与容器引擎无关,而也有些解决方案作用后,容易被特定的供应商或引擎锁定;有些专注于简单易用,而另一些的主要目标则是更丰富的功能特性等,至于哪一个解决方案更适用,通常取决于应用程序自身的需求,例如性能需求、负载位置编排机制等。通常来说,选择网络插件时应该基于底层系统环境限制、容器网络的功能需求和性能需求3个重要的评估标准来衡量插件的适用性。
  74. - **底层系统环境限制:**公有云环境多有自己专有的实现,例如Google GCEAzure CNIAWS VPC CNIAliyun Terway等,它们通常是相应环境上较佳的选择。若虚拟化环境限制较多,除Ooverlay网络模型别无选择,则可用的方案有Flannel VXLANCalico IPIPWeaveAntrea等。物理机环境几乎支持任何类型的网络插件,此时一般应该选择性能较好的CalicoBGPFlannelhost-gwDAMMIPVLAN等。
  75. - **容器网络功能需求:**支持NetworkPolicy的解决方案以CalicoWeaveNet Antrea为代表,而且后两个支持节点到节点间的通信加密。而大量Pod需要与集群外部资源互联互通时,应该选择Underlay网络模型一类的解决方案。
  76. - **容器网络性能需求:**Overlay网络中的协议报文有隧道开销,性能略差,而Underlay网络则几乎不存这方面的问题,但OverlayUnderlay路由模型的网络插件支持较快的Pod创建速度,而Underlay模型中的IPVLANMACVLAN模式则较慢。
  77. 随着Kubernetes的演进,必将会有越来越多的CNI插件涌现,它们各具特色、各有优劣。实践中,用户根据实际多方评测与需要选择合用的解决方案即可,但不建议中途改换网络插件。本章将主要介绍FlannelCalico两种主流方案及其部署与应用,并详细说明Calico网络策略的用法。
  78. <a name="fGwGE"></a>
  79. # 2 Flannel 网络插件
  80. <a name="Stz5v"></a>
  81. ### 2.1 Flannel配置基础
  82. Flannel是用于解决容器跨节点通信问题的解决方案,兼容CNI插件API,支持 KubernetesOpenShift Cloud FoundryMesos Amazon ECSSingularity OpenSVC等平台。它使用“虚拟网桥和veth设备”的方式为Pod创建虚拟网络接口,通过可配置的“后端”定义Pod间的通信网络,支持基于VXLANUDPOverlay网络,以及基于三层路由的Underlay网络。在IP地址分配方面,它将预留的一个专用网络(默认为 10.244.0.0/16)切分成多个子网后作为每个节点的Pod CIDR,而后由节点以IPAM插件的host-local形式进行地址分配,并将子网分配信息保存于etcd之中。
  83. <a name="uDgpv"></a>
  84. #### 10.2.1 Flannel 配置基础
  85. Flannel在每个主机上运行一个名为flanneld的二进制代理程序,它负责从预留的网络中按照指定或默认的掩码长度为当前节点申请分配一个子网,并将网络配置、已分配的子 网和辅助数据(例如主机的公网IP等)存储在Kubernetes APIetcd之中。Flannel 使用称为后端的容器网络机制转发跨节点的Pod报文,它目前**支持的主流后端如下**:
  86. - **vxlan**: 使用Linux内核中的vxlan模块封装隧道报文,以Overlay网络模型支持跨节点的Pod间互联互通;同时,该后端类型支持直接路由模式,在该模式下,位于同一二层网络内节点之上的Pod间通信可通过路由模式直接发送,而跨网络的节点之上的Pod间通信仍要使用VXLAN隧道协议转发;因而,VXLAN隶属于Overlay 网络模型,或混合网络模型; vxlan 后端模式中,flanneld 监听UDP8472端口发送的封装数据包。
  87. - **host-gw **:即Host GateWay, 它类似于VXLAN中的直接路由模式,但不支持跨网络的节点,因此这种方式强制要求各节点本身必须在同一个二层网络中,不太适用于较大的网络规模;host-gw 有着较好的转发性能,且易于设定,推荐对报文转发性能要求较高的场景使用。
  88. - **udp**:使用常规UDP报文封装完成隧道转发,性能较前两种方式低很多,它仅在不支持前两种后端的环境中使用;UDP后端模式中,flanneld监听UDP8285端口发送的封装报文。
  89. Flannel初创的段时期,不少环境中使用的主流Linux发行版的内核尚且不支持VXLAN,而host-gw模式有着略高的网络技术门槛,多数部署场景只好采用了UDP后端,Flannel也就不幸地被冠以性能不好的声名。好在,随着各主流Linux发行版内核版本内置 支持vxlan模块,Flannel 默认使用的后端也进化为VXLAN,再启用直接路由特性后会有着 相当不错的性能表现。另外,除了这3种后端之外,Flannel 还实验性地支持IPIPIPSec AIIVPCAWS VPCAllcGCE几种后端。
  90. 为了跟踪各子网分配信息等,Flannel 使用eted来存储虚拟IP和主机IP之间的映射,每个节点上运行的flanneld守护进程负责监视etcd中的信息并完成报文路由。默认情况下, Flannel的配置信息保存在eted存储系统的键名/coreos com/network/config之下,我们可以使用etcd服务的客户端工具来设定或修改其可用的相关配置。config的值是一一个JSON格式的字典数据结构,它可以使用的键包含以下几个。
  91. 1) Network : Flannel在全局使用CIDR格式的IPv4网络,字符串格式,此为必选键,余下的均为可选。<br />2) SubnetLen:为全局使用的IPv4网络基于多少位的掩码切割供各节点使用的子网, 在全局网络的掩码小于24 (例如16)时默认为24位。<br />3) SubnetMin:分配给节点使用的起始子网,默认为切分完成后的第一个子网: 字符串格式。<br />4) SubnetMax :分配给节点使用的最大子网,默认为切分完成后的最大-一个子网;字符串格式。<br />5) Backend : Flannel 要使用的后端类型,以及后端相关的配置,字典格式;不同的后端通常会有专用的配置参数。
  92. Flannel项目官方给出的在线配置清单中默认使用的VXLAN后端,相关的配置定义在 kube system名称空间ConfigMap资源kube-flannel-cfg中,配置内容如下所示。
  93. ```bash
  94. net-conf.json: |
  95. {
  96. "Network": "10.244.0.0/16",
  97. "Backend": {
  98. "Type": "VxLAN"
  99. }
  100. }

上面的配置示例可以看出,Flannel 预留使用的网络为默认的10.244.0.0/16,默认使用24位长度的子网掩码为各节点分配切分的子网,因而,它将有10.244.0.0/24 ~10.244.255.0/24范围内的256个子网可用,每个节点最多支持为254个Pod对象各分配一 个IP地址。它使用的后端是VXLAN类型,flanneld 将监听UDP的8472端口。

2.2 VXLAN 后端

VxLAN(Virtual extensible Local Area Network)虚拟可扩展局域网,采用MAC in UDP封装方式。Flannel会在集群中每个运行flanneld的节点之上创建一个名为flannel.1的虚拟网桥作为本节点隧道出人口的VTEP设备,其中的1表示VNI,因而所有节点上的VTEP均属于 同一VXLAN,或者属于同-一个大二层域( BD),它们依赖于二层网关进行通信。Flannel采用了分布式的网关模型,它把每个节点都视为到达该节点Pod子网的二层网关,相应的路由信息由flanneld自动生成。

Flannel需要在每个节点运行—个flanneld守护进程,启动时,该进程从etcd加载JSON格式的网络配置等信息,它会基于网络配置获取适用于当前节点的子网租约,还要根据其他节点的租约生成路由信息,以正确地路由数据报文等。与Kubernetes结合使用时,flanneld也可托管给集群之上的DeamonSet控制器。Flannel项目仓库中的在线配置清单通过名为kube-flannel-ds的DaemonSet控制器资源,在每个节点运行一个Flannel相关的Pod 对象,Pod模板中使用hostNetwork: true进行网络配置,让每个节点上的Pod资源直接共享节点的网络名称空间,因而配置结果直接在节点的根网络名称空间生效。

在VXLAN模式下,flanneld 从etcd获取子网并配置了后端之后会生成一一个环境变量文件(默认为/run/flannel/subnet.env),其中包含本节点使用的子网,以及为了承载隧道报 文而设置的MTU的定义等,如下面的配置示例所示。随后,flanneld 还将持续监视etcd中相应配置租约信息的变动,并实时反映到本地路由信息之上。

FLANNEL NETWORK=10.244.0.0/16 
FLANNEL SUBNET=10.244.1.1/24 
FLANNEL MTU=1450
FLANNEL IPMASQ=true

为了确保VXLAN机制通信过程的正确性,通常涉及VXLAN通信的IP报文一律不能分片,这就要求物理网络的链路层实现中必须提供足够大的MTU值,或修改各节点的MTU值以保证VXLAN报文的顺利传输,如上面配置示例中使用的1450字节。降低默认MTU值,以及额外的头部开销,必然会影响到报文传输过程中的数据交换效率。

使用kubeadm部署Kubernetes集群之后,基于Flannel项目的在线配置清单部署了默认VXLAN后端的Flannel网络插件,因而跨节点Pod间均是通过Overlay网络进行通信,而同节点的Pod对象关联在同一个虚拟网桥cni0之上,彼此间可无须隧道而直接进行通信,如图10-14中的Pod-1和Pod-2。
image.png
图 14 Flannel VXLAN后端

VXLAN协议使用UDP报文封装隧道内层数据帧,Pod发出的报文经隧道入口flannel.I封装成数据帧,再由flanneld进程(客户端)封装成UDP报文,之后发往目标Pod对象所在节点的flanneld进程(服务端)。该UDP报文就是所谓的VXLAN隧道,它会在已 经生成的帧报文之外再封装一组协议头 部,如图10-15所示为VXLAN头部、外层UDP头部、外层IP头部和外层帧头部。
image.png
显然,该UDP报文的IP头部中,源地址为当前节点某接口的IP地址,目标地址应该为目标Pod所在节点的某接口的IP地址。但本地节点之上并没有任何路由信息帮助指向目标节点,由fanneld生成的路由中仅指明了到达目标Pod时的隧道出口的fannel.1接口的 IP地址。事实上,Flannel 把fannel.1接口也作为网桥设备使用,该设备上附加了一个同 样由flanneld维护的、称为FDB ( Forwarding Database) 的转发数据库。该数据库指明了到达目标节点flannel.1接口需要经由的下一-跳IP,该IP是目标Pod所在节点的IP地址,即外部IP头部中的目标IP。

VXLAN Overlay网络可正常运行在任何能够传输常规UDP报文的环境中,包括存在很多底层限制的公有云环境。代价是,牺牲了网络报文的一小部分载荷能力,降低了性能。

我们也不难想到,依赖于flanneld 维护的、由各VTEP设备flannel.1接口组成的二层网络中的各设备的ARP解析记录,fannel.1 虚拟网桥上的FDB转发数据库,甚至不在同一IP网络中的集群各节点,只要它们彼此间经由路由互相可达,这种外层转发依然能够成功 达成。于是,VXLAN Overlay网络并不要求所有节点都处于同一个二层网络,这有利于在 更复杂的网络环境下组建Kubernetes 集群。

另外,VXLAN后端的可用配置参数除了Type之外还有如下几个,它们都有默认值,用户可以按需进行自定义配置。

  • VNI: VXLAN的标识符,默认为1;数值型数据。
  • Port: 用于发送封装的报文的UDP端口,默认为8472; 数值型数据。
  • GBP:全称为GroupBasedPolicy,配置是否启用VXLAN的基于组的策略机制,默认为否;布尔型数据。
  • DirectRouting: 是否为同一个二层网络中的节点启用直接路由机制,类似于host-gw后端的功能;此种场景下,VXLAN仅为不在同一个二层网络中的节点封装并转发VXLAN隧道报文;布尔型数据。

其中,直接路由参数能够配置Flannel实现三层转发式的容器网关,该网关能够以直接路由方式在Pod间转发通信报文。

具体的实现方式总结如下:

  • 1、将虚拟网络的数据帧添加到VxLAN首部,封装在物理网络的UDP报文中
  • 2、以传统网络的通信方式传送该UDP报文
  • 3、到达目的主机后,去掉物理网络报文的头部信息以及VxLAN首部,并交付给目的终端

跨节点的Pod之间的通信就是以上的一个过程,整个过程中通信双方对物理网络是没有感知的。如图 14 Flannel VXLAN。
VxLAN的部署可以直接在官方上找到其YAML文件,如下

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

#输出如下结果表示flannel可以正常运行了
[root@k8s-master ~]# kubectl get daemonset -n kube-system
NAME              DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR                   AGE
kube-flannel-ds   3         3         3         3            3           beta.kubernetes.io/arch=amd64   202d
kube-proxy        3         3         3         3            3           beta.kubernetes.io/arch=amd64   202d

flannel运行后,在各Node宿主机多了一个网络接口:

#master节点的flannel.1网络接口,其网段为:10.244.0.0
[root@k8s-master ~]# ifconfig flannel.1
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.0.0  netmask 255.255.255.255  broadcast 0.0.0.0
        inet6 fe80::31:5dff:fe01:4bc0  prefixlen 64  scopeid 0x20<link>
        ether 02:31:5d:01:4b:c0  txqueuelen 0  (Ethernet)
        RX packets 1659239  bytes 151803796 (144.7 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2115439  bytes 6859187242 (6.3 GiB)
        TX errors 0  dropped 10 overruns 0  carrier 0  collisions 0

#node1节点的flannel.1网络接口,其网段为:10.244.1.0
[root@k8s-node01 ~]# ifconfig flannel.1
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.1.0  netmask 255.255.255.255  broadcast 0.0.0.0
        inet6 fe80::2806:4ff:fe71:2253  prefixlen 64  scopeid 0x20<link>
        ether 2a:06:04:71:22:53  txqueuelen 0  (Ethernet)
        RX packets 136904  bytes 16191144 (15.4 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 180775  bytes 512637365 (488.8 MiB)
        TX errors 0  dropped 8 overruns 0  carrier 0  collisions 0

#node2节点的flannel.1网络接口,其网段为:10.244.2.0
[root@k8s-node02 ~]# ifconfig flannel.1
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.2.0  netmask 255.255.255.255  broadcast 0.0.0.0
        inet6 fe80::58b7:7aff:fe8d:2d  prefixlen 64  scopeid 0x20<link>
        ether 5a:b7:7a:8d:00:2d  txqueuelen 0  (Ethernet)
        RX packets 9847824  bytes 12463823121 (11.6 GiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2108796  bytes 185073173 (176.4 MiB)
        TX errors 0  dropped 13 overruns 0  carrier 0  collisions 0

从上面的结果可以知道 :

  • flannel默认就是VXLAN模式,即Overlay Network。
  • flanneld创建了一个flannel.1接口,它是专门用来封装隧道协议的,默认分给集群的Pod网段为10.244.0.0/16。
  • flannel给k8s-master节点配置的Pod网络为10.244.0.0段,给k8s-node01节点配置的Pod网络为10.244.1.0段,给k8s-node01节点配置的Pod网络为10.244.2.0段,如果有更多的节点,以此类推。

举个实际例子

#启动一个nginx容器,副本为3
[root@k8s-master ~]# kubectl run nginx --image=nginx:1.14 --port=80 --replicas=3
deployment.apps/nginx created

#查看Pod
[root@k8s-master ~]# kubectl get pods -o wide |grep nginx
nginx-5bd76bcc4f-8s64s                1/1       Running    0          2m        10.244.2.85    k8s-node02
nginx-5bd76bcc4f-mr6k5                1/1       Running    0          2m        10.244.1.146   k8s-node01
nginx-5bd76bcc4f-pb257                1/1       Running    0          2m        10.244.0.17    k8s-master

可以看到,3个Pod都分别运行在各个节点之上,其中master上的Pod的ip为:10.244.0.17,在master节点上查看网络接口可以发现在各个节点上多了一个虚拟接口cni0,其ip地址为10.244.0.1。它是由flanneld创建的一个虚拟网桥叫cni0,在Pod本地通信使用。 这里需要注意的是,cni0虚拟网桥,仅作用于本地通信!

[root@k8s-master ~]# ifconfig cni0
cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.0.1  netmask 255.255.255.0  broadcast 0.0.0.0
        inet6 fe80::848a:beff:fe44:4959  prefixlen 64  scopeid 0x20<link>
        ether 0a:58:0a:f4:00:01  txqueuelen 1000  (Ethernet)
        RX packets 2772994  bytes 300522237 (286.6 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 3180562  bytes 928687288 (885.6 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

flanneld为每个Pod创建一对veth虚拟设备,一端放在容器接口上,一端放在cni0桥上。 使用brctl查看该网桥:

#可以看到有一veth的网络接口桥接在cni0网桥上
[root@k8s-master ~]# brctl show cni0
bridge name    bridge id        STP enabled    interfaces
cni0        8000.0a580af40001    no        veth020fafae


#宿主机ping测试访问Pod ip
[root@k8s-master ~]# ping 10.244.0.17
PING 10.244.0.17 (10.244.0.17) 56(84) bytes of data.
64 bytes from 10.244.0.17: icmp_seq=1 ttl=64 time=0.291 ms
64 bytes from 10.244.0.17: icmp_seq=2 ttl=64 time=0.081 ms
^C
--- 10.244.0.17 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3000ms
rtt min/avg/max/mdev = 0.055/0.129/0.291/0.094 ms

在现有的Flannel VxLAN网络中,两台主机上的Pod间通信,也是正常的,如master节点上的Pod访问node01上的Pod:

[root@k8s-master ~]# kubectl exec -it nginx-5bd76bcc4f-pb257 -- /bin/bash
root@nginx-5bd76bcc4f-pb257:/# ping 10.244.1.146
PING 10.244.1.146 (10.244.1.146) 56(84) bytes of data.
64 bytes from 10.244.1.146: icmp_seq=1 ttl=62 time=1.44 ms
64 bytes from 10.244.1.146: icmp_seq=2 ttl=62 time=0.713 ms
64 bytes from 10.244.1.146: icmp_seq=3 ttl=62 time=0.713 ms
64 bytes from 10.244.1.146: icmp_seq=4 ttl=62 time=0.558 ms
^C
--- 10.244.1.146 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 0.558/0.858/1.448/0.346 ms

可以看到容器跨主机是可以正常通信的,那么容器的跨主机通信是如何实现的呢?????master上查看路由表信息:

[root@k8s-master ~]# ip route
......
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink 
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink 
......

发送到10.244.1.0/24和10.244.20/24网段的数据报文发给本机的flannel.1接口,即进入二层隧道,然后对数据报文进行封装(封装VxLAN首部–>UDP首部–>IP首部–>以太网首部),到达目标Node节点后,由目标Node上的flannel.1进行解封装。使用tcpdump进行 抓一下包,如下:

#在宿主机和容器内都进行ping另外一台主机上的Pod ip并进行抓包
[root@k8s-master ~]# ping -c 10 10.244.1.146
[root@k8s-master ~]# kubectl exec -it nginx-5bd76bcc4f-pb257 -- /bin/bash
root@nginx-5bd76bcc4f-pb257:/# ping 10.244.1.146 

[root@k8s-master ~]# tcpdump -i flannel.1 -nn host 10.244.1.146
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on flannel.1, link-type EN10MB (Ethernet), capture size 262144 bytes

#宿主机ping后抓包情况如下:
22:22:35.737977 IP 10.244.0.0 > 10.244.1.146: ICMP echo request, id 29493, seq 1, length 64
22:22:35.738902 IP 10.244.1.146 > 10.244.0.0: ICMP echo reply, id 29493, seq 1, length 64
22:22:36.739042 IP 10.244.0.0 > 10.244.1.146: ICMP echo request, id 29493, seq 2, length 64
22:22:36.739789 IP 10.244.1.146 > 10.244.0.0: ICMP echo reply, id 29493, seq 2, length 64

#容器ping后抓包情况如下:
22:33:49.295137 IP 10.244.0.17 > 10.244.1.146: ICMP echo request, id 837, seq 1, length 64
22:33:49.295933 IP 10.244.1.146 > 10.244.0.17: ICMP echo reply, id 837, seq 1, length 64
22:33:50.296736 IP 10.244.0.17 > 10.244.1.146: ICMP echo request, id 837, seq 2, length 64
22:33:50.297222 IP 10.244.1.146 > 10.244.0.17: ICMP echo reply, id 837, seq 2, length 64
22:33:51.297556 IP 10.244.0.17 > 10.244.1.146: ICMP echo request, id 837, seq 3, length 64
22:33:51.298054 IP 10.244.1.146 > 10.244.0.17: ICMP echo reply, id 837, seq 3, length 64

可以看到报文都是经过flannel.1网络接口进入2层隧道进而转发

VXLAN是Linux内核本身支持的一种网络虚拟化技术,是内核的一个模块,在内核态实现封装解封装,构建出覆盖网络,其实就是一个由各宿主机上的Flannel.1设备组成的虚拟二层网络。

由于VXLAN由于额外的封包解包,导致其性能较差,所以Flannel就有了host-gw模式,即把宿主机当作网关,除了本地路由之外没有额外开销,性能和calico差不多,由于没有叠加来实现报文转发,这样会导致路由表庞大。因为一个节点对应一个网络,也就对应一条路由条目。

host-gw虽然VXLAN网络性能要强很多。,但是种方式有个缺陷:要求各物理节点必须在同一个二层网络中。物理节点必须在同一网段中。这样会使得一个网段中的主机量会非常多,万一发一个广播报文就会产生干扰。在私有云场景下,宿主机不在同一网段是很常见的状态,所以就不能使用host-gw了。

VXLAN还有另外一种功能,VXLAN也支持类似host-gw的玩法,如果两个节点在同一网段时使用host-gw通信,如果不在同一网段中,即 当前pod所在节点与目标pod所在节点中间有路由器,就使用VXLAN这种方式,使用叠加网络。 结合了Host-gw和VXLAN,这就是VXLAN的Direct routing模式

2.3 直接路由

为了提升性能,Flannel 的VXLAN后端还支持DirectRouting 模式,即在集群中的各节点上添加必要的路由信息,让Pod间的IP报文通过节点的;二层网络直接传送,如图10-16所示。仅在通信双方的Pod对象所在的节点跨IP网络时,才启用传统的VXLAN隧道方式转发通信流量。若Kubernetes集群节点全部位于单个二层网络中,则DirectRouting模式下的Pod间通信流量基本接近于直接使用二层网络。即便节点分布在有限的几个可互相通信的网络中的Kubernetes集群来说,合理的应用部署拓扑也能省去相当-部分的隧道开销。 对于托管部署在Kubernetes.上的Flannel来说,修改kube system名称空间下的configmaps/ kube-flannel-cfg资源,为VXLAN后端添加DirectRouting 子键,并设置其值为true即可,如下面的配置示例。
修改kube-flannel.yml文件,将flannel的configmap对象改为:

[root@k8s-master ~]# vim kube-flannel.yml 
net-conf.json: |
    {
        "Network": "10.244.0.0/16",  #默认网段
    "Backend":{
            "Type": "VxLAN",
            "Directrouting": true  #增加
    }
  }

[root@k8s-master ~]# kubectl apply -f kube-flannel.yml 
clusterrole.rbac.authorization.k8s.io/flannel configured
clusterrolebinding.rbac.authorization.k8s.io/flannel configured
serviceaccount/flannel unchanged
configmap/kube-flannel-cfg configured
daemonset.extensions/kube-flannel-ds-amd64 created
daemonset.extensions/kube-flannel-ds-arm64 created
daemonset.extensions/kube-flannel-ds-arm created
daemonset.extensions/kube-flannel-ds-ppc64le created
daemonset.extensions/kube-flannel-ds-s390x created

#查看路由信息
[root@k8s-master ~]# ip route
......
10.244.1.0/24 via 192.168.56.12 dev eth0 
10.244.2.0/24 via 192.168.56.13 dev eth0 
......

image.png

从上面的结果可以看到,发往10.244.1.0/24和10.244.2.0/24的包都是直接经过eth0网络接口直接发出去的,这就是Directrouting。如果两个节点是跨网段的,则flannel自动降级为VxLAN模式。
此时,在各 个 集群节点上执行“iptables -nL”命令 可以 看到, iptables filter 表 的 FORWARD 链 上 由其 生成 了 如下 两条 转发 规则, 它 显 式 放行 了10.244.0.0/16 网络 进出 的 所有 报文, 用于 确保 由 物理 接口 接收 或 发送 的 目标 地址 或 源 地址 为10.244.0.0/16网络 的 所有 报文 均能 够 正常 通行。 这些 是 Direct Routing 模式 得以 实现 的 必要条件:

target         prot     opt     source                 destination 
ACCEPT         all     --     10. 244. 0. 0/ 16         0. 0. 0. 0/ 0 
ACCEPT         all     --     0. 0. 0. 0/ 0             10. 244. 0. 0/ 16

再在此之前创建的Pod和宿主机上进行ping测试,可以看到在flannel.1接口上已经抓不到包了,在eth0上可以用抓到ICMP的包,如下:

[root@k8s-master ~]# tcpdump -i flannel.1 -nn host 10.244.1.146
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on flannel.1, link-type EN10MB (Ethernet), capture size 262144 bytes
^C
0 packets captured
0 packets received by filter
0 packets dropped by kernel

[root@k8s-master ~]# tcpdump -i eth0 -nn host 10.244.1.146
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
22:48:52.376393 IP 10.244.0.17 > 10.244.1.146: ICMP echo request, id 839, seq 1, length 64
22:48:52.376877 IP 10.244.1.146 > 10.244.0.17: ICMP echo reply, id 839, seq 1, length 64
22:48:53.377005 IP 10.244.0.17 > 10.244.1.146: ICMP echo request, id 839, seq 2, length 64
22:48:53.377621 IP 10.244.1.146 > 10.244.0.17: ICMP echo reply, id 839, seq 2, length 64

22:50:28.647490 IP 192.168.56.11 > 10.244.1.146: ICMP echo request, id 46141, seq 1, length 64
22:50:28.648320 IP 10.244.1.146 > 192.168.56.11: ICMP echo reply, id 46141, seq 1, length 64
22:50:29.648958 IP 192.168.56.11 > 10.244.1.146: ICMP echo request, id 46141, seq 2, length 64
22:50:29.649380 IP 10.244.1.146 > 192.168.56.11: ICMP echo reply, id 46141, seq 2, length 64

显然,这种路由规则无法表达跨二层网络的节点上Pod间通信的诉求,因为到达目标网络(某Pod子网)的下一跳地址无法指向另—个网络中的节点地址。因而,集群中的每 个节点上依然保留有VXLAN隧道相关的flannel.1 设备,以支持那些跨IP网络的节点上的Pod间通信。

为了所有Pod均能得到正确的网络配置,建议在创建Pod资源之前事先配置好网络插件,甚至是事先了解并根据自身业务需求测试完成中意的目标网络插件,在选型完成后再部暑Kubemete集群,而尽量避免中途修改,否则有些Pod资源可能需要重建。

2.4 host-gw 后端

3 Calico 网络插件

3.1 Calico架构

3.2 Calico 配置基础

3.3 IPIP 隧道网络

3.4 客户端工具 calicoctl

3.5 BGP 网络与 BGP Reflector

4 网络策略

4.1 网络策略与配置基础

4.2 管控入站流量

4.3 管控出站流量

4.4 Calico的网络策略

5 小结