Date

本周为 CNM(Container Network Model) 和 CNI(Container Network Interface)。Kubernetes 采用的是 CNI

libnetwork design

The goal of libnetwork is to deliver a robust Container Network Model that provides a consistent programming interface and the required network abstractions for applications.

libnetwork 实现了 CNM。它为 Docker daemon 和网络驱动程序之间提供了接口。网络控制器(NetworkController)负责将驱动(Driver)和网络(Network)进行对接。每个驱动程序负责管理它所拥有的网络以及为该网络提供服务,例如 IPAM。网络驱动可以按提供方被划分为原生驱动(libnetwork 内置或 Docker 支持的)或者远程驱动(第三方插件)。原生驱动包括 none, bridge, overlay 以及 MACvlan。驱动也可以被按照作用范围被划分为本地(单主机)的和全局的(多主机)。CNM还支持标签(labels),以 key-value 形式定义的元数据。用户可以通过定义 label 这样的元数据来自定义 libnetwork 和驱动的行为

Components

CNM 提供了为容器添加网络所需的步骤,同时提供了可用于支持多个网络驱动程序的抽象。它建立在三个组件上,如下图

Sandbox

Sandbox 包含容器网络栈(container network stack)的配置。这包括了容器的网络接口管理,路由表、DNS 的设置。Sandbox 的实现可以是 Linux Network Namespace,FreeBSD Jail 或其他类似概念。Sandbox 可以包含来自多个网络的许多 Endpoint,如上图

Endpoint

Sandbox 通过 Endpoint 加入 Network。Endpoint 的实现可以是 veth pair,Open vSwitch internal port 或者类似的概念。它只能属于一个 Network,但可以属于不止一个 Sandbox(An Endpoint can belong to only one network but may only belong to one Sandbox)

Network

Network 是一组可以直接互相通信的 Endpoint。其实现可以是 Linux 网桥、VLAN 等。Network 由许多 Endpoint 组成

CNM Objects

  • NetworkController: 提供了 API 来分配和管理 Network,允许将指定的 Driver 绑定至给定 Network
  • Driver: Network 正常工作的背后支撑。NetworkController 提供了 API 对 Driver 进行设置。Driver 分为原生(such as Bridge, Host, None & overlay) 和远程(from plugin providers) 两种以满足各种使用场景和部署方案
  • Network: 此对象实现了 CNM 的 Network 组件。NetworkController 提供 API 来创建和管理 Network 对象。每当创建或更新 Network 时,都会通知对应的 Driver。libnetwork 以一种抽象级别对待 Network 对象来提供属于同网络的一组 Endpoint 间的连接,并与其他 Endpoint 进行隔离。Driver 为连接和隔离提供实际的工作。连接可以位于同一个主机也可以跨多主机。因此 Network 在集群中有全局的作用范围
  • Endpoint: Endpoint 表示服务端点(Service Endpoint)。它为 Network 中容器所暴露的服务提供连接。Network 对象提供 API 去创建和管理 Endpoint。一个 Endpoint 只能连接到一个 Network。由于 Endpoint 代表 Service 而不一定是某特定的容器,因此 Endpoint 在集群内也是全局的
  • Sandbox: Sandbox 对象表示容器的网络配置,比如 IP 地址、MAC 地址、路由、DNS。当请求在 Network 上创建 Endpoint 时,会创建一个 Sandbox 对象。Driver 负责分配其必须的网络资源(比如 IP 地址),并将名为 SandboxInfo 的信息传回 libnetwork。libnetwork 将使用操作系统特定的结构(例如:Linux 的 netns)将网络配置填充到由 Sandbox 代表的容器中。Sandbox 可以将多个 Endpoint 连接到不同的 Network。由于 Sandbox 与给定主机中的特定容器相关联,因此它具有代表容器所属主机的本地作用域

Container Network Interface Specification

注意我所阅读的 Specification 版本为 0.4.0-dev,它包含了一些尚未发布的改变。本文存在 中译

CNI 旨在为容器给出一种通用的基于插件的网络解决方案。它源于 rkt Networking Proposal

