Loading... 虚拟化网络我们一直在使用着,我们玩的虚拟机背后就存在着虚拟化网络。但是我们作为上层使用者,可能并不清楚背后实现的原理,接下去就由我揭开虚拟化网络的面纱,一探究竟! 虚拟化网络理论上分为两块NFV(Network Function Virtualization)和SDN(Software Defined Networking)。NFV是从硬件中抽象出网络功能,SDN则是将网络转发功能与网络控制功能分开。这两者并不是具体软件,而是对产生虚拟化网络需求提出的两个架构概念。就是从无到有,理论概念先行,而不是直接上来就干。 # NFV架构的实现 上面说了NFV是从硬件中抽象出网络功能,那到底有哪些网络功能呢?说到这个,首先要知道网络的构成有网卡、网线(成对网口)、交换机。有人会问路由器为什么不是,还有防火墙呢。这些东西并不是组成网络的必要设备,可以理解是高级点的交换机而已。 ## 虚拟网卡 虚拟网卡模式分为两类TAP和TUN,TAP在二层收发的是MAC层数据帧,TUN在三层收发的是IP数据包。作为虚拟机使用时候一般不会创建此类网卡,毕竟没有线只有口有何用。此类网卡用于隧道传输vpn的场景比较多,如OpenVPN。TAP模式通常接入到虚拟交换机(bridge)上作为局域网的一个节点,TUN模式通常用来实现三层的ip隧道。创建后的网卡表面和实际网卡区别不大,要说区别还是有点区别,下面我们创建一下看看吧! 创建TAP/TUN虚拟网卡: <pre><code>ip tuntap add dev tap0 mode tap ip tuntap add dev tun0 mode tun</code></pre> 给虚拟网卡创建IP: <pre><code>ifconfig tap0 10.0.0.1/24 ifconfig tun0 10.0.1.1/24</code></pre> 我们来看看创建结果: <pre><code>[root@vxlan_test1 ~]# ifconfig tap0 tap0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 10.0.0.1 netmask 255.255.255.0 broadcast 10.0.0.255 ether 62:e9:a3:d9:9a:f0 txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@vxlan_test1 ~]# ifconfig tun0 tun0: flags=4241<UP,POINTOPOINT,NOARP,MULTICAST> mtu 1500 inet 10.0.1.1 netmask 255.255.255.0 destination 10.0.1.1 unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 500 (UNSPEC) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@vxlan_test1 ~]# ethtool -i tap0 |grep drive driver: tun [root@vxlan_test1 ~]# ethtool -i tun0 |grep drive driver: tun</code></pre> 如上虚拟网卡和实际的网卡区别上就在于drive类型,实际网卡可能是vmnet 或者e1000或者等等用的实际的网卡驱动。虚拟网卡用的都是虚拟网卡驱动。 ## 虚拟网线(成对接口) VETH设备是成对出现的,所接收的数据的一个端部会从另一端送出,可以理解为一个虚拟网络电缆。下面我们一步步操作下看看。 创建VETH设备: <pre><code>[root@vxlan_test3 ~]# ip link add veth0 type veth peer name veth1 [root@vxlan_test3 ~]# ip a 11: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether 5e:c6:ab:aa:66:4d brd ff:ff:ff:ff:ff:ff 12: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether c6:cd:29:91:f1:97 brd ff:ff:ff:ff:ff:ff [root@vxlan_test3 ~]# ethtool -i veth0 |grep drive driver: veth [root@vxlan_test3 ~]# ip link add veth2 type veth peer name veth3 #再创建一条网线</code></pre> 网卡成双成对的出现,很像一条网线,接下去两头插哪里,现实中肯定是一端插交换机,一端插服务器。这里也是一样,接着往下看。 ## 虚拟交换机 Bridge设备有的称之为网桥,我这里叫它虚拟交换机,因为实在很像交换机。 创建Bridge设备: <pre><code>[root@vxlan_test3 ~]# ip link add br0 type bridge [root@vxlan_test3 ~]# ip a 13: br0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether 16:1b:9d:3f:c9:a4 brd ff:ff:ff:ff:ff:ff [root@vxlan_test3 ~]# ethtool -i br0 |grep drive driver: bridge [root@vxlan_test3 ~]# ip link set dev br0 up #启动交换机</code></pre> 创建两台虚拟服务器(其实是网络命名空间,后面会讲述什么是命名空间,这里先理解成container服务器即可): <pre><code>[root@vxlan_test3 ~]# ip netns add container1 [root@vxlan_test3 ~]# ip netns add container2 </code></pre> 网线插入: <pre><code>[root@vxlan_test3 ~]# ip link set dev veth0 master br0 #第一根网线一头插交换机 [root@vxlan_test3 ~]# ip link set dev veth0 up #启动端口 [root@vxlan_test3 ~]# ip link set dev veth1 netns container1 #第一根网线一头插服务器 [root@vxlan_test3 ~]# ip netns exec container1 ip link set lo up #启动端口 [root@vxlan_test3 ~]# ip netns exec container1 ip link set veth1 name eth0 #给虚拟服务器内网卡重命名 [root@vxlan_test3 ~]# ip netns exec container1 ip addr add 10.20.1.12/24 dev eth0 #给虚拟服务器网卡绑定ip [root@vxlan_test3 ~]# ip netns exec container1 ip link set eth0 up #启动端口 ######################################################################## [root@vxlan_test3 ~]# ip link set dev veth2 master br0 #第二根网线一头插交换机 [root@vxlan_test3 ~]# ip link set dev veth2 up [root@vxlan_test3 ~]# ip link set dev veth3 netns container2 #第二根网线一头插服务器 [root@vxlan_test3 ~]# ip netns exec container2 ip link set lo up [root@vxlan_test3 ~]# ip netns exec container2 ip link set veth3 name eth0 [root@vxlan_test3 ~]# ip netns exec container2 ip addr add 10.20.1.13/24 dev eth0 [root@vxlan_test3 ~]# ip netns exec container2 ip link set eth0 up</code></pre> 现在有了一台交换机,两台服务器,两条网线,且都插线和开机,可以开始联机(来一次ping对战吧)! <pre><code>[root@vxlan_test3 ~]# ip netns exec container2 ip a 14: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether c6:99:4d:45:c6:98 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.20.1.13/24 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::c499:4dff:fe45:c698/64 scope link valid_lft forever preferred_lft forever [root@vxlan_test3 ~]# ip netns exec container2 ping 10.20.1.12 PING 10.20.1.12 (10.20.1.12) 56(84) bytes of data. 64 bytes from 10.20.1.12: icmp_seq=1 ttl=64 time=0.231 ms 64 bytes from 10.20.1.12: icmp_seq=2 ttl=64 time=0.045 ms 64 bytes from 10.20.1.12: icmp_seq=3 ttl=64 time=0.048 ms 64 bytes from 10.20.1.12: icmp_seq=4 ttl=64 time=0.076 ms </code></pre> ## 命名空间解说 任何虚拟化环境,包括Docker容器(K8S),KVM虚拟化(OpenStack),vmware(Vspher)等目标都是在一台设备上运行多套环境。这也相当于资源隔离的需求,由命名空间来实现。 Linux的命名空间有很多,其中常见的是Cgroups和Network命名空间,详细参考如下表格: | 命名空间名 | 全称 | 系统API调用参数 | 隔离内容 | 内核版本 | | ---------- | -------------------------- | --------------- | ------------------------------------------------------------ | -------- | | Mount | Mount | CLONE_NEWNS | Mount points挂载点(文件系统) | 2.4.19 | | UTS | UNIX Time-sharing System | CLONE_NEWUTS | 系统主机名和 NIS(Network Information Service) 主机名(有时称为域名) | 2.6.19 | | IPC | InterProcess Communication | CLONE_NEWIPC | System V IPC, POSIX message queues信号量,消息队列 | 2.6.19 | | PID | Process IDentification | CLONE_NEWPID | Process IDs进程号 | 2.6.24 | | Network | Network | CLONE_NEWNET | Network devices, stacks, ports, etc.网络设备,协议栈,端口等等 | 2.6.24 | | User | User | CLONE_NEWUSER | 用户和用户组 | 3.8 | | Cgroups | Control groups | CLONE_NEWCGROUP | CPU、MEM、TASK资源。或者可以说是根目录 | 4.6 | | Time | Time | CLONE_NEWTIME | 系统时钟 | 5.6 | # SDN架构的实现 SDN是将网络转发功能与网络控制功能分开,SDN更多的是用来管理不同服务器上的虚拟网络(目标跨主机通讯)。如果创建一个虚拟环境,都需要手动的创建并维护各个虚拟网络部件,这将是个庞大的工作量,出错在所难免,还会消耗大量人力。SDN主要是解决这些问题,运维人员只需要做好提前规划选择适当的网络插件方案,配置维护上介入就不会很多。当前主流的网络插件方案用于K8S的有calico和flannel,用于OpenStack的有Neutron,这些都是SDN架构的实现。下面对这些主流的网络插件进行大致讲解! ## flannel 它有三个模式,Vxlan、UDP、host-gw。Vxlan是基于隧道来实现二层网络的跨主机通讯,UDP是基于三层的TUN设备和二层的Bridge网桥进行流量的传递,Vxlan模式效率更优于UDP,host-gw为纯三层的网络方案。 ## calico 这个也是纯三层的网络模型,所有的数据包都是通过路由的形式找到对应的主机和容器的,然后通过 BGP 协议来将所有路由同步到所有的机器或数据中心,从而完成整个网络的互联。二层到三层的过度是通过代理 ARP(Proxy ARP)指向默认网关来实现的。 感兴趣可以自己模拟操作一遍,尝试看看,参考博客https://blog.thexqf.top/posts/cloudnative/k8s/cni-calico/ 如上flannel和calico的相关模式我只是提取了些精华部分,详细参考博客https://www.lixueduan.com/posts/kubernetes/03-pure-layer-3-network/ 和 https://www.lixueduan.com/posts/kubernetes/02-cluster-network/ ## Neutron OpenStack关键组件之一,很强大。提供的网络虚拟化能力直接涵盖了从二层到七层。网络模式也是相当丰富。详细参考https://zhuanlan.zhihu.com/p/488911351 如上网络插件,Vxlan出现的次数较多,相信作为跨服务器通信比较主流一些,下面一起玩一下Vxlan。 ## Vxlan Vxlan是一个隧道,建立这个隧道就需要IP端口,IP可以单独指定形成点对点的隧道,也可以使用多播模式的Vxlan形成多个节点的隧道互通。IANA分配的端口都是统一的4789,但这在Llinx中默认的并不是这个端口而是8472,所以需要进行单独指定一下4789端口。 另外Vxlan中有三个常用术语: 1、VETP(VXLAN Tunnel Endpoints,VXLAN 隧道端点) VXLAN 网络的边缘设备,用来进行 VXLAN 报文的处理(封包和解包)。VTEP 可以是网络设备(比如交换机),也可以是一台机器(比如虚拟化集群中的宿主机)。 2、VNI(VXLAN Network Identifier,VXLAN 网络标识符) VNI是每个 VXLAN 段的标识,是个 24 位整数,一共有 224=16777216224=16777216(一千多万),一般每个VNI对应一个租户,也就是说使用 `VXLAN` 搭建的公有云可以理论上可以支撑千万级别的租户。 3、FDB 表(Forwarding Database entry,即转发表) Linux 网桥维护的一个二层转发表,用于保存远端虚拟机/容器的 MAC地址,远端 VTEP IP,以及 VNI 的映射关系。 4、Underlay和Overlay 通俗的讲,隧道外使用的传输报文头部称之为Uderlay,比如VETP隧道端点之间的通讯。隧道内使用的传输报文头部称之为Overlay。 使用多播模式的时候,由于Underlay采用组播的方式发送报文,对于多节点的 `VXLAN` 网络来说比较简单高效。但多播也是有它的问题的,并不是所有网络设备都支持多播(比如公有云),再加上多播方式带来的报文浪费,在实际生成中很少被采用。需要用到控制中心的概念,控制中心运行原理就是在虚拟机和容器的场景中,当虚拟机或者容器启动还没有进行网络通讯时,我们就可以知道它的 IP 和 MAC(可能是用某种方式获取,也有可能是事先控制这两个地址),分布式控制中心保存了这些信息。除此之外,控制中心还保存了每个 Vxlan 网络有哪些 VTEP,这些 VTEP的地址是多少。有了这些信息, VTEP就能发送报文时直接查询并添加头部,不需要多播去满网络地问了。 创建多播模式Vxlan: <pre><code>[root@vxlan_test1 ~]# ip link add vxlan0 type vxlan \ > id 42 \ > dstport 4789 \ > group 239.1.1.1 \ > dev ens192 [root@vxlan_test1 ~]# ip link add br0 type bridge [root@vxlan_test1 ~]# ip link set vxlan0 master br0 [root@vxlan_test1 ~]# ip link set vxlan0 up [root@vxlan_test1 ~]# ip link set br0 up ..... 创建container、网线、接入交换机操作、配置ip同上 完成后在另外一台执行相同操作,配置不同container ip。 执行 ip netns exec container2 ping (另一台服务器的container ip),发现已可以ping通。实现跨主机通信。 </code></pre> # 随便说说 理解了原理,会在使用上更加得心应手。在DIY方向上也能如鱼得水。这里我也参考了别人很多,有时候也在想别人比我写的可好多了,那我还有写的必要吗?经过一番思考后,我觉得还是有必要。一来起码我通过自己的思维逻辑总结过了;二来以后翻看起来也会比较方便,同时也加深了映像;最后嘛,通过一次次的磨炼,我希望写的可以能够尽量通俗易懂一些,让给多的人能够快速上手。 在Linux网络中,内容相当丰富,可玩性极高,这里有点羡慕那些做网络编程的人。从虚拟网卡类型,到代理ARP再到Socks、http、Stream代理,Iptables转发,乃至eBPF,都和网相关。Linux的世界很大,而我每次都不知道写什么,个人写博客也是比较随心所欲,不太想写连载,每次遇到个问题,像随笔一样来一发记录吧。 Last modification:June 4, 2024 © Allow specification reprint Like 如果觉得我的文章对你有用,请随意赞赏