1 网络基础
1.1 网桥、网卡、MAC
① 网桥:将不同物理层连接在一起。
② 网卡:接收和转换信息的硬件。
③ MAC:MAC 是设备的地址。
网桥、网卡和 MAC 作用于数据链路层。如下图所示,网卡“插入”到网桥和设备中,使用网线连接网桥和设备两端的网卡。

1.2 Veth Pair
Veth Pair 可以参考 Linux 虚拟网络设备 veth-pair 详解,看这一篇就够了
veth-pair 是一对的虚拟设备接口,一端连着协议栈,一端彼此相连。以网桥的连接方式为例,分析 veth-pair 的使用。网桥相当于交换机,可以中转两个 namespace 的流量。veth-pair 是“插在”设备和网桥之间的网卡。
1.3 iptables
【参考】
1.iptables命令参数详解
2.iptables的四表五链与NAT工作原理
3.深入浅出带你理解 iptables 原理
iptables 的工作流程如下,当用户向本机发送数据包时
① 首先经过 PREROUTING 链的过滤规则
② 判断是否为本机数据包,如果是那么经过 INPUT 链规则后到达本机的用户态
③ 如果不是本机数据包通过 FORWARD 链后再通过 POSTROUTING 链到其他地址
当本机向外发送数据包时,经过 OUTPUT 链后再通过 POSTROUTING 链到达其他地址

语法格式:iptables -t 表名 <-A/I/D/R> 规则链名 <-i/o 网卡名> -p 协议名 <-s 源IP/源子网> --sport 源端口 <-d 目标IP/目标子网> --dport 目标端口 -j 动作
-
表名、链名:指定 iptables 命令所操作的表和链,其中表分别为
filter、nat、raw和mangle,默认使用filter表; - 管理选项:表示 iptables 规则的操作方式,比如:插入、增加、删除、查看等;
- 匹配条件:指定要处理的数据包的特征,不符合指定条件的数据包不处理;
- 控制类型:指数据包的处理方式,比如:允许 accept、拒绝 reject、丢弃 drop、日志 LOG等;
# 1.iptables 的 Commands 选项
-A:在指定链的末尾添加一条新的规则
-D:删除指定链中的某一条规则,可删除指定序号或具体内容
-I:在指定链中插入一条新规则,未指定序号时默认作为第一条规则
-R:修改、替换指定链中的某一条规则,可指定规则序号或具体内容
-L:列出指定链中所有的规则,未指定链名,则列出表中的所有链
-F:清空指定链中所有的规则,未指定链名,则清空表中的所有链
-P:设置指定链的默认策略
-n:使用数字形式显示输出结果
-v:查看规则列表时显示详细的信息
-h:查看命令帮助信息
--line-numbers:查看规则列表时,同时显示规则在链中的顺序号
# 2.iptables 的 Options 选项
-p proto,针对协议过滤,例如 tcp
-s source address[/mask][...],源地址
-d destination address[/mask][...],目的地址
-m match,指定要使用的匹配项
-i input name[+],指定进入接口
-j target,跳转到目标规则,例如ACCEPT、DROP、REJECT、SNAT、MASQUERADE、DNAT、REDIRECT
-n 使用数字形式显示输出结果
-t table,指定表,默认是 filter
--line-numbers,显示规则号
iptables 使用样例:
# 本机网卡 eth0 接收 137 udp 端口、138 tcp 端口的数据包
$iptables –A INPUT –i eth0 –p udp –dport 137 –j ACCEPT
$iptables –A INPUT –i eth0 –p tcp –dport 138 –j ACCEPT
# 防火墙设置
# INPUT 链、FORWARD 链、OUTPUT 链允许全部数据包的传输
$iptables -P INPUT ACCEPT
$iptables -P FORWARD ACCEPT
$iptables -P OUTPUT ACCEPT
#数据报伪装
#将源地址是 192.168.102.0/24 的数据包进行地址伪装
$iptables -t nat -A POSTROUTING -s 192.168.102.0/24 -j MASQUERADE
#将全部来源的数据包进行地址伪装
$iptables -t nat -A POSTROUTING -j MASQUERADE
# 数据包转发:设置 422 端口转发到 22 端口
# 方法1 指定数据包从 192.168.102.37:422 转发到 192.168.102.37:22
$iptables -t nat -A PREROUTING -p tcp -d 192.168.102.37 --dport 422 -j DNAT --to 192.168.102.37:22
#方法2 指定数据包从本机网卡 eth0 端口 443 转发到 192.168.102.37:22
$iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 443 -j DNAT --to 192.168.102.37:22
1.4 iptables、bridge 和网络的关系
根据计算机网络体系结构的五层协议,bridge 作用于在数据链路层,iptables 作用于在网络层。

2 Kubernetes 网络插件 CNI 的工作流程