定义两个术语:

  • 容器(container) 可以被认为是 Linux network namespace 的同义词。它对应的单元取决于特定的容器运行时实现。例如,实现了 App Container Spec 的 rtk 中,每个 pod 运行在一个独立的 network namespace 中。而在 Docker中,network namespace 存在于每个独立的容器中
  • 网络(network) 指一组能独立寻址并且可以交互的实体。可以是一个独立的容器,一台主机,或者其他的网络设备(比如路由器)。容器可以添加至一个或多个网络或者从中删除

General considerations

  • 容器运行时(container runtime)必须在调用任何插件之前为容器创建一个新的网络名称空间
  • 运行时(runtime)必须确定该容器应属于哪个网络,并为每个网络确定哪些插件必须执行
  • 网络配置采用 JSON 格式,其中包含如 "name", "type" 等必填字段。允许其中的字段在不同的调用间发生改变
  • 容器运行时必须按顺序为每个网络执行相应的插件,将容器添加到每个网络
  • 容器生命周期结束后,运行时必须以相反的顺序执行插件(相对于执行它们添加容器的顺序)以将容器与网络断开连接
  • 容器运行时不能为同一容器的操作并行执行,但可以为不同容器的操作并行执行
  • 容器运行时必须为容器指定 ADD 和 DEL 操作。ADD 总是最后跟着一个相应的 DEL。DEL 允许跟着额外的 DEL,但是插件应该允许处理多个DEL(插件 DEL 应当幂等)
  • 容器必须由 ContainerID 唯一标识。存储状态的插件使用 (network name, CNI_CONTAINERID, CNI_IFNAME)作为主键
  • 对于相同的 (network name, container id, name of the interface inside the container) 运行时不能调用 ADD 两次。这意味着指定的容器 ID 当且仅当每次 ADD 都使用不同的接口名称时才能添加到同一网络多次

CNI Plugin

每个 CNI 插件都是以能被容器管理系统(比如,rkt或者Docker)调用的可执行文件的形式存在。CNI 插件负责将网络接口插入容器网络命名空间(例如 veth pair 中的一端),并且在宿主机中进行一些必要的配置(比如将 veth pair 的另一端加入 bridge 中)。接着通过调用适当的 IPAM 插件,将 IP 赋给 interface 并且设置路由

CNI 插件必须支持下面的操作

  • ADD: Add container to network
  • DEL: Delete container from network
  • GET: Get container network configuration
  • VERSION: Report version

这里原文给出了参数和返回值字段即结构

Network Configuration

以 JSON 格式描述,字段信息参见原文。多个 Network Configuration 可以组成 Network Configuration List

IP Allocation

作为操作的一部分,CNI 插件需要为接口分配并维护 IP 地址,并且还要配置一些和此接口相关的路由。这给了CNI插件很大的灵活性,但也给它带来了很大的负担。许多插件需要重复使用相同的代码来支持用户可能需要的多种 IP 管理方案

为了减轻各个插件的负担,并且将 IP 管理的功能独立出来,定义了第二种插件类型 IP Address Management(IPAM)插件。这样其他插件的任务就是在适当的执行过程中调用相应的 IPAM 插件。IPAM 插件用于确定接口的地址、子网、网关,路由并且将这些信息返回给插件

Why Kubernetes doesn’t use libnetwork

  1. Kubernetes 是一个支持多种容器运行环境的系统,Docker 仅是其中之一
  2. CNM 是为 Docker 自身设计的,Kubernetes 如果去使用会增加系统的复杂度。可见原文中对于 driver 还有服务发现实现的讨论
  3. CNI 和 Kubernetes 在设计哲学上是一致的。它比 CNM 简单,不需要守护进程,并且跨平台。这意味着同一个网络配置可以在多个运行环境下使用(例如 Docker, rkt 和 Hyper)
  4. 包装 CNI 插件来实现一个更加定制化的插件是非常简单的
  5. Docker 开发人员对于第三方系统开发所面临的问题态度不够积极