一, 概述
docker 的网络驱动有很多种方式, 按照 docker 官网 https://docs.docker.com/network/ 给出的网络解决方案就有 6 种, 分别是: bridge,host,overlay,macvlan,none,Network plugins, 每个网络都有自己的特点, 当然应用场景也不同, 比如当有多台主机上的 docker 容器需要容器间进行跨宿主机通讯时, overlay 和 macvlan 可提供解决方案, 而默认 docker 采用的是 bridge 模式, 而此模式不能与其他主机上的 docker 容器通讯. 本文主要介绍 docker 单主机通讯方式的几种通讯模式: bridge,host,none,container.
启动容器指明网络通过参数 --network 选择指定, 可简写为 --net, 默认如果不加选项使用的是 bridge.
二, bridge 网络模式
介绍
bridge 模式是 docker 默认的网络方式, 当 Docker 进程启动时, 会自动在主机上创建一个 docker0 虚拟网桥, 默认分配网段 172.17.0.0/16, 实际上是 Linux 的一个 bridge, 可以理解为一个软件交换机, 附加在其上的任何网卡之间都能自动转发数据包.
创建一个容器时, 容器从 docker0 中的子网分配一个 IP 地址(也可以使用 --ip 指定), 并创建一对 veth 虚拟网络设备, 一个设备在容器中作为容器的网卡名叫 eth0, 另一个设备在宿主机上叫做名称为 vethXXX 并桥接在 docker0 上, 可通过命令 brctl show 查看, 通过这样的桥接方法宿主机上的所有容器都处于同一个二层网络中, 这样使得容器与容器以及容器与宿主机之间能够互相通信, 以下介绍其通信细节.(ps: 这里的通信是单主机上容器通讯)
关于 veth
veth 是 Virtual ETHernet 的缩写, 是一种虚拟网络设备. 它的特点是: 当它被创建以后, 总是以两张虚拟网卡 (Veth peer) 形式成对出现, 并且在一个网卡上的数据包可直接转发给另一个与之对应的网卡上, 即使这两个网卡不在同一个 namespace 中. 这里推荐一篇文章介绍 veth: https://segmentfault.com/a/1190000009251098 .
容器与容器间通讯
启动一个 bs1 的容器, 并查看其 IP 地址为 172.17.0.2, 默认网关为 172.17.0.1. 而这个网关地址则是网桥 docker0 地址:
同时我们查看 docker0 桥接的网卡与刚才容器对应的 veth 网卡:
同样的在启动一个容器为 bs2,IP 地址为 172.17.0.3 , 网关也是 docker0:
此时 docker0 上会多了一个 bs2 的 veth 设备 veth7833d44:
当我们从容器 bs1 中 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 地址:
以上的两个容器间的通讯过程示意图如下:
容器与外部网络通讯
同样的, 介绍了容器与容器间的通讯, 容器与主机间的通讯就容易理解了, 如果在容器 bs1 ping 另一个主机 10.168.0.3, 它发出去的数据包经过 docker0 网桥流向 eth0,eth0 在将数据包转发给与之相通的 10.168.0.3, 于是 bs1 就能和其主机节点进行通讯了. 当然如果这个地址不是其他主机节点而是公网地址, 只要 eth0 能到达, 容器 bs1 都能与之通讯. 以下是其示意图:
宿主机与容器通讯
当宿主机访问容器时, 数据包从 docker0 流入到与容器对应的 veth 设备, 在经容器的 eth0 到达到主机中, 示意图如下:
外部访问容器
默认情况, 其他外部网络 (宿主机以外) 无法访问到容器内的端口, 通常的做法是使用 - p 或者 - P 选项 (使用方法参考 docker 基础篇) 来暴露容器中的端口到宿主机上, 外部网络通过访问宿主机的端口从而访问到容器端口. 本质上来说, 该方式是通过 iptables 规则转发实现的, 例如以下启动 nginx 容器并使用宿主机的 8080 映射到容器内部 80 端口:
查看其对应的 iptables 规则:
上述规则表明当访问宿主机的 8080 端口时, NAT 到容器中的 172.17.0.4 的 80 中.
自定义网桥
除了使用默认 docker0 作网桥以为还可以使用 docker network 相关命令自定义网桥, 以下将创建一个 br1 的网络, 子网是 172.30.0.0/16, 网关为 172.30.0.1:
查看 docker 网络:
查看对应网桥:
此时运行一个容器指明网络使用 br1, 此时的容器地址使用的是 172.30.0.0 段的网络, 此时使用的网桥为 br-9e5b3fba2e11, 如下:
三, host 网络模式
Host 模式使用是在容器启动时候指明 --network host, 此时容器共享宿主机的 Network Namespace, 容器内启动的端口直接是宿主机的端口, 并且容器不会创建网卡和 IP, 直接使用宿主机的网卡和 IP, 但是容器内的其他资源是隔离的, 如文件系统, 用户和用户组. 这种模式的好处就是效率高, 因为不需要额外的网络开始, 直接使用宿主机网络. 同样启动一个 nginx, 此时共享主机网络:
此时连入容器内部直接能看到 docker0 与宿主机网卡:
以上的 host 网络模式下的示意图:
四, 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 的网卡:
示意图:
五, none 网络模式
使用 --network none 选项指定其网络模式, 在该模式下虽然容器有着自己的 Network Namespace, 但是容器内没有网卡, IP, 路由信息, 只有一个 lo 回环接口. 如果需要网络配置则需要用户手动进行配置网卡, ip 以及路由信息, 通常这样的容器用于承担某些无需网络介入的工作, 如离线任务, 备份等. 启动一个 none 网络模式的容器如下:
示意图:
总结
本文着重对 docker 单主机网络中默认 bridge 网络模式做了较多介绍, 其核心是通过 docker0 网桥 + Veth Pair 设备实现, 而 host,container 模式都是 "共享" 方式实现, host 网络模式是容器共享宿主机 Network Namespace,continer 网络模式是容器共享已存在的容器的 Network Namespace,none 模式则是网络封闭状态, 容器内使用 lo 接口通讯.
来源: https://www.cnblogs.com/wdliu/p/10441628.html