JohnShen's Blog.

Docker 网络综述

字数统计: 1.9k阅读时长: 8 min
2020/03/29 Share

Docker 网络综述

1. Docker 网络模型

Docker的网络架构由三个主要部分构成:CNMContainer 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
2
3
4
5
6
NETWORK ID          NAME                DRIVER              SCOPE
1475f03fbecb bridge bridge local
e2d8a4bd86cb docker_gwbridge bridge local
407c477060e7 host host local
f4zr3zrswlyg ingress overlay swarm
c97909a4b198 none null local

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#宿主机
$ ip a
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
···
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
···
6: vethf49dd9f@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
···

$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242dbecf190 no vethf49dd9f

#容器
$ docker exec -it b9 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
···
inet 127.0.0.1/8 scope host lo
···
5: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
···
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
···

Linux Network Namespace

可以手动实践 Linux Network Namespace,创建两个 Namespace 后创建一对veth pair进行连接测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 创建两个 namespace
$ ip netns add test1
$ ip netns add test2
# 查看 namespace
$ ip netns ls
# 创建一对 veth pair
$ ip link add veth-test1 type veth peer name veth-test2
# 分别置入两个namespace
$ ip link set veth-test1 netns test1
$ ip link set veth-test2 netns test2
# 设置IP
$ ip netns exec test1 ip addr add 192.168.1.1/24 dev veth-test1
$ ip netns exec test2 ip addr add 192.168.1.2/24 dev veth-test2
# 将link从DOWN置为UP
$ ip netns exec test1 ip link set dev veth-test1 up
$ ip netns exec test2 ip link set dev veth-test2 up
# ping测试
$ ip netns exec test1 ping 192.168.1.2
$ ip netns exec test2 ping 192.168.1.1

2 个 namespace 之间可以借助 veth pair通信 ,多个 namespace 之间的通信则可以使用 bridge 来转接,不然每两个 namespace 都去配 veth pair 将会是一件麻烦的事。

3. 单机桥接网络

Linux Docker 创建单机桥接网络采用内置桥接驱动(Windows Docker 采用内置的 NAT 驱动)。默认情况下用户若不指定--network,新建的容器都会连接至该网络。默认的 bridge网络被映射到docker0网桥上。

1
2
docker network inspect bridge | grep com.docker.network.bridge.name
"com.docker.network.bridge.name": "docker0",

可以自己创建一个桥接网络 localnet,Docker 桥接网络 localnet 就与 Linux 上的网桥 br-eef11882ad29 一一对应。新建的容器就可以指定--network localnet使用此网络。

1
2
3
4
5
$ docker network create -d bridge localnet
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242dbecf190 no
br-eef11882ad29 8000.02421164377f no

使用新建的桥接网络和默认 Bridge 的区别在于:默认 Bridge 网络不支持通过 Docker DNS 服务进行域名解析的,但自定义桥接网络可以。因为 test2 运行了一个本地 DNS 解析器,该解析器将请求转发到了 Docker 内部DNS服务器当中。DNS服务器中记录了容器启动时通过--name或者--net-alias参数指定的名称与容器之间的映射关系。

1
2
3
4
5
$ docker run -d --name test1 --network localnet busybox /bin/sh -c "while true;do sleep 3600; done" # 5f37f36c
$ docker run -d --name test2 --network localnet busybox /bin/sh -c "while true;do sleep 3600; done" # fff713dc
$ docker exec -it 5f ping test2
PING test2 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.119 ms

桥接网络中的容器只能与位于相同网络中的容器进行通信。若想绕过此限制,可使用端口映射,不过这种方式会占用宿主机端口。

此外,容器内数据包是如何访问外网的呢?例如连接到 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章

Docker Labs

https://blog.waterstrong.me/docker-networking/

https://coding.imooc.com/class/chapter/189.html#Anchor

https://www.cnblogs.com/bakari/p/10443484.html

https://www.cnblogs.com/charlieroro/p/9897975.html

CATALOG
  1. 1. Docker 网络综述
    1. 1.1. 1. Docker 网络模型
      1. 1.1.1. CNM
      2. 1.1.2. Libnetwork
      3. 1.1.3. 驱动
    2. 1.2. 2. Docker 网络原理基础
      1. 1.2.1. Linux Network Namespace
    3. 1.3. 3. 单机桥接网络
    4. 1.4. 4. 多机覆盖网络
    5. 1.5. 5. DNS服务发现
    6. 1.6. 回顾:Docker 网络基础命令
    7. 1.7. Reference