相关文章:

《Linux 虚拟网络设备 veth-pair》 linux基础
《Linux虚拟网络设备之veth(arp incomplete)》

Docker网络(veth、网桥、host、container、none) docker上网络概述

Docker的网络配置 1 初识 docker 精讲
Docker的网络配置 2 配置 DNS和主机名
Docker的网络配置 3 user-defined网络
Docker的网络配置 4 内嵌的DNS server
Docker的网络配置 5 将容器与外部世界连接
Docker的网络配置 6 docker-proxy

【云原生】网络之桥接(网卡对) openshift下的网络桥接模式

1. 概述

docker的网络驱动有很多种方式,按照docker官网给出的网络解决方案就有6种,分别是:bridge、host、overlay、macvlan、none、Network plugins,每个网络都有自己的特点,当然应用场景也不同,比如当有多台主机上的docker容器需要容器间进行跨宿主机通讯时,overlay和macvlan可提供解决方案,而默认docker采用的是bridge模式,而此模式不能与其他主机上的docker容器通讯。

本文主要介绍docker单主机通讯方式的几种通讯模式:bridge、host、none、container。
  
  启动容器指明网络通过参数--network选择指定,可简写为--net默认如果不加选项使用的是bridge。

2. bridge网络模式

2.1 介绍

bridge模式是docker默认的网络方式,当Docker 进程启动时,会自动在主机上创建一个 docker0 虚拟网桥,默认分配网段172.17.0.0/16,实际上是 Linux 的一个 bridge,可以理解为一个软件交换机,附加在其上的任何网卡之间都能自动转发数据包。

创建一个容器时,容器从docker0中的子网分配一个IP地址(也可以使用--ip指定),并创建一对veth虚拟网络设备,一个设备在容器中作为容器的网卡名叫eth0,另一个设备在宿主机上叫做名称为vethXXX并桥接在docker0上,可通过命令brctl show 查看,通过这样的桥接方法宿主机上的所有容器都处于同一个二层网络中,这样使得容器与容器以及容器与宿主机之间能够互相通信,以下介绍其通信细节。(ps:这里的通信是单主机上容器通讯)

2.2 关于veth

veth 是 Virtual ETHernet 的缩写,是一种虚拟网络设备。它的特点是:当它被创建以后,总是以两张虚拟网卡(Veth peer)形式成对出现,并且在一个网卡上的数据包可直接转发给另一个与之对应的网卡上,即使这两个网卡不在同一个namespace中。

这里推荐一篇文章介绍veth: https://segmentfault.com/a/1190000009251098。

2.3 容器与容器间通讯

参见 【k8s】docker桥接模式下pod访问pod(直连路由)

启动一个bs1的容器,并查看其IP地址为172.17.0.2,默认网关为172.17.0.1。而这个网关地址则是网桥docker0地址:

当然可以指定为桥接模式(morning就是桥接), --network bridge
在这里插入图片描述
同时我们查看docker0桥接的网卡与刚才容器对应的veth网卡:
在这里插入图片描述

inferfaces列多了一个标识,对应一个容器;如果启动多个容器,就会有多条veth开头的标识

同样的再启动一个容器为bs2,IP地址为172.17.0.3 ,网关172.17.0.1,即也是docker0:
在这里插入图片描述
此时docker0上会多了一个bs2的veth设备veth7833d44:

在这里插入图片描述
当我们从容器bs1(IP地址为172.17.0.2)中ping 172.17.0.3(容器bs2)时候,在bs1容器里,根据路由规则,数据包从eth0转发址与之对应的veth(veth0737c95)上,该veth桥接在了docker0上,此时数据包到达了docker0,docker0此时扮演交换机角色并广播ARP请求寻找172.17.0.3的mac地址,而此时的bs2的另一个veth设备桥接在了docker0上并收到该ARP请求,发现自己是172.17.0.3,将其mac地址回复给bs1,此时的容器bs1就可以和bs2进行通讯了,并且在bs1上可以查看到缓存的172.17.0.3的mac地址:
在这里插入图片描述
以上的两个容器间的通讯过程示意图如下:
在这里插入图片描述

2.4 容器与外部网络通讯

