阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

Docker 网络部分执行流分析(Libnetwork源码解读)

165次阅读
没有评论

共计 6216 个字符,预计需要花费 16 分钟才能阅读完成。

【编者的话】Libnetwork 作为 Docker 网络部分的依赖库,在 Docker 1.9 中正式脱离实验阶段,进入主分支正式投入生产使用阶段。有了新的 Networking 我们可以创建虚拟网络,然后将 container 加入到虚拟网络中,以获得最适合所部署应用的网络拓扑结构。本文将借助于对 Docker 网络部分的源码分析,以对 Libnetwork 做一个详尽的介绍和使用及开发的样例。

Libnetwork 作为 Docker 网络部分的依赖库,在 Docker1.9 中正式脱离实验阶段,进入主分支正式投入生产使用阶段。有了新的 Networking 我们可以创建虚拟网络,然后将 container 加入到虚拟网络中,以获得最适合所部署应用的网络拓扑结构。

1 组件的标准化

为了标准化网络驱动的开发步骤和支持多种网络驱动,Docker 公司在 Libnetwork 中实现了 CNM(Container Network Model)。CNM 主要建立在如下三个组件上。

  • 沙盒(Sandbox):一个沙盒包含了一个容器网络栈的信息。沙盒可以对容器的接口,路由和 DNS 设置等进行管理。沙盒的实现可以是 Linux Network Namespace, FreeBSD Jail 或者类似的机制。一个沙盒可以有多个端点(Endpoint)和多个网络(Network)。
  • 端点(Endpoint):一个端点可以加入一个沙盒和一个网络。端点的实现可以是 veth pair, Open vSwitch 内部端口或者相似的设备。一个端点只可以属于一个网络并且只属于一个沙盒。
  • 网络(Network):一个网络是一组可以直接互相联通的端点。网络的实现可以是 Linux bridge,VLAN 等等。一个网络可以包含多个端点。

2 CNM 对象详细介绍

介绍了 CNM 中三个主要的组件之后,下面对 CNM 中的各个对象做一个详细的介绍。

  • NetworkController:NetworkController 通过暴露给用户创建和管理网络的 API,以便用户对 libnetwork 进行调用。Libnetwork 支持多种驱动,其中包括内置的 bridge,host,container 和 overlay,也对远程驱动进行了支持(即支持用户使用自定义的网络驱动)。
  • Driver:Driver 不直接暴露给用户接口,但是 driver 是真正实现了网络功能的对象。NetworkController 可以对特定 driver 提供特定的配置选项,其选项对 libnetwork 透明。为了满足用户使用需求和开发需求,driver 既可以是内置的类型,也可以是用户指定的远程插件。每一种驱动可以创建自己独特类型的 network,并对其进行管理。在未来可以会对不同插件的管理功能进行整合,以求做到方便管理不同类型的网络。
  • Network:Network 对象是 CNM 的一种实现。NetworkController 通过提供 API 对 Network 对象进行创建和管理。NetworkController 当需要创建或者更新 Network 的时候,它所对应的 Driver 会得到通知。Libnetwork 通过抽象层面对同属于一个网络的各个 endpoint 提供通信支持,同时将其与其他 endpoint 进行隔离。这种通信支持可以是同一主机的,也可以是跨主机的。
  • Endpoint:Endpoint 代表了服务端点。它可以提供不同网络中容器的互联。Endpoint 由创建对应 Sandbox 的驱动进行创建。
  • Sandbox:Sandbox 代表了一个容器中诸如 ip 地址,mac 地址,routes,DNS 等信息的配置。Sandbox 在用户要求在一个 Network 创建 Endpoint 的时候进行创建并分配对应的网络资源。Libnetwork 会用特定的操作系统机制(例如:linux 的 netns)去填充 sandbox 对应的容器中的网络配置。一个 sandbox 中可以有多个连接到不同网络的 endpoint。

3 Libnetwork 工作执行流简介

下面对 libnetwork 的工作流做一个梗概。

  • 1. 指定 network 的驱动和各项相关参数之后调用 libnetwork.New()创建一个 NetWorkController 实例。这个实例提供了各种接口,Docker 可以通过它创建新的 NetWork 和 Sandbox 等。
  • 2. 通过 controller.NewNetwork(networkType, “network1”)来创建指定类型和名称的 Network。
  • 3. 通过 network.CreateEndpoint(”Endpoint1″)来创建一个 Endpoint。在这个函数中 Docker 为这个 Endpoint 分配了 ip 和接口,而对应的 network 实例中的各项配置信息则会被使用到 Endpoint 中,其中包括 iptables 的配置规则和端口信息等。
  • 4. 通过调用 controller.NewSandbox()来创建 Sandbox。这个函数主要调用了 namespace 和 cgroup 等来创建一个相对独立的沙盒空间。
  • 5. 调用 ep.Join(sbx)将 Endpoint 加入指定的 Sandbox 中,则这个 Sandbox 也会加入创建 Endpoint 对应的 Network 中。
    总的来说,Endpoint 是由 Network 创建的,隶属于这个创建它的 Network,当 Endpoint 加入 Sandbox 的时候,就相当于这个 Sandbox 加入到了这个 Network 中来。三者简要的关系如图 1 所示。
    Docker 网络部分执行流分析(Libnetwork 源码解读)

    图 1 Libnetwork 中概念简要关系图

