Calico
跨主机 Pod
之间链路跟踪kube-proxy
使用 iptables
的 链路跟踪Flannel
部分放到下一篇讲解在童年期,我们更多是处于认知,而不是意欲的状态。—— 《作为意欲和表象的世界》第二卷第三十一章
在 K8s
的环境中。 CNI
常用的有 Flannel
和 Calico
,今天我们来看看 Flannel
和 Calico
是如何实现跨主机容器组网以及服务发布负载网络策略的。
简单总结:
在跨主机的容器网络方案中,Flannel
是使用 VXLAN
后端的覆盖网络(Overlay Network)
,基于L3
构建的L2
,可以单纯理解为通过三层UDP构建隧道传递以太网帧,而 Calico
是一个纯三层的路由网络模型(Route-based Network)
,利用Linux 内核技术 iptables
和路由表
,IPIP
,BGP协议
来模拟传统的路由组网方案。
只有 Calico
支持ACL网络策略,可以配置 Pod 级别的 白名单黑名单,基于 iptables
实现。
在讨论下面两个 CNI
前,我们需要先了解一些基本名词
VETH(Virtual Ethernet)对
是一种成对的网络设备,它们总是成对出现,一端连着网络命名空间(例如一个容器),另一端连着另一个网络命名空间。两个设备同时 UP
同时 DOWN
,彼此之间通过内核软链接实现(类似虚拟的网线),常用于两个命名空间通信,或者一个网络命名空间和网桥通信。
docker
在默认网络驱动(桥接)的情况下,即经典容器组网,veth 对 +网桥
模式,基于这个特性实现单机 docker
的多个容器组网,安装 docker
会默认创建一个 桥接设备 docker0
。
容器AB
之间的通信即 容器 A
到 网桥 docker0
,网桥 docerk0
在到容器 B
,网桥这里可以理解为一个交换机,可以实现子网中任意网段互通。 容器到 网桥的连接即通过 veth 对
实现,一端在容器对应网络命名空间,一端在网桥。
当容器 A
尝试与容器 B
通信时,它发送的数据包首先会被发送到与容器 A 相连的 VETH
设备的 docker0 网桥一端。
容器和主机之间通信也是同理,容器网络命名空间
和主机的根网络命名空间
之间通过网桥通信。
上面我们有讲到网桥设备,它充当着交换机的角色,负责将数据包从一个端口转发到另一个端口。
当数据包到达网桥时,网桥会根据目标IP地址,转发到对应的 端口,所以 在安装 docker 的时候,会默认创建一个 docker0
的网桥,同时需要修改内核参数,开启 ipv4 转发
桥接设备(Bridge Device)
是在网络层次结构中工作的二层设备(Data Link Layer)
,它主要用于连接多个网络设备。桥接设备通过学习和转发数据帧
的方式,将连接到它的网络设备组成一个共享的以太网段
,使得这些设备可以直接通信。桥接设备工作在数据链路层(第二层)
,它不涉及 IP 地址或路由。基于 MAC 地址构建地址转发表
。
VETH对 + 网桥 组网 Demo
看一个实际的 Demo,使用Linux内部网桥
实用程序创建网桥(vnet-br0)
,创建红色
和绿色
两个网络名称空间
。为red
和green
命名空间创建两个veth虚拟网卡对
,将veth对
的一端连接到特定的命名空间
,另一端连接到内部网桥,确保红色
和绿色
命名空间中的接口可以于网桥(vnet-bro)
与内部和外部网络通信。
创建两个网络命名空间,创建网桥vnet-br0
。
┌──[root@liruilongs.github.io]-[~]
└─$ip netns add red
┌──[root@liruilongs.github.io]-[~]
└─$ip netns add green
用于在 Linux 上创建一个名为 vnet-br0
的桥接设备
。
┌──[root@liruilongs.github.io]-[~]
└─$ip link add vnet-br0 type bridge
添加虚拟网卡对eth0-r 和 veth-r
、eth0-g 和 veth-g
┌──[root@liruilongs.github.io]-[~]
└─$ip link add eth0-r type veth peer name veth-r
┌──[root@liruilongs.github.io]-[~]
└─$ip link add eth0-g type veth peer name veth-g
把两个虚拟网卡对中的一端放到上面创建的网络命名空间
┌──[root@liruilongs.github.io]-[~]
└─$ip link set eth0-r netns red
┌──[root@liruilongs.github.io]-[~]
└─$ip link set eth0-g netns green
然后将虚拟网卡对的另一端连接到vnet-br0
桥。
┌──[root@liruilongs.github.io]-[~]
└─$ip link set veth-r master vnet-br0
┌──[root@liruilongs.github.io]-[~]
└─$ip link set veth-g master vnet-br0
查看根网络命名空间的桥接设备类型的网络设备(桥接表)。
┌──[root@liruilongs.github.io]-[~]
└─$ip link show type bridge
5: vnet-br0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 62:2b:41:f9:39:b3 brd ff:ff:ff:ff:ff:ff
查看桥接设备(vnet-br0)关联的网络设备。
┌──[root@liruilongs.github.io]-[~]
└─$ip link show master vnet-br0
6: veth-r@if7: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master vnet-br0 state DOWN mode DEFAULT group default qlen 1000
link/ether 62:2b:41:f9:39:b3 brd ff:ff:ff:ff:ff:ff link-netns red
8: veth-g@if9: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master vnet-br0 state DOWN mode DEFAULT group default qlen 1000
link/ether be:a3:9a:1c:a1:06 brd ff:ff:ff:ff:ff:ff link-netns green
根据输出,有两个网络设备与 vnet-br0 桥接设备关联:
veth-r@if7
:这是一个虚拟网络设备(veth pair),它与 vnet-br0 桥接设备关联。它的状态是 DOWN,表示当前处于未激活状态。它的 MAC 地址为 62:2b:41:f9:39:b3。此设备属于 red 网络命名空间。
veth-g@if9
:这是另一个虚拟网络设备(veth pair),也与 vnet-br0 桥接设备关联。它的状态是 DOWN,表示当前处于未激活状态。它的 MAC 地址为 be:a3:9a:1c:a1:06。此设备属于 green 网络命名空间。
激活桥接对应的网络设备
┌──[root@liruilongs.github.io]-[~]
└─$ip link set vnet-br0 up
┌──[root@liruilongs.github.io]-[~]
└─$ip link set veth-r up
┌──[root@liruilongs.github.io]-[~]
└─$ip link set veth-g up
激活 网络命名空间中的回环地址和对应的虚拟网卡对
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec red ip link set lo up
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec red ip link set eth0-r up
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec green ip link set lo up
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec green ip link set eth0-g up
ip link
确认设备状态
┌──[root@liruilongs.github.io]-[~]
└─$ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:93:51:67 brd ff:ff:ff:ff:ff:ff
altname enp3s0
5: vnet-br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 62:2b:41:f9:39:b3 brd ff:ff:ff:ff:ff:ff
6: veth-r@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master vnet-br0 state UP mode DEFAULT group default qlen 1000
link/ether 62:2b:41:f9:39:b3 brd ff:ff:ff:ff:ff:ff link-netns red
8: veth-g@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master vnet-br0 state UP mode DEFAULT group default qlen 1000
link/ether be:a3:9a:1c:a1:06 brd ff:ff:ff:ff:ff:ff link-netns green
进入网络命名空间 shell
环境,分配 IP, 给 red
命名空间分配IP 地址 192.168.20.2/24
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec red bash
┌──[root@liruilongs.github.io]-[~]
└─$ip address add 192.168.20.2/24 dev eth0-r
查看路由信息
┌──[root@liruilongs.github.io]-[~]
└─$ip r
# 对于目标网络 192.168.20.0/24 的数据包,它们将使用 eth0-r 设备进行本地通信。
192.168.20.0/24 dev eth0-r proto kernel scope link src 192.168.20.2
┌──[root@liruilongs.github.io]-[~]
└─$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
7: eth0-r@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether ca:b0:b2:80:25:43 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.20.2/24 scope global eth0-r
valid_lft forever preferred_lft forever
inet6 fe80::c8b0:b2ff:fe80:2543/64 scope link
valid_lft forever preferred_lft forever
┌──[root@liruilongs.github.io]-[~]
└─$exit
exit
对另一个命名空间操作,给 green
命名空间分配IP 地址 192.168.20.3/24
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec green bash
┌──[root@liruilongs.github.io]-[~]
└─$ip address add 192.168.20.3/24 dev eth0-g
┌──[root@liruilongs.github.io]-[~]
└─$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
9: eth0-g@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 36:5e:d9:8d:04:a8 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.20.3/24 scope global eth0-g
valid_lft forever preferred_lft forever
inet6 fe80::345e:d9ff:fe8d:4a8/64 scope link
valid_lft forever preferred_lft forever
┌──[root@liruilongs.github.io]-[~]
└─$ip r
192.168.20.0/24 dev eth0-g proto kernel scope link src 192.168.20.3
┌──[root@liruilongs.github.io]-[~]
└─$exit
exit
两个命名空间之间的连通性测试
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec green bash
┌──[root@liruilongs.github.io]-[~]
└─$ping -c 2 192.168.20.2
PING 192.168.20.2 (192.168.20.2) 56(84) bytes of data.
64 bytes from 192.168.20.2: icmp_seq=1 ttl=64 time=0.252 ms
64 bytes from 192.168.20.2: icmp_seq=2 ttl=64 time=0.047 ms
--- 192.168.20.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1034ms
rtt min/avg/max/mdev = 0.047/0.149/0.252/0.102 ms
┌──[root@liruilongs.github.io]-[~]
└─$exit
exit
我们在上面把两个命名空间的 veth pair
都配置到了网桥,所以可以直接通信。
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec red bash
┌──[root@liruilongs.github.io]-[~]
└─$ping -c 2 192.168.20.3
PING 192.168.20.3 (192.168.20.3) 56(84) bytes of data.
64 bytes from 192.168.20.3: icmp_seq=1 ttl=64 time=0.241 ms
64 bytes from 192.168.20.3: icmp_seq=2 ttl=64 time=0.129 ms
--- 192.168.20.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1038ms
rtt min/avg/max/mdev = 0.129/0.185/0.241/0.056 ms
┌──[root@liruilongs.github.io]-[~]
└─$exit
exit
将 IP 192.168.20.1/24
分配给根网络命名空间中的 vnet-br0
桥接口,以允许来自红色和绿色名称空间的外部通信,它将成为该网络的默认网关(这里很重要哦)
┌──[root@liruilongs.github.io]-[~]
└─$ip address add 192.168.20.1/24 dev vnet-br0
将192.168.20.1
配置为绿色和红色命名空间中的默认网关
。将所有目标不在本地网络中的数据包发送到该网关进行进一步路由。
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec red bash
┌──[root@liruilongs.github.io]-[~]
└─$route add default gw 192.168.20.1
┌──[root@liruilongs.github.io]-[~]
└─$exit
exit
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec green bash
┌──[root@liruilongs.github.io]-[~]
└─$route add default gw 192.168.20.1
┌──[root@liruilongs.github.io]-[~]
└─$exit
exit
即对应的网络命名空间内的流量都路由到网关,即网桥设备。
在 NAT 表
中添加一个规则,将源 IP 地址为 192.168.20.0/24
的数据包进行源地址转换 (Source NAT)
,即 SNAT,否则可以出去但是找不到回家的路。
┌──[root@liruilongs.github.io]-[~]
└─$iptables -s 192.168.20.0/24 -t nat -A POSTROUTING -j MASQUERADE
根命名空间
做内网和公网地址Ping
测
┌──[root@liruilongs.github.io]-[~]
└─$ping 192.169.26.149 -c 3
PING 192.169.26.149 (192.169.26.149) 56(84) bytes of data.
64 bytes from 192.169.26.149: icmp_seq=1 ttl=128 time=199 ms
64 bytes from 192.169.26.149: icmp_seq=2 ttl=128 time=199 ms
64 bytes from 192.169.26.149: icmp_seq=3 ttl=128 time=216 ms
--- 192.169.26.149 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 199.020/204.755/215.909/7.888 ms
┌──[root@liruilongs.github.io]-[~]
└─$ping baidu.com -c 3
PING baidu.com (39.156.66.10) 56(84) bytes of data.
64 bytes from 39.156.66.10 (39.156.66.10): icmp_seq=1 ttl=128 time=11.9 ms
64 bytes from 39.156.66.10 (39.156.66.10): icmp_seq=2 ttl=128 time=11.9 ms
64 bytes from 39.156.66.10 (39.156.66.10): icmp_seq=3 ttl=128 time=12.1 ms
--- baidu.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 11.919/11.999/12.142/0.100 ms
在主机系统上启用IPV4转发以允许外部通信。执行该命令后,系统将开启 IP 转发功能,允许数据包在不同的网络接口之间进行转发。
┌──[root@liruilongs.github.io]-[~]
└─$sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
在两个命名空间中做内网 ping
测试
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec green ping baidu.com -c 3
PING baidu.com (110.242.68.66) 56(84) bytes of data.
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=1 ttl=127 time=20.5 ms
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=2 ttl=127 time=20.0 ms
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=3 ttl=127 time=20.3 ms
--- baidu.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 20.031/20.261/20.475/0.181 ms
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec red ping baidu.com -c 3
PING baidu.com (110.242.68.66) 56(84) bytes of data.
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=1 ttl=127 time=20.2 ms
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=2 ttl=127 time=20.3 ms
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=3 ttl=127 time=20.1 ms
--- baidu.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 20.085/20.197/20.278/0.082 ms
在两个命名空间中做内网 ping
测试
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec red ping 192.168.26.149 -c 3
PING 192.168.26.149 (192.168.26.149) 56(84) bytes of data.
64 bytes from 192.168.26.149: icmp_seq=1 ttl=64 time=0.241 ms
64 bytes from 192.168.26.149: icmp_seq=2 ttl=64 time=0.110 ms
64 bytes from 192.168.26.149: icmp_seq=3 ttl=64 time=0.075 ms
--- 192.168.26.149 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2074ms
rtt min/avg/max/mdev = 0.075/0.142/0.241/0.071 ms
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec green ping 192.168.26.149 -c 3
PING 192.168.26.149 (192.168.26.149) 56(84) bytes of data.
64 bytes from 192.168.26.149: icmp_seq=1 ttl=64 time=0.258 ms
64 bytes from 192.168.26.149: icmp_seq=2 ttl=64 time=0.097 ms
64 bytes from 192.168.26.149: icmp_seq=3 ttl=64 time=0.094 ms
--- 192.168.26.149 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2043ms
rtt min/avg/max/mdev = 0.094/0.149/0.258/0.076 ms
┌──[root@liruilongs.github.io]-[~]
└─$
拓扑图
到这里,我们实现和两个网络命名空间彼此通信,并且和根命名空间通信,同时可以和公网通信,实际上上面讲的也就是 docker 中容器的经典组网模型(veth pair + Bridge)
简单回顾一下我们干了什么:
Linux 网桥
,创建两个 Linux 网络命名空间
veth pair
,将其中一个端口连接到根命名空间中的网桥上,另一个端口放置在目标命名空间中。net.ipv4.ip_forward=1
),给网桥分配IP地址,同时在命名空间配置默认网关地址为网桥地址。NAT
规则 SNAT,将目标命名空间中的流量转发的源IP地址转化为根命名空间中的IP地址。ipip
:即IPv4 in IPv4,没什么好说的 在IPv4报文的基础上封装一个IPv4报文,是 Linux L3
隧道的一种,底层实现原理都基于tun
设备
看一个 ipip隧道通信 Demo
通过 Linux 上的两个 network namespace
来模拟两个机器节点,每个 network namespce
是一个独立的网络栈
要使用ipip
隧道,首先需要内核模块ipip.ko
的支持。通过lsmod|grep ipip
查看内核是否加载,若没有则用modprobe ipip
加载,正常加载应该显示
liruilonger@cloudshell:~$ lsmod | grep ipip
liruilonger@cloudshell:~$ sudo modprobe ipip
liruilonger@cloudshell:~$ modprobe ipip
liruilonger@cloudshell:~$ lsmod | grep ipip
ipip 16384 0
ip_tunnel 28672 1 ipip
tunnel4 16384 1 ipip
加载ipip
内核模块后,就可以创建隧道了。方法是先创建一个tun
设备,然后将该tun
设备绑定为一个ipip
隧道。ipip
隧道网络拓扑如图
在这里插入图片描述
这里我们用两个 Linux network namespace
来模拟 ,创建两个网络命名空间,同时配置两个 veth pair
,一端放到命名空间
liruilonger@cloudshell:~$ sudo ip netns add ns1
liruilonger@cloudshell:~$ sudo ip netns add ns2
liruilonger@cloudshell:~$ sudo ip link add v1 netns ns1 type veth peer name v1-P
liruilonger@cloudshell:~$ sudo ip link add v2 netns ns2 type veth peer name v2-P
确认创建的 veth pair
liruilonger@cloudshell:~$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 0a:10:50:88:eb:09 brd ff:ff:ff:ff:ff:ff link-netnsid 0
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
link/ether 02:42:f9:2d:29:3e brd ff:ff:ff:ff:ff:ff
4: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
5: v1-P@if3: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 76:81:b0:33:e4:2b brd ff:ff:ff:ff:ff:ff link-netns ns1
6: v2-P@if3: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether aa:a6:ac:15:b1:64 brd ff:ff:ff:ff:ff:ff link-netns ns2
另一端放到 根网络命名空间,同时两个Veth-pair
配置不同网段IP启动。
liruilonger@cloudshell:~$ sudo ip addr add 10.10.10.1/24 dev v1-P
liruilonger@cloudshell:~$ sudo ip link set v1-P up
liruilonger@cloudshell:~$ sudo ip addr add 10.10.20.1/24 dev v2-P
liruilonger@cloudshell:~$ sudo ip link set v2-P up
命名空间一端的同样配置IP 并启用
liruilonger@cloudshell:~$ sudo ip netns exec ns1 ip addr add 10.10.10.2/24 dev v1
liruilonger@cloudshell:~$ sudo ip netns exec ns2 ip addr add 10.10.20.2/24 dev v2
liruilonger@cloudshell:~$ sudo ip netns exec ns1 ip link set v1 up
liruilonger@cloudshell:~$ sudo ip netns exec ns2 ip link set v2 up
确定设备在线
liruilonger@cloudshell:~$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 0a:10:50:88:eb:09 brd ff:ff:ff:ff:ff:ff link-netnsid 0
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
link/ether 02:42:f9:2d:29:3e brd ff:ff:ff:ff:ff:ff
4: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
5: v1-P@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 76:81:b0:33:e4:2b brd ff:ff:ff:ff:ff:ff link-netns ns1
6: v2-P@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether aa:a6:ac:15:b1:64 brd ff:ff:ff:ff:ff:ff link-netns ns2
liruilonger@cloudshell:~$
调整内核参数,开启 ipv4
转发
liruilonger@cloudshell:~$ cat /proc/sys/net/ipv4/ip_forward
1
这个时候,Linux 网络命名空间中的 v1 和 v2 veth 任然不通,应为是在两个不同的网段。
liruilonger@cloudshell:~$ sudo ip netns exec ns1 route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
10.10.10.0 0.0.0.0 255.255.255.0 U 0 0 0 v1
查看路由信息,没有通向 10.10.20.0/24网段的路由
所以我们在 ns1 里面配置一条路由,通向 10.10.20.0
的访问路由到 10.10.10.1
网关,实际上是 veth pair 的另一端。
liruilonger@cloudshell:~$ sudo ip netns exec ns1 route add -net 10.10.20.0 netmask 255.255.255.0 gw 10.10.10.1
再查看路由表
liruilonger@cloudshell:~$ sudo ip netns exec ns1 route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
10.10.10.0 0.0.0.0 255.255.255.0 U 0 0 0 v1
10.10.20.0 10.10.10.1 255.255.255.0 UG 0 0 0 v1
liruilonger@cloudshell:~$
同理,也给ns2
配上通往10.10.10.0/24
网段的路由。
liruilonger@cloudshell:~$ sudo ip netns exec ns2 route add -net 10.10.10.0 netmask 255.255.255.0 gw 10.10.20.1
liruilonger@cloudshell:~$ sudo ip netns exec ns2 route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
10.10.10.0 10.10.20.1 255.255.255.0 UG 0 0 0 v2
10.10.20.0 0.0.0.0 255.255.255.0 U 0 0 0 v2
liruilonger@cloudshell:~$
这时候我们在 ns1 和 ns2 之间做ping 测试,正常通信
liruilonger@cloudshell:~$ sudo ip netns exec ns1 ping -c 3 10.10.20.2
PING 10.10.20.2 (10.10.20.2) 56(84) bytes of data.
64 bytes from 10.10.20.2: icmp_seq=1 ttl=63 time=0.092 ms
64 bytes from 10.10.20.2: icmp_seq=2 ttl=63 time=0.057 ms
64 bytes from 10.10.20.2: icmp_seq=3 ttl=63 time=0.053 ms
--- 10.10.20.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2036ms
rtt min/avg/max/mdev = 0.053/0.067/0.092/0.017 ms
liruilonger@cloudshell:~$ sudo ip netns exec ns2 ping -c 3 10.10.10.2
PING 10.10.10.2 (10.10.10.2) 56(84) bytes of data.
64 bytes from 10.10.10.2: icmp_seq=1 ttl=63 time=0.042 ms
64 bytes from 10.10.10.2: icmp_seq=2 ttl=63 time=0.052 ms
64 bytes from 10.10.10.2: icmp_seq=3 ttl=63 time=0.049 ms
--- 10.10.10.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2066ms
rtt min/avg/max/mdev = 0.042/0.047/0.052/0.004 ms
liruilonger@cloudshell:~$
v1 和 v2 可以正常通信,即我们模拟了两个不在同一网段的 Linux 机器
创建tun
设备,并设置为ipip
隧道
ip tunnel add tunl
隧道模式为ipip
:mode ipip
隧道端点
,用remote
和local
表示隧道外层IP: remote 10.10.20.2 local 10.10.10.2
ip addr add 10.10.100.10 peer 10.10.200.10 dev tunl
liruilonger@cloudshell:~$ sudo ip netns exec ns1 ip tunnel add tunl mode ipip remote 10.10.20.2 local 10.10.10.2
liruilonger@cloudshell:~$ sudo ip netns exec ns1 ip link set tunl up
liruilonger@cloudshell:~$ sudo ip netns exec ns1 ip addr add 10.10.100.10 peer 10.10.200.10 dev tunl
liruilonger@cloudshell:~$
原始的IP 头
封装后的IP头
同样需要在 ns2
上做相同的配置
liruilonger@cloudshell:~$ sudo ip netns exec ns2 ip tunnel add tunr mode ipip remote 10.10.10.2 local 10.10.20.2
liruilonger@cloudshell:~$ sudo ip netns exec ns2 ip link set tunr up
liruilonger@cloudshell:~$ sudo ip netns exec ns2 ip addr add 10.10.200.10 peer 10.10.100.10 dev tunr
liruilonger@cloudshell:~$
到这里 两个 tun 设备的 隧道就建立成功了,我们可以在其中一个命名空间对另一个命名空间的 tun 设备发起 ping 测试
liruilonger@cloudshell:~$ sudo ip netns exec ns1 ping 10.10.200.10 -c 3
PING 10.10.200.10 (10.10.200.10) 56(84) bytes of data.
64 bytes from 10.10.200.10: icmp_seq=1 ttl=64 time=0.091 ms
64 bytes from 10.10.200.10: icmp_seq=2 ttl=64 time=0.062 ms
64 bytes from 10.10.200.10: icmp_seq=3 ttl=64 time=0.067 ms
--- 10.10.200.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2082ms
rtt min/avg/max/mdev = 0.062/0.073/0.091/0.012 ms
liruilonger@cloudshell:~$
在看一各个命名空间对应的 链接
liruilonger@cloudshell:~$ sudo ip netns exec ns1 ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
3: v1@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 72:de:67:0b:28:e1 brd ff:ff:ff:ff:ff:ff link-netnsid 0
4: tunl@NONE: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ipip 10.10.10.2 peer 10.10.20.2
liruilonger@cloudshell:~$
两个命名空间除了 veth-pair
对应的 veth 虚拟设备,各有个一个 tun
设备,link/ipip
中的内容表示封装后的包的两端地址,即外层IP。
liruilonger@cloudshell:~$ sudo ip netns exec ns2 ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
3: v2@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether f2:dd:3c:7d:eb:50 brd ff:ff:ff:ff:ff:ff link-netnsid 0
4: tunr@NONE: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ipip 10.10.20.2 peer 10.10.10.2
liruilonger@cloudshell:~$
这里还需要了解一下 VXLAN 技术
VXLAN
是在底层物理网络(underlay)
之上使用隧道技术
,依托UDP
层(3 层)构建的 overlay
的逻辑网络,使逻辑网络与物理网络解耦
,实现灵活的组网需求。不仅能适配虚拟机环境
,还能用于容器环境
。
VXLAN
的工作模型,它创建在原来的 IP 网络(三层)上,只要是三层可达(能够通过 IP 互相通信)的网络就能部署 VXLAN
。
在 VXLAN
网络的每个端点都有一个 VTEP
设备,负责 VXLAN
协议报文的封包和解包
,也就是在虚拟报文上封装VTEP
通信的报文头部
。
物理网络上可以创建多个 VXLAN
网络,可以将这些 VXLAN
网络看作一个隧道
,不同节点上的虚拟机/容器
能够通过隧道
直连。通过VNI
标识不同的VXLAN
网络,使得不同的VXLAN
可以相互隔离。
一个VXLAN
报文需要确定两个地址信息:
MAC
地址IP
地址。如果VNI
也是动态感知的,那么VXLAN
一共需要知道三个信息:内部MAC、VTEP IP和VNI
。
一般有两种方式获得以上VXLAN网络
的必要信息:
多播
:同一个 VXLAN
网络的VTEP
加入同一个多播网络控制中心
:某个集中式的地方保存所有虚拟机的上述信息,自动告知VTEP
它需要的信息即可,这也是 Flannel 的方式我们通过一个点对点的 VXLAN 通信Demo
来体会一下 VXLAN 网络
点对点 VXLAN
即两台机器构成一个 VXLAN
网络,每台机器上有一个 VTEP,VTEP 之间通过它们的 IP 地址进行通信。点对点 VXLAN 网络拓扑如图
只有一个机器,这里我们和上面一样使用两个 Linux netowrk namespace node1,node2
来模拟两个主机
创建两个 网络命名空间
liruilonger@cloudshell:~$ sudo ip netns add node1
liruilonger@cloudshell:~$ sudo ip netns add node2
liruilonger@cloudshell:~$ ip netns list
node2
node1
使用 veth 对
来建立通信(VETH1,VETH2),配置 IP
liruilonger@cloudshell:~$ sudo ip link add veth1 netns node1 type veth peer name veth2 netns node2
liruilonger@cloudshell:~$ sudo ip netns exec node1 ip addr add 192.168.1.2/24 dev veth1
liruilonger@cloudshell:~$ sudo ip netns exec node2 ip addr add 192.168.1.3/24 dev veth2
liruilonger@cloudshell:~$ sudo ip netns exec node1 ip link set dev veth1 up
liruilonger@cloudshell:~$ sudo ip netns exec node2 ip link set dev veth2 up
两个命名空间都开启 ipv4 转发,ping 测试
liruilonger@cloudshell:~$ sudo ip netns exec node1 sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
liruilonger@cloudshell:~$ sudo ip netns exec node2 sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
liruilonger@cloudshell:~$ sudo ip netns exec node1 ping -c 3 192.168.1.3
PING 192.168.1.3 (192.168.1.3) 56(84) bytes of data.
64 bytes from 192.168.1.3: icmp_seq=1 ttl=64 time=0.066 ms
64 bytes from 192.168.1.3: icmp_seq=2 ttl=64 time=0.047 ms
64 bytes from 192.168.1.3: icmp_seq=3 ttl=64 time=0.038 ms
--- 192.168.1.3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2088ms
rtt min/avg/max/mdev = 0.038/0.050/0.066/0.011 ms
liruilonger@cloudshell:~$
模拟好了环境,在 node1 命名空间 使用ip link
命令创建VXLAN
接口
liruilonger@cloudshell:~$ sudo ip netns exec node1 ip link add vxlan0 type vxlan id 42 dstport 4789 remote 192.168.1.3 local 192.168.1.2 dev veth1
在名为 node1 的网络命名空间中创建了一个名为 vxlan0
的 VXLAN 接口。
VID 42
,VXLAN
流量使用的目标端口号,通常使用 UDP 端口 4789
192.168.1.3
192.168.1.2
veth1
作为底层网络接口liruilonger@cloudshell:~$ sudo ip netns exec node1 ip -d link show dev veth1
2: veth1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether ea:d1:4d:8c:c0:9a brd ff:ff:ff:ff:ff:ff link-netns node2 promiscuity 0 minmtu 68 maxmtu 65535
veth addrgenmode eui64 numtxqueues 2 numrxqueues 2 gso_max_size 65536 gso_max_segs 65535
为刚创建的VXLAN
网卡配置IP
地址 172.17.1.2/24
并启用它
liruilonger@cloudshell:~$ sudo ip netns exec node1 ip addr add 172.17.1.2/24 dev vxlan0
liruilonger@cloudshell:~$ sudo ip netns exec node1 ip link set vxlan0 up
执行成功后会发现路由表项多了下面的内容,所有目的地址
是 172.17.1.0/24
网段的包要通过 vxlan0(172.17.1.2)
转发
liruilonger@cloudshell:~$ sudo ip netns exec node1 ip route
172.17.1.0/24 dev vxlan0 proto kernel scope link src 172.17.1.2
192.168.1.0/24 dev veth1 proto kernel scope link src 192.168.1.2
vxlan0 的 FDB 地址转发表项中的内容如下
liruilonger@cloudshell:~$ sudo ip netns exec node1 bridge fdb
33:33:00:00:00:01 dev veth1 self permanent
01:00:5e:00:00:01 dev veth1 self permanent
33:33:ff:8c:c0:9a dev veth1 self permanent
00:00:00:00:00:00 dev vxlan0 dst 192.168.1.3 via veth1 self permanent
默认的VTEP
对端地址为192.168.1.3
。换句话说,原始报文经过vxlan0
后会被内核添加上VXLAN
头部,而外部UDP
头的目的 IP 地址会被冠上192.168.1.3
。这里的IP地址即为我们上面的配置的 远程端点
的 IP
在另一个命名空间也进行相同的配置,配置项这里不做说明
liruilonger@cloudshell:~$ sudo ip netns exec node2 bash
root@cs-1080702884152-ephemeral-ne4n:/home/liruilonger# ip link add vxlan0 type vxlan id 42 dstport 4789 remote 192.168.1.2 local 192.168.1.3 dev veth2
root@cs-1080702884152-ephemeral-ne4n:/home/liruilonger# ip -d link show dev vxlan0
3: vxlan0: <BROADCAST,MULTICAST> mtu 1450 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 5a:46:75:cd:ce:9e brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65535
vxlan id 42 remote 192.168.1.2 local 192.168.1.3 dev veth2 srcport 0 0 dstport 4789 ttl auto ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
root@cs-1080702884152-ephemeral-ne4n:/home/liruilonger# ip addr add 172.17.1.3/24 dev vxlan0
root@cs-1080702884152-ephemeral-ne4n:/home/liruilonger# ip link set vxlan0 up
root@cs-1080702884152-ephemeral-ne4n:/home/liruilonger# ip route
172.17.1.0/24 dev vxlan0 proto kernel scope link src 172.17.1.3
192.168.1.0/24 dev veth2 proto kernel scope link src 192.168.1.3
root@cs-1080702884152-ephemeral-ne4n:/home/liruilonger# bridge fdb
33:33:00:00:00:01 dev veth2 self permanent
01:00:5e:00:00:01 dev veth2 self permanent
33:33:ff:6d:21:bb dev veth2 self permanent
00:00:00:00:00:00 dev vxlan0 dst 192.168.1.2 via veth2 self permanent
测试两个命名空间中 veth
对应的 VTEP
内的 vxlan
设备的连通性
在 node2 对应的网络命令空间 ping node1 的 vxlan IP 地址
root@cs-1080702884152-ephemeral-ne4n:/home/liruilonger# ping -c 3 172.17.1.2
PING 172.17.1.2 (172.17.1.2) 56(84) bytes of data.
64 bytes from 172.17.1.2: icmp_seq=1 ttl=64 time=0.088 ms
64 bytes from 172.17.1.2: icmp_seq=2 ttl=64 time=0.062 ms
64 bytes from 172.17.1.2: icmp_seq=3 ttl=64 time=0.063 ms
--- 172.17.1.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2035ms
rtt min/avg/max/mdev = 0.062/0.071/0.088/0.012 ms
root@cs-1080702884152-ephemeral-ne4n:/home/liruilonger#
上面即为点对点组网模型 VXLAN
网络
抓包测试:
node1 上发起 ping
liruilonger@cloudshell:~$ sudo ip netns exec node1 bash
root@cs-1080702884152-ephemeral-ne4n:/home/liruilonger# ping -c 5 172.17.1.3
PING 172.17.1.3 (172.17.1.3) 56(84) bytes of data.
64 bytes from 172.17.1.3: icmp_seq=1 ttl=64 time=0.053 ms
64 bytes from 172.17.1.3: icmp_seq=2 ttl=64 time=0.058 ms
64 bytes from 172.17.1.3: icmp_seq=3 ttl=64 time=0.059 ms
64 bytes from 172.17.1.3: icmp_seq=4 ttl=64 time=0.064 ms
64 bytes from 172.17.1.3: icmp_seq=5 ttl=64 time=0.065 ms
--- 172.17.1.3 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4115ms
rtt min/avg/max/mdev = 0.053/0.059/0.065/0.004 ms
root@cs-1080702884152-ephemeral-ne4n:/home/liruilonger#
查看 node2 上配置的 vxlan 设备信息
liruilonger@cloudshell:~$ sudo ip netns exec node2 bash
。。。。。。。。。。。。。。。
3: vxlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether 5a:46:75:cd:ce:9e brd ff:ff:ff:ff:ff:ff
inet 172.17.1.3/24 scope global vxlan0
valid_lft forever preferred_lft forever
inet6 fe80::5846:75ff:fecd:ce9e/64 scope link
valid_lft forever preferred_lft forever
指定对应的设备信息抓包
root@cs-1080702884152-ephemeral-ne4n:/home/liruilonger# tcpdump -i vxlan0
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on vxlan0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
^C07:40:51.689496 IP6 fe80::5846:75ff:fecd:ce9e > ff02::2: ICMP6, router solicitation, length 16
07:40:56.278116 IP 172.17.1.2 > 172.17.1.3: ICMP echo request, id 7030, seq 1, length 64
07:40:56.278143 IP 172.17.1.3 > 172.17.1.2: ICMP echo reply, id 7030, seq 1, length 64
07:40:57.321585 IP 172.17.1.2 > 172.17.1.3: ICMP echo request, id 7030, seq 2, length 64
07:40:57.321610 IP 172.17.1.3 > 172.17.1.2: ICMP echo reply, id 7030, seq 2, length 64
07:40:58.345557 IP 172.17.1.2 > 172.17.1.3: ICMP echo request, id 7030, seq 3, length 64
07:40:58.345582 IP 172.17.1.3 > 172.17.1.2: ICMP echo reply, id 7030, seq 3, length 64
07:40:59.369546 IP 172.17.1.2 > 172.17.1.3: ICMP echo request, id 7030, seq 4, length 64
07:40:59.369575 IP 172.17.1.3 > 172.17.1.2: ICMP echo reply, id 7030, seq 4, length 64
07:41:00.393571 IP 172.17.1.2 > 172.17.1.3: ICMP echo request, id 7030, seq 5, length 64
07:41:00.393600 IP 172.17.1.3 > 172.17.1.2: ICMP echo reply, id 7030, seq 5, length 64
07:41:01.417503 ARP, Request who-has 172.17.1.2 tell 172.17.1.3, length 28
07:41:01.417543 ARP, Request who-has 172.17.1.3 tell 172.17.1.2, length 28
07:41:01.417599 ARP, Reply 172.17.1.3 is-at 5a:46:75:cd:ce:9e (oui Unknown), length 28
07:41:01.417594 ARP, Reply 172.17.1.2 is-at c6:b1:87:67:d9:e4 (oui Unknown), length 28
15 packets captured
15 packets received by filter
0 packets dropped by kernel
root@cs-1080702884152-ephemeral-ne4n:/home/liruilonger#
上面即为一个点对点的 VXLAN
通信 ,回到我们今天要讲的 Flannel 和 Calico
,我们先来看一下 Calico
Calico
在第 3 层
工作,并依赖于 Linux
路由表和 iptables
来移动数据包。
Pod 之间的通信当使用 Calico 作为 CNI 时,每个 Pod 都会被分配一个唯一的 IP 地址,并且这些 IP 地址将用于在 Pod 之间进行通信。
Pod 通信本质上是 不同机器上的两个 network namespace
通信, network namespace
通过 veth pair
会在容器内部和宿主机映射一对虚拟网卡(veth pair)
,在部署的好的 K8s 集群中,可以在节点上看到好多虚拟网卡,这些就是 veth pair
宿主机的虚拟网卡。
Calico 使用 Linux 内核的网络堆栈来实现网络功能(宿主机的 calico
组件的 Felix
程序会在内核的路由表
里面写入数据,注明这个IP出去时下一跳地址
和进来时的由那个网卡解析
), 同时路由程序会获取ip变换,通过 BPG 路由协议扩散到其他宿主机上,这里也包括使用代理 ARP
来处理 Pod
到节点的 ARP
请求。
宿主机,也就是工作节点,可以看做是一个路由器
。pod 可以看做是连接到路由器上的网络终端。
这里创建两个 Pod ,简单分析一下,编写 YAML 文件通过拓扑分布约束调度在不同的节点。
apiVersion: apps/v1
kind:Deployment
metadata:
name:demo-deployment
labels:
app:os
spec:
replicas:2
selector:
matchLabels:
app:os
template:
metadata:
labels:
app:os
spec:
containers:
-name:centos
image:centos:latest
args:
-tail
--f
-/dev/null
topologySpreadConstraints:
-maxSkew:1
topologyKey:kubernetes.io/hostname
whenUnsatisfiable:ScheduleAnyway
labelSelector:
matchLabels:
app:os
应用之后,查看 Pod 信息
┌──[root@vms100.liruilongs.github.io]-[~/docker]
└─$kubectl get pods -n demo -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-deployment-6cbdbd86d5-fbt9d 1/1 Running 0 17m 10.244.169.66 vms105.liruilongs.github.io <none> <none>
demo-deployment-6cbdbd86d5-nm467 1/1 Running 0 16m 10.244.38.174 vms103.liruilongs.github.io <none> <none>
┌──[root@vms100.liruilongs.github.io]-[~/docker]
└─$
分别调度到了不同节点:
vms105.liruilongs.github.io
: demo-deployment-6cbdbd86d5-fbt9dvms103.liruilongs.github.io
: demo-deployment-6cbdbd86d5-nm467进入容器查看 Pod IP 信息
demo-deployment-6cbdbd86d5-fbt9d Pod
对应 IP 为 :10.244.169.66
, 生成的 veth pair
容器侧的虚拟网卡为 eth0@if16
┌──[root@vms100.liruilongs.github.io]-[~/docker]
└─$kubectlexec -it demo-deployment-6cbdbd86d5-fbt9d -n demo -- bash
[root@demo-deployment-6cbdbd86d5-fbt9d /]# 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
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
4: eth0@if16: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP group default
link/ether 42:17:dd:38:6a:10 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.244.169.66/32 scope global eth0
valid_lft forever preferred_lft forever
[root@demo-deployment-6cbdbd86d5-fbt9d /]# exit
exit
demo-deployment-6cbdbd86d5-nm467 Pod
对应 IP 为 10.244.38.174
,生成的 veth pair
容器侧的虚拟网卡为 eth0@if34
┌──[root@vms100.liruilongs.github.io]-[~/docker]
└─$kubectlexec -it demo-deployment-6cbdbd86d5-nm467 -n demo -- bash
[root@demo-deployment-6cbdbd86d5-nm467 /]# 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
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
4: eth0@if34: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP group default
link/ether 36:a2:81:c4:84:f9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.244.38.174/32 scope global eth0
valid_lft forever preferred_lft forever
[root@demo-deployment-6cbdbd86d5-nm467 /]# exit
exit
进入 demo-deployment-6cbdbd86d5-nm467
Pod 简单做 ping
测试,来看一下这个 ICMP
包是如何出去的。
┌──[root@vms100.liruilongs.github.io]-[~/ansible]
└─$kubectl exec -it demo-deployment-6cbdbd86d5-nm467 -n demo -- bash
[root@demo-deployment-6cbdbd86d5-nm467 /]# ping -c 3 10.244.169.66
PING 10.244.169.66 (10.244.169.66) 56(84) bytes of data.
64 bytes from 10.244.169.66: icmp_seq=1 ttl=62 time=0.497 ms
64 bytes from 10.244.169.66: icmp_seq=2 ttl=62 time=0.460 ms
64 bytes from 10.244.169.66: icmp_seq=3 ttl=62 time=0.391 ms
--- 10.244.169.66 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2044ms
rtt min/avg/max/mdev = 0.391/0.449/0.497/0.047 ms
当前容器 IP 为 10.244.38.174
, ping
侧的容器 IP 为 10.244.169.66
,不在同一个网络内,所以当前容器会在路由表获取一下跳地址.
查看容器路由信息
[root@demo-deployment-6cbdbd86d5-nm467 /]# ip route
default via 169.254.1.1 dev eth0
169.254.1.1 dev eth0 scope link
第一条为一条默认路由,所有目标地址不在本地子网的流量都会通过这个路由转发到下一跳地址。
下一跳地址为 169.254.1.1
,这是预留的本地 IP 网段
,这里的容器里的路由规则在所有的容器都是一样的,不需要动态更新.
这条路由表明,169.254.1.1 是一个直接可达的地址,属于 eth0 接口,使用 Calico 时,169.254.1.1
是 Calico
默认的虚拟网关地址(Link-local Gateway)
容器会查询下一跳 168.254.1.1
的 MAC
地址,这个 ARP
请求(查找目标设备的 MAC 地址)会如何发出?
这里通过 veth pair
发出,容器内部的虚拟网卡eth0@if34
发到宿主节点的对应的虚拟网卡cali7a4b00317e6
宿主机上的 veth
设备代理 ARP
请求并响应。容器中没有复杂的路由规则,所有非本地流量都通过 169.254.1.1 路由到宿主机
如何确定一对 veth pair
虚拟网卡?