3 容器网络入门
关键问题:每个容器都有属于自己的 IP 和端口,被隔离的容器进程,该如何跟其他 Network Namespace 里的容器进程进行交互呢?
3.1 同一个宿主机下容器之间的通信
深入了解 Network Namespace 可以参考一文搞懂 Linux network namespace,
容器利用 Network Namespace 实现网络的隔离,包括网络设备、IPV4 和 IPV6 协议栈、IP 路由表、防火墙、/proc/net 目录、/sys/class/net 目录、端口等。多个 Network Namespace 之间的通信需要利用 Veth Pair + 网桥,其中网桥具有 iptables 功能,只需要设置规则 iptables -A FORWARD -i bridgeA -j ACCEPT,即可允许数据包在网桥 bridgeA 中转发。
案例分析:同一个宿主机上实现容器 172.17.0.2 到容器 172.17.0.3 的通信。
Container1 172.17.0.2 访问 Container2 172.17.0.3 的流程:
数据包 从 Container1 访问 Container2 172.17.0.3 的时候首先匹配到第二条路由规则(GateWay 0.0.0.0),意味着经过本机的 eth0,通过数据链路层 MAC 协议发送到 Container2 172.17.0.3。Container1 另一端的网卡 veth9c02e56 和 Container2 另一端的网卡 vethb4963f3 都“插入”网桥中。最后Container2 172.17.0.3 也是通过 eth0 接受数据包。

#进入容器 172.17.0.2 后,查看网络设备
$ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.2 netmask 255.255.0.0 broadcast 0.0.0.0
inet6 fe80::42:acff:fe11:2 prefixlen 64 scopeid 0x20<link>
ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet)
RX packets 364 bytes 8137175 (7.7 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 281 bytes 21161 (20.6 KiB)
TX errors 0 dropped 0 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 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
#分析:虚拟网卡 Veth Pair 的一端 eth0 在“插入”容器中。
#查看容器的路由表
#Destination 表示目标地址或者主机,Gateway 表示网关地址,Genmask 表示子网掩码,Flags-U 表示路由是活动的,Iface 表示 路由表项对应的输出接口
$ route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 172.17.0.1 0.0.0.0 UG 0 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
#分析:eth0 是容器的默认路由设备。第二行表示网段 172.17.0.0/16 的请求都会交给 eth0 来处理
#登录容器所在的宿主机,查看网络设备
$ifconfig
...
docker0 Link encap:Ethernet HWaddr 02:42:d8:e4:df:c1
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:d8ff:fee4:dfc1/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:309 errors:0 dropped:0 overruns:0 frame:0
TX packets:372 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:18944 (18.9 KB) TX bytes:8137789 (8.1 MB)
veth9c02e56 Link encap:Ethernet HWaddr 52:81:0b:24:3d:da
inet6 addr: fe80::5081:bff:fe24:3dda/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:288 errors:0 dropped:0 overruns:0 frame:0
TX packets:371 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:21608 (21.6 KB) TX bytes:8137719 (8.1 MB)
#分析:宿主机有虚拟网卡 Veth Pair 的另一端 veth9c02e56
#查看网桥信息
$brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242d8e4dfc1 no veth9c02e56
#分析:通过网桥信息可以看到虚拟网卡 veth9c02e56 已经“插入”网桥 docker0 中
结论:被隔离在 Network Namespace 里的容器进程,通过 Veth Pair + 宿主机网桥实现了跟其它容器的数据交换。
3.2 容器与不同宿主机的通信
案例分析:Node1 10.168.0.2 上的 Container1 172.17.0.2 访问 Node2 10.168.0.3。首先数据包通过 container1 eth0 和 docker0 veth9c02e56 传输到 docker0,接着根据 docker0 的 iptables 规则10.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 表示目标地址是 10.168.0.0/24 的数据包都交给 eth0。最后数据包经过 Node1 eth0 转发到住宿机网络,最终达到 Node2 10.168.0.3

容器不通“外网”的定位思路:宿主机网络 》网桥 docker0 》网桥 iptables 防火墙规则。
上述例子 Node1 Container1 到 Node2。首先在 Node1 上 curl Node2,判断宿主机之间的网络有无通。然后在 Node1 上ifconfig查询 docker0 的 IP 为 172.17.0.1,接着在 Node2 上直接 curl docker0。最后检查 docker0 上的 iptables 规则iptables -A FORWARD -i docker0 -j ACCEPT,即防火墙有无打开。
4 Kubernetes 网络模型
本节以 Fannel-host-gw 为例,讲解 Kubernetes 的三层网络原理。host-gw 模式的原理是将宿主机的 Flannel 子网设置成该宿主机的 IP 地址,数据包通过路由直接在宿主机之间交换。

#CNI 插件 Fannel 在 Node1 上创建这条路由规则
$ip route
10.244.1.0/24 via 10.168.0.3 dev eth0
#这条规则的含义:目的地址属于子网 10.244.1.0/24 的数据包,都会经过本机网卡 eth0,并且下一跳地址是 10.168.0.3
#分析:宿主机 Node1 eth0 到 Node2 eth0 的通信是直接采用路由规则转发,即数据帧是通过二层网络的 mac 协议,实现了在不同宿主机之间的转发。
结合上述 2.1 中容器与宿主机的通信。Node1 的容器 container1 10.244.0.2 到 Node2 的容器 container2 10.244.1.3 的数据包转发流程:
① 10.244.0.2 到 Node1 网桥 cni0:通过 Veth Pair + 网桥 cni0,即容器侧的 eth0 与宿主机网桥 cni0 侧 vethxxxxxxx,实现容器与宿主机的通信。
② 宿主机 Node1 到 Node2:直接配置网桥 cni0 路由规则转发,实现目的地是子网 10.244.1.0/24 的数据包,经过本机网卡 eth0,转发到 10.168.0.3
③ Node2 网桥 cni0 到 10.244.1.3:同理 ①,即宿主机网桥 cni0 侧 vethxxxxxxx 与 容器侧的 eth0,实与宿主机与容器的通信。