同样的,介绍了容器与容器间的通讯,容器与主机间的通讯就容易理解了,如果在容器bs1 ping 另一个主机10.168.0.3,它发出去的数据包经过docker0网桥流向eth0,eth0在将数据包转发给与之相通的10.168.0.3,于是bs1就能和其主机节点进行通讯了。当然如果这个地址不是其他主机节点而是公网地址,只要eth0能到达,容器bs1都能与之通讯。以下是其示意图:
 在这里插入图片描述

2.4.1 容器 bs1 发送数据包

1、容器 bs1 有一个 虚拟网络接口(veth pair),一端在容器内(如 eth0@if123),另一端在主机上(如 veth123abc)。

2、容器内的 eth0 配置了一个 私有 IP(如 172.17.0.2),并设置默认网关为 docker0 网桥的 IP(如 172.17.0.1)。

当 bs1 执行:

ping 10.168.0.3

数据包的 源 IP 是 172.17.0.2,目标 IP 是 10.168.0.3。

2.4.2. 数据包进入 docker0 网桥

由于 10.168.0.3 不在容器的本地网络(172.17.0.0/16),数据包会被发送到 默认网关 172.17.0.1(即 docker0 网桥)。

docker0 是一个 Linux 网桥,负责管理容器间的通信,并连接主机的物理网络

2.4.3. 主机 eth0 处理数据包

docker0 网桥收到数据包后,会检查主机的 路由表:

ip route

输出示例:

default via 10.168.0.1 dev eth0
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
10.168.0.0/24 dev eth0 proto kernel scope link src 10.168.0.2

由于 10.168.0.3 匹配 10.168.0.0/24,数据包会通过 eth0 发送出去

2.4.4. 数据包从 eth0 发送到目标主机 10.168.0.3

主机执行 SNAT(源地址转换),将 172.17.0.2(容器 IP)转换为 10.168.0.2(主机 IP),这样目标主机 10.168.0.3 才能正确回包。

数据包通过物理网络(如交换机、路由器)到达 10.168.0.3

2.4.5 为什么“docker0 网桥收到数据包后,会检查主机的 路由表:”

在 Linux 网络模型中,网桥(Bridge) 和 路由(Routing) 是两个不同的网络层机制:

  • 网桥(docker0) 工作在 数据链路层(L2),负责在同一个网络(如 172.17.0.0/16)内转发帧(Frames)。

  • 路由(Routing) 工作在 网络层(L3),负责在不同网络(如 172.17.0.0/16 → 10.168.0.0/24)间转发数据包(Packets)。

当容器 bs1 访问外部主机 10.168.0.3 时,数据包的流向涉及 跨网络通信,因此必须依赖主机的路由决策。以下是详细解释:

docker0 网桥的处理逻辑
docker0 是一个 L2 网桥,它的核心功能是:

  • 在 同一网络内(如 172.17.0.0/16)通过 MAC 地址表 转发帧(如容器 bs1 ↔ bs2)。

  • 如果目标 MAC 地址不在网桥上,它会 泛洪(Flood) 数据包到所有端口(类似交换机)。

但这里的关键问题是:

  • 10.168.0.3 不属于 docker0 的网络(172.17.0.0/16),因此 docker0 无法直接处理跨网络通信。

主机的路由表介入( Linux 内核(Kernel)自动完成)
当 docker0 发现目标 IP(10.168.0.3)不在本地网络时:

数据包从 docker0 提交给主机的网络栈(因为 docker0 是主机的虚拟设备)。

主机内核检查 路由表(ip route)决定如何转发:

$ ip route
default via 10.168.0.1 dev eth0           # 默认路由走 eth0
172.17.0.0/16 dev docker0 scope link      # 容器网络走 docker0
10.168.0.0/24 dev eth0 scope link        # 主机本地网络走 eth0

2.5 宿主机与容器通讯

假设启动容器:

docker run --name kubia-container -p 28808:8080 -d kubia

在主机上执行 curl 172.17.0.2:8080 可以通 //本章节的原理
或 curl localhost:28808 也可以通 //另一种原理