4 Docker 使用 libnetwork 内置驱动的执行流详解

在介绍介绍 Docker 使用 libnetwork 内置驱动的执行流之前,我们先来看一下 Dorker 的默认网络模式(bridge 模式)中,各个设备和容器之间的关系。其简要关系如图 2 所示。

Docker 网络部分执行流分析(Libnetwork 源码解读)

图 2 bridge 模式中各个设备与容器关系

上图中 container1 和 container2 是两个 Docker 容器,veth0 和 veth1,veth2 和 veth3 是两对 veth pair(veth pair 可以用于不同 netns 之间的通信),docker0 是网桥(docker0 可以连接不同的网络),eth0 是宿主机的物理网卡。

图中的 container1 和 container2 都拥有不同的 netns。以 container1 为例,veth pair 中的 veth0 被加入到 container1 的 netns 中,并且被配置 ip 地址等基本信息,则 veth0 就成为了 container1 的一块网卡,与 veth0 所对应的的 veth1 被连接到 docker0 网桥上。Docker0 网桥上还连接了宿主机的物理网卡 eth0。这样 container1 就可以利用 veth0 与 Internet 进行通信了。类似的 container2 也连接到了 docker0 网桥上,显然 container1 与 container2 之间也是可以进行通信的。

结合上面提到的 CNM 中的概念来,我们可以知道 docker0 网桥就是 CNM 中的 network 组件,它可以代表一个网络。Container 所拥有的 netns 就是 CNM 中的一个 sandbox,它是一个独立的网络栈。Veth pair 则是 CNM 中的 endpoint,它的一个端点可以通过加入一个网桥来加入一个 network,另一个端点可以加入一个 sandbox,这样就将这个 sandbox 所对应的容器加入到了这个 network 中。

在了解了各个设备和容器之间的关系和各种设备和 CNM 各种组件的对应关系后,我们来看一下 Docker 是如何与 libnetwork 和 libcontainer 进行协作交互,并且为各个容器创建和配置网络的。Docker 中使用默认模式(bridge 模式)为容器创建和配置网络的过程如图 3 所示。

Docker 网络部分执行流分析(Libnetwork 源码解读)

图 3 网络创建与配置过程

结合图 3,Docker 源码和 libcontainer 源码,我们来详细看一下 Docker 在 bridge 模式下网络创建与配置的过程。

  • 1. 在 Docker daemon 启动的时候,daemon 会创建一个 bridge 驱动所对应的 netcontroller,这个 controller 可以在后面的流程里面对 network 和 sandbox 等进行创建。
  • 2. 接下来 daemon 通过调用 controller.NewNetwork()来创建指定类型(bridge 类型)和指定名称(即 docker0)的 network。Libnetwork 在接收到创建命令后会使用系统 linux 的系统调用,创建一个名为 docker0 的网桥。我们可以在 Docker daemon 启动后,使用 ifconfig 命令看到这个名为 docker0 的网桥。
  • 3. 在 Docker daemon 成功启动后,我们就可以使用 Docker Client 进行容器创建了。容器的网络栈会在容器真正启动之前完成创建。在为容器创建网络栈的时候,首先会取得 daemon 中的 netController,其值就是 libnetwork 中的向外提供的一组接口,即 NetworkController。

    接下来,Docker 会调用 BuildCreateEndpointOptions()来创建此容器中 endpoint 的配置信息。然后再调用 CreateEndpoint()使用上面配置好的信息创建对应的 endpoint。在 bridge 模式中,libnetwork 创建的设备是 veth pair。Libnetwork 中调用 netlink.LinkAdd(veth)进行了 veth pair 的创建,得到的一个 veth 设备是为了 host 所准备的,另一个是为了 sandbox 所准备的。将 host 端的 veth 加入到网桥(docker0)中。然后调用 netlink.LinkSetUp(host),启动主机端的 veth。最后对 endpoint 中的端口映射进行配置。

    从本质上来讲,这一部分所做的工作就是调用 linux 系统调用,进行 veth pair 的创建。然后将 veth pair 的一端,作为 docker0 网桥的一个接口加入到这个网桥中。

  • 4. 创建 SandboxOptions,然后调用 controller.NewSandbox()来创建属于此 container 的新的 sandbox。在接收到 Docker 创建 sandbox 的请求后,libnetwork 会使用系统调用为容器创建一个新的 netns,并将这个 netns 的路径返回给 Docker。
  • 5. 调用 ep.Join(sb)将 endpoint 加入到容器对应的 sandbox 中。先将 endpoint 加入到容器对应的 sandbox 中,然后对 endpoint 的 ip 信息和 gateway 等信息进行配置。
  • 6.Docker 在调用 libcontainer 来启动容器之后,libcontainer 会在容器中的 init 进程初始化容器坏境的时候,将容器中的所有进程都加入到 4 中得到的 netns 中。这样容器就拥有了属于自己独立的网络栈,进而完成了网络部分的创建和配置工作。

