Docker 技术依赖于近年来 Linux 内核虚拟化技术的发展,所以 Docker 对 Linux 内核有很强的依赖。本文将 Docker 使用到的与 Linux 网络有关的主要技术进行简单介绍。
一、网络命名空间
为了支持网络协议栈的多个实例, Linux 在网络栈中引入了网络命名空间,这些独立的协议栈被隔离到不同的命名空间中。处于不同命名空间中的网络栈是完全隔离的,彼此之间无法通信。通过对网络资源的隔离,就能在一个宿主机上虚拟多个不同的网络环境。Docker 正是利用了网络的命名空间特性,实现了不同容器之间的网络隔离。 在 Linux 的网络命名空间中可以有自己独立的路由表及独立的 iptables 设置来提供包转发、 NAT 及 IP 包过滤等功能。1. 网络命名空间的实现
为了隔离出独立的协议栈,需要纳入命名空间的元素有进程、套接字、网络设备等。 Linux 的网络协议栈是十分复杂的,为了支持独立的协议栈,相关的这些全局变量都必须被修改为协议栈私有。最好的办法就是让这些全局变量成为 Net Namespace 变量的成员,然后为协议栈的函数调用加入一个 Namespace 参数。这就是 Linux 实现网络命名空间的核心。 命名空间的内核数据结构: 在新生成的私有命名空间中只有回环设备(名为 lo 且是停止状态),其他设备默认都不存在,如果我们需要,则要一一手工建立。Docker 容器中的各类网络栈设备都是 Docker Daemon 在启动时自动创建和配置的。 所有网络设备(物理的或虚拟接口、桥等在内核里都叫作 Net Device)都只能属于一个命名空间。当然,物理设备(连接实际硬件的设备)通常只能关联到 root 这个命名空间中。虚拟网络设备(虚拟以太网接口或者虚拟网口对)则可以被创建并关联到一个给定的命名空间中,而且可以在这些命名空间之间移动。 不同网络命名空间是相互隔离,无法通信的,而 Veth 设备对的一个重要作用就是打通了相互看不到的协议栈之间的壁垒,它就像一条管子,一端连着这个网络命名空间的协议栈,一端连着另一个网络命名空间的协议栈。所以如果想在两个命名空间之间通信 ,就必须有一个 Veth 设备对。2. 对网络命名空间的操作
(1)对网络命名空间的基本操作
对网络命名空间的基本操作主要有:创建命名空间、查看命名空间列表、在指定命名空间执行命令、进入指定命名空间执行命令。示例如下: <1>创建命名空间 & 查看命名空间列表 <2>在指定命名空间执行命令 <3>进入指定命名空间执行命令(2)查看设备是否可以在命名空间之间转移
我们可以在不同的网络命名空间之间转移设备。因为一个设备只能属于一个命名空间,所以转移后在这个命名空间中就看不到这个设备了。 具体哪些设备能被转移到不同的命名空间中呢?在设备里面有一个重要的属性:NETIF_F_ ETNS_LOCAL,如果这个属性为 on,就不能被转移到其他命名空间中了。Veth 设备属于可以转移的设备,而很多其他设备如 lo 设备、 vxlan 设备、 ppp 设备、 bridge 设备等都是不可以转移的。
二、Veth 设备对
引入 Veth 设备对是为了在不同的网络命名空间之间通信,利用它可以直接将两个网络命名空间连接起来。由 Veth 设备的一端发送数据时,它会将数据直接发送到另一端,并触发另一端的接收操作。 Veth 设备对示意图:1. 对 Veth 设备对的基本操作实践
(1)创建 Veth 设备对 (2)将其中一个 Veth 设备移动到另一个网络命名空间 (3)给 Veth 设备对分配 IP 地址,启动,通信 在 Docker 内部,Veth 设备对是连通容器与宿主机的主要网络设备,离开它是不行的。2. Veth 设备对如何查看对端
先在当前网络命名空间查看 Veth 设备对端接口在设备列表中的序列号,再到目标网络命名空间中查看该序列号代表什么设备:三、路由
路由是指设备从一个接口上收到数据包,根据数据包的目的地址进行重定向并转发到另一个接口的过程。 Linux 系统包含一个完整的路由功能。当 IP 层在处理数据发送或者转发时,会使用由 IP 层维护的路由表来决定发往哪里。当从网络侧接收到数据报文时,IP 层首先会检查报文的 IP 地址是否与主机自身的地址相同。如果数据报文中的 IP 地址是主机自身的地址,那么报文将被发送到传输层相应的协议中。如果报文中的 IP 地址不是主机自身的地址,并且主机配置了路由功能,那么报文将被转发,否则报文将被丢弃。1. 路由策略
策略路由是指按照用户指定的策略进行路由选择,是一种比基于目标网络进行路由更加灵活的数据包路由转发机制,它使网络管理员不仅能够根据目的地址而且能够根据报文大小、应用或 IP 源地址等属性来选择转发路径。 查看当前主机路由策略: 可以看到当前有三条路由策略,其中 0、32766、32767 表示策略优先级,数字越小优先级越高,而 local、main、default 是路由表的名称,还可以通过 from 关键字设置指定来源的数据包查询指定路由表,这里 from all 表示所有,不对来源地址进行过滤。用户还可以通过 ip rule add/del 等命令对路由策略进行增删改查操作,但 local 是一个特殊的路由表,包含对于本地和广播地址的高优先级控制路由,rule 0 不能被删除或者覆盖。 当主机收到数据包时,会按照路由策略中设置的优先级依次遍历查询路由表,直到决策出下一跳。所以,不在路由策略中的路由表,是不会被用于路由的。2. 路由表
可以通过文件 /etc/iproute2/rt_tables 查看当前系统中的路由表: 可以看到,linux 系统维护了 4 个路由表,序号分别为 0、253、254、255,其作用分别如下: (1)0 unspec:系统保留表 (2)253 default:没特别指定的默认路由都放在该表 (3)254 main:没指明路由表的所有路由都放在该表 (4)255 local:保存本地接口地址、广播地址、NAT 地址,由系统维护,用户不得更改(LOCAL 表用于供 Linux 协议栈识别本地地址,以及进行本地各个不同网络接口之间的数据转发) 除此之外,用户可以自定义序号为 1- 252 的路由表,手动编辑文件 /etc/iproute2/rt_tables 即可,保存即生效。 路由查看: 在显示的信息中,如果标志 (Flags)是 U(代表 Up) ,则说明该路由是有效的;如果标志是 G(代表 Gateway),则说明这个网络接口连接的是网关;如果标志是 H(代表 Host ),则说明目的地是主机而非网络域,等等。 路由解析:以第 5 条路由为例,表示发送到网段 30.102.72.0/24 的数据包由本地接口 eth0 发送到下一个路由器 30.102.73.254(在这里也是网关)。 默认查看的是路由表 main 的路由,还可以通过指定路由表名称、路由类型进行查看: 用户可以通过 ip route add/del 等命令对路由进行增删改查。3. 静态路由与动态路由
(1)静态路由
静态路由是指用户或网络管理员手工配置的路由信息。当网络的拓扑结构或链路状态发生变化时,网络管理员需要手工去修改路由表中相关的静态路由信息。 静态路由信息在缺省情况下是私有的,不会传递给其他的路由器。 静态路由一般适用于比较简单的网络环境。(2)动态路由
动态路由是指路由器能够根据路由器之间交换的路由信息自动地建立自己的路由表,并且能够根据实际情况的变化适时地进行调整。 当网络中节点或节点间的链路发生故障,或存在其他可用路由时,动态路由可用自行选择最佳的可用路由并继续转发报文。 常见的动态路由协议:RIP、OSPF、BGP 等。动态路由发现协议一般使用组播功能来通过发送路由发现数据,动态地交换和获取网络的路由信息,并更新到路由表中。四、网桥
Linux 可以支持多个不同的网络,它们之间能够相互通信,如何将这些网络连接起来并实现各网络中主机的相互通信呢?可以用网桥。网桥是一个二层的虚拟网络设备,把若干个网络接口“连接”起来,以使得网络接口之间的报文能够相互转发。网桥能够解析收发的报文,读取目标 MAC 地址的信息,将其与自己记录的 MAC 表结合,来决策报文的转发目标网络接口。为了实现这些功能,网桥会学习源 MAC 地址(二层网桥转发的依据就是 MAC 地址 )。在转发报文时,网桥只需向特定的网口进行转发,来避免不必要的网络交互。如果它遇到一个自己从未学习到的地址,就无法知道这个报文应该向哪个网络接口转发,将报文广播给所有的网络接口(报文来源的网络接口除外)。 在实际的网络中,网络拓扑不可能永久不变。设备如果被移动到另一个端口上,却没有发送任何数据,网桥设备就无法感知这个变化,网桥还是向原来的端口转发数据包,在这种情况下数据会丢失。所以网桥还要对学习到的 MAC 地址表加上超时时间(默认为5min) 。如果网桥收到了对应端口 MAC 地址回发的包,则重置超时时间,否则过了超时时间,就认为设备已经不在那个端口上了,它会重新广播发送。 在 Linux 的内部网络栈里实现的网桥设备,作用和上面的描述相同。 Linux 主机过去一般只有一个网卡,现在多网卡的机器越来越多,而且有很多虚拟设备存在,所以 Linux 网桥提供了在这些设备之间相互转发数据的二层设备。1. Linux 网桥的实现
Linux 内核是通过一个虚拟网桥设备 (Net Device) 来实现桥接的。这个虚拟设备可以绑定若干个以太网接口设备,从而将它们桥接起来。这种 Net Device 网桥和普通的设备不同,最明显的一个特性是它还可以有一个 IP 地址。 网桥的位置: 如图所示,网桥设备 br0 绑定了 eth0 和 eth1。对于网络协议栈的上层来说,只看得到 br0 就行。因为桥接是在数据链路层实现的,上层不需要关心桥接的细节,所以协议栈上层需要发送的报文被送到 br0,网桥设备的处理代码判断报文应该被转发到 eth0 还是 eth1,或者两者应该皆转发;反过来,从 eth0 或从 eth1 接收到的报文被提交给网桥的处理代码,在这里会判断报文应该被转发、丢弃还是被提交到协议栈上层。 而有时 eth0、eth1 也可能会作为报文的源地址或目的地址,直接参与报文的发送与接收,从而绕过网桥。 Linux 网桥常被用于虚拟机与容器之间的网络联通,相当于一个虚拟机交换机的功能。2. Linux 网桥的基本操作
(1)创建网桥 (2)将设备连接到网桥上 这里将 Veth 设备 veth0 连接到网桥 wjt-bridge 上,此时 Veth 设备 veth0 成为网桥 wjt-bridge 的一个网口,在链路层工作,不再需要 IP 地址了,所以 veth0 上面的 IP 地址会失效: 两边都无法 ping 通了。 (3)为网桥配置 IP 地址
参考: 《Kubernetes 权威指南第 5 版》 标签:报文,网络,网桥,Linux,命名,Docker,路由,设备 From: https://www.cnblogs.com/wujuntian/p/18092757