深入解析docker内核网桥
今天做虚拟桌面,朋友问我,为什么vnc 连接另一个docker 容器一直超时,原因是在docker 启动的时候没有组网,那么接下来我就要解析下docker的内核网络。
我们思考几个问题,带你了解linux 中docker 网络实现的基本原理。
文章分析的基石来源于 Docker 的moby源码:
https://github.com/moby/moby
一、docker 网络组成
docker 默认有主机模式和网桥模式,这里只说网桥模式,我们说一下docker内核的网桥代码
先简单说一下容器,容器底层调用的是linux 的 clone,我们使用clone的时候有一些独特的标志位
CLONE_NEWNES,
CLONE_NEWUTS,
CLONE_NEWIPC,
CLONE_NEWPID,
CLONE_NEWNET
使用linux内核的这些标志位,我们都可以创建出不同NS的进程,其中我们需要注意的是CLONE_NEWNET
二、Docker 桥接模式分析
Docker Bridge 实现方式如下:
1)我们实现要考虑不同的两个进程 netns 是不一样的,那么这两个不同的netns之间如何通信?
我们可以使用linux 的 veth 接口 ,申请两个veth 设备假设是veth0 和 veth1。而veth pair 设备确保无论哪一个收到报文都会发送给另一端
2)我们把veth0 附加到 docker daemon 创建的docker0 网桥上。保证宿主机有能力把报文发送到veth0
3) 然后我们把 veth1 添加到容器所属的命名空间下,veth1 在Docker 容器里看来就是eth0。一方面保证网络报文发往veth0,可以被veth1收到,实现宿主机到docker 容器之间连通性。
1)问题反思?
docker 网桥是如何实现的?
docker 又是怎么通过建立linux 设备veth的?
docker 创建这些基础网络组件的过程中,到底是用了linux 内核的哪些特性?
2)docker 网桥是如何实现的?
go 部分
我们翻阅Docker 源码,发现这么一段
位于源码的
daemon/daemon_unix.go
// --ip processingif cfg.DefaultIP != nil {netOption[bridge.DefaultBindingIP] = cfg.DefaultIP.String()}_, err = controller.NewNetwork("bridge", "bridge", "",libnetwork.NetworkOptionEnableIPv6(cfg.EnableIPv6),libnetwork.NetworkOptionDriverOpts(netOption),libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil),libnetwork.NetworkOptionDeferIPv6Alloc(deferIPv6Alloc))
libnetwork/controller.go
func (c *Controller) addNetwork(n *Network) error {d, err := n.driver(true)if err != nil {return err}// Create the networkif err := d.CreateNetwork(n.id, n.generic, n, n.getIPData(4), n.getIPData(6)); err != nil {return err}n.startResolver()return nil
}
最后我们发现网桥的实现最后到了CreateNetwork中,我们在追踪进入CreateNetwork中
libnetwork/drivers/bridge/bridge_linux.go
CreateNetwork 的实现我们发现具体关键代码位于
// If the bridge interface doesn't exist, we need to start the setup steps// by creating a new device and assigning it an IPv4 address.bridgeAlreadyExists := bridgeIface.exists()if !bridgeAlreadyExists {bridgeSetup.queueStep(setupDevice)bridgeSetup.queueStep(setupDefaultSysctl)}
再继续看 setupDevice,一切水落石处,Docker 网桥创建的关键在于 setupDevice
func setupDevice(config *networkConfiguration, i *bridgeInterface) error {// We only attempt to create the bridge when the requested device name is// the default one. The default bridge name can be overridden with the// DOCKER_TEST_CREATE_DEFAULT_BRIDGE env var. It should be used only for// test purpose.var defaultBridgeName stringif defaultBridgeName = os.Getenv("DOCKER_TEST_CREATE_DEFAULT_BRIDGE"); defaultBridgeName == "" {defaultBridgeName = DefaultBridgeName}if config.BridgeName != defaultBridgeName && config.DefaultBridge {return NonDefaultBridgeExistError(config.BridgeName)}// Set the bridgeInterface netlink.Bridge.i.Link = &netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: config.BridgeName,},}// Set the bridge's MAC address. Requires kernel version 3.3 or up.hwAddr := netutils.GenerateRandomMAC()i.Link.Attrs().HardwareAddr = hwAddrlog.G(context.TODO()).Debugf("Setting bridge mac address to %s", hwAddr)if err := i.nlh.LinkAdd(i.Link); err != nil {log.G(context.TODO()).WithError(err).Errorf("Failed to create bridge %s via netlink", config.BridgeName)return err}return nil
}
linux 接口部分:
既然我们找到了库的实现,那么我们继续思考netlink库为什么能调在linux 系统里创建网桥,它到底是使用了kernel 开发的哪些能力?
分析go netlink 库我发现了一个函数叫linkModify,他的一段代码实现如下:
_, err := req.Execute(unix.NETLINK_ROUTE, 0)if err != nil {return err}
底层使用的是
func getNetlinkSocket(protocol int) (*NetlinkSocket, error) {fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW|unix.SOCK_CLOEXEC, protocol)if err != nil {return nil, err}s := &NetlinkSocket{fd: int32(fd),}s.lsa.Family = unix.AF_NETLINKif err := unix.Bind(fd, &s.lsa); err != nil {unix.Close(fd)return nil, err}return s, nil
}
也就是说Docker 的网络库最终使用的是
netlink socket API:
socket()函数
socket域(地址族)是AF_NETLINK
socket类型是SOCK_RAW或SOCK_DGRAM,因为netlink是一种面向数据的服务
netlink协议类型定义在netlink.h(以下以NETLINK_ROUTE为例),也可以自定义
AF_NETLINK 无疑是linux 中内核态和用户态通信的重要手段,他和ioctl的最大区别是,AF_NETLINK
是一种协议族,提供了一种标准化的机制,用于用户态程序与内核之间进行网络相关的通信。它定义了不同的协议类型,如NETLINK_ROUTE
、NETLINK_SELINUX
等,以支持特定的网络操作和功能。使用AF_NETLINK
,用户态程序可以发送特定类型的消息给内核,以请求或传递网络相关的信息。
docker 是使用AF_NETLINK 做的,而我们使用brctl 却是使用的ioctl
docker veth 设备是如何实现的
case *Veth:data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)peer := data.AddRtAttr(nl.VETH_INFO_PEER, nil)nl.NewIfInfomsgChild(peer, unix.AF_UNSPEC)peer.AddRtAttr(unix.IFLA_IFNAME, nl.ZeroTerminated(link.PeerName))if base.TxQLen >= 0 {peer.AddRtAttr(unix.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen)))}if base.NumTxQueues > 0 {peer.AddRtAttr(unix.IFLA_NUM_TX_QUEUES, nl.Uint32Attr(uint32(base.NumTxQueues)))}if base.NumRxQueues > 0 {peer.AddRtAttr(unix.IFLA_NUM_RX_QUEUES, nl.Uint32Attr(uint32(base.NumRxQueues)))}if base.MTU > 0 {peer.AddRtAttr(unix.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU)))}if link.PeerHardwareAddr != nil {peer.AddRtAttr(unix.IFLA_ADDRESS, []byte(link.PeerHardwareAddr))}if link.PeerNamespace != nil {switch ns := link.PeerNamespace.(type) {case NsPid:val := nl.Uint32Attr(uint32(ns))peer.AddRtAttr(unix.IFLA_NET_NS_PID, val)case NsFd:val := nl.Uint32Attr(uint32(ns))peer.AddRtAttr(unix.IFLA_NET_NS_FD, val)}}
我们发现还是AF_NETLINK 实现的
三、快速组网组成一个测试网络
创建网桥
ip link add br0 type bridge
启动网桥
ip link set br0 up
创建网络命名空间ns1 和ns2
~# ip netns add ns1
~# ip netns add ns2
创建 veth 对
~# ip link add veth0 type veth peer br-veth0
~# ip link add veth1 type veth peer br-veth1
查看创建的 veth 对,通过查看此时 veth对 在 root名称空间下
ip address show
将创建的 veth对 的一端插入到指定的名称空间
~# ip link set veth0 netns ns1
~# ip link set veth1 netns ns2
通过进入不同的名称空间查看网卡的一端
~# ip netns exec ns1 ip a
~# ip netns exec ns2 ip a
将创建的 veth对 的另一端插入到 br0 的桥接网卡
~# ip link set br-veth0 master br0
~# ip link set br-veth1 master br0
启动网卡 veth0 veth1 br-veth0 br-veth1,并配置 ip 地址
// 开启网桥部分
~# ip link set br-veth0 up
~# ip link set br-veth1 up// 开启ns里的veth
~# ip netns exec ns1 ip link set veth0 up
~# ip netns exec ns2 ip link set veth1 up// 设置容器内的veth 设备ip
~# ip netns exec ns1 ifconfig veth0 192.168.100.10/24
~# ip netns exec ns2 ifconfig veth1 192.168.100.20/24
四、总结
Docker 创建网桥和Veth设备都是通过AF_NETLINK 套接字实现的,我需要去读 <精通linux内核网络>这本书去调研一下
相关文章:

