为了支持网络协议栈的多个实例,Linux 在网络协议栈中引入了网络命名空间。这些独立的协议栈被隔离到不同的命名空间中,处于不同命名空间中的网络协议栈是完全隔离的,彼此无法通信。通过对网络资源的隔离,就能在一台宿主机上虚拟多个不同的网络环境,Docker 正是利用了网络的命名空间特性,实现不同容器之间的网络隔离。
在 Linux 的网络命名空间中,每个空间都拥有独立的路由表和 iptables 设置,负责包转发、网络地址转换(NAT)和 IP 包过滤等功能。由于网络命名空间代表独立的协议栈,它们相互隔离且无法直接通信。但 Docker 通过虚拟接口 veth 实现了容器之间以及容器与宿主机之间的通信。虚拟以太网(veth)以成对的形式出现,就像管道的两端一样。数据从一个 veth 进入,经过协议栈后,从另一个 veth 出去,打通了互相隔离的协议栈之间的壁垒。通过连接两个网络命名空间或全局命名空间,veth 可以将物理网卡存在的命名空间内进行通信。要在两个网络命名空间之间实现通信,必须有一对虚拟接口对。
2、Linux 网络虚拟化
Docker 利用了 Linux 上的网络命名空间和虚拟网络设备来实现本地网络功能。了解 Linux 网络虚拟化技术有助于理解 Docker 网络的实现过程,其中 Linux 网络虚拟化是 LXC 项目的一部分。LXC 包括文件系统虚拟化、进程空间虚拟化、用户虚拟化和网络虚拟化等。
Docker 使用了LXC的网络虚拟化来模拟多个网络环境。Linux网络虚拟化主要有以下类型:
- 桥接:创建虚拟桥设备(网桥),将虚拟机连接至桥设备上,再配置桥设备的 IP 地址,即可与外部通信。此时虚拟机使用的是公网地址
- 隔离:将需要通信的虚拟机的网卡添加到同一个虚拟桥设备上,实现虚拟机之间的通信,与外网仍然隔离
- 路由:将虚拟机关联至虚拟桥设备上,配置与虚拟机同段的 IP 地址作为虚拟机的网关,最后打开物理主机的核心转达功能,实现虚拟机与外部主机的通信
- NAT:在路由模型的基础上,配置源地址转换(SNAT)规则,实现虚拟机与外网通信,但自己使用的是内网地址。外网无法主动访问内网的虚拟机,需要额外配置目标地址转换(DNAT)规则
Docker 中的网络接口默认为虚拟接口,其最大优势是转发效率高。Linux 通过在内核中进行数据复制来实现虚拟接口之间的数据转发,即直接将发送接口的数据包复制到接收接口的缓存中,无需通过外部物理网络设备进行交换。对于本地系统和容器内系统而言,虚拟接口和普通以太网卡没有区别,但速度更快。
Docker 容器网络利用了 Linux 虚拟网络技术,通过在本地宿主机和容器内部创建虚拟接口 veth 并连接二者,这样的一对虚拟接口称为 veth pair。
通常情况下,Docker 创建容器时执行以下操作:
- 创建一对虚拟接口,一个放在本地宿主机,另一个放在新容器的命名空间中
- 将本地宿主机端的虚拟接口连接到默认的网桥 docker0(实际上是一个 Linux 网桥)或指定的网桥上,并赋予一个以 veth 开头的唯一名称,如 veth18
- 将容器端的虚拟接口放置到新创建的容器中,并将其名称修改为 eth0。此接口仅在容器的命名空间中可见
- 从网桥可用地址段中获取一个空闲地址,分配给容器的 eth0 接口(如172.10.0.22/16