k8s网络打鱼篇一-弄懂CNI插件底层网络技术

k8s 向我们提供各种网络插件、网络资源,例如 Flannel、Calico、Cilium、Weave 各种网络插件。在使用之前我们需要理解它们是如何工作的,这样才能在遇到问题时能有清晰的认识,能够知其所以然。本文将依次介绍 Linux 为我们提供的工具:namespace、veth pair、iptables、Linux bridge、tun/tap 设备、iptables、ipip、vxlan、macvlan、ipvlan,以及当前最新的 ebpf 技术,这个后续开单独模块介绍。建议按照顺序阅读理解,前后存在关联性。如部分细节描述不准确,烦请指正🌾


namespace

  • 作用: 为进程提供一个隔离的环境,从进程角度来看,整个服务器就它一个进程存在,完全独占系统资源,Linux 在各个内核版本升级过程中给我们提供了不同的 namespace 隔离,包括存储挂载、系统信号量、网络、主机名、进程、cgroup
  • 原理: Linux 使用 clone()函数创建 namespace,会在/proc/pid/ns 目录下生成符号链接,链接到对应文件,只要文件一直存在,namespace 就不会消失。通过 setns()方法可以将进程放入指定 namespace,unshare()可以将指定进程从 namespace 移出。例如我们如果要进入某个 namesapce 下,也是要先拿到 namespace 对应的文件描述符,然后通过 setns 添加进程,然后就可以执行自己的程序。至于这些函数底层原理是什么,感兴趣自行了解。

veth pair

  • 作用: veth 就是一个虚拟以太网卡,pair 就是一对的意思,那么一对虚拟网卡就是说我们往其中一个 veth 发送数据,就会转发到另外一个 veth,要注意 veth pair 的作用仅仅是将数据从一个网卡发送到另外一个网卡,前面我们也提到 namesapce 网络空间隔离,那我们就可以把 veth 放在根命令空间和新的网络空间,实现有限制的网络访问。
  • 案例: 接下来我们将创建一对 veth pair,并测试他们之间的网络访问情况
    如上图所示,当前服务器只有一个默认的 ens160 网卡,我们可以通过 ip link add vethname1 type veth peer name vethname2,创建一对 veth pair,当前这一对 veth 都是在根 namespace 下,且网卡状态都是 down,也就是当前没啥用,只是单纯创建出来。

    接下来将两个 veth 启动并设置两个不同的网段,veth1 的 IP 地址是 10.0.2.100,veth0 的地址是 10.0.1.100。

    此时我们在根 namespace ping veth1 和 veth0 都可以 ping 通,路由表也可以看到分别创建了两条路由规则,这个时候其实跟 pair 没啥关系,单纯可以理解就是分别创建了两个不同的虚拟网卡,只有他们在不同的网络 namespace 才能感受到 pair 的意义。接着我们通过 ip netns 创建两个不同的命令空间,这个是 linux 为我们封装好的一个工具去操作网络 namespace,其他 namespace 的操作都需要编写相应的 c 代码。接着再把 veth0 放入 netns0 空间,veth1 放到 netns1 空间,此时我们再去 ping 这两个网址,已经无法 ping 通,因为他们分别处在不同的网络 namespace。此时如果进入 netns0 或者 netns1,可以看到网卡已经被重置变成 down 状态,路由表信息也是全为空的。

    如上图所示,我们再次把 veth0 和 veth1 启动起来,然后在 netns0 的空间下去 ping veth1,发现 ping 不通,那么问题来了不是说两个是个 pair 吗?可以互相通的?但是这是我们逻辑上的理解,那还是要强调一下 pair 的作用:将一端网卡的数据传送到另外一端。是不是可以看出问题所在,我们需要先将数据送到我们的网卡上,那这个原因是本地路由没有 10.0.2.0/24,所以我们的数据压根送不到我们自己的 veth0 网卡上,所以我们只要给我们的 namespace 添加一条路由即可。但是这也只是解决了报文去的问题,回的问题也需要在 netns1 上创建相应的路由。至此跨 namespace 的网络访问就可以通了,对于各自 namespace 的网络设备都是独立的,其实如果 vth0 和 veth1 如果是在同一网段,那么也就可以直接使用默认的路由规则进行访问,无须手工添加路由规则。

Linux Bridge

  • 作用: 对于两个不同的 namespace 可以通过 veth pair 进行访问,但是当存在多个 namespace 隔离的时候,就需要一个东西来实现不同 namespace 之间进行访问,这个就是 Linux Bridge,也可以理解为她是一个二层的物理交换机,bridge 上面有不同端口,可根据 mac 地址转发到相应端口。
  • 案例: 创建一个 bridge,并绑定 veth pair,进行网络测试,总结一下就是说只要一个虚拟网卡链接上去,那它就不会再和网络协议栈通信,而是把流量交给网桥,至于网桥能不能和网络协议栈通信,那就不管了。k8s 中也有应用,就是 pod 中放置一个 veth pair 中一个,另外一个 veth 挂载根 namespace 空间的网桥上,外面的 veth 没有 ip 地址,pod 内部流量通过 veth pair 转发到根 namespace veth 上,然后再转给 bridge,由 bridge 根网络协议栈通信。至此单节点 pod 之间内部通信其实就可以使用这种方式完成。
