Kubernetes CNI 网络(cni0)

  • Kubernetes 通过 CNI(Container Network Interface)规范抽象网络管理,允许第三方插件(如 Flannel、Calico)接管网络。
  • Docker 仅用于运行容器,但网络由 CNI 插件接管。

什么是 veth pair?

  • veth pair(虚拟以太网设备对) 是 Linux 内核提供的一种虚拟网络设备,由两个互联的虚拟网卡组成:
    • 一端在 容器(Pod)的网络命名空间中,命名为 eth0
    • 另一端在 宿主机的根命名空间 中,命名为 vethXXXX(如 veth78b826c3@if2)
kubectl get pod -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
busybox1-857448d9ff-shdfz   1/1     Running   0          84s   10.244.2.2   node02   <none>           <none>
可以看到pod在node02节点上

kubectl exec -it busybox1-857448d9ff-shdfz  -- cat /sys/class/net/eth0/iflink
6  #表明接口的序号为9,然后在node02节点查看接口号

ip link show
6: veth78b826c3@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default
    link/ether da:aa:84:90:7c:a9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
  • 核心作用:
    • 打破网络命名空间的隔离,允许容器与宿主机及其他容器通信。
    • 类似于一根“虚拟网线”,将容器的流量引出到宿主机的网络栈。
  • veth pair 的工作原理
    • 数据流向:
      • 当 Pod 发送数据包时,流量通过 eth0(Pod 内的 veth 端)传输到宿主机的 vethXXXX端。

flannel环境

关于flannel环境我们只讲解VXLAN模型(默认)的流程,host-gw模型不经过封包解包比较简单,想了解的可以查看【flannel】章节

同一node节点上的pod互通

解析案例环境

kubectl get pod -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
busybox1-857448d9ff-shdfz   1/1     Running   0          84s   10.244.2.2   node02   <none>           <none>
busybox2-5c8f48d797-kk8mx   1/1     Running   0          10s   10.244.2.3   node02   <none>           <none>

可以看到busybox1(10.244.2.2)和busybox2(10.244.2.3)在同一个节点,busybox1(10.244.2.2)访问busybox2(10.244.2.3)

图解

image-20250508124143853

通信原理详解

  • busybox1(10.244.2.2)发起请求,目标IP:10.244.2.3
  • 数据包在busybox1中进行路由
ip route
default via 10.244.2.1 dev eth0
10.244.0.0/16 via 10.244.2.1 dev eth0
10.244.2.0/24 dev eth0 scope link  src 10.244.2.2  #目标IP为10.244.2.3,走这条路由到eth0网卡
  • 数据包在busybox1容器的eth0网卡通过veth pair到达veth78b826c3@if2接口
kubectl exec -it busybox1-857448d9ff-shdfz  -- cat /sys/class/net/eth0/iflink
6  #表明接口的序号为6,然后在node02节点查看接口号

ip link show
6: veth78b826c3@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default
    link/ether da:aa:84:90:7c:a9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
  • 数据包从veth78b826c3@if2接口通过网桥到达cni0网桥
验证网桥关系:
brctl show cni0
bridge name    bridge id        STP enabled    interfaces
cni0        8000.22bf3056c6a9    no        veth78b826c3(veth78b826c3@if2接口确实是和cni0网桥是绑定关系)
                                              veth8dcd4c25
  • 数据包从cni0网桥根据目标IP10.244.2.3通过网桥到达veth8dcd4c25@if2接口
验证网桥关系:
brctl show cni0
bridge name    bridge id        STP enabled    interfaces
cni0        8000.22bf3056c6a9    no        veth78b826c3
                                              veth8dcd4c25(veth8dcd4c25@if2接口确实是和cni0网桥是绑定关系)
  • 数据包从veth8dcd4c25@if2接口通过veth pair到达busybox2(10.244.2.3)的eth0网卡
kubectl exec -it busybox2-5c8f48d797-kk8mx   -- cat /sys/class/net/eth0/iflink
7  #表明接口的序号为9,然后在node02节点查看接口号

ip link show
7: veth8dcd4c25@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default
    link/ether 6a:df:3f:58:5d:e0 brd ff:ff:ff:ff:ff:ff link-netnsid 1

不同node节点上的pod互通

解析案例环境

kubectl get pod  -o wide
busybox1-857448d9ff-shdfz   1/1     Running   0          92m   10.244.2.2   node02   <none>           <none>
busybox3-c997b9cc4-tlvrq    1/1     Running   0          8s    10.244.1.2   node01   <none>           <none>
可以看到busybox1(10.244.2.2)和busybox3(10.244.1.2)在同一个节点,busybox1(10.244.2.2)访问busybox3(10.244.1.2)

