首页
/ Moby libnetwork 容器网络模型(CNM)设计解析:Sandbox、Endpoint、Network 与 Driver 扩展机制

Moby libnetwork 容器网络模型(CNM)设计解析:Sandbox、Endpoint、Network 与 Driver 扩展机制

2026-09-04 17:20:35作者:乔或婵

本文以 Moby 仓库中 daemon/libnetwork/docs/design.md 设计文档为主体,系统讲解 libnetwork 如何基于容器网络模型(Container Network Model, CNM)实现容器网络抽象:Sandbox、Endpoint、Network 三要素的定义与关系、NetworkController 与各 Driver 的职责划分、完整的对象生命周期,以及当前仓库中驱动接口、Linux Sandbox 实现的源码级对应关系。读完本文,你可以理解 Docker 引擎背后容器网络的抽象层是如何组织的,并能在阅读 daemon/libnetwork 源码时快速定位关键对象与调用链。

CNM 模型:Sandbox 通过 Endpoint 连接到 Network

设计目标与背景

设计文档开宗明义:libnetwork 遵循 Docker 与 Linux 的哲学——开发小型、高度模块化且可组合、并且能独立良好工作的组件(small, highly modular and composable tools)。libnetwork 的目标就是满足容器网络场景下的这种可组合需求,即提供一个稳定的抽象层,让具体的网络实现(网桥、VLAN、VXLAN 等)以驱动的形式可插拔地接入。

文档同时指出,libnetwork 的许多设计决策源自 Docker v1.6 时代网络设计的经验教训;更早的设计可以在 Docker v1.6 网络设计文档 中查阅。理解这一背景有助于把握 CNM 各概念为何被这样划分。

容器网络模型(CNM):Sandbox、Endpoint、Network

libnetwork 实现的核心是 Container Network Model(CNM):它把“为容器提供网络”这件事形式化为几个固定步骤,并给出一套抽象,使得同一套上层 API 可以支撑多种不同的网络驱动。CNM 建立在 3 个主要组件之上(如文首图片所示):

Sandbox(沙箱)

  • 包含一个容器网络栈的完整配置:容器接口的管理、路由表、DNS 设置;
  • 一种典型的实现是 Linux Network Namespace,也可以是 FreeBSD Jail 或其他类似概念;
  • 一个 Sandbox 可以包含来自多个网络多个 Endpoint。

Endpoint(端点)

  • 负责把一个 Sandbox 连接到某个 Network;
  • 一种典型的实现是 veth 对、Open vSwitch 内部端口或类似机制;
  • 约束很明确:一个 Endpoint 只能属于一个网络,且在已连接的情况下只能属于一个 Sandbox

Network(网络)

  • 是一组可以直接互相通信的 Endpoint 的集合;
  • 实现可以是 Linux 网桥、VLAN 等;
  • 一个网络包含多个 Endpoint。

三者组合起来表达了 CNM 的核心不变量:容器(Sandbox)多网卡、多网络接入的能力——一个容器可以同时接入 bridge、overlay 等多个网络,每个接入关系由一个独立的 Endpoint 表示。

CNM 对象:Controller、Driver、Network、Endpoint、Sandbox

这一节逐一对应文档中“CNM Objects”部分定义的五个对象及其语义边界。

NetworkController

NetworkController 是进入 libnetwork 的入口点,向 Docker Engine 等上层使用者暴露简单 API,用于分配和管理网络。libnetwork 支持多个同时活动的驱动(内建的与远程的),NetworkController 允许使用者把特定的驱动绑定到特定的网络。

在源码中,Controller 由 libnetwork.New() 创建,入口在 controller.go#L147;创建网络的核心 API 是 Controller.NewNetwork(),位于 controller.go#L503,其签名接收 networkTypename,并支持变参的 NetworkOption 传入驱动级选项。

Driver(驱动)

Driver 不是面向使用者的可见对象,但驱动提供了实际的网络实现。NetworkController 提供 API 让用户以驱动特定的选项/标签(对 libnetwork 透明、由驱动直接处理)来配置驱动。驱动既可以是内建的(Bridge、Host、None、overlay 等),也可以是远程的(由插件提供者实现),以覆盖各种用例与部署场景。文档特别强调:在这一阶段,驱动拥有一个网络,并负责该网络的全部管理(包括 IPAM 等);未来可以由多个驱动分别参与不同的网络管理功能。

