Docker 网络综述
1. Docker 网络模型
Docker的网络架构由三个主要部分构成:CNM
(Container Networking Mode 容器网络模型)、Libnetwork
、驱动
。CNM 是设计标准,该方案是开源且支持插接式连接。而 Libnetwork 是 CNM 的具体实现,通过 Go 语言编写实现了 CNM 中列举的核心组件。驱动则以实现特定网络拓扑的形式达成模型扩展。
CNM
CNM 中定义了几个基本元素:
Sandbox 沙盒:Sandbox 作为一个独立的网络栈,管理着网络接口、DNS配置、路由表等。一个 Sandbox 可以包含来自多个网络的不同 Endpoint。Snadbox 的典型实现为 Linux Network Namespace。
Endpoint 终端:负责将 Sandbox 连接至网络。其意义就在于服务可使用不同类型的 Driver,不必关心与网络的连接方式。
Network 网络:CNM 并没有明确定义 Network 的形式,其典型实现可以是 Linux 网桥或者 VLAN。Network 就是需要进行交互的 Endpoint 的集合。
Libnetwork
早期 Docker 的网络部分集中在 Daemon 上,后续 Docker 将网络部分从其中拆分并重构一个叫作 Libnetwork 的类库,实现了 CNM 中定义的全部三个组件,还实现了网络控制层和管理层的功能。
驱动
驱动负责处理网络连通性和隔离性,Docker 内置的驱动包括:bridge、overlay、MACVLAN、host、none。
默认情况下,none 、host、bridge 会在每个 Docker host 中存在且无法被移除。当初始化 Swarm 时,两个额外的网络:名为docker_gwbridge
的 bridge 网络和名为ingress
的 overaly 会自动创建以进行集群通信。
1 | NETWORK ID NAME DRIVER SCOPE |
Docker 内置网络驱动借助于 Linux 网络模块,如Linux bridges, network namespaces, veth pairs, iptables 来实现复杂网络的转发规则、分隔、管理。
2. Docker 网络原理基础
Docker 启动时会在宿主机建立一个名为docker0
的虚拟网桥,当启动容器时会基于 docker0
网段划分私有IP,容器内部对应的接口是eth0
。
创建容器时的网络处理如下:
创建一对
veth pair
放入宿主机和容器中宿主机一端桥接到默认的
docker0
网桥,具有唯一名称 vethf49dd9f (veth*
)进入容器中的一端名称改为
eth0
,该网络接口只在容器内可见。从网桥的可用地址中获取一个空闲地址分配给容器中的
eth0
,并配置默认路由到桥接网卡veth*
。
当容器销毁时,容器内的eth0
会随网络命名空间一起被清除,veth*
接口也会从docker0
中卸载。
1 | #宿主机 |
Linux Network Namespace
可以手动实践 Linux Network Namespace,创建两个 Namespace 后创建一对veth pair
进行连接测试。
1 | # 创建两个 namespace |
2 个 namespace 之间可以借助 veth pair
通信 ,多个 namespace 之间的通信则可以使用 bridge 来转接,不然每两个 namespace 都去配 veth pair
将会是一件麻烦的事。
3. 单机桥接网络
Linux Docker 创建单机桥接网络采用内置桥接驱动(Windows Docker 采用内置的 NAT 驱动)。默认情况下用户若不指定--network
,新建的容器都会连接至该网络。默认的 bridge
网络被映射到docker0
网桥上。
1 | docker network inspect bridge | grep com.docker.network.bridge.name |
可以自己创建一个桥接网络 localnet,Docker 桥接网络 localnet 就与 Linux 上的网桥 br-eef11882ad29 一一对应。新建的容器就可以指定--network localnet
使用此网络。
1 | $ docker network create -d bridge localnet |
使用新建的桥接网络和默认 Bridge 的区别在于:默认 Bridge 网络不支持通过 Docker DNS 服务进行域名解析的,但自定义桥接网络可以。因为 test2 运行了一个本地 DNS 解析器,该解析器将请求转发到了 Docker 内部DNS服务器当中。DNS服务器中记录了容器启动时通过--name
或者--net-alias
参数指定的名称与容器之间的映射关系。
1 | $ docker run -d --name test1 --network localnet busybox /bin/sh -c "while true;do sleep 3600; done" # 5f37f36c |
桥接网络中的容器只能与位于相同网络中的容器进行通信。若想绕过此限制,可使用端口映射,不过这种方式会占用宿主机端口。
此外,容器内数据包是如何访问外网的呢?例如连接到 docker0 的容器想要访问外网,宿主机的 eth0 可以访问外网。在 docker0 和 eth0 有一层 NAT(网络地址转换),将经由 docker0 的地址转化为经由 eth0 的地址。NAT 是由 iptables
实现的。
4. 多机覆盖网络
5. DNS服务发现
在 Swarm 中服务可以通过名称相互定位,其底层实现就是利用了 Docker 内置的 DNS 解析器,为每个容器提供 DNS 解析功能。以 c1 容器中 ping c2 为例:
- ping c2 会调用本地 DNS 解析器,每个 Docker 容器都有本地 DNS 解析器。
- 若本地解析器的本地缓存没有找到 c2 对应的 IP,本地解析器会向 Docker DNS 服务器发起递归查询,本地解析器会预先配置好 Docker DNS 服务器信息的。
- Docker DNS 服务器记录全部容器名称与 IP 的映射关系,容器名称是在创建时通过
--name
或者--net-alias
参数设置的。 - Docker DNS 服务将 c2 IP 发送 c1 本地解析器。
- 发送 ping 命令至 c2。
服务发现受网络限制的,DNS 解析只对位于同一网络中的容器和服务生效。如果两个容器在不同的网络,那么就不能互相解析。
回顾:Docker 网络基础命令
2 | |
---|---|
docker network ls | 列出运行在本地 Docker 上的全部网络 |
docker network create | 创建新的Docker网络,可使用-d 参数指定驱动 |
docker network inspect | Docker 网络详细配置信息 |
docker network prune | 删除 Docker 主机上全部未使用的网络 |
docker network rm | 删除 Docker 主机上指定网络 |
Reference
深入浅出 Docker 第11章、第12章
https://blog.waterstrong.me/docker-networking/
https://coding.imooc.com/class/chapter/189.html#Anchor