图解

image-20250508134633948

通信原理详解

  • busybox1(10.244.2.2)发起请求,目标IP:10.244.1.2
  • 数据包在busybox1中进行路由
ip route
default via 10.244.2.1 dev eth0    #目标IP为10.244.1.2,走这条默认路由到eth0网卡
10.244.0.0/16 via 10.244.2.1 dev eth0
10.244.2.0/24 dev eth0 scope link  src 10.244.2.2
  • 数据包在busybox1容器的eth0网卡通过veth pair到达veth78b826c3@if2接口
kubectl exec -it busybox1-857448d9ff-shdfz  -- cat /sys/class/net/eth0/iflink
6  #表明接口的序号为6,然后在node02节点查看接口号

ip link show
6: veth78b826c3@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default
    link/ether da:aa:84:90:7c:a9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
  • 数据包从veth78b826c3@if2接口通过网桥到达cni0网桥
验证网桥关系:
brctl show cni0
bridge name    bridge id        STP enabled    interfaces
cni0        8000.22bf3056c6a9    no        veth78b826c3(veth78b826c3@if2接口确实是和cni0网桥是绑定关系)
                                              veth8dcd4c25
  • 数据包从cni0网桥通过路由到达flannel.1网卡
查看node02路由:
ip route
default via 100.100.137.1 dev ens192 proto static metric 100
10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink  #目标IP为 10.244.1.2,走这条路由到flannel.1网卡
10.244.2.0/24 dev cni0 proto kernel scope link src 10.244.2.1
100.100.137.0/24 dev ens192 proto kernel scope link src 100.100.137.202 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
  • 数据包在 node02 的flannel.1网卡进行 VXLAN 封装之后路由到ens192网卡

    • FDB 表查询: Node02 查询 FDB 表,获取目标 VTEP(10.244.1.0/24)对应的物理 IP(100.100.137.201)和 MAC 地址:

      # 查看 node02 的 FDB 表
      bridge fdb show dev flannel.1
      de:65:a0:34:f0:d6 dst 100.100.137.200 self permanent
      42:88:6a:88:a9:3f dst 100.100.137.201 self permanent   #根据flannel为每个node分配的子网和目标ip进行匹配,找到 10.244.1.0/24 对应的条目是这条
      
    • 封装 VXLAN 数据包:

      • 外层 IP 头:源 IP 100.100.137.202,目标 IP 100.100.137.201。
      • 外层 UDP 头:源端口随机,目标端口 8472(Flannel 默认 VXLAN 端口)。
      • VXLAN 头:VNI(VXLAN Network Identifier)为 1
      • 原始数据包:源 IP 10.244.2.2,目标 IP 10.244.1.2。
    • 封装之后路由到ens192网卡

      查看路由:
      ip route
      default via 100.100.137.1 dev ens192 proto static metric 100
      10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink
      10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink
      10.244.2.0/24 dev cni0 proto kernel scope link src 10.244.2.1
      100.100.137.0/24 dev ens192 proto kernel scope link src 100.100.137.202 metric 100  #封装之后目标IP为100.100.137.201,走这条路由
      172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
      [root@node02 ~]# bridge fdb show dev flannel.1
      de:65:a0:34:f0:d6 dst 100.100.137.200 self permanent
      42:88:6a:88:a9:3f dst 100.100.137.201 self permanent
      
  • 数据包从 node02 的ens192网卡进行物理网络传输到node01的ens192网卡

  • 数据包到达node01的ens192网卡之后进行识别

内核根据 VXLAN Network Identifier (VNI)(默认 1)判断该数据包属于哪个 VXLAN 设备(这里是 flannel.1)。

  • 流量到达 node01 flannel.1的网卡进行VXLAN解封装之后路由到cni0网桥

    • node01 的 flannel.1 接口监听 UDP 端口 8472,接收数据包后:

      • 验证 VNI 是否匹配(1)。
      • 剥离外层 IP、UDP 和 VXLAN 头,得到原始数据包。
      • 根据目标 IP 10.244.1.2,通过路由将数据包交给 cni0 网桥。
      查看路由:
      ip route
      default via 100.100.137.1 dev ens192 proto static metric 100
      10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink
      10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1  #解封装之后目标IP为10.244.1.2,走这条路由到cni0网桥
      10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink
      100.100.137.0/24 dev ens192 proto kernel scope link src 100.100.137.201 metric 100
      172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
      
  • 数据包从cni0网桥到达 busybox3(10.244.1.2)容器

    • cni0 网桥通过 MAC 地址表以网桥方式将数据包转发到 busybox3容器 的 veth42779e36@if2接口
    查看网桥绑定情况:
    brctl show cni0
    bridge name    bridge id        STP enabled    interfaces
    cni0        8000.1a70cf92258b    no            veth42779e36(可以看到是和veth42779e36@if2接口绑定的状态)
    
    • veth42779e36@if2接口通过veth pair到达busybox3(10.244.1.2)容器
    kubectl exec -it busybox3-c997b9cc4-tlvrq   -- cat /sys/class/net/eth0/iflink
    6  #表明接口的序号为6,然后在node01节点查看接口号
    
    ip link show
    6: veth42779e36@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default
        link/ether 26:a9:23:fb:7a:c7 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    

