Docker网络插件开发:能否在容器启动前创建netns?
在Docker容器启动前预创建NetNS的可行方案
我之前用libnetwork开发网络插件时也踩过一模一样的坑——在Join()里手动创建并挂载NetNS到sandboxkey路径后,Docker转头就把它卸载重建了。这其实是由libnetwork的核心工作流程决定的,不过确实有办法在容器启动前提前创建NetNS并让Docker认可它。
为什么你的NetNS会被覆盖?
当Docker触发Join()请求时,sandboxkey对应的路径是Docker预先预留的位置,它期望插件复用这个已经由Docker初始化好的NetNS,而不是自行创建。插件返回响应后,Docker会执行一系列校验和配置步骤,如果发现NetNS不是它创建的,就会判定为无效,随即卸载并重新生成符合它规范的NetNS。
可行的解决方案
这里有两种亲测有效的思路,你可以根据自己的插件需求选择:
方案一:在CreateEndpoint阶段提前准备NetNS
这是最贴合libnetwork流程的方法:
- 在
CreateEndpoint()方法中,提前创建你需要的NetNS(可以用netlink库直接在Go代码中创建,或者调用ip netns add命令)。 - 将预创建的NetNS路径存储到Endpoint的元数据中,方便后续
Join()阶段调用。 - 在
Join()请求中,把预创建的NetNS软链接到sandboxkey指定的路径,而不是直接创建到该路径下。 - 一定要确保NetNS的权限设置为
root:root,路径放在Docker默认的NetNS目录(/var/run/docker/netns/)下,这样Docker才会认可它的有效性。
方案二:自定义NetNS生命周期管理
如果需要更灵活的控制,可以完全接管NetNS的创建和清理:
- 在插件初始化时,或者监听Docker的容器创建事件,提前为即将启动的容器创建好NetNS。
- 在
Join()请求中,直接返回你预创建的NetNS路径作为SandboxKey,同时在插件配置中禁用libnetwork自动管理NetNS的逻辑。 - 注意:这种方法需要你手动处理NetNS的清理工作——在容器销毁时(比如
DeleteEndpoint或Leave阶段)删除对应的NetNS,避免资源泄漏。
关键代码示例(Go语言)
下面是方案一的简化代码参考:
import ( "github.com/vishvananda/netlink" "os" "path/filepath" "github.com/docker/go-plugins-helpers/libnetwork" ) // CreateEndpoint阶段预创建NetNS func (p *MyNetworkPlugin) CreateEndpoint(req *libnetwork.CreateEndpointRequest) (*libnetwork.CreateEndpointResponse, error) { // 生成自定义NetNS路径,用EndpointID做唯一标识 customNetnsPath := filepath.Join("/var/run/docker/netns", "my-netns-"+req.EndpointID) // 创建NetNS if err := netlink.NetNSCreate(customNetnsPath); err != nil { return nil, err } // 设置正确的权限,确保Docker可以访问 if err := os.Chmod(customNetnsPath, 0666); err != nil { return nil, err } // 把NetNS路径存入Endpoint元数据 req.Endpoint.Metadata["custom-netns-path"] = customNetnsPath return &libnetwork.CreateEndpointResponse{}, nil } // Join阶段绑定预创建的NetNS到sandboxkey func (p *MyNetworkPlugin) Join(req *libnetwork.JoinRequest) (*libnetwork.JoinResponse, error) { customNetnsPath := req.Endpoint.Metadata["custom-netns-path"] // 软链接预创建的NetNS到Docker指定的sandbox路径 if err := os.Symlink(customNetnsPath, req.SandboxKey); err != nil { return nil, err } // 返回响应,指定使用该NetNS return &libnetwork.JoinResponse{ SandboxKey: req.SandboxKey, // 补充你的其他网络配置,比如IP地址、路由等 }, nil } // 别忘了在DeleteEndpoint阶段清理NetNS func (p *MyNetworkPlugin) DeleteEndpoint(req *libnetwork.DeleteEndpointRequest) error { customNetnsPath := req.Endpoint.Metadata["custom-netns-path"] return netlink.NetNSDelete(customNetnsPath) }
额外注意事项
- 插件必须以root权限运行,否则没有操作NetNS的权限。
- 测试时可以用
ip netns list命令查看NetNS的状态,确认是否被Docker正确使用。 - 如果使用软链接的方式,要确保Docker启动时不会清理掉自定义的NetNS(可以通过调整Docker的NetNS清理策略实现)。
内容的提问来源于stack exchange,提问作者Phocs
相关产品推荐
相关产品推荐

