Docker学习七:网络模型——Libnetwork

一、容器网络标准

Docker 从 1.7 版本开始,便把网络和存储从 Docker 中正式以插件的形式剥离开来,并且分别为其定义了标准,Docker 定义的网络模型标准称之为 CNM (Container Network Model) 。

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

从此,容器的网络标准便分为两大阵营,一个是以 Docker 公司为代表的 CNM,另一个便是以 Google、Kubernetes、CoreOS 为代表的 CNI 网络标准

CNM (Container Network Model) 是 Docker 发布的容器网络标准,意在规范和指定容器网络发展标准,CNM 抽象了容器的网络接口 ,使得只要满足 CNM 接口的网络方案都可以接入到 Docker 容器网络,更好地满足了用户网络模型多样化的需求。

CNM 只是定义了网络标准,对于底层的具体实现并不太关心,这样便解耦了容器和网络,使得容器的网络模型更加灵活。

CNM 定义的网络标准包含三个重要元素:

  • 沙箱(Sandbox):沙箱代表了一系列网络堆栈的配置,其中包含路由信息、网络接口等网络资源的管理,沙箱的实现通常是 Linux 的 Net Namespace,但也可以通过其他技术来实现,比如 FreeBSD jail 等。
  • 接入点(Endpoint):接入点将沙箱连接到网络中,代表容器的网络接口,接入点的实现通常是 Linux 的 veth 设备对。
  • 网络(Network):网络是一组可以互相通信的接入点,它将多接入点组成一个子网,并且多个接入点之间可以相互通信

为了更好地构建容器网络标准,Docker 团队把网络功能从 Docker 中剥离出来,成为独立的项目 libnetwork,它通过插件的形式为 Docker 提供网络功能。Libnetwork 是开源的,使用 Golang 编写,它完全遵循 CNM 网络规范,是 CNM 的官方实现

由于 Kubernetes 逐渐成为了容器编排的标准,而 Kubernetes 最终选择了 CNI 作为容器网络的定义标准,CNM 最终没有成为跨主机容器通信的标准,但是CNM 却为推动容器网络标准做出了重大贡献,且 Libnetwork 也是 Docker 的默认网络实现

二、Libnetwork

Libnetwork 是 Docker 启动容器时,用来为 Docker 容器提供网络接入功能的插件,它可以让 Docker 容器顺利接入网络,实现主机和容器网络的互通

1. 工作过程
  • 第一步,Docker 通过调用 libnetwork.New 函数来创建 NetworkController 实例。NetworkController 是一个接口类型,提供了各种接口,代码如下:
type NetworkController interface {
   // 创建一个新的网络。 options 参数用于指定特性类型的网络选项。
   NewNetwork(networkType, name string, id string, options ...NetworkOption) (Network, error)
   // ... 此次省略部分接口
}
  • 第二步,通过调用 NewNetwork 函数创建指定名称和类型的 Network,其中 Network 也是接口类型,代码如下:
type Network interface {
   // 为该网络创建一个具有唯一指定名称的接入点(Endpoint)
   CreateEndpoint(name string, options ...EndpointOption) (Endpoint, error)

   // 删除网络
   Delete() error
// ... 此次省略部分接口
}
  • 第三步,通过调用 CreateEndpoint 来创建接入点(Endpoint)。在 CreateEndpoint 函数中为容器分配了 IP 和网卡接口。其中 Endpoint 也是接口类型,代码如下:
// Endpoint 表示网络和沙箱之间的逻辑连接。
type Endpoint interface {
   // 将沙箱连接到接入点,并将为接入点分配的网络资源填充到沙箱中。
   // the network resources allocated for the endpoint.
   Join(sandbox Sandbox, options ...EndpointOption) error
   // 删除接入点
   Delete(force bool) error
   // ... 此次省略部分接口
}
  • 第四步,调用 NewSandbox 来创建容器沙箱,主要是初始化 Namespace 相关的资源
  • 第五步,调用 Endpoint 的 Join 函数将沙箱和网络接入点关联起来,此时容器就加入了 Docker 网络并具备了网络访问能力
2. 网络模型

Libnetwork 比较典型的网络模式主要有四种:

  • null 空网络模式:可以构建一个没有网络接入的容器环境,以保障数据安全。
  • bridge 桥接模式:可以打通容器与容器间网络通信的需求。
  • host 主机网络模式:可以让容器内的进程共享主机网络,从而监听或修改主机网络。
  • container 网络模式:可以将两个容器放在同一个网络命名空间内,让两个业务通过 localhost 即可实现访问。
a. null 空网络模式