#当前环境虚拟机ip是192.168.123.111,网关是192.168.123.1

## 创建一个名称为br0的网桥,并启动  也可以通过 brctl addbr br0创建
[root@node1 ~]# ip link add name br0 type bridge
[root@node1 ~]# ip link set br0 up

## 创建一个veth0和veth1的pair,启动并设置ip地址
[root@node1 ~]# ip link add veth0 type veth peer name veth1
[root@node1 ~]# ip addr add 1.2.3.101/24 dev veth0
[root@node1 ~]# ip addr add 1.2.3.102/24 dev veth1
[root@node1 ~]# ip link set veth0 up
[root@node1 ~]# ip link set veth1 up

## 将veth0绑定到br0网桥上 或者使用 brctl addif br0 veth0
[root@node1 ~]# ip link set dev veth0 master br0

## 查看bridge绑定情况
[root@node1 ~]# bridge link
7: veth0 state UP @veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 2

## 此时我们理解的网络拓扑图应该是这样,但是后面通过实验可以发现有些地方存在问题

网络协议栈<------> br0 <------> veth0 <------> veth1
||’          ’||---------------------------------|------------|

## 此时从veth0 ping veth1地址,会发现无法ping通,在网卡veth1抓包发现接受到报文,但是无返回
## 同样操作从veth1 ping veth0,也是同样现象
## 同时我们发现veth0的mac地址和br0 mac地址相同
##🙅‍♂️这里还是存在一些问题,可能跟我的本地环境有关系,我是用的虚拟机,也许跟内核版本有关系,正常这
#里会返回reply报文给veth0,然后veth0会返回给br0,但是br0并没有将数据给网络栈,因此无法获得
#veth1的mac地址,从而导致二层网络不通,无法不通,折腾很久,后续有机会再验证????
[root@node1 ~]# ping -c 1 -I veth0 1.2.3.102
PING 1.2.3.102 (1.2.3.102) from 1.2.3.101 veth0: 56(84) bytes of data.

--- 1.2.3.102 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
[root@node1 ~]# tcpdump -n -i veth1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth1, link-type EN10MB (Ethernet), capture size 262144 bytes
19:03:10.496023 ARP, Request who-has 1.2.3.102 tell 1.2.3.101, length 28
19:03:11.574961 ARP, Request who-has 1.2.3.102 tell 1.2.3.101, length 28
19:03:12.608699 ARP, Request who-has 1.2.3.102 tell 1.2.3.101, length 28


Tun/Tap

  • 为啥要这个设备?: 先按照 osi 网络模型角度来思考一下,流量通过网线以二进制流形式送到我们的服务器物理网卡,然后通过我们的网络协议栈,最底层就是物理层,然后解析数据经过数据链路层转成 mac 头+data,然后经过网络层拆包转换成 ip 头+data,然后到传输层拆包变成端口+data,这个时候其实都是在内核态进行操作的,后面就到了会话层、表示层、应用层,这里就是到具体的用户态程序去处理了。到这里我们就会有一个疑问?我们能不能参与一下数据链路层、网络层的一些工作,这个时候 Linux 就为我们提供了 Tun/Tap 设备。
  • 工作机制: Tun 设备是一个点对点设备,工作在三层,所以它没有 mac 地址,Tap 设备工作在二层。Tun/Tap 设备对于整个 Linux 内核来说都是平等的网卡,只是它们是虚拟化出来的虚拟网卡,我们的物理网卡一端连接着外部物理网络,一边连接着我们的 Linux 网络协议栈。而我们 Tun/Tap 设备一边连接网落协议栈,一边连着用户空间的字符设备,一般位置是在/dev/ney/tunX。因此用户程序可以通过操作这个字符设备来读取写入数据。可以拿 vpn 举个例子:互联网环境下,要远程连接到公司应用,需要安装 vpn,当我们安装好之后就可以使用公司应用,我们可以分析一下公司应用流量是如何流转的,从而理解 Tun/Tap 设备的作用。我们假设本地 IP、本地 Tun 设备 IP、公司设备 IP、公司 Tun 设备 IP、公司服务 IP。我们肯定是直接访公司服务 IP,但是正常环境下肯定是无法 ping 通,因此当我们访问公司 IP 时,我们的应用通过 socket 和网络协议栈通信,然后把流量路由到本地 Tun 设备网卡,这个时候数据就会被送到对端的字符设备/dev/net/tun0,然后我们的 vpn 程序肯定是在监听这个设备的数据,这个时候就会收到这个数据报文 data,这个时候它是一个三层的数据包,这个时候 vpn 设备就把它作为普通的数据包,为他添加应用层相关协议参数,然后再把数据包写入字符设备,然后送入内核空间的 Tun 设备,这个时候内核设备就会对它进行封包,填充公司设备 ip、公司设备端口,然后路由到本地设备 ip 填充 mac 地址,然后通过互联网送到公司设备 eth0 网卡,然后通过内核协议栈拆包,然后可以得到我们之前生成的数据 data,这个时候再把 data 写入公司 Tun 字符设备,然后交给 Tun 网卡,接着边根据对应的路由规则访问对应服务即可。
  • 总结: 有了 Tun/Tap 虚拟网卡设备,我们可以对网络二层数据包、或者三层数据包进行二次处理,flannel 中 UDP 模式就是使用这个机制,k8s 跨节点跨 Pod 通信很多都是这种思想,通过在 IP 报文中封装 IP 报文,IPIP 实现通信,但是这种机制也存在问题,对于大数据量数据包拆包、封包可能有性能损耗。这里大家理解一下这个概念,后面会有具体实践,就好理解了。

