首页
/ Moby libnetwork IPAM Driver:远程 IPAM 插件契约、生命周期流程与协议详解

Moby libnetwork IPAM Driver:远程 IPAM 插件契约、生命周期流程与协议详解

2026-09-04 16:09:31作者:宣利权Counsellor

本文以 Moby 仓库中 libnetwork 的 IPAM(IP Address Management)驱动文档为核心,系统讲解 CNM 模型下 IP 地址分配的控制机制:内置 IPAM 驱动与第三方远程 IPAM 驱动的注册方式、IpamDriver.* 各 HTTP 接口的请求/响应契约、地址空间(Address Space)的语义、网络与端点创建/删除时 libnetwork 与 IPAM 驱动的完整交互流程,以及 RequiresMACAddressRequiresRequestReplay 两项能力声明。读完后,你将具备实现一个可被 libnetwork 动态加载的远程 IPAM 插件所需的完整 API 契约知识,并能从源码层面理解默认 IPAM 驱动的分配策略。

CNM 模型:容器内 Network Sandbox 中的 Endpoint 挂接到后端/前端 Network

如上所示的 CNM(Container Network Model)中,每个容器拥有独立的 Network Sandbox,其中可包含一个或多个 Endpoint,Endpoint 再挂接到具体的 Network 上。正是在 Network 与 Endpoint 的整个生命周期中,CNM 模型通过 IPAM 驱动控制网络与端点接口的 IP 地址分配。libnetwork 内置了一个默认 IPAM 驱动,同时允许第三方 IPAM 驱动以插件方式动态插入。创建网络时,用户可以指定 libnetwork 使用哪个 IPAM 驱动来管理该网络的地址。

IPAM 驱动的注册机制

libnetwork 将 IPAM 分为内置驱动与远程驱动两类:

  • 内置驱动:随 libnetwork 初始化即注册,如 ipams/defaultipam 包提供的默认驱动(实现见 defaultipam),以及不做任何地址分配的 ipams/null 空驱动;
  • 远程驱动:通过 Docker 插件机制动态加载,ipams.remote 包负责为其提供代理进程。

从源码结构看,所有 IPAM 驱动(无论内置还是远程)都必须实现 Ipam 接口

type Ipam interface {
	// GetDefaultAddressSpaces returns the default local and global address spaces for this ipam
	GetDefaultAddressSpaces() (string, string, error)
	// RequestPool allocate an address pool either statically or dynamically
	RequestPool(req PoolRequest) (AllocatedPool, error)
	// ReleasePool releases the address pool identified by the passed id
	ReleasePool(poolID string) error
	// RequestAddress request an address from the specified pool ID
	RequestAddress(string, net.IP, map[string]string) (*net.IPNet, map[string]string, error)
	// ReleaseAddress releases the address from the specified pool ID
	ReleaseAddress(string, net.IP) error
	// IsBuiltIn returns true if it is a built-in driver.
	IsBuiltIn() bool
}

远程 IPAM 驱动的注册流程

与远程网络驱动的注册机制完全一致(详见 remote 驱动文档),libnetwork 通过 Init() 函数初始化 ipams.remote 包(实现见 remote.go)。Init() 接收一个 ipamapi.Callback 参数,该回调实现了 RegisterIpamDriver() 方法(接口定义见 Registerer):

type Registerer interface {
	RegisterIpamDriver(name string, driver Ipam) error
	RegisterIpamDriverWithCapabilities(name string, driver Ipam, capability *Capability) error
}

远程驱动包利用该接口,通过 plugins.Handle 回调把自己的远程驱动注册到 libnetwork 的 NetworkController 上。远程驱动经由 Docker 插件包与 libnetwork 完成注册和通信,ipams.remote 则充当远程驱动进程的代理(proxy),把 libnetwork 侧的接口调用转成 HTTPS 请求发给插件进程。

协议与握手

  • 协议:远程 IPAM 驱动的通信协议与远程网络驱动完全相同(基于 Docker 插件框架的 HTTPS 双向 RPC)。
  • 握手(Handshake):驱动注册期间,libnetwork 会向远程驱动查询其默认的 local / global 地址空间名称以及驱动能力(capabilities),对应下文的 GetDefaultAddressSpacesGetCapabilities 两个接口。
  • 数据持久化要求:数据库(即池与地址分配记录)的管理是远程驱动自己的责任,libnetwork 不代为持久化;如果驱动选择不持久化本地作用域网络的池分配,可依赖 RequiresRequestReplay 能力在守护进程重载时重建。

IPAM 契约:远程驱动必须实现的 5+1 个接口

