本文需要读者熟悉 Ethernet(以太网)的基本原理和 Linux 系统的基本网络命令,以及 TCP/IP 协议族并了解传统的网络模型和协议包的流转原理。文中涉及到 Linux 内核的具体实现时,均以内核 v4.19.215 版本为准。一 内核网络包接收流程1 从网卡到内核协议栈如图[1],网络包到达 NC(Network Computer,本文指物理机)时,由 NIC(Network Interface Controller,网络接口控制器,俗称网卡)设备处理,NIC 以中断的方式向内核传递消息。Linux 内核的中断处理分为上半部(Top Half)和下半部(Bottom Half)。上半部需要尽快处理掉和硬件相关的工作并返回,下半部由上半部激活来处理后续比较耗时的工作。具体到 NIC 的处理流程如下:当 NIC 收到数据时,会以 DMA 方式将数据拷贝到 Ring Buffer (接收队列) 里描述符指向的映射内存区域,拷贝完成后会触发中断通知 CPU 进行处理。这里可以使用 ethtool -g {设备名,如eth0} 命令查看 RX/TX (接收/发送)队列的大小。CPU 识别到中断后跳转到 NIC 的中断处理函数开始执行。此时要区分 NIC 的工作模式,在早先的非 NAPI(New API)[2]模式下,中断上半部更新相关的寄存器信息,查看接收队列并分配 sk_buff 结构指向接收到的数据,最后调用 netif_rx() 把 sk_buff 递交给内核处理。在 Netif_rx() 的函数的流程中,这个分配的 sk_buff 结构被放入 input_pkt_queue队列后,会把一个虚拟设备加入poll_list 轮询队列并触发软中断 NET_RX_SOFTIRQ 激活中断下半部。此时中断上半部就结束了,详细的处理流程可以参见 net/core/dev.c 的 netif_rx() -> netif_rx_internal() -> enqueue_to_backlog() 过程。下半部 NET_RX_SOFTIRQ 软中断对应的处理函数是 net_rx_action(),这个函数会调用设备注册的 poll() 函数进行处理。非 NAPI 的情况下这个虚拟设备的 poll() 函数固定指向 process_backlog() 函数。这个函数将 sk_buff 从 input_pkt_queue 移动到 process_queue 中,调用 __netif_receive_skb() 函数将其投递给协议栈,最后协议栈相关代码会根据协议类型调用相应的接口进行后续的处理。特别地,这里的 enqueue_to_backlog() 以及 process_backlog() 函数也用于和启用了 RPS 机制后的相关逻辑。非 NAPI(New API)模式下每个网络包的到达都会触发一次中断处理流程,这么做降低了整体的处理能力,已经过时了。现在大多数 NIC 都支持 NAPI 模式了。NAPI 模式下在首包触发 NIC 中断后,设备就会被加入轮询队列进行轮询操作以提升效率,轮询过程中不会产生新的中断。为了支持 NAPI,每个 CPU 维护了一个叫 softnet_data 的结构,其中有一个 poll_list 字段放置所有的轮询设备。此时中断上半部很简单,只需要更新 NIC 相关的寄存器信息,以及把设备加入poll_list 轮询队列并触发软中断 NET_RX_SOFTIRQ就结束了。中断下半部的处理依旧是 net_rx_action() 来调用设备驱动提供的 poll() 函数。只是 poll() 此时指向的就是设备驱动提供的轮询处理函数了(而不是非 NAPI 模式下的内核函数 process_backlog())。这个设备驱动提供的轮询 poll() 函数最后也会调用 __netif_receive_skb() 函数把 sk_buff 提交给协议栈处理。非 NAPI 模式和 NAPI 模式下的流程对比如下(其中灰色底色是设备驱动要实现的,其他都是内核自身的实现):关于 NAPI 模式网络设备驱动的实现以及详细的 NAPI 模式的处理流程,这里提供一篇文章和其译文作为参考[3](强烈推荐)。这篇文章很详细的描述了 Intel Ethernet Controller I350 这个 NIC 设备的收包和处理细节(其姊妹篇发包处理过程和译文[4])。另外收包这里还涉及到多网卡的 Bonding 模式(可以在/proc/net/bonding/bond0 里查看模式)、网络多队列(sudo lspci -vvv 查看 Ethernet controller 的 Capabilities信息里有 MSI-X: Enable+ Count=10 字样说明 NIC 支持,可以在 /proc/interrupts 里查看中断绑定情况)等机制。这些本文都不再赘述,有兴趣的话请参阅相关资料[5]。2 内核协议栈网络包处理流程前文说到 NIC 收到网络包构造出的 sk_buff 结构最终被 __netif_receive_skb() 提交给了内核协议栈解析处理。这个函数首先进行 RPS[5] 相关的处理,数据包会继续在队列里转一圈(一般开启了 RSS 的网卡不需要开启 RPS)。如果需要分发包到其他 CPU 去处理,则会使用 enqueue_to_backlog() 投递给其他 CPU 的队列,并在 process_backlog()) 中触发 IPI(Inter-Processor Interrupt,处理器间中断,于 APIC 总线上传输,并不通过 IRQ)给其他 CPU 发送通知(net_rps_send_ipi()函数)。最终,数据包会由 __netif_receive_skb_core() 进行下一阶段的处理。这个处理函数主要的功能有:
ip netns exec qianyi-test-2 ip link set dev lo up状态变成了 UNKOWN,这是正常的。这个状态是驱动提供的,但是 lo 的驱动没有做这个事情。创建 Veth Pair 设备
2、NAPI, linuxfoundation, /d/file/20220329/napibr3、Monitoring and Tuning the Linux Networking Stack: Receiving Data, Joe Damato,译文:Linux 网络栈监控和调优:接收数据(2016):http://arthurchiao.art/blog/tuning-stack-rx-zh/
4、Monitoring and Tuning the Linux Networking Stack: Sending Data, Joe Damato, 译文:Linux 网络栈监控和调优:发送数据(2017):http://arthurchiao.art/blog/tuning-stack-tx-zh/
5、Scaling in the Linux Networking Stack, https://github.com/torvalds/linux/blob/master/documentation/networking/scaling.rst
6、Understanding TCP internals step by step for Software Engineers and System Designers, Kousik Nath
7、Netfilter, https://www.netfilter.org/
8、Netfilter-packet-flow, https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg
9、Analysis TCP in Linux, https://github.com/fzyz999/Analysis_TCP_in_Linux
10、NAT - Network Address Translation, 译文:NAT - 网络地址转换(2016):http://arthurchiao.art/blog/nat-zh/
11、Virtual networking in Linux, By M. Jones, IBM Developer:https://developer.ibm.com/tutorials/l-virtual-networking/
12、Open vSwitch, http://www.openvswitch.org/
13、Linux Namespace, https://man7.org/linux/man-pages/man7/namespaces.7.html
14、ip, https://man7.org/linux/man-pages/man8/ip.8.html
15、Veth, https://man7.org/linux/man-pages/man4/veth.4.html
16、VxLAN, https://en.wikipedia.org/wiki/Virtual_Extensible_LAN
17、QinQ vs VLAN vs VXLAN, John, https://community.fs.com/blog/qinq-vs-vlan-vs-vxlan.htm
18、Introduction to Linux interfaces for virtual networking, Hangbin Liu: /d/file/20220329/introduction-to-linux-interfaces-for-virtual-networking Networking, 英文地址https://kubernetes.io/zh/docs/concepts/cluster-administration/networking/
20、THE ConTAINER NETWORKING LANDSCAPE: CNI FROM COREOS AND CNM FROM DOCKER, Lee Calcote:https://thenewstack.io/container-networking-landscape-cni-coreos-cnm-docker/
21、CNI - the Container Network Interface, https://github.com/containernetworking/cni
22、Making the Kubernetes Service Abstraction Scale using eBPF, [译] 利用 eBPF 支撑大规模 K8s Service (LPC, 2019):https://linuxplumbersconf.org/event/4/contributions/458/
23、基于 BPF/XDP 实现 K8s Service 负载均衡 (LPC, 2020)https://linuxplumbersconf.org/event/7/contributions/674/24、A Deep Dive into Iptables and Netfilter Architecture, Justin Ellingwood:https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture25、Iptables Tutorial 1.2.2, Oskar Andreasson:https://www.frozentux.net/iptables-tutorial/iptables-tutorial.html26、Virtual IPs and service proxies, 英文地址: /d/file/20220329/ 英文地址:https://kubernetes.io/docs/concepts/services-networking/ingress/28、NGINX Ingress Controller, https://www.nginx.com/products/nginx-ingress-controller/29、Ingress Controllers, 英文地址:https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/30、Cracking kubernetes node proxy (aka kube-proxy), [译] 深入理解 Kubernetes 网络模型:自己实现 Kube-Proxy 的功能:https://cloudnative.to/blog/k8s-node-proxy/原文链接:https://developer.aliyun.com/article/813025?utm_content=g_1000310145本文为阿里云原创内容,未经允许不得转载。
- 处理ptype_all 上所有的 packet_type->func(),典型场景是 tcpdump 等工具的抓包回调(paket_type.type 为 ETH_P_ALL,libcap 使用 AF_PACKET Address Family)
- 处理 VLAN(Virtual Local Area Network,虚拟局域网)报文 vlan_do_receive() 以及处理网桥的相关逻辑(skb->dev->rx_handler() 指向了 br_handle_frame())
- 处理 ptype_base上所有的 packet_type->func() , 将数据包传递给上层协议层处理,例如指向 IP 层的回调 ip_rcv() 函数
- Tap/Tun 是 Linux 内核实现的一对虚拟网络设备,Tap/Tun 分别工作在二层/三层。Linux 内核通过 Tap/Tun 设备和绑定该设备的用户空间之间交换数据。基于 Tap 驱动即可实现虚拟机 vNIC 的功能,Tun 设备做一些其他的转发功能。
- Veth 设备总是成对创建(Veth Pair),一个设备收到内核发送的数据后,会发送到另一个设备上去,可以把 Veth Pair 可以想象成一对用网线连接起来的 vNIC 设备。
- Bridge 是工作在二层的虚拟网桥。这是虚拟设备,虽然叫网桥,但其实类似 vSwitch 的设计。当 Bridge 配合 Veth 设备使用时,可以将 Veth 设备的一端绑定到一个Bridge 上,相当于真实环境把一个 NIC 接入一个交换机里。
# 创建名为 qianyi-test-1 和 add qianyi-test-2 的命名 netns,可以在 /var/run/netns/ 下查看
ip netns add qianyi-test-1
ip netns add qianyi-test-2
# 查看所有的 Network Namespace
ip netns list
# 删除 Network Namespace
ip netns del qianyi-test-1
ip netns del qianyi-test-2
执行结果如图(删除先不执行):有兴趣的话可以使用 strace 命令跟踪这个创建过程看看 ip 命令是怎么创建的(strace ip netns add qianyi-test-1)。在 netns 中执行命令# 在 qianyi-test-1 这个 netns 中执行 ip addr 命令(甚至可以直接执行 bash 命令得到一个 shell)
# nsenter 这个命令也很好用,可以 man nsenter 了解
ip netns exec qianyi-test-1 ip addr
执行结果如下:图片这个新创建的 netns 里一贫如洗,只有一个孤独的 lo 网卡,还是 DOWN 状态的。下面开启它:开启 lo 网卡,这个很重要ip netns exec qianyi-test-1 ip link set dev lo upip netns exec qianyi-test-2 ip link set dev lo up状态变成了 UNKOWN,这是正常的。这个状态是驱动提供的,但是 lo 的驱动没有做这个事情。创建 Veth Pair 设备
# 分别创建 2 对名为 veth-1-a/veth-1-b 和 veth-2-a/veth-2-b 的 Veth Pair 设备
ip link add veth-1-a type veth peer name veth-1-b
ip link add veth-2-a type veth peer name veth-2-b
使用 ip addr 命令可以查看:8-9,10-11 便是上面创建出来的 2 对 Veth Pair 设备,此时它们都没有分配 IP 地址且是 DOWN 状态。将 Veth Pair 设备加入 netns# 将 veth-1-a 设备加入 qianyi-test-1 这个 netns
ip link set veth-1-a netns qianyi-test-1
# 将 veth-1-b 设备加入 qianyi-test-2 这个 netns
ip link set veth-1-b netns qianyi-test-2
# 为设备添加 IP 地址/子网掩码并开启
ip netns exec qianyi-test-1 ip addr add 10.0.0.101/24 dev veth-1-a
ip netns exec qianyi-test-1 ip link set dev veth-1-a up
ip netns exec qianyi-test-2 ip addr add 10.0.0.102/24 dev veth-1-b
ip netns exec qianyi-test-2 ip link set dev veth-1-b up
此时我们分别在两个 netns 中执行 ip addr 命令,即可看到设备已经存在,且路由表(route 或 ip route 命令)也被默认创建了:这里操作和查看设备都必须采用 ip netns exec {...} 的方式进行,如果命令很多,也可以把执行的命令换成 bash,这样可以方便的在这个 shell 里对该 netns 进行操作。现在通过 veth-1-a/veth-1-b 这对 Veth Pair 联通了 qianyi-test-1 和 qianyi-test-2 这两个 netns,这两个 netns 之间就可以通过这两个 IP 地址相互访问了。ping 的同时在 101 上抓包的结果如下:可以很清楚的看到,eth-1-a(10.0.0.101)先通过 arp (Address Resolution Protocol,地址解析协议)询问 10.0.0.102 的 MAC 地址。得到回应后,就以 ICMP (Internet Control Message Protocol,Internet 报文控制协议) request 和 reply 了,这也正是 ping 使用的协议。ARP 解析的缓存信息也可以通过 arp 命令查看:此时的网络连接模式是这样的:这种连接模式,就很像是现实中把两个带有 NIC 的设备用网线连接起来,然后配置了相同网段的 IP 后彼此就可以通信了。那如果超过一个设备需要建立互联呢?现实中就需要交换机等网络设备了。还记得前文中说的 Linux 自带的 Bridge 么?接下来就使用 Bridge 机制建立网络。进行下面的试验前需要把 veth-1-a/veth-1-b 这对 Veth Pair 从 qianyi-test-1 和 qianyi-test-2 移动回宿主机的 netns 里,恢复初始的环境。# 宿主机的 netns id 是 1(有些系统可能不是,请查询相关系统的文档)
ip netns exec qianyi-test-1 ip link set veth-1-a netns 1
ip netns exec qianyi-test-2 ip link set veth-1-b netns 1
创建 Linux Bridge 并配置网络# 创建一个名为 br0 的 bridge 并启动(也可以使用 brctl 命令)
ip link add br0 type bridge
ip link set br0 up
# 将 veth-1-a/veth-1-b 和 veth-2-a/veth-2-b 这两对 Veth Pair 的 a 端放入 qianyi-test-1 和 qianyi-test-2
ip link set veth-1-a netns qianyi-test-1
ip link set veth-2-a netns qianyi-test-2
# 为 veth-1-a 和 veth-2-a 配置 IP 并开启
ip netns exec qianyi-test-1 ip addr add 10.0.0.101/24 dev veth-1-a
ip netns exec qianyi-test-1 ip link set dev veth-1-a up
ip netns exec qianyi-test-2 ip addr add 10.0.0.102/24 dev veth-2-a
ip netns exec qianyi-test-2 ip link set dev veth-2-a up
# 将 veth-1-a/veth-1-b 和 veth-2-a/veth-2-b 这两对 Veth Pair 的 b 端接入 br0 网桥并开启接口
ip link set veth-1-b master br0
ip link set dev veth-1-b up
ip link set veth-2-b master br0
ip link set dev veth-2-b up
执行完可以查看创建好的网桥和配置好的 IP,实际上 brctl show 命令显示的结果更易懂,可以很清楚的看到 veth-1-b 和 veth-2-b 连接在了网桥的接口上。当 Veth Pair 的一端连接在网桥上时,就会从“网卡”退化成一个“水晶头”。当下模式抓包的结果并没有什么区别,但网络连接模式不同:按照这个模式,如果有更多的 Network Namespace 和 Veth Pair 的话,使用相同的方式添加就可以水平扩展了。但是尝试从 qianyi-test-1 中 ping 宿主机自然是不通的,因为没有任何网络规则可以访问到宿主机的网络:上面的截图中有个 Docker0 的网桥。当机器上安装了 Docker 之后会被自动设置好这个网桥供 Docker 使用。可能你注意到了,这个名为 docker0 的网桥居然是有 IP 地址的。现实中的网桥自然是没有 IP 的,但是 Linux Bridge 这个虚拟设备是可以设置的。当 Bridge 设置 IP 之后,就可以将其设置成这个内部网络的网关(Gateway),再配合路由规则就可以实现最简单的虚拟网络跨机通信了(类似现实中的三层交换机)。下面继续给 br0 网桥创建地址并在 veth-1-a 和 veth-2-a 上设置其为默认的网关地址:# 确认路由转发开启
echo "1" > /proc/sys/net/ipv4/ip_forward
# 为 br0 设置 IP 地址
ip addr add local 10.0.0.1/24 dev br0
# 为 veth-1-a 和 veth-2-a 设置默认网关
ip netns exec qianyi-test-1 ip route add default via 10.0.0.1
ip netns exec qianyi-test-2 ip route add default via 10.0.0.1
此时就能成功的访问宿主机地址了(宿主机上的路由表在 ip link set br0 up 这一步自动创建了):网络模型进一步变成了这样:如果此时,另一台宿主机上也存在另一个网段的网桥和若干个 netns 的话,怎么让他们互通呢?分别在两边宿主机上配置一条到目的宿主机的路由规则就好了。假设另一台宿主机的 IP 地址是 10.97.212.160,子网是 10.0.1.0/24 的话,那么需要在当前机器上加一条 10.0.1.0/24 via 10.97.212.160 的规则,10.97.212.160 上加一条 10.0.0.0/24 via 10.97.212.159 的规则就可以了(或者 iptables 配置 SNAT/DNAT 规则)。那如果有 N 台呢?那就会是个 N * N 条的规则,会很复杂。这就一个简单的 Underlay 模式的容器通信方案了。缺点也很明显,要求对宿主机底层网络有修改权,且比较难和底层网络解耦。那如果能在物理网络上构建出一个横跨所有宿主机的虚拟网桥,把所有相关的 netns 里面的设备都连接上去,不就可以解耦了么。这就是 Overlay Network(覆盖网络)方案,下文会进行阐述。至于本节其他虚拟网络设备的安装和配置(比如 Open vSwitch)也是比较类似的,这里不再赘述,有兴趣的话可以自行查看文档并测试。2 Overlay 网络方案之 VXLANVXLAN(Virtual eXtensible Local Area Network,虚拟可扩展局域网,RFC7348)[16],VLAN 的扩展协议,是由 IETF 定义的 NVO3(Network Virtualization over Layer 3)标准技术之一(其他有代表性的还有 NVGRE、STT)。但是 VXLAN 和 VLAN 想要解决的问题是不一样的。VXLAN 本质上是一种隧道封装技术,它将数据链路层(L2)的以太网帧(Ethernet frames)封装成传输层(L4)的 UDP 数据报(Datagrams),然后在网络层(L3)中传输。效果就像数据链路层(L2)的以太网帧在一个广播域中传输一样,即跨越了三层网络却感知不到三层的存在。因为是基于 UDP 封装,只要是 IP 网络路由可达就可以构建出庞大的虚拟二层网络。也因为是基于高层协议再次封装,性能会比传统的网络低 20%~30% 左右(性能数据随着技术发展会有变化,仅代表当前水平)。这里简要介绍下 VXLAN 的 2 个重要概念:- VTEP(VXLAN Tunnel Endpoints,VXLAN 隧道端点),负责 VXLAN 报文的封装和解封,对上层隐藏了链路层帧的转发细节
- VNI(VXLAN Network Identifier,VXLAN 网络标识符),代表不同的租户,属于不同 VNI 的虚拟网络之间不能直接进行二层通信。
# 10.97.212.159 上操作
# 创建名为 qianyi-test-1 和 add qianyi-test-2 的命名 netns,可以在 /var/run/netns/ 下查看
ip netns add qianyi-test-1
ip netns add qianyi-test-2
# 开启 lo 网卡,这个很重要
ip netns exec qianyi-test-1 ip link set dev lo up
ip netns exec qianyi-test-2 ip link set dev lo up
# 创建一个名为 br0 的 bridge 并启动(也可以使用 brctl 命令)
ip link add br0 type bridge
ip link set br0 up
# 分别创建 2 对名为 veth-1-a/veth-1-b 和 veth-2-a/veth-2-b 的 Veth Pair 设备
ip link add veth-1-a type veth peer name veth-1-b
ip link add veth-2-a type veth peer name veth-2-b
# 将 veth-1-a/veth-1-b 和 veth-2-a/veth-2-b 这两对 Veth Pair 的 a 端放入 qianyi-test-1 和 qianyi-test-2
ip link set veth-1-a netns qianyi-test-1
ip link set veth-2-a netns qianyi-test-2
# 为 veth-1-a 和 veth-2-a 配置 IP 并开启
ip netns exec qianyi-test-1 ip addr add 10.0.0.101/24 dev veth-1-a
ip netns exec qianyi-test-1 ip link set dev veth-1-a up
ip netns exec qianyi-test-2 ip addr add 10.0.0.102/24 dev veth-2-a
ip netns exec qianyi-test-2 ip link set dev veth-2-a up
# 将 veth-1-a/veth-1-b 和 veth-2-a/veth-2-b 这两对 Veth Pair 的 b 端接入 br0 网桥并开启接口
ip link set veth-1-b master br0
ip link set dev veth-1-b up
ip link set veth-2-b master br0
ip link set dev veth-2-b up
# 11.238.151.74 上操作
# 创建名为 qianyi-test-3 和 add qianyi-test-4 的命名 netns,可以在 /var/run/netns/ 下查看
ip netns add qianyi-test-3
ip netns add qianyi-test-4
# 开启 lo 网卡,这个很重要
ip netns exec qianyi-test-3 ip link set dev lo up
ip netns exec qianyi-test-4 ip link set dev lo up
# 创建一个名为 br0 的 bridge 并启动(也可以使用 brctl 命令)
ip link add br0 type bridge
ip link set br0 up
# 分别创建 2 对名为 veth-3-a/veth-3-b 和 veth-4-a/veth-4-b 的 Veth Pair 设备
ip link add veth-3-a type veth peer name veth-3-b
ip link add veth-4-a type veth peer name veth-4-b
# 将 veth-3-a/veth-3-b 和 veth-4-a/veth-4-b 这两对 Veth Pair 的 a 端放入 qianyi-test-3 和 qianyi-test-4
ip link set veth-3-a netns qianyi-test-3
ip link set veth-4-a netns qianyi-test-4
# 为 veth-3-a 和 veth-4-a 配置 IP 并开启
ip netns exec qianyi-test-3 ip addr add 10.0.0.103/24 dev veth-3-a
ip netns exec qianyi-test-3 ip link set dev veth-3-a up
ip netns exec qianyi-test-4 ip addr add 10.0.0.104/24 dev veth-4-a
ip netns exec qianyi-test-4 ip link set dev veth-4-a up
# 将 veth-3-a/veth-3-b 和 veth-4-a/veth-4-b 这两对 Veth Pair 的 b 端接入 br0 网桥并开启接口
ip link set veth-3-b master br0
ip link set dev veth-3-b up
ip link set veth-4-b master br0
ip link set dev veth-4-b up
这一长串的命令和之前的步骤完全一致,构建了一个如下图所示的网络环境:这个环境里,10.0.0.101 和 10.0.0.102 是通的,10.0.0.103 和 10.0.0.104 也是通的,但是显然 10.0.0.101/10.0.0.102 和 10.0.0.103/10.0.0.104 是无法通信的。接下来配置 VXLAN 环境打通这四个 netns 环境:# 10.97.212.159 上操作(本机有多个地址时可以用 local 10.97.212.159 指定)
ip link add vxlan1 type vxlan id 1 remote 11.238.151.74 dstport 9527 dev bond0
ip link set vxlan1 master br0
ip link set vxlan1 up
# 11.238.151.74 上操作(本机有多个地址时可以用 local 11.238.151.74 指定)
ip link add vxlan2 type vxlan id 1 remote 10.97.212.159 dstport 9527 dev bond0
ip link set vxlan2 master br0
ip link set vxlan2 up
使用 brctl show br0 命令可以看到两个 VXLAN 设备都连接上去了:然后从 10.0.0.101 上就可以 ping 通 10.0.0.103 了:在 10.0.0.101 上抓的包来看,就像是二层互通一样:直接查看 arp 缓存,也是和二层互通一模一样:使用 arp -d 10.0.0.103 删掉这个缓存项目,在宿主机上重新抓包并保存文件,然后用 WireShark 打开看看(因为上面设置的不是 VXLAN 默认端口 4789,还需要设置 WireShark 把抓到的 UDP 解析为 VXLAN 协议):我们得到了预期的结果。此时的网络架构如图所示:那么问题来了,这里使用 UDP 协议能实现可靠通信吗?当然可以,可靠性不是这一层考虑的事情,而是里层被包裹的协议需要考虑的。完整的通信原理其实也并不复杂,两台机器上各自有 VTEP(VXLAN Tunnel Endpoints,VXLAN 隧道端点)设备,监听着 9527 端口上发送的 UDP 数据包。在收到数据包后拆解通过 Bridge 传递给指定的设备。那 VETP 这个虚拟设备怎么知道类似 10.0.0.3 这样的地址要发送给哪台机器上的 VETP 设备呢?这可是虚拟的二层网络,底层网络上可不认识这个地址。事实上在 Linux Bridge 上维护着一个名为 FDB(Forwarding Database entry)的二层转发表,用于保存远端虚拟机/容器的 MAC 地址、远端 VTEP 的 IP,以及 VNI 的映射关系,可以通过 bridge fdb 命令来对 FDB 表进行操作:# 新增条目
bridge fdb add <remote_host_mac_addr> dev <vxlan_interface> dst <remote_host_ip_addr>
# 删除条目
bridge fdb del <remote_host_mac_addr> dev <vxlan_interface>
# 替换条目
bridge fdb replace <remote_host_mac_addr> dev <vxlan_interface> dst <remote_host_ip_addr>
# 显示条目
bridge fdb show
上面这个简单的实验就 2 台机器,使用了命令的方式直接指定了彼此的 VTEP 地址,当 fdb 表查不到信息时发给对方就行了,这是最简单的互联模式。大规模 VXLAN 网络下,就需要考虑如何发现网络中其他的 VETP 地址了。解决这个问题一般有 2 种方式:一是使用组播/多播( IGMP, Internet Group Management Protocol),把节点组成一个虚拟的整体,包不清楚发给谁的话就广播给整个组了(上述实验中的创建 VETH 设备的命令修改为组播/多播地址比如 224.1.1.1 就行,remote 关键字也要改成 group,具体请参阅其他资料);二是通过外部的分布式控制中心来收集 FDB 信息并分发给同一个 VXLAN 网络的所有节点。组播/多播受限于底层网络的支持情况和大规模下的的性能问题,比如很多云网络上不一定允许这么做。所以下文在讨论和研究 K8s 的网络方案时会看到很多网络插件的实现采用的都是类似后者的实现方式。这节就介绍到这里了。当然 Overlay 网络方案也不止 VXLAN 这一种方式,只是目前很多主流的方案都采用了这种方式。其他的 Overlay 模式看上去眼花缭乱,其实说白了,无非就是 L2 over L4,L2 over L3,L3 over L3 等等各种包装方式罢了,懂了基本原理之后都没什么大不了的。网络虚拟化的设备和机制也有很多[18],细说的话一天都说不完,但是基本的网络原理掌握之后,无非是各种协议包的流转罢了。三 K8s 的网络虚拟化实现1 K8s 的网络模型每一个 Pod 都有它自己的 IP 地址,这就意味着你不需要显式地在每个 Pod 之间创建链接, 你几乎不需要处理容器端口到主机端口之间的映射。这将创建一个干净的、向后兼容的模型,在这个模型里,从端口分配、命名、服务发现、 负载均衡、应用配置和迁移的角度来看,Pod 可以被视作虚拟机或者物理主机。Kubernetes 对所有网络设施的实施,都需要满足以下的基本要求(除非有设置一些特定的网络分段策略):- 节点上的 Pod 可以不通过 NAT 和其他任何节点上的 Pod 通信
- 节点上的代理(比如:系统守护进程、kubelet)可以和节点上的所有 Pod 通信
- 那些运行在节点的主机网络里的 Pod 可以不通过 NAT 和所有节点上的 Pod 通信
- CNI 插件:遵守 CNI(Container Network Interface,容器网络接口)规范,其设计上偏重互操作性
- Kubenet 插件:使用 bridge 和 host-local CNI 插件实现了基本的 cbr0
- Flannel:https://github.com/flannel-io/flannel
- Calico:https://github.com/projectcalico/calico
- Cilium:https://github.com/cilium/cilium
2、NAPI, linuxfoundation, /d/file/20220329/napibr3、Monitoring and Tuning the Linux Networking Stack: Receiving Data, Joe Damato,译文:Linux 网络栈监控和调优:接收数据(2016):http://arthurchiao.art/blog/tuning-stack-rx-zh/
4、Monitoring and Tuning the Linux Networking Stack: Sending Data, Joe Damato, 译文:Linux 网络栈监控和调优:发送数据(2017):http://arthurchiao.art/blog/tuning-stack-tx-zh/
5、Scaling in the Linux Networking Stack, https://github.com/torvalds/linux/blob/master/documentation/networking/scaling.rst
6、Understanding TCP internals step by step for Software Engineers and System Designers, Kousik Nath
7、Netfilter, https://www.netfilter.org/
8、Netfilter-packet-flow, https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg
9、Analysis TCP in Linux, https://github.com/fzyz999/Analysis_TCP_in_Linux
10、NAT - Network Address Translation, 译文:NAT - 网络地址转换(2016):http://arthurchiao.art/blog/nat-zh/
11、Virtual networking in Linux, By M. Jones, IBM Developer:https://developer.ibm.com/tutorials/l-virtual-networking/
12、Open vSwitch, http://www.openvswitch.org/
13、Linux Namespace, https://man7.org/linux/man-pages/man7/namespaces.7.html
14、ip, https://man7.org/linux/man-pages/man8/ip.8.html
15、Veth, https://man7.org/linux/man-pages/man4/veth.4.html
16、VxLAN, https://en.wikipedia.org/wiki/Virtual_Extensible_LAN
17、QinQ vs VLAN vs VXLAN, John, https://community.fs.com/blog/qinq-vs-vlan-vs-vxlan.htm
18、Introduction to Linux interfaces for virtual networking, Hangbin Liu: /d/file/20220329/introduction-to-linux-interfaces-for-virtual-networking Networking, 英文地址https://kubernetes.io/zh/docs/concepts/cluster-administration/networking/
20、THE ConTAINER NETWORKING LANDSCAPE: CNI FROM COREOS AND CNM FROM DOCKER, Lee Calcote:https://thenewstack.io/container-networking-landscape-cni-coreos-cnm-docker/
21、CNI - the Container Network Interface, https://github.com/containernetworking/cni
22、Making the Kubernetes Service Abstraction Scale using eBPF, [译] 利用 eBPF 支撑大规模 K8s Service (LPC, 2019):https://linuxplumbersconf.org/event/4/contributions/458/
23、基于 BPF/XDP 实现 K8s Service 负载均衡 (LPC, 2020)https://linuxplumbersconf.org/event/7/contributions/674/24、A Deep Dive into Iptables and Netfilter Architecture, Justin Ellingwood:https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture25、Iptables Tutorial 1.2.2, Oskar Andreasson:https://www.frozentux.net/iptables-tutorial/iptables-tutorial.html26、Virtual IPs and service proxies, 英文地址: /d/file/20220329/ 英文地址:https://kubernetes.io/docs/concepts/services-networking/ingress/28、NGINX Ingress Controller, https://www.nginx.com/products/nginx-ingress-controller/29、Ingress Controllers, 英文地址:https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/30、Cracking kubernetes node proxy (aka kube-proxy), [译] 深入理解 Kubernetes 网络模型:自己实现 Kube-Proxy 的功能:https://cloudnative.to/blog/k8s-node-proxy/原文链接:https://developer.aliyun.com/article/813025?utm_content=g_1000310145本文为阿里云原创内容,未经允许不得转载。
本文经授权 由答答网发布,转载联系作者并注明出处:http://www.dadazzz.com:6443/sh/show-83850.html
如对文章、图片、字体等版权有疑问,请联系我们。