iptables

  • 作用: iptables 底层是利用 netfilter 框架,可以通过对网络流量处理设置一些 hook 函数,比如屏蔽指定网址请求、SNAT、DNAT 转换等,k8s 中 servers 的负载均衡也可以采用这种机制来实现。
流量通常会经过系统chain,也可以为流量设置drop、return、jump、reject指令等
默认流量如下:
prerouting-->经过路由-->forwared--->postrouting-->离开主机
               |
               |
             input--->本地进程--->output--->postrouting ---->离开主机
主要就看需要给本地进程,如果需要给本地进程,则进入input chain、output chain
这也就所说的五链,每天链路可以挂不同的表,并不是所有链上都有这些表,可以设置相应规则:
	-managle 用于修改数据包头信息
	-nat 用于修改数据包ip地址
	-filter 同于控制链路上的数据包是否放行,可以reject、drop
	-raw iptables连接是有状态的,可以通过raw去除这种跟踪机制
	-security 用于数据包上应用SELinux
  • 常用指令:
#展示所有链路情况,如果需要指定链路,需要在后面加上 -t
iptables -L -n

#配置允许指定ip ssh访问,如果要阻止则直接
iptables -A INPUT -s 10.20.30.40/24 -p tcp --dport 22 -j ACCEPT

#配置端口转发
iptables -t nat -A PREROUTING -i eth0 -p tcp -dport -j REDIRECT --to-port 8080

#删除所有规则
iptables -F

#规则永久生效
iptables-save

#备份与恢复
iptables-save > iptables.bak
iptables-restore < iptables.bak

IPIP

  • 原理: ipip 底层就是基于我们前面说的 Tun 点对点设备实现的,ipip 也只是隧道一种模式,还有 sit 模式:用 ipv4 报文封装 ipv6 报文,ISATAP:也是一种用于封装 ipv6 的方式,GRE:定义了一种通用的方式,用于在 IP 层封装另外一个 IP 层的协议,适用于 ipv4 和 ipv6
  • 实践: 我本地准备两台 centos7 arm64 架构虚拟机,两台机器 A、B 分别是 192.168.123.111、192.168.123.114
#两台机器分别加载ipip模块
modprobe ipip
[root@node4 ~]# lsmod |grep ipip
ipip                   16384  0
tunnel4                16384  1 ipip
ip_tunnel              36864  1 ipip

#在节点A上添加tun设备,设置设备ip以及本地路由
ip tunnel add tunnel1 mode ipip remote 192.168.123.114 local 192.168.123.111
ip addr add 1.2.3.100/24 dev tunnel1
ip link set tunnel1 up
ip route add 1.2.4.0/24 dev tunnel1

#同样在节点B上添加tun设备,设置设备ip以及本地路由
ip tunnel add tunnel1 mode ipip remote 192.168.123.111 local 192.168.123.114
ip addr add 1.2.4.100/24 dev tunnel1
ip link set tunnel1 up
ip route add 1.2.3.0/24 dev tunnel1

#然后A节点ping 1.2.4.100
[root@node1 ~]# ping 1.2.4.100
PING 1.2.4.100 (1.2.4.100) 56(84) bytes of data.
64 bytes from 1.2.4.100: icmp_seq=1 ttl=64 time=0.632 ms
64 bytes from 1.2.4.100: icmp_seq=2 ttl=64 time=0.906 ms
^C
--- 1.2.4.100 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1068ms
rtt min/avg/max/mdev = 0.632/0.769/0.906/0.137 ms

这个时候我们去对节点 B 主机 物理网卡 ens160 抓包,得到下面图片的结果,No.10 reply 报文有两个协议号都是 4 的的报文,最里面使我们 Tun 设备 ip 地址,最外面是我们物理网卡地址,这下子好像就能明白了!!!我们先分析一下发送的流程:Tun 设备会把我们的 IP 报文标识成数据报文交给网络协议栈,然后封装物理网卡地址,然后通过物理网络传输。接受的流程就是:物理网卡接受数据包,然后交给网络协议栈解析,然后在根据里面的数据报文路由到目标 Tun 设备。