5 libnetwork 对插件的支持

正如前面所说,libnetwork 不仅内置了 bridge,host,container 和 overlay 四种驱动,也对第三方插件提供了支持。第三方插件为了对 libnetwork 提供网络支持,需要实现并提供如下的 API。

  • driver.Config
  • driver.CreateNetwork
  • driver.DeleteNetwork
  • driver.CreateEndpoint
  • driver.DeleteEndpoint
  • driver.Join
  • driver.Leave

这些驱动不使用设备的名称作为其 API 的参数,而是使用唯一的 id 作为其参数,如 networkid,endpointid 等。

当用户指定自定义的网络驱动时,Docker 会将 libnetwork 中的驱动设置为 remote。Remote 实际是上并没有进行具体的驱动实现,而是通过调用第三方插件来完成网络的创建,配置和管理工作。libnetwork 与第三方插件的交互��式如图 4 所示。

Docker 网络部分执行流分析(Libnetwork 源码解读)

图 4 libnetwork 与第三方插件交互过程

首先,我们需要手动启动 libnetwork 的第三方插件,这样 libnetwork 才能与第三方插件进行交互工作。

然后,我们在使用 Docker 创建容器的时候,需要指定第三方插件作为 Docker 的网络驱动。当 libnetwork 初始化其中的 remote 启动的时候,就会将第三方插件在 libnetwork 中进行注册,并且写入 NetworkController 当中。Libnetwork 也会通过 socket 来和第三方插件进行连接和通信,并且进行 libnetwork 中所规定的握手协议等,来进行信息交换。

这样 Docker 可以像使用内置驱动一样来调用第三方插件的各种 API。当 libnetwork 接收到 Docker 的调用请求之后,libnetwork 会调用 remote 驱动中所对应的函数进行处理。这些函数都是通过对请求参数的封装,然后编码成 JSON 串的形式,通过之前建立的 socket 连接发送调用请求。

第三方插件在接收到请求之后,会解析请求中的 JSON 串,完成 JSON 串中所请求的操作,然后将执行结果打包成 JSON 串,并且通过 socket 返回给 libnetwork。libnetwork 解析第三方插件发送过来的返回信息,并将执行结果返回给 Docker。

===========================

作者高相林,浙江大学 SEL 实验室硕士研究生,目前在云平台团队从事科研和开发工作。浙大团队对 PaaS、Docker、大数据和主流开源云计算技术有深入的研究和二次开发经验,团队现联合社区将部分技术文章贡献出来,希望能对读者有所帮助。

更多 Docker 相关教程见以下内容

Docker 安装应用(CentOS 6.5_x64) http://www.linuxidc.com/Linux/2014-07/104595.htm 

Ubuntu 14.04 安装 Docker  http://www.linuxidc.com/linux/2014-08/105656.htm 

Ubuntu 使用 VNC 运行基于 Docker 的桌面系统  http://www.linuxidc.com/Linux/2015-08/121170.htm 

阿里云 CentOS 6.5 模板上安装 Docker http://www.linuxidc.com/Linux/2014-11/109107.htm 

Ubuntu 15.04 下安装 Docker  http://www.linuxidc.com/Linux/2015-07/120444.htm 

在 Ubuntu Trusty 14.04 (LTS) (64-bit)安装 Docker http://www.linuxidc.com/Linux/2014-10/108184.htm 

在 Ubuntu 15.04 上如何安装 Docker 及基本用法 http://www.linuxidc.com/Linux/2015-09/122885.htm 

Docker 的详细介绍:请点这里
Docker 的下载地址:请点这里 

本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-05/130864.htm

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-21发表,共计6216字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中