远程 IPAM 驱动必须处理以下请求:

  1. GetDefaultAddressSpaces — 返回默认 local/global 地址空间;
  2. RequestPool — 注册(申请)一个地址池;
  3. ReleasePool — 释放一个已注册的地址池;
  4. RequestAddress — 预留一个 IP 地址;
  5. ReleaseAddress — 释放一个 IP 地址;
  6. GetCapabilities — 查询驱动能力(可选,但注册握手会尝试调用)。

这些接口与 Go 侧 ipamapi.Ipam 一一对应,其 HTTPS 请求/响应结构体完整定义在 ipams/remote/api/api.go 中,下文逐个说明。

GetDefaultAddressSpaces

GetDefaultAddressSpaces 返回该 IPAM 默认的 local 与 global 地址空间名称。地址空间是一组与"其他地址空间中的池"相互隔离的、互不重叠的地址池集合——同一个池可以同时存在于 N 个不同的地址空间中。地址空间天然地映射到租户(tenant)概念。

在 libnetwork 中,localglobal 地址空间的含义是:local 地址空间不需要在集群内各节点间同步,而 global 地址空间需要同步。除非 IPAM 配置中另行指定,libnetwork 会根据所创建网络的作用域选择从默认 local 或默认 global 地址空间中申请地址池。例如:bridge 网络(本地作用域)默认从 default local 地址空间取池,overlay 网络(全局作用域)默认从 default global 地址空间取池。

注册期间,远程驱动会收到一个 POST /IpamDriver.GetDefaultAddressSpaces 请求,无 payload。驱动响应格式(对应源码 GetAddressSpacesResponse):

{
    "LocalDefaultAddressSpace":  "string",
    "GlobalDefaultAddressSpace": "string"
}

RequestPool

该 API 用于向 IPAM 驱动注册一个地址池。多次相同参数的调用必须返回相同结果(幂等),池的引用计数(reference count)由 IPAM 驱动自己维护——这也是 ReleasePool 成对出现的原因。

Go 侧签名(文档中的原始形式,当前源码已演进为以 PoolRequest 结构体传参,见 PoolRequest):

RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error)

远程驱动将收到 POST /IpamDriver.RequestPool,payload 如下(对应 RequestPoolRequest):

{
    "AddressSpace": "string",
    "Pool":         "string",
    "SubPool":      "string",
    "Options":      {"k": "v"},
    "V6":           false
}

字段说明:

  • AddressSpace:IP 地址空间,表示一组互不重叠的池。这是唯一必填字段
  • Pool:IPv4 或 IPv6 地址池,CIDR 格式;
  • SubPool:地址池的一个可选子集,CIDR 格式的 IP 范围;
  • Options:IPAM 驱动专有的选项 map;
  • V6:当希望 IPAM 自选择一个池时,指示该池是否为 IPv6。

校验规则:若未指定 Pool,IPAM 驱动可以返回一个自选择的池;此情况下若调用方想要 IPv6 池必须置位 V6 标志。Pool 且非空 SubPool 的请求应被视为非法而拒绝;未指定 Pool 时,IPAM 将分配其默认池之一,此时如果网络需要分配 IPv6 地址,V6 标志应当置位。

成功响应格式(对应 RequestPoolResponse):

{
    "PoolID": "string",
    "Pool":   "CIDR string",
    "Data":   {"k": "v"}
}
  • PoolID:池的标识符。相同的池必须返回相同的 pool id
  • Pool:池本身,CIDR 格式;
  • Data:IPAM 驱动为该池提供的元数据。

ReleasePool

释放一个先前注册的地址池。远程驱动收到 POST /IpamDriver.ReleasePool,payload(对应 ReleasePoolRequest):

{
    "PoolID": "string"
}

PoolID 即池标识符。成功响应为空对象 {}

RequestAddress

预留一个 IP 地址。Go 侧签名:

RequestAddress(string, net.IP, map[string]string) (*net.IPNet, map[string]string, error)

远程驱动收到 POST /IpamDriver.RequestAddress,payload(对应 RequestAddressRequest):

{
    "PoolID":  "string",
    "Address": "string",
    "Options": {"k": "v"}
}
  • PoolID:池标识符;
  • Address:期望地址,常规 IP 形式(A.B.C.D)。若无法满足该指定地址,请求失败;若为空,则由 IPAM 驱动在池内任选一个可用地址;
  • Options:IPAM 驱动专有的选项(如携带端点 MAC 地址,见后文能力说明)。

成功响应(对应 RequestAddressResponse):

{
    "Address": "A.B.C.D/MM (CIDR)",
    "Data":    {"k": "v"}
}
  • Address:分配出的地址,CIDR 格式;
  • Data:IPAM 驱动专有的元数据。