所以对于 tun 设备来说,你只能看到 1.2.3.0/24、1.2.4.0/24 的报文,下图我们抓包注解 A tunnel1 网卡数据包也是如此,只能看到这个两个 ip 数据报文,看不到主机 IP 报文 1.2.3.100 和 1.2.4.100 跨主机跨网段访问通过这种 ipip 模式,而不是路由模式或者通过网关转发,其实过程大概是这样。


VXLAN

  • 背景: Virtual eXtensible Lan,可扩展的虚拟局域网。就是基于三层网络构建了一个大二层网络,这么说可能有点抽象。可以看一下这个技术具体解决了什么问题?刚开始的时候大家只有几台机器,那么弄一个二层交换机,就可以实现各个节点网络访问。但是随着节点数变多,每次请求都要发起arp报文获取目标客户端ip地址,这个请求会广播到广播域中每个节点,对机器性能有一定影响。因此又搞了一个vlan,将一个大的广播域通过路由器设备,划分成小的广播域,这样也就限制住了arp广播范围减少影响。但是随着发展vlan子网划分也是有限制的,同时云原生环境下,服务器迁移会导致IP范围发生变化,同时多租户网络要求隔离,既要保证调整的时候影响小又要灵活,那就提出来一个概念,就是对一个机器访问另外一个机器屏蔽三层网络的影响,我只要知道你的mac地址,我就可以跟你通信,这也就是基于三层网络的overlay二层网络。具体要怎么屏蔽,应该就可以想到我们之前使用的Tun虚拟网卡,实际物理IP和TunIP没啥关系互不影响。比如在容器环境下,假设我们在每个节点都安装一个Tun设备,这个时候我就不关心实际物理IP是多少,因为我只关心我的逻辑IP地址,只要你这个Tun设备能把对端mac地址给我就可以,那就可以用Tun设备为我们建立的隧道跟对端通信。这样从整体上看,所以节点就可以在二层统一起来了,不用关心三层具体情况,这就是大二层网络。
  • 原理: Vxlan的报文是Mac in UDP,当我们封装好二层报文的时候,交付给VTEP(Virtual Tunnel EndPoint)也就我上面说的Tun设备,会将我们的二层报文进行封装一个UDP报文,再通过物理机网络发送到对端。会依次套上VXLAN参数(8字节)、正常的UDP头部(8字节)、IP头部(20字节)、MAC头部(14字节),会比正常报文多50个字节数据。
Ethernet Header + IP Header + UDP header + Vxlan header + 原始的二层报文
  • 实践: 接下来将通过几个具体的例子直观感受Vxlan到底如何实现大二层网络,以及在实现过程中还存在那些问题,以及如何去解决的。
#需要先检查内核是否加载vxlan模块
lsmod | grep vxlan
modprobe vxlan
  • 点对点Vxlan模式
#还是使用刚才A、B机器:192.168.123.111、192.168.123.114
#A节点创建vxlan接口 vni(vxlan network indetifier)是42
ip link add vxlan0 type vxlan id 100 \
dstport 4789 \
remote 192.168.123.114 \
local 192.168.123.111

#为A节点vxlan配置ip并启动
ip addr add 1.2.5.100/24 dev vxlan0
ip link set vxlan0 up

#这里可以看到A节点已经自动生成好路由信息,以及fdb中的对端信息
[root@node1 ~]# ip route
default via 192.168.123.1 dev ens160 proto static metric 100
1.2.3.0/24 dev tunnel1 proto kernel scope link src 1.2.3.100
1.2.4.0/24 dev tunnel1 scope link
1.2.5.0/24 dev vxlan0 proto kernel scope link src 1.2.5.100
192.168.123.0/24 dev ens160 proto kernel scope link src 192.168.123.111 metric 100
[root@node1 ~]# bridge fdb
01:00:5e:00:00:01 dev ens160 self permanent
00:00:00:00:00:00 dev vxlan0 dst 192.168.123.114 via ens160 self permanent

#同样操作在B节点
ip link add vxlan0 type vxlan id 100 \
dstport 4789 \
remote 192.168.123.111 \
local 192.168.123.114

ip addr add 1.2.5.101/24 dev vxlan0
ip link set vxlan0 up

#验证vxlan网络是否已经通了,在B节点ping
ping 1.2.5.100

#Tips 关闭防火墙
sudo systemctl stop firewalld
sudo iptables -F
sudo iptables -X
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT

如下图所示抓取B节点ens160物理网卡数据tcpdump -i ens160 host 192.168.123.111 -n,我这里图片其实有点问题,原因就是我安装的两台虚拟机防火墙没有被关闭,所以4789端口被reject,只要systemctl stop firewalld关闭防火墙,即可正常访问。这里我们也可以看一下这里的报文结构跟我上述描述的也是一致的,内部封装的是二层的数据包、然后添加了vxlan头部,可以看到这里vni也是100。

  • 多播Vxlan模式
    上述点对点vxlan模式我们是指明了VTEP对端IP,实际情况我们也不太可能手工去维护这样一个。多播模式就是把大家都放在一个虚拟组内,我要找对端的IP的时候,我就组内广播一下,跟所有人要一下,肯定有一个VTEP会响应我的请求,其他VTEP收到我的广播,也会在本地Fdb转发表记录一下我的IP和源mac地址,然后丢弃数据报文,专业点说法那就是这个设备学习到了。同时多播模式也需要底层支持。