该模式下的容器就像一个没有联网的电脑,处于一个相对较安全的环境,确保数据不被他人从网络窃取

使用 Docker 创建 null 空网络模式的容器时,容器拥有自己独立的 Net Namespace,但是此时的容器并没有任何网络配置。在这种模式下,Docker 除了为容器创建了 Net Namespace 外,没有创建任何网卡接口、IP 地址、路由等网络配置。

# docker run 命令启动时,添加 --net=none 参数启动一个空网络模式的容器
$ docker run --net=none -it busybox

# 查看一下容器内网络配置信息
/ # ifconfig
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
# 除了 Net Namespace 自带的 lo 网卡并没有创建任何虚拟网卡

# 查看一下容器内的路由信息
/ # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
# 容器内没有配置任何路由信息
b. bridge 桥接模式

启动容器时默认的网络模式,使用 bridge 网络可以实现容器与容器的互通,可以从一个容器直接通过容器 IP 访问到另外一个容器。同时使用 bridge 网络可以实现主机与容器的互通,在容器内启动的业务,可以从主机直接请求。

Docker 的 bridge 模式基于Linux 的 veth 和 bridge 这两种技术

  • Linux veth: Linux 中的虚拟设备接口,veth 都是成对出现的,它在容器中,通常充当一个桥梁。veth 可以用来连接虚拟网络设备,例如 veth 可以用来连通两个 Net Namespace,从而使得两个 Net Namespace 之间可以互相访问。
  • Linux bridge:一个虚拟设备,是用来连接网络的设备,相当于物理网络环境中的交换机。Linux bridge 可以用来转发两个 Net Namespace 内的流量。
Lark20200929-162853.png

bridge 就像一台交换机,而 veth 就像一根网线,通过交换机和网线可以把两个不同 Net Namespace 的容器连通,使得它们可以互相通信

Docker 启动时,libnetwork 会在主机上创建 docker0 网桥,docker0 网桥就相当于图 1 中的交换机,而 Docker 创建出的 brige 模式的容器则都会连接 docker0 上,从而实现网络互通

c. host 主机网络模式

host 主机网络模式通常适用于想要使用主机网络,但又不想把运行环境直接安装到主机上的场景中。

使用 host 主机网络模式时:

  • libnetwork 不会为容器创建新的网络配置和 Net Namespace。
  • Docker 容器中的进程直接共享主机的网络配置,可以直接使用主机的网络信息,此时,在容器内监听的端口,也将直接占用到主机的端口。
  • 除了网络共享主机的网络外,其他的包括进程、文件系统、主机名等都是与主机隔离的。
# 启动一个主机网络模式的 busybox 镜像
$ docker run -it --net=host busybox

# 查看一下容器内的网络环境
/ # ip a
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
2: eth0: <BROADCAST,MULTICAST,UP,LOWER\_UP> mtu 1500 qdisc pfifo\_fast qlen 1000
link/ether 02:11:b0:14:01:0c brd ff:ff:ff:ff:ff:ff
inet 172.20.1.11/24 brd 172.20.1.255 scope global dynamic eth0
valid\_lft 85785286sec preferred\_lft 85785286sec
inet6 fe80::11:b0ff:fe14:10c/64 scope link
valid\_lft forever preferred\_lft forever
3: docker0: \<NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue
link/ether 02:42:82:8d:a0:df brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid\_lft forever preferred\_lft forever
inet6 fe80::42:82ff:fe8d:a0df/64 scope link
valid\_lft forever preferred\_lft forever
# 容器内的网络环境与主机完全一致
d. container 网络模式

container 网络模式允许一个容器共享另一个容器的网络命名空间。当两个容器需要共享网络,但其他资源仍然需要隔离时就可以使用 container 网络模式。

# 启动一个 busybox1 容器
$ docker run -d --name=busybox1 busybox sleep 3600

# 查看一下网络配置
$ docker exec -it busybox1 sh
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02
inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:11 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:906 (906.0 B) TX bytes:0 (0.0 B)

lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
#  busybox1 的 IP 地址为 172.17.0.2

# 新打开一个命令行窗口,再启动一个 busybox2 容器,通过 container 网络模式连接到 busybox1 的网络
$ docker run -it --net=container:busybox1 --name=busybox2 busybox sh

# 查看一下 busybox2 容器内的网络配置
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02
inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:14 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1116 (1.0 KiB) TX bytes:0 (0.0 B)

lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
# busybox2 容器的网络 IP 也为 172.17.0.2,与 busybox1 的网络一致

以下是四种网络模式的对比:
Lark20200929-162901.png

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值