虚拟网络namespace 到bridge

2023-05-16

9f6893bc5ee506f6e1eb2fedcb28600c.gif

前言

容器的网络是一大难点,不管是docker 还是kubernetes 都绕不开计算机网络。以下的介绍主要以计算机网络的namespace 和bridge 两个方面来展开介绍,方便深入理解容器的网络原理。

1.namespace分析

linux 支持六种资源的namespace :mount ns 、UTS ns 、IPC ns 、PID ns、Network ns 、User ns

为了让每一个进程从属于某一个namespace ,linux 内核为描述符增加了一个struct nsproxy 结构。

struct task_struct {
    ...
    /* namespaces */
    struct nsproxy *nsproxy;
    ...
}

struct nsproxy {
    atomic_t count;
    struct uts_namespace  *uts_ns;
    struct ipc_namespace  *ipc_ns;
    struct mnt_namespace  *mnt_ns;
    struct pid_namespace  *pid_ns;
    struct user_namespace *user_ns;
    struct net            *net_ns;
};

从上面的结构体可以看出,Linux 为每一种不同类型的资源定义了不同的命名空间的结构体进行管理。这里结构体就是上面说到的六种namespace。

这里主要以pid_namespace 为例来介绍:

首先看下pid_namespace 的结构体

struct pid_namespace {
    struct kref kref; //成员是一个引用计数器,用于记录引用这个结构的进程数
    struct pidmap pidmap[PIDMAP_ENTRIES];//成员用于快速找到可用pid的位图
    int last_pid;//成员是记录最后一个可用的pid
    struct task_struct *child_reaper; 
    struct kmem_cache *pid_cachep;
    unsigned int level;// 成员记录当前 pid命名空间 所在的层次
    struct pid_namespace *parent;//成员记录当前 pid命名空间 的父命名空间
#ifdef CONFIG_PROC_FS
    struct vfsmount *proc_mnt;
#endif
};

由于PID 命名空间是分层的,也就是说新创建一个pid命名空间时会记录父级pid 命名空间到

parent 字段中,所以随着pid 命名空间的创建,在内核中会形成一颗pid 命名空间的树。

  • 成员记录当前 pid命名空间 的父命名空间。pid 初始化的是在level0 这一层。在这个命名空间树中,低层的pid 对高层的pid 命名空间进程不可见。

  • 37bb03e3ec9a9c7d13b2345cba124983.png

从level0 到 levelN 是如何保存这些进程ID 信息的。在解析完子进程pid 后,如何将信息保存在task_struct 中,看看PID相关代码,有两处保存进程ID 信息相关:

p->pid = pid_nr(pid);   //获取global pid_ns中的pid,即第0层
        … …
init_task_pid(p, PIDTYPE_PID, pid);   //将struct pid 结构地址保存到进程描述符中

首先task_struct->pid中存的是global pid namespace中的PID value,因为对于内核来说,它只需要看全局的pid namespace即可(init进程),里面包含系统全局进程的global PID。

在linux 中并不区分进程和线程,都是同task_struct 抽象,只不过支持多线程的进行是由一组task_struct 来抽象的。

接下来看看系统调用clone() ,传入以上 不同类型的参数实现复制不同的ns 。这里以clone_newpid 参数为例。实际是复制pid 命名空间,在新的pid 命名空间里可以使用与其他pid 命名空间相同的pid

#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>

char child_stack[5000];

int child(void* arg)
{
    printf("Child - %d\n", getpid());
    return 1;
}

int main()
{
    printf("Parent - fork child\n");
    int pid = clone(child, child_stack+5000, CLONE_NEWPID, NULL);
    if (pid == -1) {
        perror("clone:");
        exit(1);
    }
    waitpid(pid, NULL, 0);
    printf("Parent - child(%d) exit\n", pid);
    return 0;
}

输入如下:

Parent - fork child
Parent - child(9054) exit
Child - 1

从运行结果来看,在子进程的PID命名空间里,当前进程的PID为1,但在父进程的pid 命名空间中子进程的pid 却是9045。

2.birdge

从上文中可以看到namespace 隔离了内核资源,让一些进程只能看到与自己相关的一部分资源。进程与进程之间没办法通信。但是Linux 之间需要两个或者两个以上的ns 链接。这个时候就需要veth pair 和bridge 出场了,veth pair 在这里不着重介绍,以bridge 为主来展开。