当前仓库中,Linux 平台下内建驱动的注册逻辑集中在 drivers_linux.go#L22-L45registerNetworkDrivers():依次注册 bridgehostipvlanmacvlannulloverlay 六个网络类型。可以看出,相对设计文档最初列出的 null/bridge/overlay/remote,仓库后来把 host、macvlan、ipvlan 也纳入了内建驱动集合,这正是“驱动可插拔注册”这一设计的直接体现。

Network

Network 对象是前文 CNM : Network 的实现。NetworkController 提供 API 创建和管理 Network;每当网络被创建或更新,对应的 Driver 都会收到该事件的通知。libnetwork 以抽象层面对待 Network 对象——提供“属于同一网络的一组端点之间的连通性”以及“与其余部分的隔离”;而实际提供连通性与隔离的工作由 Driver 完成。连通性既可以在同一主机内,也可以跨多主机,因此 Network 在集群内具有全局作用域

Endpoint

Endpoint 代表一个服务端点(Service Endpoint),为容器在某个网络中暴露的服务、与网络中其他容器提供的服务之间提供连通。Network 对象提供 API 创建和管理 Endpoint。一个 Endpoint 只能附加到一个网络。Endpoint 的创建会调用到对应网络的 Driver,由驱动负责为该 Sandbox 分配所需资源。由于 Endpoint 表达的是服务而非某个特定容器,它同样具有集群内全局作用域

Sandbox

Sandbox 对象表达容器的网络配置:IP 地址、MAC 地址、路由、DNS 条目。当用户请求在某网络创建 Endpoint 时,会创建 Sandbox 对象。处理该网络的 Driver 负责分配所需网络资源(如 IP 地址),并把结果(SandboxInfo)传回 libnetwork;libnetwork 再利用操作系统特定机制(Linux 上即 netns)把网络配置写入容器。一个 Sandbox 可以附加多个连接到不同网络的 Endpoint。由于 Sandbox 关联的是某台主机上的某个容器,它具有本地作用域——即容器所在的那台 Host。

Options 与 Labels

这两个机制是驱动与用户之间的配置通道:

  • Options:向 Driver 直接传递驱动特定配置选项的通用、灵活机制。Options 就是键值对:key 是字符串,value 是通用对象(如 Go 的 interface{})。libnetwork 只有在 key 命中 net-labels 包中定义的“知名标签”(well-known Labels)时才会对 Options 进行操作;Options 在语义上包含下面介绍的 LabelsOptions 通常不面向最终用户(不在 UI 中展示)。
  • Labels:与 Options 非常相似,实际上就是 Options 的子集。Labels 通常面向最终用户可见,在 UI 中通过 --labels 选项显式表达,从 UI 传递到 Driver,供驱动执行驱动特定操作(例如指定从哪个子网分配 IP 地址)。

在当前仓库中,知名标签的定义位于 daemon/libnetwork/netlabel 包(文档中的 net-labels 即其前身命名),IPAM 相关子网配置的完整设计另见 IPAM 设计文档

CNM 生命周期:从驱动注册到网络删除

