Docker 网络与 Libnetwork

一、容器网络概述

1. CNM

  • Docker 从 1.7 版本开始,便把网络和存储从 Docker 中正式以插件的形式剥离开来,并且分别为其定义了标准。Docker 定义的网络模型标准称之为 CNM(Container Network Model)
  • CNM 抽象了容器的网络接口,使得只要满足 CNM 接口的网络方案都可以接入到 Docker 容器网络,更好地满足了用户网络模型多样化的需求
  • CNM 定义的网络标准包含三个重要元素:
    • 沙箱(Sandbox):沙箱代表了一系列网络堆栈的配置,包含路由信息、网络接口等网络资源的管理。沙箱的实现通常是 Linux 的 Net Namespace,也可以通过其他技术来实现,比如 FreeBSD jail
    • 接入点(Endpoint):接入点将沙箱连接到网络中,代表容器的网络接口。接入点的实现通常是 Linux 的 veth 设备对
    • 网络(Network):网络是一组可以互相通信的接入点,它将多个接入点组成一个子网
  • Docker 团队把网络功能从 Docker 中剥离出来,成为独立的项目 libnetwork,它通过插件的形式为 Docker 提供网络功能。Libnetwork 是开源的,使用 Golang 编写,它完全遵循 CNM 网络规范,是 CNM 的官方实现

Docker 推出 CNM 的同时,CoreOS 推出了 CNI(Container Network Model)。起初,以 Kubernetes 为代表的容器编排阵营考虑过使用 CNM 作为容器的网络标准,但是后来由于很多技术和非技术原因,Kubernetes 决定支持 CoreOS 推出的容器网络标准 CNI

2. Libnetwork

  • Libnetwork 是 Docker 启动容器时,用来为 Docker 容器提供网络接入功能的插件。它可以让 Docker 容器顺利接入网络,实现主机和容器网络的互通
  • 第一步,Docker 通过调用 libnetwork.New 创建 NetworkController 实例
  • 第二步,通过调用 NewNetwork 创建指定名称和类型的 Network
1
2
3
4
5
type NetworkController interface {
// 创建一个新的网络。options参数用于指定类型的网络选项
NewNetwork(networkType, name string, id string, options ...NetworkOption) (Network, error)
// ......
}
  • 第三步,通过调用 CreateEndpoint 来创建接入点,并为容器分配 IP 和网卡接口
1
2
3
4
5
6
7
type Network interface {
// 为该网络创建一个具有唯一名称的接入点(Endpoint)
CreateEndpoint(name string, options ...EndpointOption) (Endpoint, error)
// 删除网络
Delete() error
// ......
}
  • 第四步,通过调用 NewSandbox 创建容器沙箱,主要是初始化 Namespace 相关的资源
  • 第五步,通过调用 Join 将沙箱和网络接入点关联起来,此时容器就加入了 Docker 网络并具备了网络访问能力
1
2
3
4
5
6
7
type Endpoint interface {
// 将沙箱连接到接入点,并将为接入点分配的网络资源填充到沙箱中
Join(sandbox Sandbox, options ...EndpointOption) error
// 删除接入点
Delete(force bool) error
// ......
}

二、Libnetwork 常见网络模式

1. null 空网络模式

  • 一个隔离的网络环境,容器有自己独立的 Net Namespace,但没有创建任何网卡接口、IP 地址、路由等网络配置
1
2
3
4
5
6
7
8
9
10
11
$ docker run --net=none -it busybox
/ # ip a # 只有lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
/ # route -n # 无路由信息
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface

2. bridge 桥接模式

  • 默认的网络模式,使用 bridge 网络可以实现容器与容器的互通以及主机与容器的互通
  • 底层原理:
    • Linux veth:一个虚拟设备接口,成对出现,它在容器中通常充当一个桥梁,用来连接虚拟网络设备。可以用来连通两个 Net Namespace
    • Linux bridge:一个用来连接网络的虚拟设备,相当于物理网络环境中的交换机。可以用来转发两个 Net Namespace 内的流量
    • bridge 就像一台交换机,而 veth 就像一根网线,通过交换机和网线可以把两个不同 Net Namespace 的容器连通,使得它们可以互相通信

bridge-veth

  • Docker 启动时,Libnetwork 会在主机上创建 docker0 网桥,docker0 网桥就相当于上图的交换机,而 Docker 创建出的 brige 模式的容器则都会连接 docker0 上,从而实现网络互通
1
2
3
4
5
6
$ ip a
......
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:84:78:c0:dc brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever

3. host 主机网络模式

  • 使用 host 主机网络模式时:
    • Libnetwork 不会为容器创建新的网络配置和 Net Namespace
    • Docker 容器中的进程直接共享主机的网络配置,可以直接使用主机的网络信息,此时在容器内监听的端口,也将直接占用到主机的端口
    • 除了共享主机的网络外,其他的包括进程、文件系统、主机名等都是与主机隔离的
1
2
3
4
5
$ docker run -it --net=host busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue

4. container 网络模式

  • 允许一个容器共享另一个容器的网络命名空间。当两个容器需要共享网络,但其他资源仍然需要隔离时就可以使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ docker run -d --name=busybox1 busybox sleep 3600
$ docker exec -it busybox1 sh
/ # ip a
4: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever

$ docker run -it --net=container:busybox1 --name=busybox2 busybox sh
/ # ip a
4: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever