1. Flannel是由CoreOS提出的跨主通信容器网络解决方案,通过分配和管理全局唯一容器IP以及实现跨组网络转发的方
    2. 式,构建基于Overlay Network的容器通信网络。作为最早出现的网络编排方案,Flannel是最简单的集群编排方案之
    3. 为容器跨节点通信提供了多种网络连接方式,后续很多插件的方案也是基于Flannel的方案进行扩展。
    4. Flannel的框架包含以下组件:每个节点上的代理服务flanneld,负责为每个主机分配和管理子网;全局的网络配置存
    5. etcd(或K8S API)负责存储主机和容器子网的映射关系;多种网络转发功能的后端实现。
    6. UDP Mode:
    7. UDP Docker网桥模式最相似的实现模式。不同的是,UDP模式在虚拟网桥基础上引入了TUN
    8. flannel0)。TUN设备的特殊性在于它可以把数据包转给创建它的用户空间进程,从而实现内核到用户空间的拷
    9. 贝。在Flannel中,flannel0flanneld进程创建,因此会把容器的数据包转到flanneld,然后由flanneld封包转给宿
    10. 主机发向外部网络。
    11. UDP转发的过程为:Node1Pod-1发起的IP包(目的地址为Node2Pod-2)通过容器网关发到cni0,宿主机根
    12. 据本地路由表将该包转到flannel0,接着发给flanneldFlanneld根据目的容器容器子网与宿主机地址的关系(由
    13. etcd维护)获得目的宿主机地址,然后进行UDP封包,转给宿主机网卡通过物理网络传送到目标节点。在UDP
    14. 据包到达目标节点后,根据对称过程进行解包,将数据传递给目标Pod
    15. UDP模式使用了Flannel自定义的一种包头协议,实现三层网络Overlay网络处理跨主通信的问题。但是由于数据在
    16. 内核和用户态经过了多次拷贝:容器是用户态,cni0flannel0是内核态,flanneld是用户态,最终又要通过内核将
    17. 数据发到外部网络,因此性能损耗较大,对于有数据传输有要求的在线业务并不适用。
    18. Kubernetes CNI Flannel [UDP Mode]

    Kubernetes CNI Flannel Pod - Pod Communication[Different Node][UDP Mode]
    image.png

    1. 运行在用户空间
    2. [root@master ~]# netstat -nlptu | grep flanneld
    3. udp 0 0 192.168.130.10:8285 0.0.0.0:* 22962/flanneld
    4. [root@master ~]# netstat -nlpu | grep flanneld
    5. udp 0 0 192.168.130.10:8285 0.0.0.0:* 22962/flanneld

    Kubernetes CNI Flannel Container - Container Communication[Same Pod]
    image.png
    Container:一个容器直接使用另外一个已经存在容器的网络配置:IP信息和网络端口等所有网络相关的信息都是共享的。需要注意的是:这两个容器的计算和存储资源还是隔离的。kubernetes 的 pod 就是用这个实现的,同一个 pod 中的容器共享一个 network namespace。container网络模式用于容器和容器直接频繁交流的情况.

    Kubernetes CNI Flannel Pod - Pod Communication[Same Node][UDP Mode]
    image.png
    Pod - Pod[Same Node]:通常情况下,在Flannel上解决同节点Pod之间的通信依赖的是Linux Bridge,和我们在Docker中不同的是,在Kubernetes Flannel的环境中使用的Linux Bridge为cni0,而不是原来的docker0。可通过:brctl show查看对应的Linux Bridge的bridge name和interfaces。

    1. [root@node ~]# brctl show
    2. bridge name bridge id STP enabled interfaces
    3. cni0 8000.ceed8853aa9e no veth0e17916d
    4. veth98110d62
    5. docker0 8000.0242e2e0fd6f no

    Kubernetes CNI VETH Pair

    1. The veth devices are virtual Ethernet devices. They can act as tunnels between network namespaces to create a
    2. bridge to a physical network device in another namespace, but can also be used as standalone network devices.
    3. veth devices are always created in interconnected pairs. A pair can be created using the command:
    4. # ip link add <p1-name> type veth peer name <p2-name>
    5. In the above, p1-name and p2-name are the names assigned to the two connected endpoints.
    6. Packets transmitted on one device in the pair are immediately received on the other device. When either devices
    7. is down the link state of the pair is down.
    8. veth device pairs are useful for combining the network facilities of the kernel together in interesting ways. A
    9. particularly interesting use case is to place one end of a veth pair in one network namespace and the other end in
    10. another network namespace, thus allowing communication between network namespaces.
    11. To do this, one can provide the netns parameter when creating the interfaces:
    12. # ip link add <p1-name> netns <p1-ns> type veth peer <p2-name> netns <p2-ns>
    13. ethtool can be used to find the peer of a veth network interfaceusing commands something like:
    14. ethtool -S ve_A
    15. NIC statistics:
    16. peer_ifindex: 16
    17. # ip link | grep '^16:' # Look up interface
    18. 16: ve_B@ve_A: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc ...
    19. Kubernetes CNI Flannel - VETH Pair

    虚拟网卡对:一端是POD里面eth0网卡,一端linux bridge里面的某个接口(这里指的是cni0而不是docker0)
    image.png
    验证1:Node节点验证

    1. kubectl run wang1 --image=burlyluo/nettoolbox创建一个容器
    2. [root@node ~]# kubectl exec -it wang1 bash
    3. kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
    4. bash-5.1# clear
    5. bash-5.1# ethtool -S eth0
    6. NIC statistics:
    7. peer_ifindex: 9

    进入容器查看peer,以及查看linux bridge

    1. [root@node ~]# kubectl exec -it wang1 bash
    2. kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
    3. bash-5.1# clear
    4. bash-5.1# ethtool -S eth0
    5. NIC statistics:
    6. peer_ifindex: 9
    7. [root@node ~]# brctl show
    8. bridge name bridge id STP enabled interfaces
    9. cni0 8000.ceed8853aa9e no veth0e17916d
    10. veth98110d62
    11. docker0 8000.0242e2e0fd6f no

    image.png
    在宿主机上查看,容器中的eth0网卡与宿主机上的bridge cni0形成了veth peer。
    image.png
    验证2:Master节点验证

    1. kubectl taint nodes --all node-role.kubernetes.io/master- 去掉master节点上污点
    2. [root@master ~]# brctl show
    3. bridge name bridge id STP enabled interfaces
    4. cni0 8000.f673f7ea038a no veth6fb5e260
    5. docker0 8000.024225466d00 no
    6. [root@master ~]#
    1. [root@master ~]# ip a
    2. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    3. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    4. inet 127.0.0.1/8 scope host lo
    5. valid_lft forever preferred_lft forever
    6. inet6 ::1/128 scope host
    7. valid_lft forever preferred_lft forever
    8. 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    9. link/ether 00:0c:29:84:78:2e brd ff:ff:ff:ff:ff:ff
    10. inet 192.168.130.10/24 brd 192.168.130.255 scope global noprefixroute ens33
    11. valid_lft forever preferred_lft forever
    12. inet6 fe80::a14c:3318:52d:de89/64 scope link noprefixroute
    13. valid_lft forever preferred_lft forever
    14. 3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    15. link/ether 02:42:25:46:6d:00 brd ff:ff:ff:ff:ff:ff
    16. inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
    17. valid_lft forever preferred_lft forever
    18. 4: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000
    19. link/ether 96:59:4e:51:1f:9b brd ff:ff:ff:ff:ff:ff
    20. 5: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
    21. link/ether ca:b9:51:ea:8f:ee brd ff:ff:ff:ff:ff:ff
    22. inet 10.96.0.10/32 brd 10.96.0.10 scope global kube-ipvs0
    23. valid_lft forever preferred_lft forever
    24. inet 10.96.0.1/32 brd 10.96.0.1 scope global kube-ipvs0
    25. valid_lft forever preferred_lft forever
    26. 6: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
    27. link/ether de:e9:d8:e2:14:65 brd ff:ff:ff:ff:ff:ff
    28. inet 10.244.0.0/32 scope global flannel.1
    29. valid_lft forever preferred_lft forever
    30. inet6 fe80::dce9:d8ff:fee2:1465/64 scope link
    31. valid_lft forever preferred_lft forever
    32. 7: flannel0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN group default qlen 500
    33. link/none
    34. inet 10.244.0.0/32 scope global flannel0
    35. valid_lft forever preferred_lft forever
    36. inet6 fe80::e972:9e58:b895:774/64 scope link flags 800
    37. valid_lft forever preferred_lft forever
    38. 8: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default qlen 1000
    39. link/ether f6:73:f7:ea:03:8a brd ff:ff:ff:ff:ff:ff
    40. inet 10.244.0.1/24 brd 10.244.0.255 scope global cni0
    41. valid_lft forever preferred_lft forever
    42. inet6 fe80::f473:f7ff:feea:38a/64 scope link
    43. valid_lft forever preferred_lft forever
    44. 9: veth6fb5e260@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 qdisc noqueue master cni0 state UP group default
    45. link/ether fe:35:16:e8:6f:18 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    46. inet6 fe80::fc35:16ff:fee8:6f18/64 scope link
    47. valid_lft forever preferred_lft forever

    image.png

    image.png
    真正数据报文路径:
    image.png
    Note:veth pair 试用于不同的network namespace 间进行通信,veth pair 将一个network namespace 数据发往另一个network namespace 的veth。
    模拟两个不同Namespace之前的POD进行通信
    [root@master ~]# ip netns add ns1 创建名称空间1
    [root@master ~]# ip netns add ns2 创建名称空间2
    [root@master ~]# ip link add veth0 type veth peer name veth1 创建一对veth peer,但还没有加入到任何的名称空间中,处于游离状态

    可以通过ifconfig -a 或者 ip -a来查看
    veth0: flags=4098<BROADCAST,MULTICAST>  mtu 1500
            ether 96:52:7a:b3:f5:ef  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
    
    veth1: flags=4098<BROADCAST,MULTICAST>  mtu 1500
            ether 56:ce:8d:a5:ef:af  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
    

    将veth0 veth1分别加入到ns1,ns2中;
    [root@master ~]# ip link set veth0 netns ns1 将veth0加入ns1
    [root@master ~]# ip link set veth1 netns ns2 将veth1加入到ns2
    将网卡加入到namespace中,使用ip -a或ifconfig -a看不到了
    [root@master ~]# ip netns exec ns1 ip a
    [root@master ~]# ip netns exec ns2 ip a

    [root@master ~]# ip netns exec ns1 ip a
    1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    12: veth0@if11: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
        link/ether 96:52:7a:b3:f5:ef brd ff:ff:ff:ff:ff:ff link-netnsid 1
    [root@master ~]# ip netns exec ns2 ip a
    1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    11: veth1@if12: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
        link/ether 56:ce:8d:a5:ef:af brd ff:ff:ff:ff:ff:ff link-netnsid 0
    [root@master ~]#
    

    给两个veth0 veth1配上ip并启动
    [root@master ~]# ip netns exec ns1 ip a a 10.1.1.2/24 dev veth0
    [root@master ~]# ip netns exec ns2 ip a a 10.1.1.3/24 dev veth1
    [root@master ~]# ip netns exec ns1 ip l s veth0 up
    [root@master ~]# ip netns exec ns2 ip l s veth1 up
    image.png

    [root@master ~]# ip netns exec ns1 ethtool -S veth0 查看index
    NIC statistics:
         peer_ifindex: 11
    
    查看不在宿主机root namespace下
    [root@master ~]# ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> 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
        inet6 ::1/128 scope host 
           valid_lft forever preferred_lft forever
    2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
        link/ether 00:0c:29:84:78:2e brd ff:ff:ff:ff:ff:ff
        inet 192.168.130.10/24 brd 192.168.130.255 scope global noprefixroute ens33
           valid_lft forever preferred_lft forever
        inet6 fe80::a14c:3318:52d:de89/64 scope link noprefixroute 
           valid_lft forever preferred_lft forever
    3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
        link/ether 02:42:25:46:6d:00 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
    4: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000
        link/ether 96:59:4e:51:1f:9b brd ff:ff:ff:ff:ff:ff
    5: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default 
        link/ether ca:b9:51:ea:8f:ee brd ff:ff:ff:ff:ff:ff
        inet 10.96.0.10/32 brd 10.96.0.10 scope global kube-ipvs0
           valid_lft forever preferred_lft forever
        inet 10.96.0.1/32 brd 10.96.0.1 scope global kube-ipvs0
           valid_lft forever preferred_lft forever
    6: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default 
        link/ether de:e9:d8:e2:14:65 brd ff:ff:ff:ff:ff:ff
        inet 10.244.0.0/32 scope global flannel.1
           valid_lft forever preferred_lft forever
        inet6 fe80::dce9:d8ff:fee2:1465/64 scope link 
           valid_lft forever preferred_lft forever
    7: flannel0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN group default qlen 500
        link/none 
        inet 10.244.0.0/32 scope global flannel0
           valid_lft forever preferred_lft forever
        inet6 fe80::e972:9e58:b895:774/64 scope link flags 800 
           valid_lft forever preferred_lft forever
    8: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default qlen 1000
        link/ether f6:73:f7:ea:03:8a brd ff:ff:ff:ff:ff:ff
        inet 10.244.0.1/24 brd 10.244.0.255 scope global cni0
           valid_lft forever preferred_lft forever
        inet6 fe80::f473:f7ff:feea:38a/64 scope link 
           valid_lft forever preferred_lft forever
    10: veth6a5fe3d9@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 qdisc noqueue master cni0 state UP group default 
        link/ether 26:dd:aa:d6:0e:e7 brd ff:ff:ff:ff:ff:ff link-netnsid 1
        inet6 fe80::24dd:aaff:fed6:ee7/64 scope link 
           valid_lft forever preferred_lft forever
    
    [root@master ~]# ip netns exec ns2 ip a
    1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    11: veth1@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
        link/ether 56:ce:8d:a5:ef:af brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet 10.1.1.3/24 scope global veth1
           valid_lft forever preferred_lft forever
        inet6 fe80::54ce:8dff:fea5:efaf/64 scope link 
           valid_lft forever preferred_lft forever
    
    [root@master ~]# ip netns exec ns1 ping 10.1.1.3
    PING 10.1.1.3 (10.1.1.3) 56(84) bytes of data.
    64 bytes from 10.1.1.3: icmp_seq=1 ttl=64 time=0.061 ms
    64 bytes from 10.1.1.3: icmp_seq=2 ttl=64 time=0.063 ms
    c64 bytes from 10.1.1.3: icmp_seq=3 ttl=64 time=0.041 ms
    

    使用tcpdump进行抓包分析
    image.png

    [root@master ~]# ip netns exec ns1 tcpdump -n -e -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
    
    13:27:10.608741 96:52:7a:b3:f5:ef > 56:ce:8d:a5:ef:af, ethertype IPv4 (0x0800), length 98: 10.1.1.2 > 10.1.1.3: ICMP echo request, id 39136, seq 1, length 64
    13:27:10.608781 56:ce:8d:a5:ef:af > 96:52:7a:b3:f5:ef, ethertype IPv4 (0x0800), length 98: 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 39136, seq 1, length 64
    13:27:11.610111 96:52:7a:b3:f5:ef > 56:ce:8d:a5:ef:af, ethertype IPv4 (0x0800), length 98: 10.1.1.2 > 10.1.1.3: ICMP echo request, id 39136, seq 2, length 64
    13:27:11.610138 56:ce:8d:a5:ef:af > 96:52:7a:b3:f5:ef, ethertype IPv4 (0x0800), length 98: 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 39136, seq 2, length 64
    13:27:12.609654 96:52:7a:b3:f5:ef > 56:ce:8d:a5:ef:af, ethertype IPv4 (0x0800), length 98: 10.1.1.2 > 10.1.1.3: ICMP echo request, id 39136, seq 3, length 64
    13:27:12.609780 56:ce:8d:a5:ef:af > 96:52:7a:b3:f5:ef, ethertype IPv4 (0x0800), length 98: 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 39136, seq 3, length 64
    13:27:13.611400 96:52:7a:b3:f5:ef > 56:ce:8d:a5:ef:af, ethertype IPv4 (0x0800), length 98: 10.1.1.2 > 10.1.1.3: ICMP echo request, id 39136, seq 4, length 64
    13:27:13.611425 56:ce:8d:a5:ef:af > 96:52:7a:b3:f5:ef, ethertype IPv4 (0x0800), length 98: 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 39136, seq 4, length 64
    13:27:14.611247 96:52:7a:b3:f5:ef > 56:ce:8d:a5:ef:af, ethertype IPv4 (0x0800), length 98: 10.1.1.2 > 10.1.1.3: ICMP echo request, id 39136, seq 5, length 64
    13:27:14.611306 56:ce:8d:a5:ef:af > 96:52:7a:b3:f5:ef, ethertype IPv4 (0x0800), length 98: 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 39136, seq 5, length 64
    13:27:15.612292 96:52:7a:b3:f5:ef > 56:ce:8d:a5:ef:af, ethertype IPv4 (0x0800), length 98: 10.1.1.2 > 10.1.1.3: ICMP echo request, id 39136, seq 6, length 64
    13:27:15.612314 56:ce:8d:a5:ef:af > 96:52:7a:b3:f5:ef, ethertype IPv4 (0x0800), length 98: 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 39136, seq 6, length 64
    

    image.png
    image.png
    通过验证是这两个名称空间的网卡是通过veth peer来进行通信,通过tcpdump进行抓包是通过内核抓包。

    Kubernetes CNI Flannel Pod - Pod Communication[Same Node][UDP Mode]
    image.png
    Pod - Pod[Same Node]:通常情况下,在Flannel上解决同节点Pod之间的通信依赖的是Linux Bridge,和我们在Docker中不同的是,在Kubernetes Flannel的环境中使用的Linux Bridge为cni0,而不是原来的docker0。
    可通过:brctl show查看对应的Linux Bridge的bridge name和interfaces。
    相同节点不同pod如何通信(POD测试)

    [root@node ~]# kubectl get pods -owide 
    NAME                          READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
    bb-5767fb4df4-jsn9s           1/1     Running   0          26s   10.244.1.6   node     <none>           <none>
    cc-6785cd79fd-l5r2s           1/1     Running   0          44m   10.244.0.3   master   <none>           <none>
    nettoolbox-6cd998555d-h48b9   1/1     Running   0          44m   10.244.1.5   node     <none>           <none>
    [root@node ~]# kubectl  exec -it bb-5767fb4df4-jsn9s ping 10.244.1.5 
    PING 10.244.1.5 (10.244.1.5): 56 data bytes
    64 bytes from 10.244.1.5: seq=0 ttl=64 time=0.196 ms
    64 bytes from 10.244.1.5: seq=1 ttl=64 time=0.090 ms
    ^C
    --- 10.244.1.5 ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 0.090/0.143/0.196 ms
    [root@node ~]#
    

    image.png
    抓包

    [root@node ~]# kubectl exec -it bb-5767fb4df4-jsn9s -- tcpdump -n -e -i eth0 
    tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
    
    
    
    
    05:44:41.218336 72:ae:f1:b5:af:dc > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.244.1.5 tell 10.244.1.6, length 28
    05:44:41.218343 72:ae:f1:b5:af:dc > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.244.1.5 tell 10.244.1.6, length 28
    05:44:41.218361 e6:ea:b8:35:c3:35 > 72:ae:f1:b5:af:dc, ethertype ARP (0x0806), length 42: Reply 10.244.1.5 is-at e6:ea:b8:35:c3:35, length 28
    05:44:41.218363 72:ae:f1:b5:af:dc > e6:ea:b8:35:c3:35, ethertype IPv4 (0x0800), length 98: 10.244.1.6 > 10.244.1.5: ICMP echo request, id 3584, seq 0, length 64
    05:44:41.218472 e6:ea:b8:35:c3:35 > 72:ae:f1:b5:af:dc, ethertype IPv4 (0x0800), length 98: 10.244.1.5 > 10.244.1.6: ICMP echo reply, id 3584, seq 0, length 64
    05:44:42.219660 72:ae:f1:b5:af:dc > e6:ea:b8:35:c3:35, ethertype IPv4 (0x0800), length 98: 10.244.1.6 > 10.244.1.5: ICMP echo request, id 3584, seq 1, length 64
    05:44:42.219700 e6:ea:b8:35:c3:35 > 72:ae:f1:b5:af:dc, ethertype IPv4 (0x0800), length 98: 10.244.1.5 > 10.244.1.6: ICMP echo reply, id 3584, seq 1, length 64
    05:44:46.231953 e6:ea:b8:35:c3:35 > 72:ae:f1:b5:af:dc, ethertype ARP (0x0806), length 42: Request who-has 10.244.1.6 tell 10.244.1.5, length 28
    05:44:46.231962 72:ae:f1:b5:af:dc > e6:ea:b8:35:c3:35, ethertype ARP (0x0806), length 42: Reply 10.244.1.6 is-at 72:ae:f1:b5:af:dc, length 28
    

    image.png
    Srcip-10.244.1.6 有>Desip-10.244.1.5有
    Src-mac(72:ae:f1:b5:af:dc)有>Dst-mac未知 (广播macff:ff:ff:ff:ff:ff) ARP会广播出来,谁有这个10.244.1.5。一来一回完成了由arp解析到icmp的整个环。
    image.png
    第二次进行icmp ping测的时候,发现目的mac不是全ffffff的mac地址了
    image.png
    MAC(Media Access Control)地址用来定义网络设备的位置。MAC地址由48比特长、12位的16进制数字组成其中从左到右开始,0到23bit是厂商向IETF等机构申请用来标识厂商的代码(OUI),24到47bit由厂商自行分派,是各个厂商制造的所有网卡的一个唯一编号。
    MAC地址可以分为3种类型:
    ⚫ 物理MAC地址:这种类型的MAC地址唯一的标识了以太网上的一个终端,该地址为全球唯一的硬件地址。
    ⚫ 广播MAC地址:全1的MAC地址(FF-FF-FF-FF-FF-FF),用来表示LAN上的所有终端设备。
    ⚫ 组播MAC地址:除广播地址外,第8bit为1的MAC地址为组播MAC地址(例如01-00-00-00-00-00),用来代表LAN上的一组终端。
    静态MAC地址(Static MAC)
    1、由用户通过命令配置的静态转发的MAC地址,静态MAC地址和动态MAC地址的功能不同 ,静态地址一旦被加入,该地址在删除之前将一直有效,不受最大老化时间的限制。
    2、动态MAC地址(Dynamic MAC)
    由交换机从接受到报文自动学习到的MAC地址,当端口收到一个报文时,会查找报文的源MAC地址是否存 在于MAC地址表中,如果不存在则会将相应的端口、VLAN和源MAC地址关联起来,并保存到MAC地址表中, 动态MAC地址在达到一定老化时间后会被老化删除,但如果该地址在老化时间内被正确使用过,则会重新 激活该条地址的老化时间,同时MAC地址和端口的对应关系会随着设备所连的交换机的端口的变化而变化。
    3、过滤MAC地址、黑洞MAC地址
    由用户通过命令配置的静态过滤的MAC地址,当网关接收到的报文中,源或者目的MAC 地址为过滤MAC地址,则直接丢弃该报文。
    ARP Request Packet Format:
    image.png

    [root@node ~]# brctl showmacs cni0
    port no    mac addr        is local?    ageing timer
      2    4e:8a:b7:f1:4b:05    yes           0.00
      2    4e:8a:b7:f1:4b:05    yes           0.00
      2    6a:00:0a:f2:62:64    no           0.17
      3    6e:ca:62:74:e8:37    yes           0.00
      3    6e:ca:62:74:e8:37    yes           0.00
      3    72:ae:f1:b5:af:dc    no          25.28    POD1
      4    92:8b:7c:e2:04:18    yes           0.00
      4    92:8b:7c:e2:04:18    yes           0.00
      1    ce:26:a9:80:f8:3e    yes           0.00
      1    ce:26:a9:80:f8:3e    yes           0.00
      4    e6:ea:b8:35:c3:35    no          25.28   POD2
      1    fa:8b:31:f4:03:f5    no           0.17
    [root@node ~]#
    

    image.png类似于交换机上面的3口4口。
    基于华为ensp+wireshark抓包工具
    image.png
    image.png

    <Huawei>dis mac-address
    MAC address table of slot 0:
    -------------------------------------------------------------------------------
    MAC Address    VLAN/       PEVLAN CEVLAN Port            Type      LSP/LSR-ID  
                   VSI/SI                                              MAC-Tunnel  
    -------------------------------------------------------------------------------
    5489-98e7-75c0 1           -      -      Eth0/0/1        dynamic   0/-         
    5489-98d4-231c 1           -      -      Eth0/0/2        dynamic   0/-         
    -------------------------------------------------------------------------------
    Total matching items on slot 0 displayed = 2
    

    同节点不同pod之间的通信

    image.png

    
    同节点不同Pod之间的通信:
    /#/ 有一个前提,我们在iconfig中看到的网卡都是在内核级别的,或是说在内核这个层面。
    Pod - Pod[Same Node]:通常情况下,在Flannel上解决同节点Pod之间的通信依赖的是Linux Bridge,和我们在Docker中不同的是,在Kubernetes Flannel的环境中使用的Linux Bridge为cni0,而不是原来的docker0。
    可通过:brctl  show查看对应的Linux Bridge的bridge name和interfaces。
    [root@k8s-1 ~]# brctl show 
    bridge name     bridge id               STP enabled     interfaces
    cni0            8000.1edb12e1c079       no              veth54a9e98a  # cni0 为Linux下的一个虚拟Bridge。
    docker0         8000.0242da0ca579       no
    [root@k8s-1 ~]# 
    此时我们看到一端在ROOT NS中的一个接口,而Pod中的eth0中的接口在Pod所在的namespace中。此时两者之间有通信的需求,何种方案比较合适呢?--- #[veth pair]
    其中veth device(pair)的定义为:
    # The veth devices are virtual Ethernet devices.  They can act as tunnels between network namespaces。topo like below:
         APP                            APP
          |                              |
          |                              |
    kernel ------------------------------ kernel 
          |                              |
    network stack                  network stack 
          |                              |
            |______________________________|
         veth-m                       veth-n
    #
    对于此种模式我们普通Linux中是怎么实现呢?
    #5.2.1:创建 namespace
    ip netns a ns1
    ip netns a ns2
    
    #5.2.2:创建一对 veth-pair veth0 veth1
    ip l a veth0 type veth peer name veth1
    
    #5.2.3:将 veth0 veth1 分别加入两个 ns
    ip l s veth0 netns ns1
    ip l s veth1 netns ns2
    
    #5.2.4:给两个 veth0 veth1 配上 IP 并启用
    ip netns exec ns1 ip a a 10.1.1.2/24 dev veth0
    ip netns exec ns1 ip l s veth0 up
    
    ip netns exec ns2 ip a a 10.1.1.3/24 dev veth1
    ip netns exec ns2 ip l s veth1 up
    
    # veth0 ping veth1
    [root@k8s-1 ~]# ip netns exec ns1 ping 10.1.1.3
    PING 10.1.1.3 (10.1.1.3) 56(84) bytes of data.
    64 bytes from 10.1.1.3: icmp_seq=1 ttl=64 time=0.060 ms
    
    
    /
    
    此时需要弄清楚两个问题:
    1.此时如何知道Pod中的eth0的pair是谁?
    2.此时由Pod-1进入内核,如果想要把数据包转发给另外一个Pod-2?
    #1.使用ethtool -S eth0
    [root@k8s-1 ~]# kubectl exec -it cni-59h6g  bash 
    kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
    bash-5.1# ethtool -S eth0
    NIC statistics:
         peer_ifindex: 6   ##此时在Pod中查看peer的index为6.我们可以在ROOT NS中查看,ifindex为6的网卡。
         rx_queue_0_xdp_packets: 0
         rx_queue_0_xdp_bytes: 0
         rx_queue_0_drops: 0
         rx_queue_0_xdp_redirect: 0
         rx_queue_0_xdp_drops: 0
         rx_queue_0_xdp_tx: 0
         rx_queue_0_xdp_tx_errors: 0
         tx_queue_0_xdp_xmit: 0
         tx_queue_0_xdp_xmit_errors: 0
    bash-5.1# exit
    exit
    [root@k8s-1 ~]# ip a 
    1: lo: <LOOPBACK,UP,LOWER_UP> 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
        inet6 ::1/128 scope host 
           valid_lft forever preferred_lft forever
    2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
        link/ether 00:0c:29:bd:fb:4a brd ff:ff:ff:ff:ff:ff
        inet 172.12.1.11/24 brd 172.12.1.255 scope global noprefixroute ens33
           valid_lft forever preferred_lft forever
        inet6 fe80::e222:32bb:f400:f0c3/64 scope link noprefixroute 
           valid_lft forever preferred_lft forever
    3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
        link/ether 02:42:da:0c:a5:79 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
    4: flannel0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN group default qlen 500
        link/none 
        inet 10.244.0.0/32 brd 10.244.0.0 scope global flannel0
           valid_lft forever preferred_lft forever
        inet6 fe80::9af7:c926:3592:e41f/64 scope link flags 800 
           valid_lft forever preferred_lft forever
    5: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default qlen 1000
        link/ether 1e:db:12:e1:c0:79 brd ff:ff:ff:ff:ff:ff
        inet 10.244.0.1/24 brd 10.244.0.255 scope global cni0
           valid_lft forever preferred_lft forever
        inet6 fe80::1cdb:12ff:fee1:c079/64 scope link 
           valid_lft forever preferred_lft forever
    6: veth54a9e98a@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 qdisc noqueue master cni0 state UP group default   # 这里的ifindex为6.
        link/ether 66:c5:ab:c8:03:4e brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet6 fe80::64c5:abff:fec8:34e/64 scope link 
           valid_lft forever preferred_lft forever
    [root@k8s-1 ~]# 
    [root@k8s-1 ~]# brctl show 
    bridge name     bridge id               STP enabled     interfaces
    cni0            8000.1edb12e1c079       no              veth54a9e98a # 此时该接口是在cni0这个bridge上。
    docker0         8000.0242da0ca579       no
    [root@k8s-1 ~]# 
    
    下边以两个在同一个节点上的两个Pod的情况分析:
    # network topo:
    10.244.1.10  10.244.1.7
        [ns1]      [ns2]
          |          |
          -- [cni0] --
    #
    [root@k8s-1 ~]# kubectl get pods -o wide | grep k8s-2
    cc          1/1     Running   0          32h    10.244.1.10   k8s-2   <none>           <none>
    cni-svtwf   1/1     Running   1          109d   10.244.1.7    k8s-2   <none>           <none>
    [root@k8s-1 ~]# 
    在pod cni-svtwf中去ping cc这个pod。抓包显示为:
    [root@k8s-1 ~]#  kubectl exec -it cni-svtwf  bash 
    kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
    bash-5.1# tcpdump -n -e  -i  eth0      
    tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
    14:31:06.751041 9a:16:45:30:3a:c6 > ee:6f:75:01:ed:bb, ethertype IPv4 (0x0800), length 98: 10.244.1.7 > 10.244.1.10: ICMP echo request, id 17920, seq 0, length 64
    14:31:06.751096 ee:6f:75:01:ed:bb > 9a:16:45:30:3a:c6, ethertype IPv4 (0x0800), length 98: 10.244.1.10 > 10.244.1.7: ICMP echo reply, id 17920, seq 0, length 64
    ^C
    #从抓包可以看出,cni-svtwf 的eth0的网卡的MAC地址为:9a:16:45:30:3a:c6
                    cc 的eth0的网卡的MAC地址为:ee:6f:75:01:ed:bb
    此时我们在cni0 bridge中查看MAC地址表:
    [root@k8s-2 ~]# brctl showmacs cni0
    port no mac addr                is local?       ageing timer
      1     1e:89:b9:2c:44:b1       yes                0.00
      1     1e:89:b9:2c:44:b1       yes                0.00
      3     32:2e:01:1d:a1:53       yes                0.00
      3     32:2e:01:1d:a1:53       yes                0.00
      2     4a:bc:c1:08:30:04       no                 1.70
      2     5e:14:4c:e2:2b:22       yes                0.00
      2     5e:14:4c:e2:2b:22       yes                0.00
      3     9a:16:45:30:3a:c6       no                32.42   # 此地址对应 cni-svtwf的eht0 MAC地址,对应bridge上的端口3.
      4     b2:8e:21:90:45:39       yes                0.00
      4     b2:8e:21:90:45:39       yes                0.00
      1     ce:22:d7:ee:59:7d       no                 1.70
      4     ee:6f:75:01:ed:bb       no                32.42   # 此地址对应cc 的eth0 的MAC地址,对应bridge上的端口4.
    [root@k8s-2 ~]#
    
    #此种Bridge模式把相应的peer建立在pod和bridge之间。此时Linux中又是如何实现呢:
    # network topo:
    10.1.1.2  10.1.1.3
     [ns1]      [ns2]
       |          |
       -- [br0] --
    #
    #创建ns
    ip netns a ns1
    ip netns a ns2
    #首先创建 bridge br0
    ip l a br0 type bridge
    ip l s br0 up 
    
    #然后创建两对 veth-pair
    ip l a veth0 type veth peer name br-veth0
    ip l a veth1 type veth peer name br-veth1
    
    #分别将两对 veth-pair 加入两个 ns 和 br0
    ip l s veth0 netns ns1
    ip l s br-veth0 master br0
    ip l s br-veth0 up
    
    ip l s veth1 netns ns2
    ip l s br-veth1 master br0
    ip l s br-veth1 up
    
    #给两个 ns 中的 veth 配置 IP 并启用
    ip netns exec ns1 ip a a 10.1.1.2/24 dev veth0
    ip netns exec ns1 ip l s veth0 up
    ip netns exec ns2 ip a a 10.1.1.3/24 dev veth1
    ip netns exec ns2 ip l s veth1 up
    # ping 测:
    [root@k8s-2 ~]# ip netns exec ns1 ping 10.1.1.3
    PING 10.1.1.3 (10.1.1.3) 56(84) bytes of data.
    64 bytes from 10.1.1.3: icmp_seq=1 ttl=64 time=0.173 ms
    64 bytes from 10.1.1.3: icmp_seq=2 ttl=64 time=0.068 ms
    ^C
    --- 10.1.1.3 ping statistics ---
    2 packets transmitted, 2 received, 0% packet loss, time 1020ms
    rtt min/avg/max/mdev = 0.068/0.120/0.173/0.053 ms
    
    # 查看bridge br0上的端口:
    [root@k8s-2 ~]# brctl show 
    bridge name     bridge id               STP enabled     interfaces
    br0             8000.8e495ef498dd       no              br-veth0
                                                            br-veth1
    # 查看对应的MAC地址和不同ns中的MAC。
    [root@k8s-2 ~]# brctl showmacs br0
    port no mac addr                is local?       ageing timer
      2     06:b5:78:d9:7b:75       no                16.28      # ns2
      1     1a:7e:30:46:72:11       no                12.18      # ns1
      1     8e:49:5e:f4:98:dd       yes                0.00
      1     8e:49:5e:f4:98:dd       yes                0.00
      2     ca:b6:7e:6:c2:bf       yes                0.00
      2     ca:b6:7e:06:c2:bf       yes                0.00
    [root@k8s-2 ~]#
    

    不同节点的不同pod之前的通信

    物理网卡是硬件网卡,它位于硬件层,虚拟网卡则可以看作是用户空间的网卡,就像用户空间的文件系统(fuse)一样。
    物理网卡和虚拟网卡唯一的不同点在于物理网卡本身的硬件功能:物理网卡以比特流的方式传输数据。也就是说,内
    核会公平对待物理网卡和虚拟网卡,物理网卡能做的配置,虚拟网卡也能做。比如可以为虚拟网卡接口配置IP地址、
    设置子网掩码,可以将虚拟网卡接入网桥等等。只有在数据流经物理网卡和虚拟网卡的那一刻,才会体现出它们的不
    同,即传输数据的方式不同:物理网卡以比特流的方式传输数据,虚拟网卡则直接在内存中拷贝数据(即,在内核之
    间和读写虚拟网卡的程序之间传输)。正因为虚拟网卡不具备物理网卡以比特流方式传输数据的硬件功能,所以,绝
    不可能通过虚拟网卡向外界发送数据,外界数据也不可能直接发送到虚拟网卡上。能够直接收发外界数据的,只能是
    物理设备。
    虽然虚拟网卡无法将数据传输到外界网络,但却:
     可以将数据传输到本机的另一个网卡(虚拟网卡或物理网卡)或其它虚拟设备(如虚拟交换机)上。
     可以在用户空间运行一个可读写虚拟网卡的程序,该程序可将流经虚拟网卡的数据包进行处理,这个用户程序就
    像是物理网卡的硬件功能一样,可以收发数据(可将物理网卡的硬件功能看作是嵌入在网卡上的程序),比如
    OpenVPN和VTun就是这样的工具。
    Note:
    很多人会误解这样的用户空间程序,认为它们可以对数据进行封装。比如认为OpenVPN可以在数据包的基础上再封
    装一层隧道IP首部,但这种理解是错的。
    一定请注意,用户空间的程序是无法对数据包做任何封装和解封操作的,所有的封装和解封都只能由内核的网络协议
    栈来完成。
    使用OpenVPN之所以可对数据再封装一层隧道IP层,是因为OpenVPN可以读取已经封装过一次IP首部的数据,并将
    包含ip首部的数据作为普通数据通过虚拟网卡再次传输给内核。因为内核接收到的是来自虚拟网卡的数据,所以内核
    会将其当作普通数据从头开始封装(从四层封装到二层封装)。当数据从网络协议栈流出时,就有了两层IP首部的封装。
    

    用户空间和内核空间与标准TCP/IP协议栈对应关系:用户空间的程序是无法对数据包做任何封装和解封操作的,所有的封装和解封都只能由内核的网络协议栈来完成。
    image.png
    image.png
    tap/tun 是 Linux 内核 2.4.x 版本之后实现的虚拟网络设备,不同于物理网卡靠硬件板卡实现,tap/tun 虚拟网卡完全由软件实现,功能和硬件实现完全没差别,它们都属于网络设备,都可配置 IP,都归 Linux 网络设备管理模块统一管理。

    tap/tun 提供了一台主机内用户空间的数据传输机制。它虚拟了一套网络接口,这套接口和物理的接口无任何区别,
    可以配置 IP,可以路由流量,不同的是,它的流量只在主机内流通。
    作为网络设备,tap/tun 也需要配套相应的驱动程序才能工作。tap/tun 驱动程序包括两个部分,一个是字符设备驱动,
    一个是网卡驱动。这两部分驱动程序分工不太一样,字符驱动负责数据包在内核空间和用户空间的传送,网卡驱动负
    责数据包在 TCP/IP 网络协议栈上的传输和处理。
    tap/tun 有些许的不同,tun 只操作三层的 IP 包,而 tap 操作二层的以太网帧。
    在 Linux 中,用户空间和内核空间的数据传输有多种方式,字符设备就是其中的一种。tap/tun 通过驱动程序和一个与
    之关联的字符设备,来实现用户空间和内核空间的通信接口。
    在 Linux 内核 2.6.x 之后的版本中,tap/tun 对应的字符设备文件分别为:
     tap:/dev/tap0
     tun:/dev/net/tun
    设备文件即充当了用户空间和内核空间通信的接口。当应用程序打开设备文件时,驱动程序就会创建并注册相应的虚
    拟设备接口,一般以 tunX 或 tapX 命名。当应用程序关闭文件时,驱动也会自动删除 tunX 和 tapX 设备,还会删除已
    经建立起来的路由等信息。
    tap/tun 设备文件就像一个管道,一端连接着用户空间,一端连接着内核空间。当用户程序向文件 /dev/net/tun 或
    /dev/tap0 写数据时,内核就可以从对应的 tunX 或 tapX 接口读到数据,反之,内核可以通过相反的方式向用户程序
    发送数据。
    
    tun和tap都是虚拟网卡设备:
     tun是三层设备,其封装的外层是IP头。
     tap是二层设备,其封装的外层是以太网帧(frame)头。
     tun是PPP点对点设备,没有MAC地址。
     tap是以太网设备,有MAC地址tap比tun更接近于物理网卡,可以认为,tap设备等价于去掉了硬件功能的物理网卡。
    这意味着,如果提供了用户空间的程序去收发tun/tap虚拟网卡的数据,所收发的内容是不同的。
     收发tun设备的用户程序,只能间接提供封装和解封数据包的IP头的功能
     收发tap设备的用户程序,只能间接提供封装和解封数据包的帧头的功能
     注意,此处用词是【收发数据】而非【处理数据】,是【间接提供】而非【直接提供】,因为在不绕过内核网络
    协议栈的情况下,读写虚拟网卡的用户程序是不能封装和解封数据的,只有内核的网络协议栈才能封装和解封数
    据。
    虚拟网卡的两个主要功能是:
     连接其它设备(虚拟网卡或物理网卡)和虚拟交换机(bridge)。
     提供用户空间程序去收发虚拟网卡上的数据
    基于这两个功能,tap设备通常用来连接其它网络设备(它更像网卡),tun设备通常用来结合用户空间程序实现再次封
    装。换句话说,tap设备通常接入到虚拟交换机(bridge)上作为局域网的一个节点,tun设备通常用来实现三层的ip隧道。
    但tun/tap的用法是灵活的,只不过上面两种使用场景更为广泛。例如,除了可以使用tun设备来实现ip层隧道,使用
    tap设备实现二层隧道的场景也颇为常见。
    tun、tap作为虚拟网卡,除了不具备物理网卡的硬件功能外,它们和物理网卡的功能是一样的,此外tun、tap负责在
    内核网络协议栈和用户空间之间传输数据。
    

    程序 A 希望构造数据包发往 192.168.1.0/24 网段的主机 192.168.1.1。
    image.png

    1. 应用程序 X 构造数据包,目的 IP 是 192.168.1.1,通过 socket X 将这个数据包发给协议栈。
    2. 协议栈根据数据包的目的 IP 地址,匹配路由规则,发现要从 tun0 出去。
    3. tun0 发现自己的另一端被应用程序 Y 打开了,于是将数据发给程序 Y.
    4. 程序 Y 收到数据后,做一些跟业务相关的操作,然后构造一个新的数据包,源 IP 是 eth0 的 IP,目的 IP 是 10.1.1.0/24 的网关 10.1.1.1,封装原来的数
    据的数据包,重新发给协议栈。
    5. 协议栈再根据本地路由,将这个数据包从 eth0 发出。
    

    UDP模式下flanneld进程在启动时会通过打开/dev/net/tun的方式生成一个TUN设备(flannel0),TUN设备可以简单理解为Linux当中提供的一种内核网络与用户空间(应用程序)通信的一种机制, TUN设备的特殊性在于它可把数据包转给创建它的用户空间进程,从而实现内核到用户空间的拷贝。即可通过直接读写tun设备的方式收发RAW IP包。
    image.png

    [root@master ~]# netstat -nlptu | grep flanneld
    udp        0      0 192.168.130.10:8285     0.0.0.0:*                           22962/flanneld
    
    flannel0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1472
            inet 10.244.0.0  netmask 255.255.255.255  destination 10.244.0.0
            inet6 fe80::e972:9e58:b895:774  prefixlen 64  scopeid 0x20<link>
            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 3  bytes 144 (144.0 B)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    
    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::dce9:d8ff:fee2:1465  prefixlen 64  scopeid 0x20<link>
            ether de:e9:d8:e2:14:65  txqueuelen 0  (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 8 overruns 0  carrier 0  collisions 0
    
    lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
            inet 127.0.0.1  netmask 255.0.0.0
            inet6 ::1  prefixlen 128  scopeid 0x10<host>
            loop  txqueuelen 1000  (Local Loopback)
            RX packets 464419  bytes 89419120 (85.2 MiB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 464419  bytes 89419120 (85.2 MiB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    
    [root@master ~]# ip -d link show flannel0
    7: flannel0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 500
        link/none  promiscuity 0 
        tun addrgenmode random numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    

    image.png

                                                           不同节点之间POD如何实现通信<br />![](https://cdn.nlark.com/yuque/0/2021/png/5357487/1626966789044-2bd87753-4ff2-4224-a06f-b02054879359.png#crop=0&crop=0&crop=1&crop=1&from=url&id=ttZkt&margin=%5Bobject%20Object%5D&originHeight=796&originWidth=1411&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
    
    不同节点之前POD如何通信
    [root@master ~]# kubectl get pods -owide 
    NAME   READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
    aa     1/1     Running   0          12m   10.244.1.3   node     <none>           <none>
    bb     1/1     Running   0          12m   10.244.0.3   master   <none>           <none>          <none>
    pod-aa10.244.1.3 pod-bb10.244.0.3
    
    aa容器: 路由表为
    bash-5.1# route -n
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    0.0.0.0         10.244.1.1      0.0.0.0         UG    0      0        0 eth0
    10.244.0.0      10.244.1.1      255.255.0.0     UG    0      0        0 eth0
    10.244.1.0      0.0.0.0         255.255.255.0   U     0      0        0 eth0
    路由匹配规则是:
        1.按照最长匹配原则。  
        2.路由优先级  前提是同一网络  对象是不同路由协议。
        3.路由度量  前提是同一网络 同一路由协议  对象是不同开销。
    
    容器aa的mac地址:
    bash-5.1# ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> 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
    3: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default 
        link/ether f6:17:d8:dc:0a:78 brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet 10.244.1.3/24 brd 10.244.1.255 scope global eth0
           valid_lft forever preferred_lft forever
    bash-5.1# 
    
    容器bb的mac地址:
    bash-5.1# ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> 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
    3: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default 
        link/ether fa:f7:7a:f9:7b:d6 brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet 10.244.0.3/24 brd 10.244.0.255 scope global eth0
           valid_lft forever preferred_lft forever
    
    S_ip D_ip S_Mac D_Mac四元组,进行三层转发的时候S_ip跟D_ip不会变,而Mac一直变。
    aa_ip  bb_ip aa_mac (bb_Mac??)
    
    master节点: mac地址为fe80::a8ac:56ff:fe2e:fc3b
    进行抓包ping测
    [root@master ~]# kubectl  exec -it aa ping 10.244.0.3 
    [root@master ~]# kubectl exec -it aa -- tcpdump -n -e -i eth0
    [root@master ~]# kubectl exec -it aa -- tcpdump -n -e -i eth0
    tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
    03:23:56.808045 f6:17:d8:dc:0a:78 > 82:e8:46:c2:89:63, ethertype IPv4 (0x0800), length 98: 10.244.1.3 > 10.244.0.3: ICMP echo request, id 7680, seq 0, length 64
    03:23:56.808478 82:e8:46:c2:89:63 > f6:17:d8:dc:0a:78, ethertype IPv4 (0x0800), length 98: 10.244.0.3 > 10.244.1.3: ICMP echo reply, id 7680, seq 0, length 64
    03:23:57.808217 f6:17:d8:dc:0a:78 > 82:e8:46:c2:89:63, ethertype IPv4 (0x0800), length 98: 10.244.1.3 > 10.244.0.3: ICMP echo request, id 7680, seq 1, length 64
    03:23:57.808557 82:e8:46:c2:89:63 > f6:17:d8:dc:0a:78, ethertype IPv4 (0x0800), length 98: 10.244.0.3 > 10.244.1.3: ICMP echo reply, id 7680, seq 1, length 64
    
    
    目的mac是cni0也就是路由表Gateway的mac地址
    到达宿主机以后,宿主机启动路由表查询过程
    [root@master ~]# route -n 
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    0.0.0.0         10.0.4.1        0.0.0.0         UG    0      0        0 eth0
    10.0.4.0        0.0.0.0         255.255.252.0   U     0      0        0 eth0
    10.244.0.0      0.0.0.0         255.255.255.0   U     0      0        0 cni0
    10.244.0.0      0.0.0.0         255.255.0.0     U     0      0        0 flannel0 
    10.244.1.0      10.244.1.0      255.255.255.0   UG    0      0        0 flannel.1
    169.254.0.0     0.0.0.0         255.255.0.0     U     1002   0        0 eth0
    172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker
    
    
    Flannel0为tun设备,发送给flannel0接口的RAW IP包(无MAC信息)将被flanneld进程接收到,flanneld进程接收到RAW IP包后在原有的基础上进行UDP封包.UDP封包的形式为:src_ip:src port -> 10.0.4.2:8285
    [root@master ~]# netstat -nlptu | grep 8285
    udp        0      0 10.0.4.2:8285           0.0.0.0:*                           14189/flanneld 
    此时在flannel上抓包:
    [root@master ~]# tcpdump -n -e -i flannel.1
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on flannel.1, link-type EN10MB (Ethernet), capture size 262144 bytes
    12:29:11.697201 26:7e:4a:b7:2f:0f > 52:6d:93:c5:85:ae, ethertype IPv4 (0x0800), length 98: 10.244.1.3 > 10.244.0.3: ICMP echo request, id 14848, seq 31, length 64
    12:29:11.697258 52:6d:93:c5:85:ae > 26:7e:4a:b7:2f:0f, ethertype IPv4 (0x0800), length 98: 10.244.0.3 > 10.244.1.3: ICMP echo reply, id 14848, seq 31, length 64
    12:29:12.697382 26:7e:4a:b7:2f:0f > 52:6d:93:c5:85:ae, ethertype IPv4 (0x0800), length 98: 10.244.1.3 > 10.244.0.3: ICMP echo request, id 14848, seq 32, length 64
    12:29:12.697464 52:6d:93:c5:85:ae > 26:7e:4a:b7:2f:0f, ethertype IPv4 (0x0800), length 98: 10.244.0.3 > 10.244.1.3: ICMP echo reply, id 14848, seq 32, length 64
    12:29:13.697570 26:7e:4a:b7:2f:0f > 52:6d:93:c5:85:ae, ethertype IPv4 (0x0800), length 98: 10.244.1.3 > 10.244.0.3: ICMP echo request, id 14848, seq 33, length 64
    12:29:13.697616 52:6d:93:c5:85:ae > 26:7e:4a:b7:2f:0f, ethertype IPv4 (0x0800), length 98: 10.244.0.3 > 10.244.1.3: ICMP echo reply, id 14848, seq 33, length 64
    12:29:14.697680 26:7e:4a:b7:2f:0f > 52:6d:93:c5:85:ae, ethertype IPv4 (0x0800), length 98: 10.244.1.3 > 10.244.0.3: ICMP echo request, id 14848, seq 34, length 64
    12:29:14.697728 52:6d:93:c5:85:ae > 26:7e:4a:b7:2f:0f, ethertype IPv4 (0x0800), length 98: 10.244.0.3 > 10.244.1.3: ICMP echo reply, id 14848, seq 34, length 64
    flanneld在启动时会将该节点的网络信息通过api-server保存到etcd当中,故在发送报文时可以通过查询etcd得到bb这个容器的IP属于10.0.4.2。
    flanneld将封装好的UDP报文从用户空间发往Linux内核协议栈,然后经ens33发出,从这里可以看出网络包在通过ens33发出前先是加上了UDP头(8个字节),再然后加上了IP头(20个字节)进行封装,这也是为什么flannel0的MTU要比ens33的MTU小28个字节的原因(防止封装后的以太网帧超过ens33的MTU而在经过ens33时被丢弃
    此过程中flanneld的作用:
      # UDP封包解包
      # 节点上的路由表的动态更新