CNM 的使用者(如 Docker)通过 CNM 对象及其 API 来为所管理的容器配置网络。文档给出的生命周期共 9 个步骤,这里完整保留并结合当前源码逐条说明:

  1. 驱动注册DriversNetworkController 注册。内建驱动在 libnetwork 内部注册(对应 drivers_linux.go 中的 registerNetworkDrivers),远程驱动则通过插件机制注册(文档标注 plugin-mechanism 当时仍在完善中)。每个 driver 处理一种 networkType
  2. 创建控制器:使用 libnetwork.New() API 创建 NetworkController 对象,管理网络的分配,并可任选地以驱动特定 Options 配置驱动。源码入口见 controller.go#L147
  3. 创建网络:使用控制器的 NewNetwork() API,提供 namenetworkType 创建 NetworknetworkType 用于选择对应的 Driver,并把创建的 Network 绑定到该驱动;从此,对该 Network 的任何操作都由该驱动处理。
  4. 驱动选项controller.NewNetwork() 还接受可选的 options 参数,携带驱动特定选项与 Labels,供驱动自行使用。
  5. 创建端点network.CreateEndpoint() 在指定网络中创建新 Endpoint,可附带可选 options(其中既可能包含知名标签,也可能包含驱动特定标签)。驱动随后被以 driver.CreateEndpoint 调用,它可以在 Endpoint 创建时预留 IPv4/IPv6 地址。驱动通过 driverapi 中定义的 InterfaceInfo 接口给端点分配地址——这些地址是完成端点作为“服务定义”所必需的,因为服务端点本质上就是“网络地址 + 应用容器监听的端口号”。 在源码中,InterfaceInfo 的当前定义见 driverapi.go#L167-L193,包含 SetMacAddressSetIPAddressAddress()NetnsPath()SetCreatedInContainer 等方法;以 bridge 驱动为例,其 CreateEndpoint 实现在 bridge_linux.go#L1061
  6. 加入端点endpoint.Join() 用于把容器附加到 Endpoint。若该容器的 Sandbox 尚不存在,Join 操作会创建一个。驱动可以利用 Sandbox Key 来识别附加到同一容器上的多个端点。该 API 同样接受可选 options。 文档特别给出两点工程建议与 FAQ:
    • 强烈建议 Docker 之类的上层在容器 Start() 生命周期中、容器进入运行状态之前调用 endpoint.Join(),Docker 集成会负责这一点;
    • 为什么需要“创建 Endpoint”和“加入 Endpoint”两个 API? 答案是:Endpoint 表达的是服务,服务可能有、也可能没有容器作为载体。Endpoint 创建时资源即被预留,任何容器之后都可以附加到该端点并获得一致的网络行为。 bridge 驱动的 Join 实现可参考 bridge_linux.go#L1420
  7. 离开端点:容器停止时可调用 endpoint.Leave(),驱动借此清理 Join() 期间分配的状态。当最后一个引用某 Sandbox 的端点离开网络时,libnetwork 会删除该 Sandbox;但只要端点仍然存在,libnetwork 会保留 IP 地址,并在容器(或任何容器)再次加入时复用——这保证了容器 Stop/Start 后资源被复用而非重新分配。
  8. 删除端点endpoint.Delete() 从网络中删除端点,同时清理缓存的 sandbox.Info
  9. 删除网络network.Delete() 用于删除网络;若网络上还存在任何已附加的端点,libnetwork 不会允许删除继续执行。

实现细节:Network/Endpoint 的委托与 Sandbox 的 Linux 实现

Networks & Endpoints:libnetwork 的 Network 与 Endpoint API 主要用于管理对应对象并做簿记(book-keeping),以提供 CNM 所需的抽象层;实际实现被委托给驱动,由驱动兑现 CNM 承诺的功能。

Sandbox:libnetwork 提供了一个跨多操作系统实现 Sandbox 的框架。设计文档写作时,Linux 上的 Sandbox 实现在 sandbox 包中的 namespace_linux.goconfigure_linux.go;每个 Sandbox 对应一个网络命名空间(Network Namespace),由宿主文件系统上的唯一路径标识。接口从全局 namespace 移入 Sandbox namespace、以及命名空间内路由表的管理,都通过 Netlink 调用完成。

需要指出的是,这一描述对应的是文档写作时期的代码布局:从当前源码结构看,Sandbox 相关实现已经上移到 daemon/libnetwork 顶层的 sandbox.gosandbox_linux.gosandbox_store.go 等文件中,netns 的创建与接口迁移逻辑仍基于 netlink 封装(见 daemon/libnetwork/nlwrap 包)。核心机制——“一个 Sandbox = 一个以宿主机路径标识的 netns,接口与路由经 netlink 配置”——与文档描述一致。

Drivers:面向驱动的 API 与语义

驱动本质上是 libnetwork 的扩展,为上文所有 LibNetwork API 提供实际实现。因此对每一个 Network/Endpoint API 都存在 1-1 对应的驱动侧 API。

Driver 接口

文档列举的核心驱动 API 为:driver.Configdriver.CreateNetworkdriver.CreateNetworkdriver.DeleteNetworkdriver.CreateEndpointdriver.DeleteEndpointdriver.Joindriver.Leave。这些面向驱动的 API 使用唯一标识符(networkidendpointid 等)而非名字(用户侧 API 使用名字)。文档也如实说明:这些 API 在当时仍在完善中,尤其是在多主机网络需求下可能还会变化。

