一、网络隧道介绍及原理
IP隧道是指一种可在两网络间用网际协议进行通信的通道。在该通道里,会先封装其他网络协议的数据包,之后再传输信息。
IP隧道经常用于连接两个不是用路由直接链接的IP网络,IP隧道会通过底层路由协议来架构中间传输网络。若IP隧道与两个或多个IPSec一起使用时,可以创建虚拟专用网(Virtual Private Network,VPN),让二个或多个被公开网络(如因特网)隔开的私有网络彼此访问,另一个主要应用也是目前常用的,让各IPv6网络隔着IPv4网络上通信。
IP隧道封装 在IP隧道中,每个IP包、来源/目的地址信息都被封装在一个数据包中,该数据包用于实际物理网络传递。
在源网络与传输网络的边界,以及传输网络和目的网络的边界,会用网关来创建跨网络的隧道端点(endpoint)。因此,IP隧道端点可以变成本地IP路由器,在源网络与目的网络间创建标准路由。端点会截取通过端点数据包的隧道协议报头及报尾,再转换为标准IP格式,与其他来源的数据包一样注入到隧道端点的IP栈(IP stack)上。 ——摘自维基百科
1.1 ip tunnel 命令
Linux 原生支持多种三层隧道,其底层实现原理都是基于 tun
设备。我们可以通过命令 ip tunnel help 查看 IP
隧道的相关操作。

可以从mode中看到, Linux系统原生支持ipip、gre、sit 、isatap、vti五种隧道类型。此外还有一种:vxlan, vxlan的原理是封装以太网栈的方式实现了虚拟二层网络。
ipip:即IPv4 in IPv4,在 IPv4 报文的基础上再封装一个 IPv4 报文。gre:即通用路由封装(Generic Routing Encapsulation),定义了在任意一种网络层协议上封装其他任意一种网络层协议的机制,IPv4 和 IPv6 都适用。sit:和ipip类似,不同的是sit是用 IPv4 报文封装 IPv6 报文,即IPv6 over IPv4。isatap:即站内自动隧道寻址协议(Intra-Site Automatic Tunnel Addressing Protocol),和sit类似,也是用于 IPv6 的隧道封装。vti:即虚拟隧道接口(Virtual Tunnel Interface),是 cisco 提出的一种IPsec隧道技术。- vxlan:虚拟二层网络,是应用最为广泛的overlay网络方案
1.2 隧道和协议封装
ipip、gre、vxlan都是通过UDP协议封装报文来实现的。如图,以IPIP为例:TUN 网络设备能将三层(IP 网络数据包)数据包封装在另外一个三层数据包之中。当报文抵达隧道的对端, 对端会进行解封装, 解封装就是去掉封装加上去的一层后把报文交给Linux网络栈。

隧道的原理就是在一端封装另一端解封装,不同的隧道协议封包的开销也不一样。
因为ipip、gre、vxlan都已经在内核中集成,所以封装和解封装都在内核态完成,
避免了用户空间到内核空间的切换,大大的提升了效率。像flannel早期使用的隧道模式为自行实现的UDP封装,
未集成在Linux内核,
封装解封运行在用户态,而数据的交互和传递则在内核态完成,这就造成了为了传递数据,需要内核态和用户态的频繁切换,导致切换成本高昂。
此外不同的隧道协议封包的包头大小,也会产生额外的开销:
- vxlan封装二层报文,额外开销(50 bytes),
虽然额外开销变大了,但是vxlan是功能最强大的隧道模式,
VXLAN的
VLAN ID有24位(传统的VLAN ID只有12位) ,可以支持千万级别的网络租户, 非常适用于大型网络。 - ipip封装三层报文,额外开销(20 bytes), 不能像vxlan一样在一个网络里面划分多个租户(VLAN),但是开销最小,小型单一网络需求非常适用。
- gre则是通用封装,其理论上可以封装任何有效的三层协议类型,包括路由协议,额外开销:20 + 4 bytes到16 bytes 开销适中,常用于VPN 技术。
二、实践
我们下面以 ipip
作为例子,结合跨主机间的容器网络,来实践下 Linux 的隧道通信。
2.1 环境
主机名=k8s-node-2,IP=192.168.56.12,容器网段=172.20.0.0/24,隧道虚拟设备地址(tun1)=10.233.0.1/24
主机名=k8s-node-3,IP=192.168.56.13,容器网段=172.40.0.0/24,隧道虚拟设备地址(tun2)=10.233.0.2/24
2.2 创建容器网络
192.168.56.12主机上执行:
1 | # 新建一个容器网络 |
192.168.56.13主机上执行:
1 | # 新建一个容器网络 |
此时,主机能ping通本地的容器IP,但ping不通跨主机的容器IP 

2.3 创建隧道互联
192.168.56.12主机上执行:
1 | # 创建一个远程地址是192.168.56.13, 本端地址是192.168.56.12的ipip隧道, 虚拟设备:tun1 |
192.168.56.13主机上执行:
1 | # 创建一个远程地址是192.168.56.12, 本端地址是192.168.56.13的ipip隧道, 虚拟设备:tun2 |
此时主机之间能相互ping通隧道ip。到此隧道已经建立,
但是这只实现了端到端的网络互通,
如果我们想要利用隧道实现整个网络的互通, 还需要进行路由的配置 

2.3 配置容器网段的路由
192.168.56.12主机上执行:
1 | # 将容器网络指向到对端 |
192.168.56.13主机上执行:
1 | # 将容器网络指向到对端 |
至此已经通过隧道网络,实现了两台主机间的跨容器网段通信。 