docker 中也用到了namespace 做隔离,但是docker 的隔离只是部分资源隔离,docker 的网络模式也有bridge。

2.1 网桥的实际应用

关于网桥配置ip 地址,首先先看看我自己的本机的网桥ip地址,再来看看有什么作用。

可以看到两个网桥都配置了对应的ip 地址,但是其实网桥不用配置ip的,给网桥配制ip地址,是为了方便网桥所在的主机和网桥所桥接的网卡(包括虚拟网卡)进行通信。一般情况下会配成 同一网段的ip .但是其对应的虚拟机(容器)可以配制ip地址,如果和网桥的ip是同一个网段的话,网桥所在的物理主机和这个网桥上桥接的网卡所对应的虚拟主机就可以进行通信了(能够ping通。那一下例子,我在本机ping 不同虚拟机的地址,是因为不在同一网段内。我自己本机的地址是192.168.138... 虚拟机的ip 地址是

7e9a1a1e1fcd4a5b3d4f72c1d8b55d32.png

而在虚拟集中我自己的IP地址是192.168.122.. 不在同一网段内,所以造成了无法通信的情况。

a68a7e3df664538d8efdc68fe601d1d7.png

2.2 网桥的工作原理

网桥是以混杂的模式工作的,混杂模式简称Promisc mode,即监听模式,常被用来诊断网络问题,混杂模式是指一个网卡会把它接受的所有的网络流量都交给cpu,而不是只想把它转交的部分交给cpu。每个桥维护了一个基于MAC地址的过滤数据库,网桥根据这个数据库,把收到的帧往相应的局域网(端口)进行转发。在过滤数据库中,列出了每个可能的目的地(目的MAC地址),以及它属于哪一条输出线路(一个端口号,即表示转发给哪个LAN),每个表项还有一个超时设置。通过下图网桥的实际作用来看网桥时候如何工作的。

72f625602aa4c493a707d44457e27464.png

如上图所示,当网络接口A接收到数据包后,网桥 会将数据包复制并且发送给连接到 网桥 的其他网络接口(如上图中的网卡B和网卡C)。通过上图可以将网桥总结为以下几点:

2f1d2b894519b41ca9376bc0af469975.png

收到一个报文后,根据报文的目的MAC,选择适当的端口进行转发。对于不同的报文,起转发流程略有差异:
a. 已知单播 - 单播:对于单播报文,如果知道目的地址在哪个端口(MAC表中能找到该目的MAC),就从该端口转发出去
b. 未知单播 - 泛洪:对于单播报文,如果暂时还不知道目的地址是哪个端口,则从所有非源端口泛洪出去
c. 组播广播 - 泛洪:对于广播和组播,也需要泛洪。

网桥的这种工作方式,是为了尽可能小的代价保证报文能最终到达它的目的地。

3.3 通过命令行来看看网桥实现

增加网桥

[root@controller]# brctl addbr br0

查看网桥

[root@controller]# brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.000000000000       no
docker0         8000.000000000000       no

这里回到内核源码来看看创建网桥源码:

int br_add_bridge(char *name)
{
    struct net_bridge *br;

    if ((br = new_nb(name)) == NULL) // 创建一个网桥设备对象
        return -ENOMEM;

    if (__dev_get_by_name(name) != NULL) { // 设备名是否已经注册过?
        kfree(br);
        return -EEXIST; // 返回错误, 不能重复注册相同名字的设备
    }

    // 添加到网桥列表中
    br->next = bridge_list;
    bridge_list = br;
    ...
    register_netdev(&br->dev); // 把网桥注册到网络设备中

    return 0;
}

上述代码清晰的显示了,网桥到网络设备的注册

再看看net_bridge 的数据结构

struct net_bridge
{
    struct net_bridge           *next;               // 连接内核中所有的网桥对象
    rwlock_t                    lock;                // 锁
    struct net_bridge_port      *port_list;          // 网桥端口列表
    struct net_device           dev;                 // 网桥设备信息
    struct net_device_stats     statistics;          // 信息统计
    rwlock_t                    hash_lock;           // 用于锁定CAM表
    struct net_bridge_fdb_entry *hash[BR_HASH_SIZE]; // CAM表
    struct timer_list           tick;

    /* STP */
    ...
};

网桥中比较重要的两个字段是:port_list、hash[BR_HASH_SIZE](保存着以网络接口 MAC地址 为键值,以网桥端口为值的哈希表)

网桥端口列表的数据结构:

struct net_bridge_port
{
    struct net_bridge_port  *next;   // 指向下一个端口
    struct net_bridge       *br;     // 所属网桥设备对象
    struct net_device       *dev;    // 网络接口设备对象
    int                     port_no; // 端口号

    /* STP */
    ...
};

net_bridge_fdb_entry 数据结构主要用于描述网络接口设备MAC 地址与网桥端口的对应关系:

struct net_bridge_fdb_entry
{
    struct net_bridge_fdb_entry *next_hash;
    struct net_bridge_fdb_entry **pprev_hash;
    atomic_t                    use_count;
    mac_addr                    addr;  // 网络接口设备MAC地址
    struct net_bridge_port      *dst;  // 网桥端口
    ...
};

以上三个结构,我在搜索的过程中,一张图很好的展示了三者的关系

0a9c2cb5f309e6744644536f49963bc0.png

可见,要将 网络接口设备 绑定到一个 网桥 上,需要使用 net_bridge_port 结构来关联的,

小结

总的来说,不管是以上介绍的namespace 还是网桥,都是计算机网络的鼻祖,从docker 的网络连接方式到kubenetes的网络方式,其底层的技术原理都与之有关。理解了namespace 和网桥的连接,后续理解docker 、kubernetes网络会更加简单。


  

9e4013ed89ab3d6117e28ddf6cdf5544.gif


  

10T 技术资源大放送!包括但不限于:Linux、虚拟化、容器、云计算、网络、Python、Go 等。在 开源Linux 公众号内回复 10T,即可免费获取!

Linux学习指南
有收获,点个在看
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

虚拟网络namespace 到bridge 的相关文章

随机推荐

  • 串口高波特率下如何稳定接收

    无论是蓝牙 WiFi xff0c 还是4G 5G xff0c 亦或是其它模组 xff0c 都支持AT指令 43 透传模式 AT指令模式下 xff0c 执行查询指令和操作 设置 指令 响应速度快 xff0c 逻辑交互明确 xff0c 不需要复
  • stm32 串口发送多字节数据(结构体版本)

    话不多说先上代码 typedef struct shuju u8 sj0 帧头 u8 sj1 u8 sj2 u8 sj3 u8 sj4 u8 sj5 u8 sj6 u8 sj7 u8 sj8 u8 sj9 帧尾 shuju 实际使用 shu
  • C++ Primer Plus(第六版)读书笔记

    文章目录 C 43 43 Primer Plus xff08 第六版 xff09 第1章 预备知识第2章 开始学习C 43 43 2 1 进入C 43 43 2 2 1 main 指令 2 2 C 43 43 语句2 2 2 赋值语句 第3
  • STM32 (5) 自己写库 构建库函数雏形1 寄存器结构体定义

    前面把基础部分讲得差不多 xff0c 比如说什么是寄存器 xff0c 寄存器映射 xff0c 怎么样来寄存器编程 xff0c 寄存器编程的时候应该参考官方的什么手册 xff0c 前面讲了什么是寄存器 怎么使用寄存器编程 寄存器编程的时候应该
  • 编译器优化对自定义延时程序的影响(volatile详解实验一)

    由此可见 xff08 C语言volatile关键字详解 xff09 xff0c 编译器优化会对自定义延时程序有影响 xff0c 我们深入汇编程序去探讨产生怎样的影响 xff01 首先是未加 volatie 使用和未使用编译器优化汇编程序的对
  • C语言之大小端转换

    include lt stdio h gt unsigned int reverse byte char c char num unsigned int r 61 0 int i for i 61 0 i lt num i 43 43 r
  • 世界坐标系、相机坐标系和图像坐标系的转换

    相机标定笔记 坐标系转换四个不同类型的坐标系1 世界坐标系2 相机坐标系3 图像物理坐标系4 图像像素坐标系 坐标转换世界坐标 相机坐标 xff08 刚性变换 xff09 绕 X X X 旋转
  • 【C++】strpbrk() 字符串检索函数

    strpbrk 字符串检索函数 需要包含头文件 string h xff1b 声明 span class token keyword char span span class token operator span span class t
  • 干货 | 手把手教你搭建一套OpenStack云平台

    1 前言 今天我们为一位朋友搭建一套OpenStack云平台 我们使用Kolla部署stein版本的OpenStack云平台 kolla是用于自动化部署OpenStack的一个项目 xff0c 它基于docker和ansible来实现 xf
  • 完全卸载nginx的详细步骤

    一个执着于技术的公众号 前言 在开局配置Nginx时有可能会配置错误 xff0c 报各种错误代码 看不懂或者懒得去看这个报错时 xff0c 其实最简单的方式是卸载并重装咯 今天就带大家一起学习下 xff0c 如何彻底卸载nginx程序 卸载
  • Windows 11的这19个新功能,你都知道吗?

    参考资料 xff1a https www windowslatest com 2021 10 06 windows 11 new features everything you need to know Windows 11 是 Windo
  • HttpClient 4.3 - 实现HTTP摘要认证(Digest authentication)

    HttpClient 4 实现HTTP摘要认证 HttpClient 4 实现HTTP摘要认证 什么是摘要认证用DefaultHttpClient实现HttpClient 4 3 实现 什么是摘要认证 说到摘要认证 Digest authe
  • 全国DNS服务器IP地址大全、公共DNS大全

    各省公共DNS服务器IP大全 名称各省公共DNS服务器IP大全 114 DNS114 114 114 114114 114 115 115阿里 AliDNS223 5 5 5223 6 6 6百度 BaiduDNS180 76 76 76
  • 如何在CentOS7上禁用或关闭SELinux

    介绍 SELinux 是内置于 Linux 内核中的强制访问控制 MAC 执行器 它限制了可能对系统构成威胁的个别服务的权限 没有 SELinux 的 CentOS 系统依赖于其所有特权软件应用程序的配置 单个错误配置可能会危及整个系统 为
  • 运维常用的 35 个Linux Shell 脚本,一定能帮到你!

    作为一名 Linux 工程师 xff0c 会写好的脚本不仅能提高工作效率 xff0c 还能有更多的时间做自己的事 最近在网上冲浪的时候 xff0c 也注意收集一些大佬写过的脚本 xff0c 汇总整理一下 xff0c 欢迎收藏 xff0c 与
  • 超好用的开源 IP 地址管理系统,告别传统 Excel 统计方式!

    来自 xff1a 释然IT杂谈 一 前言 xff1a 对于运维管理人员 xff0c ip地址进行管理很重要 xff0c 很多公司都是采用电子文档的形式 xff0c 以手工更新为主 xff0c 对ip地址和子网的实际使用情况无法进行有效的实时
  • Linux运维从入门到精通,看这一篇就够了~

    作为一名 Linux 运维工程师 xff0c 总是会有种 书到用时方恨少 的感觉 xff0c 其根本原因还是技能掌握的不够扎实 所以运维朋友一定要多学习 xff0c 提升技能 xff0c 下面分享一份专门针对运维朋友的资料包 xff0c 相
  • K8S CPU 请求和限制,是否有很好的选择?

    Limits 和 Requests 并不是 CPU 管理的灵丹妙药 xff0c 在某些情况下 xff0c 其他替代方案可能是更好的选择 在这篇博文中 xff0c 您将了解到 xff1a CPU requests 如何工作CPU limits
  • 作为一名Linux用户,你得了解这15个工具!

    来源 xff1a 浩道Linux 在普通人眼里 xff0c 使用Linux系统的用户本身已经很有 极客范儿 了 xff0c 但是在技术人员眼中 xff0c 这只是很普通的层级 使用本文推荐的几个Linux系统下的工具 xff0c 能让你瞬间
  • 虚拟网络namespace 到bridge

    前言 容器的网络是一大难点 xff0c 不管是docker 还是kubernetes 都绕不开计算机网络 以下的介绍主要以计算机网络的namespace 和bridge 两个方面来展开介绍 xff0c 方便深入理解容器的网络原理 1 nam