ReleaseAddress

释放一个 IP 地址。远程驱动收到 POST /IpamDriver.ReleaseAddress,payload(对应 ReleaseAddressRequest):

{
    "PoolID":  "string",
    "Address": "string"
}
  • PoolID:池标识符;
  • Address:要释放的 IP 地址。

GetCapabilities

驱动注册期间,libnetwork 会查询驱动的能力。该 URL 端点并非强制要求:若驱动不支持它,注册会自动成功,并以内置空能力附加到内部驱动句柄上。

注册期间,远程驱动收到 POST /IpamDriver.GetCapabilities,无 payload。响应格式(对应 GetCapabilityResponse):

{
    "RequiresMACAddress":    false,
    "RequiresRequestReplay": false
}

能力(Capabilities)详解

能力是远程 IPAM 驱动在注册时向 libnetwork 表达的需求/特性声明,结构体定义见 ipamapi.Capability。目前 libnetwork 接受以下两种:

RequiresMACAddress

布尔值,告知 libnetwork 该 IPAM 驱动是否需要知道接口 MAC 地址才能正确处理 RequestAddress() 调用。若为 true,则在 CreateEndpoint() 时,libnetwork 会为端点生成一个随机 MAC 地址(除非用户已显式提供 MAC),并在请求 IP 时通过 options map 传给 RequestAddress(),使用的 key 是 netlabel.MacAddress 常量:"com.docker.network.endpoint.macaddress"。这类驱动典型场景是需要按 MAC 做地址绑定或 ARP 代理的设备。

RequiresRequestReplay

布尔值,告知 libnetwork 该驱动是否需要在守护进程重载(daemon reload)时收到 RequestPool()RequestAddress() 请求的重放。libnetwork 控制器初始化时会从本地存储中取出当前本地作用域网络的列表;若此能力标志已置位,它会重放本地网络所属各池的 RequestPool() 请求以及各本地网络网关地址的 RequestAddress() 请求,使 IPAM 驱动得以重建其池数据库。这对那些选择不持久化本地作用域网络池分配的 IPAM 驱动非常有用。

IPAM 配置与完整生命周期流程

通过 NetworkOptionIpam 传入 IPAM 配置

libnetwork 用户在创建网络时,可以通过 NetworkOptionIpam 设置函数提供 IPAM 相关配置,定义见 network.go

func NetworkOptionIpam(ipamDriver string, addrSpace string, ipV4 []*IpamConf, ipV6 []*IpamConf, opts map[string]string) NetworkOption

调用方必须提供 IPAM 驱动名,并可提供地址空间以及 IPv4 的 IpamConf 列表和 IPv6 的 IpamConf 列表。IPAM 驱动名是唯一必填字段,若未提供,网络创建将失败。这一点在集成测试中有直接验证,libnetwork_linux_test.go 中明确断言:

_, err := lnb.CreateNetwork("test",
    libnetwork.NetworkOptionIpam("my-ipam", "", nil, nil, nil), // 合法
    libnetwork.NetworkOptionIpam("", "", ipamV4ConfList, nil, nil), // 缺驱动名,应失败
    libnetwork.NetworkOptionIpam("", "", nil, ipamV6ConfList, nil), // 缺驱动名,应失败
)

列表中每个 IpamConf 元素具有如下形式:

// IpamConf contains all the ipam related configurations for a network
type IpamConf struct {
	// The master address pool for containers and network interfaces
	PreferredPool string
	// A subset of the master pool. If specified,
	// this becomes the container pool
	SubPool string
	// Input options for IPAM Driver (optional)
	Options map[string]string
	// Preferred Network Gateway address (optional)
	Gateway string
	// Auxiliary addresses for network driver. Must be within the master pool.
	// libnetwork will reserve them if they fall into the container pool
	AuxAddresses map[string]string
}

各字段语义:PreferredPool 是容器与网络接口用的主地址池;SubPool 是主池的子集,若指定则成为容器地址池(容器地址只从该子池分配);Options 是传给 IPAM 驱动的选项;Gateway 是期望的网络网关地址;AuxAddresses 是网络驱动用的辅助地址,必须位于主池内,若落在容器池内 libnetwork 会为其预留。

网络创建时的 IPAM 请求序列

网络创建时,libnetwork 遍历配置列表,向 IPAM 驱动依次执行:

  1. RequestPool:通过 RequestPool() 申请地址池,并顺带传递 Options
  2. 网关地址:若指定了 Gateway 则申请该特定地址;否则从池中申请任意地址用作网络网关。均通过 RequestAddress() 完成;
  3. 辅助地址:通过 RequestAddress() 依次申请每个指定的 AuxAddresses 地址。