calico环境

关于calico环境我们只讲解IPIP模型(默认)的流程

同一node节点上的pod互通

解析案例环境

kubectl get pod  -o wide
NAME                                READY   STATUS    RESTARTS       AGE   IP               NODE     NOMINATED NODE   READINESS GATES
busybox1-857448d9ff-shdfz           1/1     Running   1 (125m ago)   25h   10.244.140.65    node02   <none>           <none>
busybox2-5c8f48d797-kk8mx           1/1     Running   1 (125m ago)   25h   10.244.140.67    node02   <none>           <none>
可以看到busybox1(10.244.140.65)和busybox2(10.244.140.67)在同一个节点,busybox1(10.244.140.65)访问busybox2(10.244.140.67)

图解

image-20250509135233193

通信原理详解

  • busybox1(10.244.140.65)发起请求,目标IP:10.244.140.67
  • 数据包在busybox1中进行路由
kubectl exec -it busybox1-857448d9ff-shdfz -- sh
ip route
default via 169.254.1.1 dev eth0   #目标IP为10.244.140.67,走这条路由到eth0网卡
169.254.1.1 dev eth0 scope link  

为什么容器内的默认网关是 169.254.1.1?
Calico 会配置一个虚拟的默认网关为 169.254.1.1,这不是实际存在的设备,而是 由 Calico CNI 插件在设置容器网络时写入的路由配置。
  • 数据包在busybox1容器的eth0网卡通过veth pair到达cali16cfa08a6f9@if3接口
kubectl   exec -it  busybox1-857448d9ff-shdfz    -- cat /sys/class/net/eth0/iflink
4 #表明接口的序号为4,然后在node02节点查看接口号

4: cali16cfa08a6f9@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0
  • 数据包从cali16cfa08a6f9@if3接口通过路由到calibc123ec5773@if4接口
ip route
default via 100.100.137.1 dev ens192 proto static metric 100
blackhole 10.244.140.64/26 proto bird
10.244.140.65 dev cali16cfa08a6f9 scope link
10.244.140.66 dev caliae3c1d6879e scope link
10.244.140.67 dev calibc123ec5773 scope link   #目标IP为10.244.140.67,走这条路由
10.244.196.128/26 via 100.100.137.201 dev tunl0 proto bird onlink
10.244.241.64/26 via 100.100.137.200 dev tunl0 proto bird onlink
100.100.137.0/24 dev ens192 proto kernel scope link src 100.100.137.202 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
  • 数据包从calibc123ec5773@if4接口通过veth pair到busybox2容器
kubectl   exec -it  busybox2-5c8f48d797-kk8mx   -- cat /sys/class/net/eth0/iflink
7
7: calibc123ec5773@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 2

不同node节点上的pod互通

解析案例环境

kubectl exec -it busybox1-857448d9ff-shdfz -- sh
kubectl get pod  -o wide
busybox1-857448d9ff-shdfz           1/1     Running   1 (174m ago)   26h   10.244.140.65    node02   <none>           <none>
busybox3-c997b9cc4-tlvrq            1/1     Running   1 (175m ago)   25h   10.244.196.131   node01   <none>           <none>
可以看到两个pod在不同的节点,busybox1(10.244.140.65)访问busybox3(10.244.196.131)

图解

image-20250509143008475

通信原理详解

  • busybox1(10.244.140.65)发起请求,目标IP:10.244.196.131
  • 数据包在busybox1中进行路由
ip route
default via 169.254.1.1 dev eth0   #目标IP为10.244.196.131,走这条路由到eth0网卡
169.254.1.1 dev eth0 scope link  

为什么容器内的默认网关是 169.254.1.1?
Calico 会配置一个虚拟的默认网关为 169.254.1.1,这不是实际存在的设备,而是 由 Calico CNI 插件在设置容器网络时写入的路由配置。
  • 数据包在busybox1容器的eth0网卡通过veth pair到达cali16cfa08a6f9@if3接口