[root@node4 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
# MULTICAST就代表当前服务器支持多播
#查看当前服务器组播地址

#还是使用刚才A、B机器:192.168.123.111、192.168.123.114,将虚拟机镜像回退,上个例子已经创建
#了一个vxlan需要重置,再创建会报错
#A节点创建vxlan接口 vni(vxlan network indetifier)是101,但是这里对端地址已经换成了一个
#group参数,表示将这个设备加入到一个多播组里
ip link add name vxlan0 type vxlan id 100 \
dstport 4789 \
group 224.1.1.1 \
local 192.168.123.111 \
dev ens160

#为A节点vxlan配置ip并启动,这里范围要在多播地址范围内224.0.0.0-239.255.255.255
ip addr add 1.2.5.100/24 dev vxlan0
ip link set vxlan0 up

#这里可以看到A节点已经自动生成好路由信息,以及fdb中的对端信息
[root@node1 ~]# ip route
default via 192.168.123.1 dev ens160 proto static metric 100
1.2.5.0/24 dev vxlan0 proto kernel scope link src 1.2.5.100
192.168.123.0/24 dev ens160 proto kernel scope link src 192.168.123.111 metric 100
[root@node1 ~]#  bridge fdb
01:00:5e:00:00:01 dev ens160 self permanent
00:00:00:00:00:00 dev vxlan0 dst 240.1.1.1 self permanent

#同样操作在B节点,这个时候对端也不是A节点地址,而是一个多播地址
ip link add vxlan0 type vxlan id 100 \
dstport 4789 \
group 224.1.1.1 \
local 192.168.123.114 \
dev ens160

ip addr add 1.2.5.101/24 dev vxlan0
ip link set vxlan0 up

#验证vxlan网络是否已经通了,在B节点ping
[root@node4 ~]# ping 1.2.5.100
PING 1.2.5.100 (1.2.5.100) 56(84) bytes of data.
64 bytes from 1.2.5.100: icmp_seq=1 ttl=64 time=1.48 ms
64 bytes from 1.2.5.100: icmp_seq=2 ttl=64 time=1.49 ms
64 bytes from 1.2.5.100: icmp_seq=3 ttl=64 time=0.866 ms
^C
--- 1.2.5.100 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2005ms
rtt min/avg/max/mdev = 0.866/1.280/1.490/0.294 ms

#这里也可以看到节点ens160被加入到广播组224.1.1.1
[root@node4 ~]# ip maddr
1:      lo
        inet  224.0.0.1
        inet6 ff02::1
        inet6 ff01::1
2:      ens160
        link  01:00:5e:00:00:01
        link  01:00:5e:01:01:01
        inet  224.1.1.1
        inet  224.0.0.1
        inet6 ff02::1
        inet6 ff01::1
#这个也可以看到节点A 已经学习到B的mac地址,这样就不需要再在广播组里面进行广播了
[root@node1 ~]# bridge fdb
01:00:5e:00:00:01 dev ens160 self permanent
01:00:5e:01:01:01 dev ens160 self permanent
00:00:00:00:00:00 dev vxlan0 dst 224.1.1.1 via ens160 self permanent
ca:6b:a0:6c:91:0e dev vxlan0 dst 192.168.123.114 self
  • Vxlan+桥接
    上面多播Vxlan模式也解决了点对点如何找到对端问题,但是案例中对于单个机器上我们只在根namespace可以使用VTEP设备(就是那个vxlan0网口),在实际环境中,单个机器肯定要部署多个容器,分别在不同的namespace下,这个我们应该可以想到使用我们最开始介绍得veth pair+bridge方案,把vxlan接口也连接bridge上,这样大家就可以通过bridge把流量打到vxlan接口上,再overlay二层封包处理。
#先在A节点部署多播vxlan 设置多播网络地址
ip link add name vxlan0 type vxlan id 100 \
dstport 4789 \
group 224.1.1.1 \
local 192.168.123.111 \
dev ens160
#添加网桥bridge0 绑定vlan0,然后启动网卡
ip link add name bridge0 type bridge
ip link set vxlan0 master bridge0
ip link set vxlan0 up
ip link set bridge0 up

#B节点同样部署
ip link add name vxlan0 type vxlan id 100 \
dstport 4789 \
group 224.1.1.1 \
local 192.168.123.114 \
dev ens160

ip link add name bridge0 type bridge
ip link set vxlan0 master bridge0
ip link set vxlan0 up
ip link set bridge0 up

#接下来就是在A节点上创建namespace,测试连通性,也就是模拟跨节点不同namespace通过vxlan模式访问
#创建c1命令空间、创建veth0、veth1 pair对、绑定veth0到bridge0
ip netns add c1
ip link add name veth0 type veth peer name veth1
ip link set veth0 master bridge0
ip link set veth0 up
#把veth0放入c1,并启动本地回环地址,配置veth0地址并启动
ip link set dev veth1 netns c1
ip netns exec c1 ip link set lo up
ip netns exec c1 ip  addr add 1.2.7.100/24 dev veth1
ip netns exec c1 ip link set veth1 up

#接下来就是在B节点上创建namespace
#创建c1命令空间、创建veth0、veth1 pair对、绑定veth0到bridge0
ip netns add c1
ip link add name veth0 type veth peer name veth1
ip link set veth0 master bridge0
ip link set veth0 up
#把veth0放入c1,并启动本地回环地址,配置veth0地址并启动
ip link set dev veth1 netns c1
ip netns exec c1 ip link set lo up
ip netns exec c1 ip  addr add 1.2.7.101/24 dev veth1
ip netns exec c1 ip link set veth1 up

#接着我们在B节点c1 namespace下ping A节点C1 namespace
ip netns exec c1 ping 1.2.7.100
[root@node4 ~]# ip netns exec c1 ping 1.2.7.100
PING 1.2.7.100 (1.2.7.100) 56(84) bytes of data.
64 bytes from 1.2.7.100: icmp_seq=1 ttl=64 time=5.68 ms
64 bytes from 1.2.7.100: icmp_seq=2 ttl=64 time=0.932 ms
64 bytes from 1.2.7.100: icmp_seq=3 ttl=64 time=0.964 ms
^C
--- 1.2.7.100 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 0.932/2.527/5.687/2.234 ms

下面我们可以先分析一下整个流程,再通过tcpdump验证一下我们的流程

B节点c1 namespace去ping 1.2.7.100,发现是同网段,所以就要发起arp报文
找到1.2.7.100的mac地址-->流量根据本地路由通过veth1到达根namespace veth0
转发给bridge-->bridge作为逻辑意义上的二层交换机,要把流量转发给其他接口,
这个时候就到了vxlan0接口-->vxlan接口就会对这个二层报文,封装vxlan信息以及其他头转发给广播组所有节点
-->广播组节点接受到报文,先由ens160网卡解析报文,发现是个vxlan报文,然后转发给vxlan0接口
-->vxlan接口接受到报文,看一下目的mac是不是自己,如果不是则记录源mac以及VTEP信息到转发表Fdb,
如果是自己则返回reply报文。至此就可以建立一个点对点的通信,也无需再向广播组广播报文。

#我们在节点B ping A,然后抓包B的ens160网卡信息,这里可以看到报文里面有响应报文,
但是外层是物理网卡包着,因为对于物理网卡来说,看到的都是overlay的报文,
这里既有响应报文,又有返回时需要先请求arp的报文。
ip netns exec c1 ping 1.2.7.100
#以下分别抓取B节点ens160、vxlan0、bridge、veth0、veth1流量,这个也是流量流入顺序
[root@node4 ~]# tcpdump -n -i ens160 host 192.168.123.111
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens160, link-type EN10MB (Ethernet), capture size 262144 bytes
16:42:50.782847 IP 192.168.123.114.41027 > 192.168.123.111.4789: VXLAN, flags [I] (0x08), vni 100
IP 1.2.7.101 > 1.2.7.100: ICMP echo request, id 1970, seq 1, length 64
16:42:50.783476 IP 192.168.123.111.58986 > 192.168.123.114.4789: VXLAN, flags [I] (0x08), vni 100
IP 1.2.7.100 > 1.2.7.101: ICMP echo reply, id 1970, seq 1, length 64

[root@node4 ~]# tcpdump -n -i vxlan0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on vxlan0, link-type EN10MB (Ethernet), capture size 262144 bytes
16:42:25.981131 IP 1.2.7.101 > 1.2.7.100: ICMP echo request, id 1968, seq 1, length 64
16:42:25.982779 IP 1.2.7.100 > 1.2.7.101: ICMP echo reply, id 1968, seq 1, length 64
16:42:31.054805 ARP, Request who-has 1.2.7.100 tell 1.2.7.101, length 28
16:42:31.055499 ARP, Request who-has 1.2.7.101 tell 1.2.7.100, length 28
16:42:31.055506 ARP, Reply 1.2.7.100 is-at fa:09:b5:07:3c:b3, length 28
16:42:31.055555 ARP, Reply 1.2.7.101 is-at 7e:7a:bb:02:72:f8, length 28

[root@node4 ~]# tcpdump -n -i bridge0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on bridge0, link-type EN10MB (Ethernet), capture size 262144 bytes
16:43:41.770772 IP 1.2.7.101 > 1.2.7.100: ICMP echo request, id 1972, seq 1, length 64
16:43:41.771353 IP 1.2.7.100 > 1.2.7.101: ICMP echo reply, id 1972, seq 1, length 64
16:43:46.809801 ARP, Request who-has 1.2.7.100 tell 1.2.7.101, length 28
16:43:46.814237 ARP, Reply 1.2.7.100 is-at fa:09:b5:07:3c:b3, length 28
16:43:46.814349 ARP, Request who-has 1.2.7.101 tell 1.2.7.100, length 28
16:43:46.814440 ARP, Reply 1.2.7.101 is-at 7e:7a:bb:02:72:f8, length 28

[root@node4 ~]# tcpdump -n -i veth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth0, link-type EN10MB (Ethernet), capture size 262144 bytes
16:45:32.349535 IP 1.2.7.101 > 1.2.7.100: ICMP echo request, id 1979, seq 1, length 64
16:45:32.350343 IP 1.2.7.100 > 1.2.7.101: ICMP echo reply, id 1979, seq 1, length 64
16:45:37.377110 ARP, Request who-has 1.2.7.100 tell 1.2.7.101, length 28
16:45:37.377498 ARP, Reply 1.2.7.100 is-at fa:09:b5:07:3c:b3, length 28
16:45:37.377501 ARP, Request who-has 1.2.7.101 tell 1.2.7.100, length 28
16:45:37.377506 ARP, Reply 1.2.7.101 is-at 7e:7a:bb:02:72:f8, length 28

[root@node4 ~]# ip netns exec c1 tcpdump -n -i veth1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth1, link-type EN10MB (Ethernet), capture size 262144 bytes
16:46:46.541374 IP 1.2.7.101 > 1.2.7.100: ICMP echo request, id 1984, seq 1, length 64
16:46:46.545927 IP 1.2.7.100 > 1.2.7.101: ICMP echo reply, id 1984, seq 1, length 64
  • 分布式控制中心和自动维护VTEP组
    前文我们可以实现组播找到对端,实现点对点通信,但是有些机器不支持组播通信,那么该如何解决这个问题?两个思路:一个就是说我们在每个节点弄一个agent去手工维护这样一个VTEP的信息,包括arp表(容器mac地址和IP对应关系)、fdb表(VTEP设备IP和mac地址对应关系)。这就是分布式控制中心。另外一个思路就是动态更新这些配置,通过Linux提供的一种事件驱动机制,在需要arp或者fdb信息时,发送事件给订阅的程序,这样容器程序就可以从内核拿到相关信息,然后再维护相关信息,这种方式的好处只维护自己需要的,前者是维护全量的,更加优雅
#程跟前面类似,就是原本自动化过程,变成手工维护了,这里手工是指通过一个agent完成手工配置
#分布式控制中心: 这里就不需要设置remote或者group参数了
ip link add name vxlan0 type vxlan id 100 \
dstport 4789 \
local 192.168.123.111 \
dev ens160 \
nolearning \
proxy 当arp表项在vtep维护时添加这个参数,标识arp请求由vetp提供给我们
#手工添加FDB表项 这个全是0的地址就是默认路由,当其他匹配不到时就会广播到这些地方
bridge fdb append 00:00:00:00:00:00 dev vxlan0 dst 192.168.123.111
bridge fdb append 00:00:00:00:00:00 dev vxlan0 dst 192.168.123.114
bridge fdb append d2:3b:27:d4:00:1b dev vxlan0 dst 192.168.123.111
bridge fdb append 22:ac:ef:a5:aa:8a dev vxlan0 dst 192.168.123.111
#手工arp表项维护有两种方式,一种是在各种namespace空间维护,另外一种就是在vetp设备维护
ip neigh add 1.2.7.101 lladdr 7e:7a:bb:02:72:f8 dev vxlan0
ip neigh add 1.2.7.100 lladdr fa:09:b5:07:3c:b3 dev vxlan0

#自动维护VTEP组 多了l2miss l3miss 参数,这两个参数分别对应arp表项、FDB表项
ip link add name vxlan0 type vxlan id 100 \
dstport 4789 \
local 192.168.123.111 \
dev ens160 \
nolearning \
proxy \
l2miss \
l3miss

#当我们ping 其他容易时就会触发事件,然后下面这个命令就可以观察到这个事件,然后就可以从内核拿到信息手工配置,同上命令
ip monitor all dev vxlan0

Macvlan

  • 作用: 基于物理网卡划分出逻辑上同等地位的网卡设备,然乎提供给给虚拟机或者容器,拥有独立的mac地址和IP地址,宿主机网段和容器网段在同一网段。通过物理网卡拆分,扩展单个节点的二层网络范围。
  • 工作模式:
    • bridge模式
      无须进行mac寻址和生成树协议,直接基于物理网卡,在各种容器内点对点进行流量转发。
    • VEPA模式
      virtual ethernet port aggregate,就是容器之间不直接通信,然后把流量全发给宿主机物理网卡口,再由宿主机网卡帮我们转发到对应容器,所有流量都在宿主机物理网口聚集了。需要外部交换机支持hairpinning(流量源mac地址和目的mac地址相同)
    • private模式
      在VEPA模式上加强了容器的隔离性,同一父mac接口的容器之间无法访问,流量在父接口聚合后也会被丢弃
    • Passthru模式
      直通模式,宿主机网卡只能和一个容器的mac地址绑定,而且大家用一个地址访问
    • Source模式
      容器只接受指定mac源的数据包,其他丢弃
  • 实践:
#本地节点A、B地址为192.168.123.111,192.168.123.114,网关为192.168.123.1
#A节点创建namespace ns1
[root@node1 ~]# ip netns add ns1
#A节点创建macvlan设备 并 添加到ns1,
[root@node1 ~]# ip link add ens160.1 link ens160 type macvlan mode bridge
[root@node1 ~]# ip link set netns ns1 ens160.1
[root@node1 ~]# ip netns exec ns1 ip link set dev ens160.1 up
[root@node1 ~]# ip netns exec ns1 ip addr add 192.168.123.115/24 dev ens160.1
[root@node1 ~]# ip netns exec ns1 ip route add default via 192.168.123.1 dev ens160.1

#在B节点 ping 192.168.123.115,说明流量已经由我们物理网卡转发到ns1的物理网卡
[root@node4 ~]# ping -c 1 192.168.123.115
PING 192.168.123.115 (192.168.123.115) 56(84) bytes of data.
64 bytes from 192.168.123.115: icmp_seq=1 ttl=64 time=1.24 ms
#抓包也可以看到不同的arp报文中115的mac地址
[root@node1 ~]# tcpdump -i ens160 -n host 192.168.123.114
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens160, link-type EN10MB (Ethernet), capture size 262144 bytes
13:52:25.217570 IP 192.168.123.114 > 192.168.123.115: ICMP echo request, id 2164, seq 1, length 64
13:52:25.217730 IP 192.168.123.115 > 192.168.123.114: ICMP echo reply, id 2164, seq 1, length 64
13:52:30.362313 ARP, Request who-has 192.168.123.114 tell 192.168.123.115, length 28
13:52:30.363129 ARP, Reply 192.168.123.114 is-at ce:23:b2:2d:96:36, length 46
13:52:30.363145 ARP, Request who-has 192.168.123.115 tell 192.168.123.114, length 46
13:52:30.363162 ARP, Reply 192.168.123.115 is-at 86:1e:6d:66:5e:33, length 28
#在ns1 ping A物理网卡,无法ping通,对于ns1的网卡来说,宿主机网卡只是作为交换机转发流量
[root@node1 ~]# ip netns exec ns1 ping 192.168.123.111

#在ns1 ping B物理网卡,然后抓包A物理网卡数据,可以看到数据被正确转发
[root@node1 ~]# tcpdump -n -i ens160 dst 192.168.123.114
listening on ens160, link-type EN10MB (Ethernet), capture size 262144 bytes
13:58:21.870829 ARP, Request who-has 192.168.123.114 tell 192.168.123.115, length 28
13:58:21.872126 IP 192.168.123.115 > 192.168.123.114: ICMP echo request, id 17766, seq 1, length 64
13:58:22.927021 IP 192.168.123.115 > 192.168.123.114: ICMP echo request, id 17766, seq 2, length 64


IPvlan

  • 背景: 如果使用macvlan进程节点之间通信,就会分配大量mac地址,但是物理网卡mac地址分配也是有限的,交换机mac路由也是有限的,同时无线网络标准也不支持单机器多mac地址。为了解决这些问题就提出IPvlan。
  • 原理: IPvlan也是跟macvlan一样在物理网卡上划分子接口,只不过子接口跟父接口共用同一个mac地址,IP地址配置是根据clientId在dhcp服务器进行分配。工作模式主要有两种:一种是工作在L2层,这个时候跟macvlan bridge模式很像,子接口和子接口之间能够直接访问,访问外部需要父接口转发。另外一种是工作在L3层,这个时候父接口就是充当路由器,子接口可以在不同网段上。对于外部网络是无法感知到子接口的IP地址,除非明确指明路由,因此子接口也接受不到多播请求。
  • 实践:
#本地节点A、B地址为192.168.123.111,192.168.123.114,网关为192.168.123.1
#A节点创建namespace ns1
[root@node1 ~]# ip netns add ns1
#A节点创建ipvlan设备 并 添加到ns1,
[root@node1 ~]# ip link add ens160.1 link ens160 type ipvlan mode l3
[root@node1 ~]# ip link set netns ns1 ens160.1
[root@node1 ~]# ip netns exec ns1 ip link set dev ens160.1 up
[root@node1 ~]# ip netns exec ns1 ip addr add 192.168.123.115/24 dev ens160.1
[root@node1 ~]# ip netns exec ns1 ip route add default via 192.168.123.1 dev ens160.1

#在A节点 ns1 下 ping B节点,正常返回
[root@node1 ~]# ping 192.168.123.114
PING 192.168.123.114 (192.168.123.114) 56(84) bytes of data.
64 bytes from 192.168.123.114: icmp_seq=1 ttl=64 time=1.65 ms

#在B节点ping ns1网卡,无法ping通,对于外部网络来说,无法感知到子接口网络,需要配置路由规则
[root@node4 ~]# ping 192.168.123.115
PING 192.168.123.115 (192.168.123.115) 56(84) bytes of data.
From 192.168.123.114 icmp_seq=1 Destination Host Unreachable
ip route add 192.168.123.115 dev ens160
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值