当宿主机访问容器时,数据包从docker0流入到与容器对应的veth设备,在经容器的eth0到达到主机中,示意图如下:
  在这里插入图片描述
主机Host访问自己节点上的容器,答案是:直接访问就行了。
先来看Host主机的路由表:

在这里插入图片描述
因为根据路由信息:
所有发往Docker容器的地址(即目标为 172.17.* )的报文,统统给docker0 网卡。而根据上面Bridge章节可以知道,这个docker0就是Bridge网桥,它是连着所有容器的veth网线的。所以这个报文会发送到所有容器里面,那么目标容器就会应答你。

在这里插入图片描述

2.6 外部访问容器

默认情况,其他外部网络(宿主机以外)无法访问到容器内的端口,通常的做法是使用-p或者-P选项(使用方法参考docker基础篇)来暴露容器中的端口到宿主机上,外部网络通过访问宿主机的端口从而访问到容器端口。

本质上来说,该方式是通过iptables规则转发实现的,例如以下启动nginx容器并使用宿主机的8080映射到容器内部80端口:
在这里插入图片描述

查看其对应的iptables规则:iptables -t nat -nL

在这里插入图片描述
上述规则表明当访问宿主机的8080端口时,NAT到容器中的172.17.0.4的80中。

2.7 自定义网桥

除了使用默认docker0作网桥以为还可以使用docker network 相关命令自定义网桥,以下将创建一个br1的网络,子网是172.30.0.0/16,网关为172.30.0.1:
  在这里插入图片描述
查看docker网络:
在这里插入图片描述
查看对应网桥:
在这里插入图片描述
此时运行一个容器指明网络使用br1,此时的容器地址使用的是172.30.0.0段的网络,此时使用的网桥为br-9e5b3fba2e11,如下:
在这里插入图片描述

3. host网络模式

Host模式使用是在容器启动时候指明--network host,此时容器共享宿主机的Network Namespace,容器内启动的端口直接是宿主机的端口,并且容器不会创建网卡和IP,直接使用宿主机的网卡和IP,但是容器内的其他资源是隔离的,如文件系统、用户和用户组。这种模式的好处就是效率高,因为不需要额外的网络开始,直接使用宿主机网络。同样启动一个nginx,此时共享主机网络:
  在这里插入图片描述
此时连入容器内部直接能看到docker0与宿主机网卡:
在这里插入图片描述
以上的host网络模式下的示意图:
在这里插入图片描述
问题来了:

  • 如果是容器A内发送消息给宿主
  • 如果是容器A内发送消息给其他容器,比如容器B
  • 如果外部往容器A怎么发送?

4. container网络模式

理解了host模式就很容易理解container模式,host模式是容器共享宿主机的Network Namespace,而container模式即共享已存在的容器的Network Namespace,此时这两容器共同使用同一网卡、主机名、IP地址,容器间通讯可直接通过lo回环接口通讯,但是其他名称空间是隔离的,例如User、Mount。如果你了解kubernetes中的pod的话,pod内的网络也是靠这样的共享方式来实现的。

这里启动一个bs-1容器,IP地址为172.17.0.2:
  在这里插入图片描述
此时再运行一个容器共享bs-1的网络,此时在bs-2的容器同样看到的是bs1的网卡:
在这里插入图片描述
示意图:
在这里插入图片描述

5. none网络模式

使用--network none选项指定其网络模式,在该模式下虽然容器有着自己的Network Namespace,但是容器内没有网卡、IP、路由信息,只有一个lo回环接口。如果需要网络配置则需要用户手动进行配置网卡、ip以及路由信息,通常这样的容器用于承担某些无需网络介入的工作,如离线任务、备份等。启动一个none网络模式的容器如下:
  在这里插入图片描述
示意图:
在这里插入图片描述

6. 总结

本文着重对docker 单主机网络中默认bridge网络模式做了较多介绍,其核心是通过docker0网桥+Veth Pair设备实现,而host、container模式都是“共享”方式实现,host网络模式是容器共享宿主机Network Namespace,continer网络模式是容器共享已存在的容器的Network Namespace,none模式则是网络封闭状态,容器内使用lo接口通讯。

参考

Docker网络
Docker网络实现原理图文详解

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