kubectl   exec -it  busybox1-857448d9ff-shdfz    -- cat /sys/class/net/eth0/iflink
4 #表明接口的序号为4,然后在node02节点查看接口号

4: cali16cfa08a6f9@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0
  • 数据包从cali16cfa08a6f9@if3接口通过路由到达tunl0网卡
ip route
default via 100.100.137.1 dev ens192 proto static metric 100
blackhole 10.244.140.64/26 proto bird
10.244.140.65 dev cali16cfa08a6f9 scope link
10.244.140.66 dev caliae3c1d6879e scope link
10.244.140.67 dev calibc123ec5773 scope link
10.244.196.128/26 via 100.100.137.201 dev tunl0 proto bird onlink   #目标IP为10.244.196.131,走这条路由到达tunl0网卡
10.244.241.64/26 via 100.100.137.200 dev tunl0 proto bird onlink
100.100.137.0/24 dev ens192 proto kernel scope link src 100.100.137.202 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
  • 数据包在tunl0网卡进行IPIP隧道封装

    • 进行路由查询获取目标IP(10.244.196.131)对应的node IP (100.100.137.201)
    ip route
    default via 100.100.137.1 dev ens192 proto static metric 100
    blackhole 10.244.140.64/26 proto bird
    10.244.140.65 dev cali16cfa08a6f9 scope link
    10.244.140.66 dev caliae3c1d6879e scope link
    10.244.140.67 dev calibc123ec5773 scope link
    10.244.196.128/26 via 100.100.137.201 dev tunl0 proto bird onlink   #目标IP为10.244.196.131,走这条路由,可以看到网关为:100.100.137.201
    10.244.241.64/26 via 100.100.137.200 dev tunl0 proto bird onlink
    100.100.137.0/24 dev ens192 proto kernel scope link src 100.100.137.202 metric 100
    172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
    
    • 封装数据包:
      • 外层 IP 头: 源 IP 100.100.137.202,目标 IP 100.100.137.201。
      • 内层原始包: 源IP 10.244.140.65, 目标IP 10.244.196.131 。
      • 隧道设备: tunl0(MTU=1480)
      • 协议:ipip-proto-4
  • 数据包经过tunl0封装之后路由到ens192网卡

ip route
default via 100.100.137.1 dev ens192 proto static metric 100
blackhole 10.244.140.64/26 proto bird
10.244.140.65 dev cali16cfa08a6f9 scope link
10.244.140.66 dev caliae3c1d6879e scope link
10.244.140.67 dev calibc123ec5773 scope link
10.244.196.128/26 via 100.100.137.201 dev tunl0 proto bird onlink
10.244.241.64/26 via 100.100.137.200 dev tunl0 proto bird onlink
100.100.137.0/24 dev ens192 proto kernel scope link src 100.100.137.202 metric 100  #数据包封装之后目标ip为100.100.137.201,走这条路由
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
  • 数据包从node02的ens192网卡传输到node01的ens192网卡
  • 数据包到达 node01 的ens192网卡自动送到 node01的tunl0网卡
为什么数据包会自动送到 tunl0?
IPIP 协议绑定:
tunl0 设备在内核注册为 IPIP 隧道的端点,所有协议号为 4 的 IP 包会自动递交给它。
类似 HTTP 流量交给 80 端口的服务。
  • 数据包到达 node01tunl0网卡进行解封装之后通过路由到达calibd1d74ace38@if4接口

    • 剥离外层封装,得到原始数据包。
    • 根据目标 IP 10.244.196.131 ,通过路由将数据包交给calibd1d74ace38@if4接口 。
    ip route
    default via 100.100.137.1 dev ens192 proto static metric 100
    10.244.140.64/26 via 100.100.137.202 dev tunl0 proto bird onlink
    blackhole 10.244.196.128/26 proto bird
    10.244.196.129 dev cali48e9a291abc scope link
    10.244.196.130 dev calif738bc18da9 scope link
    10.244.196.131 dev calibd1d74ace38 scope link   #解封装之后真实目标IP为10.244.196.131,走此条路由
    10.244.241.64/26 via 100.100.137.200 dev tunl0 proto bird onlink
    100.100.137.0/24 dev ens192 proto kernel scope link src 100.100.137.201 metric 100
    172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
    
    • 数据包从calibd1d74ace38@if4接口通过veth pair交给到busybox3容器
    为什么交给busybox3容器?
    kubectl exec -it busybox3-c997b9cc4-tlvrq  -- cat /sys/class/net/eth0/iflink
    7 #表名接口的序号为7,然后在node01节点查看接口号
    ip link show
    7: calibd1d74ace38@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default
        link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 2
    

results matching ""

    No results matching ""