当前仓库中,驱动必须实现的核心接口定义在 driverapi.go#L15-L55Driver 接口,包含:

  • CreateNetwork(ctx, nid, options, nInfo, ipV4Data, ipV6Data)
  • DeleteNetwork(nid)
  • CreateEndpoint(ctx, nid, eid, ifInfo, options)
  • DeleteEndpoint(nid, eid)
  • EndpointOperInfo(nid, eid)
  • Join(ctx, nid, eid, sboxKey, jinfo, epOpts, sbOpts)
  • Leave(nid, eid)
  • Type()IsBuiltIn()

对比可见,相对文档中的清单,接口演进了 EndpointOperInfoIsBuiltIn 等方法,且 Join 增加了 sboxKey 与选项参数——与生命周期第 6 步中“驱动用 Sandbox Key 识别同一容器的多个端点”的描述相吻合。

除核心接口外,driverapi 还定义了一组可选接口(见 driverapi.go#L74-L150):TableWatcher(向 gossip 层注册表并接收 CRUD 通知,仅对 global scope 驱动有效)、ExtConner(外部连通性/默认网关的编程)、PortManager(动态增删已加入端点的发布端口)、IPv6ReleaserGwAllocChecker 等。从源码结构看,这些可选接口正是文档中“未来可由多个驱动参与不同网络管理功能”这一演进方向的具体落地。

Driver 语义:CreateEndpoint 的 InterfaceInfo 约定

文档对 Driver.CreateEndpoint 给出了严格的资源分配语义:

  • 该方法接收一个 EndpointInfo 接口(当前源码中对应 driverapi.InterfaceInfo),其中提供 Interface(读取)与 AddInterface(写入)两类方法;
  • Interface 返回非 nil 值:驱动必须使用其中的接口信息(例如把地址视为静态提供的),若无法使用必须返回错误;
  • 若返回 nil:驱动应当恰好分配一个全新的接口,并用 AddInterface 记录下来,无法做到则返回错误;
  • Interface 非 nil 时,禁止使用 AddInterface

这段约定明确了“静态地址注入”与“动态地址分配”两条路径的互斥性,避免驱动与上层 IPAM 分配结果相互覆盖。

内置驱动实现

设计文档列出 libnetwork 包含以下驱动包:nullbridgeoverlayremote

  • Null:驱动 API 的 noop(空操作)实现,仅用于不希望有任何网络的情形,用以向后兼容 Docker 的 --net=none 选项。
  • Bridge:基于 Linux Bridge 的 Linux 特定网桥实现,详见 Bridge 驱动文档
  • Overlay:实现可跨多主机、使用 VXLAN 等 overlay 封装的网络。
  • Remoteremote 包本身不提供驱动,而是提供通过远程传输层支持驱动的手段,允许用任意语言编写驱动,详见 Remote 驱动设计文档

在当前仓库中,这些驱动位于 daemon/libnetwork/drivers/ 目录下(nullbridgeoverlayremote,以及后加入的 hostmacvlanipvlan),Linux 下的注册顺序与入口见 drivers_linux.go#L22-L45。此外,macvlan 驱动另有专门文档 macvlan.md,网络数据库(全局作用域网络在多主机间同步状态的基础)设计见 networkdb.md

小结

libnetwork 的设计可以用三句话概括:其一,用 Sandbox—Endpoint—Network 三元组把“容器如何接入网络”形式化为 CNM,其中 Sandbox 是主机本地作用域的容器网络栈,Endpoint 是全局作用域的服务端点,Network 是端点集合的连通域;其二,用 NetworkController + Driver 分离“抽象管理”与“具体实现”,Options/Labels 作为两层配置通道把用户意图透传给驱动;其三,用一条九步生命周期(注册驱动 → New → NewNetwork → 选项 → CreateEndpoint → Join → Leave → DeleteEndpoint → DeleteNetwork)约束上层(如 Docker Engine)的调用顺序,使容器 Stop/Start 时 IP 等网络资源可以被稳定复用。理解了这套模型,就能沿着本文列出的 controller.godriverapi.godrivers_linux.godrivers/ 各子包,完整追溯 Moby 中容器网络从 API 到内核 netlink 调用的全部路径。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384