关于配置列表为空的默认行为:

  • IPv4 列表为空:libnetwork 会自动追加一个空 IpamConf 结构。这会触发 libnetwork 向 IPAM 驱动申请一个"驱动自选"的 IPv4 地址池——在已配置地址空间上(若指定了)或在 IPAM 驱动默认地址空间上(否则)。若 IPAM 驱动无法提供地址池,网络创建失败
  • IPv6 列表为空:libnetwork 不做任何动作(IPv6 属于完全可选)。

执行第 1)~3) 步期间从 IPAM 驱动获取的数据会存储在 network 结构中,分别为 IPv4 与 IPv6 保存为 IpamInfo 结构列表。

端点创建时的地址分配

端点(endpoint)创建时,libnetwork 遍历配置列表并执行:

  1. 从 IPv4 池申请一个 IPv4 地址,并赋给端点接口的 IPv4 地址。成功则停止遍历
  2. 从 IPv6 池(若存在)申请一个 IPv6 地址,并赋给端点接口的 IPv6 地址。成功则停止遍历

上述任一操作失败,端点创建即失败。 这里也体现了 RequiresMACAddress 能力的生效点:若驱动声明该能力,libnetwork 会在步骤 1/2 的 RequestAddress() options 中携带 com.docker.network.endpoint.macaddress

端点删除时的地址释放

端点删除时:

  1. 释放端点接口的 IPv4 地址;
  2. 若存在,释放端点接口的 IPv6 地址。

网络删除时的池释放序列

网络删除时,libnetwork 遍历 IpamData 结构列表,向 IPAM 驱动执行:

  1. 通过 ReleaseAddress() 释放网络网关地址;
  2. 通过 ReleaseAddress() 释放每个辅助地址;
  3. 通过 ReleasePool() 释放地址池。

该顺序与创建时严格相反(先地址后池),配合 RequestPool 的幂等语义与驱动侧引用计数,保证了网络对象在存储中重建后池状态可以正确收敛。

内置默认 IPAM 驱动的源码印证

作为对照,内置默认驱动(ipams/defaultipam)在进程内实现同一契约。从源码结构看,address_space.go 中的 addrSpace 结构维护了地址空间内"已分配子网"的有序列表、按前缀索引的子网池数据,以及按前缀排序、去掉重叠项后的 predefined 预定义池列表——排序与去重是为了保证动态子网分配时不会因为长短前缀重叠而选错池。其预定义池正是 docker network create --subnet 系列场景的落点,与远程驱动契约中 Pool/SubPool 的语义(静态分配 vs 动态分配)一一对应。

此外,ipamapi 集中定义了 IPAM 的通用错误集(如 ErrNoAvailableIPsErrIPAlreadyAllocatedErrIPOutOfRangeErrPoolOverlap 等),远程驱动返回的错误经由 api.Response.Error 字段(见 api.goError == "" 即视为成功)传递回 libnetwork,实现与文档所列契约在错误语义上保持一致。

在测试层面,libnetwork_internal_test.go 展示了典型用法:NetworkOptionIpam(defaultipam.DriverName, "", []*IpamConf{{PreferredPool: "10.35.0.0/16", Gateway: "10.35.255.253"}}, nil, nil),即显式主池 + 显式网关的组合,验证了上文配置流程中"指定 Gateway 时申请特定地址"的分支。

小结

  • 契约核心:远程 IPAM 驱动必须实现 GetDefaultAddressSpacesRequestPoolReleasePoolRequestAddressReleaseAddress 五个 POST 端点,GetCapabilities 可选;所有 payload 结构在 ipams/remote/api/api.go 中有权威定义。
  • 关键约束AddressSpaceRequestPool 唯一必填字段;相同 RequestPool 调用必须幂等且返回相同 PoolID;引用计数由驱动维护;数据库持久化是驱动自己的责任。
  • 生命周期对应关系:网络创建 = RequestPool + 网关/辅助地址申请;端点创建 = 按 v4、v6 顺序请求地址(任一失败即失败);端点/网络删除 = 严格逆序的 Release 序列。
  • 能力协商RequiresMACAddress 决定地址请求 options 中是否携带端点 MAC(key 为 com.docker.network.endpoint.macaddress);RequiresRequestReplay 决定守护进程重载时是否重放池与网关地址请求,以支撑不持久化本地池分配的驱动实现。
  • 实现入口:Go 侧统一接口为 ipamapi.Ipam,远程接入参考 remote 驱动文档ipams/remote 的代理实现;IPAM 配置入口为 NetworkOptionIpam
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

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