深入解析docker内核网桥
今天做虚拟桌面,朋友问我,为什么vnc 连接另一个docker 容器一直超时,原因是在docker 启动的时候没有组网,那么接下来我就要解析下docker的内核网络。 我们思考几个问题,带你了解linux 中docker 网络实现的基本原理。 文…...
ubuntu18.04服务器双网口配置上外网
记录一下配置服务器过程,本以为简单,结果整了一天。 服务器有2个网口,网口2是用来上外网的,原来用的01-netcfg.yaml进行ip地址设置,主要就用2条命令: vi /etc/netplan/01-netcfg.yaml (打开后…...
【安全体系架构】——防御深度架构
防御深度架构: 防御深度架构是一种多层次的安全模型,旨在通过在网络和系统的各个层次上部署多个安全措施,以抵御不同类型的威胁和攻击。这个模型承认单一的安全措施可能无法全面防御所有潜在威胁,因此采用了多层次的安全防御策略…...

Opencv之RANSAC算法用于直线拟合及特征点集匹配详解
Opencv之RANSAC算法用于直线拟合及特征点集匹配详解 讲述Ransac拟合与最小二乘在曲线拟合上的优缺点 讲述在进行特征点匹配时,最近邻匹配与Ransac匹配的不同之处 另外,Ransac也被用于椭圆拟合、变换矩阵求解等 1. 直线拟合 1.1 原理 RANSAC(RANdom …...

Jenkins环境部署与任务构建
一、CI/CD 1、CI/CD 概念: CI/CD 是一种软件开发和交付方法,旨在加速应用程序的开发、测试和部署过程,以提高软件交付的质量和效率。 (1) 持续集成 (CI Continuous Integration): 持续集成是开发团队频繁集成其代码更改的过程。开发者将其…...

ES6 Class和Class继承
1.class的基本语法 class可以理解为是一个语法糖,将js只能通过构造函数创建实例的方法进行了补充 构造函数: function Person ({ name, age18 }) {this.name namethis.age age } new Person({name: 张三}) Class类: class Person {con…...
C++11 packaged_task
std::packaged_task 把一个方法打包成一个task扔到线程中执行,然后通过packaged_task中的furture等待执行结果。 void test_promise() {std::packaged_task <int()> task([]()->int {std::cout << "packaged_task begin \n" << std…...
delete、drop、truncate三兄弟
比较方面/具体命令deletetruncatedrop删除范围逐行删除(记录行)逐页删除(数据页)整张表(数据表结构)所属范畴数据操作语言(DML)数据定义语言(DDL)数据定义语言…...
C/C++运算优先级
文章目录 前言1.运算优先级表2.举例说明:总结 前言 最近复习C基础知识的时候,发现对这部分还是有些模糊。常用的 - ,括号等运算符对于它们的优先级还是比较明确的。但是涉及到移位运算,逻辑运算这种,再结合四则运算…...

apache搭建静态网站,moongoose搭建网站后台,出现的跨域问题解决
文章目录 1,问题描述1.1,当网页和后台是不同服务时会产生跨域问题1.2,跨域问题 2,nginx端口转发解决跨域问题2.1,下载并安装nginx2.1.1,解压后如下所示2.1.2,进入解压目录后,执行配置…...

LiveQing视频点播流媒体RTMP推流服务功能-支持视频点播分屏大屏展示视频轮巡分组播放RMP推流直播大屏展示
LiveQing支持视频点播分屏大屏展示视频轮播分组播放RMP推流直播大屏展示 1、分屏展示2、轮巡播放3、RTMP推流视频直播和点播流媒体服务 1、分屏展示 LiveQing支持将视频点播、鉴权直播,拉转直播视频流,进行分屏播放。 2、轮巡播放 3、RTMP推流视频直播和…...
tf loss构建常用到函数
1、tf.map_fn tf.map_fn是TensorFlow中的一个函数,用于对给定的函数和输入进行逐元素的映射,其定义如下: tf.map_fn(fn,elems,dtypeNone,parallel_iterationsNone,back_propTrue,swap_memoryFalse,infer_shapeTrue,nameNone,fn_output_sign…...
行为型模式-备忘录模式
备忘录模式保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 意图:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。 主要解决:所谓备忘录模式就是在不破坏…...
Android Studio初学者实例:RecyclerView学习--模仿今日头条--续
新学期开始了,这篇文章收到了很多人的评论有很多地方不懂,所以写下了以下的文章--续篇 首先使用RecyclerView也好还是使用ListView,更或是GridView你都要先构思需要什么 这些东西无一例外通常都是用在列表显示下,那么需要一些&a…...

栈和队列的C++模拟实现
一、栈stack 1.介绍(库里面的文档介绍) 1. stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。 2. stack是作为容器适配器被实现的,容器适配器即是对…...

UE4/5:通过Blender制作BlendShape导入【UE4/5曲线、变形目标,blender形态键】
UE4/5里面,我们经常可以在一些骨骼模型上面看到相关的曲线,如Metahuman里面就是通过这个曲线来改变人物的脸部表情。 而这里笔者将教导如何去制作这种曲线。 这种曲线都是存在于骨骼模型上的,所以我们要么直接制作骨骼模型导入ue࿰…...

微信小程序进阶——后台交互
目录 一、后台准备 1.1 pom.xml 1.2 配置数据源 1.3 整合mybatis 二、前后端交互 2.1 method1 2.2 method2 2.2.1 封装request 2.2.2 头部引用util 2.2.3 编写方法 2.2.4 展示效果 三、WXS的使用 3.1 会议状态 3.1.2 引入wxs 3.1.3 修改代码 3.1.4 展示效果 3…...

二维码智慧门牌管理系统升级解决方案:突破传统,实现质检与抽检的个性化配置
文章目录 前言一、引入“独立质检”二、个性化抽检类别设定三、触发重采要素的功能升级四、升级优势与展望 前言 在数字化时代,智慧门牌管理系统已经成为社会管理的重要工具。为了满足各种复杂需求,系统升级是必然趋势。本次升级主要针对质检和抽检两大…...

《动手学深度学习 Pytorch版》 9.4 双向循环神经网络
之前的序列学习中假设的目标是在给定观测的情况下对下一个输出进行建模,然而也存在需要后文预测前文的情况。 9.4.1 隐马尔可夫模型中的动态规划 数学推导太复杂了,略。 9.4.2 双向模型 双向循环神经网络(bidirectional RNNs)…...

【Axure高保真原型】可视化图表图标
今天和粉丝们免费分享可视化图表图标原型模板,包括柱状图、条形图、环形图、散点图、水波图等常用的可视化图表图标。 【原型效果】 【原型预览】 https://axhub.im/ax9/d402c647c82f9185/#c1 【原型下载】 这个模板可以在 Axure高保真原型哦 小程序里免费下载哦…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...