Linux虚拟化入门(三)Fedora 安装 KVM 管理环境

部署步骤

  • 第一步、检查环境要求

使用如下命令检查您的 CPU 是否支持虚拟化:

$ egrep '^flags.*(vmx|svm)' /proc/cpuinfo

如果没有任何输出,则说明您的系统不支持相关扩展功能。您仍然可以使用 QEMU/KVM ,但是虚拟将只能使用软件虚拟化(想当慢)。

  • 第二步、安装虚拟化软件包

当安装 Fedora 时,可以通过勾选安装基本组中的虚拟化组以安装虚拟化软件包。

在已经完成 Fedora 安装的系统中, QEMU、KVM和其他一些虚拟化工具的安装可以通过运行如下命令安装虚拟化组:

su -c "yum install @virtualization"

该命令将安装 qemu-kvm、 python-virtinst、 qemu、 virt-manager、 virt-viewer 以及所有需要的依赖软件包。

su -c "systemctl start libvirtd"

确认所有 kvm 内核模块已正常加载:

$ lsmod | grep kvm
kvm_amd                55563  0
kvm                   419458  1 kvm_amd

如果该命令没有列出 kvm_intel 或 kvm_amd, 则说明 KVM 没有正常配置。参看 确保系统正常使用 KVM 以获得解决问题的建议。

  • 第三步、使用虚拟机

您可以使用命令行工具 virsh 管理虚拟机。 你可以在命令行下使用 virsh 工具管理guest 。 virsh 工具是基于 libvirt 管理 API 实现的:

  • virsh 有一套稳定的命令,其语法与虚拟化平台无关。
  • virsh 可以作为仅有只读权限的工具使用(如:列出所有主机及其统计信息)。
  • virsh 可以管理 Xen,Qemu/KVM,esx 及其他一些类具有相同贵发后端下的主机。

一个有效地址可以使用 “-c” 参数传递给 virsh 来连接到远程 libvirtd 实例。详情请参看 http://libvirt.org/uri.html

如下命令启动虚拟机:

su -c "virsh create <name of virtual machine>"

要列出当前运行的虚拟机,执行:

su -c "virsh list"

列出所有虚拟机(不管是否运行):

su -c "virsh list --all"

正常关闭 guest :

su -c "virsh shutdown <virtual machine (name | id | uuid)>"

强制关闭 guest :

su -c "virsh destroy <virtual machine (name | id | uuid)>"

保存虚拟机快照到文件:

su -c "virsh save <virtual machine (name | id | uuid)> <filename>"

从快照恢复虚拟机:

su -c "virsh restore 
<filename>"

导出虚拟机配置文件:

su -c "virsh dumpxml <virtual machine (name | id | uuid)"

列出全部 virsh 可用命令:

su -c "virsh help"

也可以查看手册: man 1 virsh

参考文献

Linux虚拟化入门(二)Hyper-V 开启 KVM 嵌套虚拟化

日常办公使用 Windows 平台,需要研究 KVM 的使用,此时就需要在 Windows 提供的 Hyper-V 工具运行 Linux 虚拟机来测试 KVM 相关的使用,但是在 Hyper-V 虚拟机中再次运行 KVM 虚拟化属于嵌套虚拟化,需要开启相关功能。

下面给出 Hyper-V 开启嵌套虚拟化的方法,默认您已经创建出一个虚拟机实例,下面的操作在虚拟实例中进行。

  • 查看 Hyper-V 虚拟机是否支持虚拟化
egrep -o 'vmx|svm' /proc/cpuinfo

没有输出说明不支持,下面进行设置,在 Windows 宿主机进行:

  • 查看虚拟机参数

关闭虚拟机,管理员权限打开 Powershell

Get-VM  ##列出虚拟机
Get-VMProcessor -VMName [KVM主机] | fl
#查看虚拟化选项参数

# 示例,ExposeVirtualizationExtensions 为 false 说明不支持虚拟化
PS C:\Users\lenovo> Get-VMProcessor -VMName Fedora-Dev | fl
ResourcePoolName                             : Primordial
Count                                        : 2
CompatibilityForMigrationEnabled             : False
CompatibilityForOlderOperatingSystemsEnabled : False
HwThreadCountPerCore                         : 0
ExposeVirtualizationExtensions               : False
EnablePerfmonPmu                             : False
EnablePerfmonLbr                             : False
EnablePerfmonPebs                            : False
EnablePerfmonIpt                             : False
EnableLegacyApicMode                         : False
AllowACountMCount                            : False
Maximum                                      : 100
Reserve                                      : 0
RelativeWeight                               : 100
MaximumCountPerNumaNode                      : 12
MaximumCountPerNumaSocket                    : 1
EnableHostResourceProtection                 : False
OperationalStatus                            : {Ok, HostResourceProtectionDisabled}
StatusDescription                            : {确定, 主机资源保护已禁用。}
Name                                         : 处理器
Id                                           : Microsoft:369F6873-EDEE-4FCB-B154-E09A3095C743\b637f346-6a0e-4dec-af52-b
                                               d70cb80a21d\0
VMId                                         : 369f6873-edee-4fcb-b154-e09a3095c743
VMName                                       : Fedora-Dev
VMSnapshotId                                 : 00000000-0000-0000-0000-000000000000
VMSnapshotName                               :
CimSession                                   : CimSession: .
ComputerName                                 : MYIEUCD_DP
IsDeleted                                    : False
VMCheckpointId                               : 00000000-0000-0000-0000-000000000000
VMCheckpointName                             :
  • 开启嵌套虚拟化
Set-VMProcessor -ExposeVirtualizationExtensions $true -VMName [KVM主机]
##将其设置为True
# 重启虚拟机,查看已支持虚拟化
# 示例,ExposeVirtualizationExtensions 已经被设置为 true

PS C:\Users\lenovo> Get-VMProcessor -VMName Fedora-Dev | fl

ResourcePoolName                             : Primordial
Count                                        : 2
CompatibilityForMigrationEnabled             : False
CompatibilityForOlderOperatingSystemsEnabled : False
HwThreadCountPerCore                         : 0
ExposeVirtualizationExtensions               : True
EnablePerfmonPmu                             : False
EnablePerfmonLbr                             : False
EnablePerfmonPebs                            : False
EnablePerfmonIpt                             : False
EnableLegacyApicMode                         : False
AllowACountMCount                            : False
Maximum                                      : 100
Reserve                                      : 0
RelativeWeight                               : 100
MaximumCountPerNumaNode                      : 12
MaximumCountPerNumaSocket                    : 1
EnableHostResourceProtection                 : False
OperationalStatus                            : {}
StatusDescription                            : {}
Name                                         : 处理器
Id                                           : Microsoft:369F6873-EDEE-4FCB-B154-E09A3095C743\b637f346-6a0e-4dec-af52-b
                                               d70cb80a21d\0
VMId                                         : 369f6873-edee-4fcb-b154-e09a3095c743
VMName                                       : Fedora-Dev
VMSnapshotId                                 : 00000000-0000-0000-0000-000000000000
VMSnapshotName                               :
CimSession                                   : CimSession: .
ComputerName                                 : MYIEUCD_DP
IsDeleted                                    : False
VMCheckpointId                               : 00000000-0000-0000-0000-000000000000
VMCheckpointName                             :
# 虚拟机上查看,已经有多个VMX,有几个就意味着有几个CPU
$ egrep -o 'vmx|svm' /proc/cpuinfo
vmx
vmx
vmx
vmx

参考文献

Linux虚拟化入门(一)Qemu,KVM,Virsh 概念指南

当你安装了一台Linux,想启动一个KVM虚拟机的时候,你会发现需要安装不同的软件,启动虚拟机的时候,有多种方法:

  • virsh start
  • kvm命令
  • qemu命令
  • qemu-kvm命令
  • qemu-system-x86_64命令

QEMU

首先看qemu,其中关键字emu,全称emulator,模拟器,所以单纯使用qemu是采用的完全虚拟化的模式。

Qemu向Guest OS模拟CPU,也模拟其他的硬件,GuestOS认为自己和硬件直接打交道,其实是同Qemu模拟出来的硬件打交道,Qemu将这些指令转译给真正的硬件。由于所有的指令都要从Qemu里面过一手,因而性能比较差。

完全虚拟化是非常慢的,所以要使用硬件辅助虚拟化技术Intel-VT,AMD-V,所以需要CPU硬件开启这个标志位,一般在BIOS里面设置。查看是否开启

# 对于Intel CPU 可用命令判断
grep "vmx" /proc/cpuinfo 

# 对于AMD CPU 可用命令判断
grep "svm" /proc/cpuinfo 

当确认开始了标志位之后,通过KVM,GuestOS的CPU指令不用经过Qemu转译,直接运行,大大提高了速度。

所以KVM在内核里面需要有一个模块,来设置当前CPU是Guest OS在用,还是Host OS在用。

KVM

基于内核的虚拟机(英语:Kernel-based Virtual Machine,缩写为KVM)是一种用于Linux内核中的虚拟化基础设施,可将Linux内核转化为一个虚拟机监视器。

KVM提供抽象的设备,但不模拟处理器。它开放了/dev/kvm接口,供用户模式的主机使用。

https://imagehost-cdn.frytea.com/images/2021/07/05/2021070511463525bc131220fa136e.png

qemu-kvm

Qemu将KVM整合进来,通过ioctl调用/dev/kvm接口,将有关CPU指令的部分交由内核模块来做,就是qemu-kvm (qemu-system-XXX)

qemu和kvm整合之后,CPU的性能问题解决了,另外Qemu还会模拟其他的硬件,如Network, Disk,同样全虚拟化的方式也会影响这些设备的性能。

于是qemu采取半虚拟化或者类虚拟化的方式,让Guest OS加载特殊的驱动来做这件事情。

例如网络需要加载virtio_net,存储需要加载virtio_blk,Guest需要安装这些半虚拟化驱动,GuestOS知道自己是虚拟机,所以数据直接发送给半虚拟化设备,经过特殊处理,例如排队,缓存,批量处理等性能优化方式,最终发送给真正的硬件,一定程度上提高了性能。

https://imagehost-cdn.frytea.com/images/2021/07/05/202107051150145c91ed5fd0d33987.png

virsh

然而直接用 qemu 或者 qemu-kvm 或者 qemu-system-xxx 的少,大多数还是通过 virsh 启动, virsh 属于 libvirt 工具, libvirt 是目前使用最为广泛的对 KVM 虚拟机进行管理的工具和 API ,可不止管理KVM。

Libvirt 分服务端和客户端, Libvirtd 是一个daemon进程,是服务端,可以被本地的virsh调用,也可以被远程的virsh调用,virsh相当于客户端。

Libvirtd调用 qemu-kvm 操作虚拟机,有关CPU虚拟化的部分,qemu-kvm调用 kvm 的内核模块来实现

https://imagehost-cdn.frytea.com/images/2021/07/05/202107051152022b49de7970473781.png

这下子,整个相互关系才搞清楚了。

参考文献

C++中冒号(:)和双冒号(::)的用法总结

冒号(:)用法

(1)表示机构内位域的定义(即该变量占几个 bit 空间)

typedef struct _XXX{
unsigned char a:4;
unsigned char c;
} ; XXX

(2)构造函数后面的冒号起分割作用,是类给成员变量赋值的方法,初始化列表,更适用于成员变量的常量 const 型。

// 例1
struct _XXX{
    _XXX() : y(0xc0) {}
};

相当于
struct _XXX{
    _XXX() {
        y = 0xc0;
    }
};

// 例2
class myClass
{
public :
myClass();// 构造函数,无返回类型,可以有参数列表,这里省去
~myClass();// 析构函数
int a;
const int b;
}

myClass::myClass():a(1),b(1)// 初始化列表
{
}
  • 注 1:初始化列表的作用相当于在构造函数内进行相应成员变量的赋值,但两者是有差别的。

在初始化列表中是对变量进行初始化,而在构造函数内是进行赋值操作。两都的差别在对于像 const 类型数据的操作上表现得尤为明显。我们知道,const 类型的变量必须在定义时进行初始化,而不能对 const 型的变量进行赋值,因此 const 类型的成员变量只能(而且必须)在初始化列表中进行初始化,即下面的代码将会出错:

myClass::myClass()
{
a = 1;// 没错,效果相当于在初始化列表中进行初始化
b = 1;// 出错,const变量不能进行赋值操作;
}
  • 注 2:初始化的顺序与成员变量声名的顺序相同。

先看一下下面的程序:

myClass::myClass():b(1),a(b)
{
}

这样的执行结果 a,b 各是多少呢?b=1,a=1?不是,b=1 而 a 是个随机数。这一点是相当重要的哦,一般在初始化列表中进行初始化时,初始化的顺序应与声明的顺序保持一致,防止出现不必要的错误。

  • 注 3:对于继承的类来说,在初始化列表中也可以进行基类的初始化,初始化的顺序是先基类初始化,然后再根据该类自己的变量的声明顺序进行初始化。

(3) public: 和 private: 后面的冒号,表示后面定义的所有成员都是公有或私有的,直到下一个 public: 或 private: 出现为止。

(4)类名冒号后面的是用来定义类的继承。

class 派生类名 :继承方式 基类名
{
派生类的成员
};

// 继承方式:public、private和protected,默认处理是public。

双冒号(::)用法

(1)表示 域操作符 / 作用域分解运算符

[cpp] view plaincopy
class CA {  
public:  
  int ca_var;  
  int add(int a, int b);  
  int add(int a);  
};   

//那么在实现这个函数时,必须这样书写:  
int CA::add(int a, int b)  
{  
  return a + b;  
}  

//另外,双冒号也常常用于在类变量内部作为当前类实例的元素进行表示,比如:  
int CA::add(int a)  
{  
  return a + ::ca_var;  
}   
//表示当前类实例中的变量ca_var

(2)全局作用域符号:当全局变量在局部函数中与其中某个变量重名,那么就可以用 :: 来区分如

char zhou; //全局变量 
void sleep()
{
    char zhou; //局部变量
    zhou(局部变量) = zhou(局部变量) * zhou(局部变量);
    ::zhou(全局变量) =::zhou(全局变量) *zhou(局部变量);
}

(3)表示引用成员函数及变量,作用域成员运算符

System::Math::Sqrt()
// 相当于
System.Math.Sqrt()

参考文献

Typecho 博客文首自动添加本页链接

自己的博客不觉间已经上线两年多了,随着内容和浏览量的增加,我的博客开始被一些搬运站盯上,常常搜索自己博客内容却在其他人博客里找到完全一样的内容,关键是还不署名!

https://imagehost-cdn.frytea.com/images/2021/06/09/20210609092801367a020d9b6ee926.png

为了防止这种脑残爬虫党,我会在博客文首新增 “本文首发于: “ 字样,后面跟上本页地址链接,这样及时博客被爬虫爬取,也会保留本文原始链接,需要的人可以通过这个链接找到我的源站。但是一篇一篇手动加起来太累了,就想了一种很简单的自动添加的方法。

https://imagehost-cdn.frytea.com/images/2021/06/09/20210609092900ae858a6c62a85958.png

方法介绍

原理大概就是在文章页首部 新增一个 `

Bullet Journal (肆)& 日常管理

不知不觉,Bullet Journal 模版系列文章就要迎来尾声啦。今天带来的是 Bullet Journal 中的核心部分 —— 日常管理。

我认为 Bullet Journal 的核心就在于其每日记录,用最快的方式回顾这一天,顺便为其加入复盘的属性,让每一天发生的特别的、有趣的事情留在你的 Bullet Journal 中。等一个月完毕,或是一年后的某一天翻开某一天的 Bullet Journal ,还能回忆起当时的那种感动。

每日一句(月末回顾)

为了统一管理每日记录的数据条目,在 Notion 首页创建一个 Journal 数据库用于存放每日的 Bullet Journal:

https://imagehost-cdn.frytea.com/images/2021/06/08/2021-06-08-11.40.3245499a34be22aea8.png

在数据库中,可以包括你希望每天记录的各种记录值,习惯养成也可以在这里记录,甚至可以记下每日的早餐、睡眠时长、运动状况等等数据,记录的越丰富,月末复盘时可以回顾更多的内容。

https://imagehost-cdn.frytea.com/images/2021/06/08/2021-06-08-11.41.584b30d8d836dd6ccf.png

之后将该数据库插入每月记录中的 Journal 标题下,用列表视图,Name 字段就作为每日一句话,可以记录今日最让我印象深刻的一件事,这样一个月下来,每天的大致情况就一目了然啦!

https://imagehost-cdn.frytea.com/images/2021/06/08/2021-06-08-11.45.51ad9120f409ecea75.png

这大概就是每日一句的内容啦,记下当天最令我印象深刻的话,就顺便再花五分钟做一个简单的复盘吧!

每日复盘(每日精进)

在每日记录模块,我结合多种日记模版,制作了一个属于自己的模版,既能快速记录当日特别的事情,又能在想记录的时候记录细节。

https://imagehost-cdn.frytea.com/images/2021/06/08/2021-06-08-11.47.543b05d241307d9812.png

主要包括四个部分:

✨ 特别的事:记录几件我认为今天最特别的事;

??‍? 进步的事:记录我认为今天做的比之前要好的事;

?? 成功故事:记录我认为今天做的最成功或是可以更好的事情;

? 特别故事:记录我认为今天有意思的小故事。

这几个部分可以按照自己的喜好分别调整,也无需强求填满,只需按照实际情况及心情、灵感状况,随性填写即可,放得开可能可以记下更多有意思的事情。如果很累,或是没什么特别的事,就简单写几条也可,重点是要每天去记录,每天去精进。 等到月末总结时,翻开每天的复盘日记,就可以一目了然想起当时发生的事情,说不定还可以想起许多有意思的回忆呢,而这一切,只需要每天抽出 5 分钟就可以完成啦!

每日打卡(习惯养成)

出了每日精进,还要记得好习惯的养成哟!在 Journal 数据库中内嵌了几个字段用于习惯养成,还做了公示统计当日的完成程度。等到月末总结时,只需简单 checked 一下,就知道这个月坚持了几天,复盘了几次,直观数据一目了然。

https://imagehost-cdn.frytea.com/images/2021/06/09/2021-06-09-12.01.53f0e2f430dfd87a67.png

好习惯一定要坚持呀!

终极模版(完全体)

至此,我的 Bullet Journal 模版系列文章正式完结!在文章的最后,祭出本人使用了数个月,反复调整后的集大成之 Bullet Journal 模版,免费提供给您克隆使用,如果好用记得评论告诉我!

Bullet Journal 个人管理模版:https://www.notion.so/Bullet-Journal-6367af1cab744eb59c33b7bd857919f3 作者:宋天伦

从 Redis 表项看 SONiC 架构

SONiC 系统的架构由各种模块组成,这些模块通过集中式和可扩展的基础架构相互交互。这个基础设施依赖于使用一个 redis-database 引擎来提供一个独立于语言的接口,一个在所有 SONiC 子系统之间进行数据持久化、复制和多进程通信的方法。

通过依赖 redis 引擎基础设施提供的 发布者/订阅者 消息传递范式,应用程序可以只订阅它们需要的数据视图,并避免与其功能无关的实现细节。

SONiC 将每个模块放置在独立的 docker 容器中。这些组件中的每一个都是完全独立于平台特定细节而编写的,这些细节是与底层抽象交互所必需的。

SONiC 容器架构

截至目前(6 Jun 2019),SONiC 将其主要功能组件分解为以下 docker 容器:

  • Dhcp-relay: 将DHCP请求从没有DHCP服务器的子网中继到其他子网的一个或多个DHCP服务器。
  • Pmon: 负责运行“sensor”,这是一个守护进程,用于定期记录硬件组件的传感器读数,并在警报发出时发出警报。Pmon容器还承载“风扇控制”进程,从相应的平台驱动程序中收集风扇相关的状态。
  • Snmp: 承载Snmp特性。
  • Lldp: 承载Lldp功能。
  • Bgp: 运行支持的路由栈之一: Quagga或FRR。(实际上,这些路由栈可以运行各种其他协议(如ospf、isis、ldp等))
  • Teamd: 在SONiC设备中运行链接聚合功能(LAG)。“teamd”是一个基于linux的LAG协议的开源实现。“团队同步”过程允许“团队”和南向子系统之间的交互。
  • Database: 承载redis-database引擎:
  • Swss: Switch State Service (Swss)容器由一组工具组成,允许所有SONiC模块之间进行有效通信,主要侧重于提供促进所有不同方之间的通信和仲裁的机制。
  • Syncd: 容器的目标是提供一种机制,允许交换机的网络状态与交换机的实际硬件/ASIC同步。这包括初始化、配置和收集交换机的ASIC当前状态。

右图显示了每个docker容器中包含的功能的高级视图,以及这些容器之间如何相互作用。注意,并不是所有的SONiC应用程序都与其他SONiC组件交互,因为其中一些组件从外部实体收集它们的状态。我们使用 蓝色箭头 表示与集中的 redis引擎 的交互,使用 黑色箭头 表示所有其他的( netlink , /sys 文件系统等)。

尽管 SONiC 的大部分主要组件都在 docker 容器中,但也有一些关键模块位于 linux 主机系统本身。这就是 SONiC 的配置模块 SONiC -cfggen 和 SONiC 的 CLI 。

数据库架构

https://imagehost-cdn.frytea.com/images/2021/06/08/202106081716469ccbb04be94fc671.png

以下是redis引擎所承载的主要数据库:

APPL_DB:存储所有应用程序容器生成的状态——路由、下一跳、邻居等。这是所有希望与其他SONiC子系统交互的应用程序的南向入口点。

CONFIG_DB:存储由SONiC应用程序创建的配置状态——端口配置、接口、vlan等。

STATE_DB:为系统中配置的实体存储“key”操作状态。此状态用于解析不同SONiC子系统之间的依赖关系。例如,一个LAG端口通道(由teamteam子模块定义)可以潜在地引用系统中可能存在也可能不存在的物理端口。另一个例子是VLAN的定义(通过vlanmgrd组件),它可能引用系统中未确定是否存在的端口成员。本质上,这个DB存储了解决跨模块依赖关系所必需的所有状态。

ASIC_DB:存储驱动asic配置和操作所需的状态——这里的状态以asic友好的格式保存,以简化syncd(参见后面的详细信息)和asic sdk之间的交互。

COUNTERS_DB:存储与系统中每个端口关联的计数器/统计信息。此状态可用于满足CLI本地请求,或为远程使用提供遥测通道。

SONiC 子系统交互

LLDP 状态交互

下图描述了在 lldp 状态转移期间观察到的一组相互作用。在这个特定的示例中,我们迭代了在携带状态变化的 LLDP 消息到达时发生的一系列步骤。

(1)在 LLDP 容器初始化期间, lldpmgrd 订阅 STATE_DB 以实时获取系统中物理端口的状态—— lldpmgrd 的轮询周期每5秒运行一次。基于这些信息, Lldpd (及其网络对等体)将了解系统端口状态的变化以及影响其运行的任何配置变化。

(2)一个新的 LLDP 报文到达内核空间的 LLDP socket 。内核的网络栈最终将相关的有效负载交付给 lldp 进程。

(3) Lldp 解析并消化这个新状态, lldp_syncd 在执行 lldpctl cli 命令(通常每10秒运行一次)的过程中最终获取这个新状态。

Lldp_syncd 将这个新状态推到 APPL_DB 中,具体地说,推到LLDP_ENTRY_TABLE 表中。

(5)从现在开始,所有订阅这个表的实体都应该收到一个新状态的副本(目前, snmp 是唯一感兴趣的侦听器)。

https://imagehost-cdn.frytea.com/images/2021/06/08/2021060817171113f0b73ae9072558.png


SNMP 状态交互

如前所述, snmp 容器同时承载一个snmp主代理(snmpd)和一个特定于sonic的agentX进程( snmp_subagent )。该子代理与所有redis数据库/表进行交互,这些redis数据库/表提供了可以派生MIB状态的信息。具体来说, snmp-agent 订阅了以下数据库/表:

  • APPL_DB: PORT_TABLE, LAG_TABLE, LAG_MEMBER_TABLE, LLDP_ENTRY_TABLE
  • STATE_DB: *
  • COUNTERS_DB: *
  • ASIC_DB: ASIC_STATE:SAI_OBJECT_TYPE_FDB*

下图描述了系统处理传入 snmp 查询期间各种 SONiC 组件之间的典型交互。

(0)在初始化snmp-subagent进程中支持的不同MIB子组件时,该MIB子组件与上述各个db建立连接。从这一刻起,从所有这些db获得的状态被本地缓存到snmp-subagent中。该信息每隔几秒(< 60)刷新一次,以确保db和snmp-subagent完全同步。

(1)一个snmp查询到达内核空间的snmp的套接字。内核的网络栈将数据包发送给snmpd进程。

(2) snmp消息被解析,一个相关的请求被发送到SONiC的agentX子代理(即sonic_ax_impl)。

(3) Snmp-subagent服务于其本地数据结构中缓存的状态之外的查询,并将信息发送回snmpd进程。

(4) Snmpd最终通过常用的socket接口向发起者发送一个应答。

https://imagehost-cdn.frytea.com/images/2021/06/08/202106081717261c81afc21457834b.png


路由状态交互

在本节中,我们将遍历发生在SONiC中的一系列步骤,以处理从eBGP对等体接收到的新路由。我们假设这个会话已经建立,并且我们正在学习一条新的路由,它使用一个直接连接的对等体作为它的下一跳。

(0)在 BGP 容器初始化过程中, zebra 通过常规TCP套接字连接到 fpmsyncd 。在稳定/非瞬态条件下,存放在 zebra 、 linux 内核、APPL_DB和ASIC_DB中的路由表应该是完全一致/等效的。

(1)一个新的TCP报文到达内核空间的bgp socket。内核的网络栈最终将相关的有效载荷传递给bgpd进程。

(2) Bgpd解析新报文,处理bgp-update,并通知zebra这个新前缀的存在及其相关的下一跳协议。

(3) zebra通过判断该前缀的可行性/可达性(例如现有的转发nh),生成一个route-netlink消息将这个新的状态注入到kernel中。 Zebra利用FPM接口将这个网络链路路由消息传递给fpmsyncd。

(5) Fpmsyncd处理netlink消息,并将此状态推入 APPL_DB。

作为一个APPL_DB订阅者,它将接收先前推送到 APPL_DB 的信息的内容。

(7)处理完接收到的信息后,orchagentd会调用sairedis api将路由信息注入到ASIC_DB 中。同步一个ASIC_DB订阅者时,它将接收由orchagentd生成的新状态。

(9) Syncd将处理该信息,并调用SAI api将该状态注入到相应的asic驱动程序中。

(10)新路由最终推送到硬件。

https://imagehost-cdn.frytea.com/images/2021/06/08/20210608171744539868b84fce9068.png


端口状态交互

本节描述在端口相关信息传输过程中发生的系统交互。考虑到portsyncd扮演的关键角色,以及它在其他SONiC子系统中施加的依赖关系,我们将从介绍它的初始化过程开始本节。

这个练习有两个目的。首先,我们公开了系统中对生成或使用端口相关信息感兴趣的多个组件。其次,我们将通过一个图形示例向读者介绍 STATE_DB 在系统中是如何使用的,以及不同的应用程序如何依赖它的信息进行内部操作。

(0) 在初始化过程中,portsyncd 与redis-engine 中的主要数据库建立通信通道。Portsyncd 声明其意图充当 APPL_DB 和 STATE_DB 的发布者,以及 CONFIG_DB 的订阅者。同样,portsyncd 也订阅系统的 netlink 通道,负责携带端口/链路状态信息。

(1) Portsyncd 通过解析与系统中使用的硬件配置文件/sku 相关联的端口配置文件 (port_config.ini) 开始(有关更多详细信息,请参阅配置部分)。通道、接口名称、接口别名、速度等与端口相关的信息通过该通道传输到 APPL_DB。

(2) Orchagent 会听到所有这些新状态,但会推迟对其采取行动,直到 portsyncd 通知它已完全解析 port_config.ini 信息。一旦发生这种情况,orchagent 将继续在硬件/内核中初始化相应的端口接口。Orchagent 调用 sairedis API 以通过通常的 ASIC_DB 接口将此请求传送到同步。

(3) Syncd 通过 ASIC_DB 接收到这个新请求,并准备调用满足 Orchagent 请求所需的 SAI API。

(4) Syncd 利用 SAI APIs + ASIC SDK 创建与正在初始化的物理端口相关联的内核主机接口。

(5) 上一步将生成一个 netlink 消息,该消息将被 portsyncd 接收。当与先前从 port_config.ini 解析的所有端口相关联的消息到达 portsyncd 时(在步骤 1 中),portsyncd 将继续声明“初始化”过程已完成。

(6) 作为上一步的一部分,portsyncd 将记录条目写入与成功初始化的每个端口对应的 STATE_DB。

(7) 从这一刻起,之前订阅了 STATE_DB 内容的应用程序将收到通知,允许这些应用程序开始使用它们所依赖的端口。换句话说,如果在 STATE_DB 中找不到特定端口的有效条目,则任何应用程序都无法使用它。

https://imagehost-cdn.frytea.com/images/2021/06/08/202106081717566d8d80df28b7b6c0.png

NOTE : As of today, these are the applications actively
listening to the changes in STATE_DB: teamsyncd, intfmgrd, vlanmgrd
and lldpmgr. We will cover all these components in subsequent
sections -- lldpmgr has been already tackled abov

现在,让我们遍历物理端口关闭时发生的一系列步骤:

(0) 正如前面概述部分中提到的,syncd 在 ASIC_DB 的上下文中既作为发布者又作为订阅者执行。“订阅者”模式显然是因为需要 syncd 从北向应用程序接收状态,就像迄今为止看到的所有模块交互的情况一样。需要“发布者”模式以允许 syncd 将硬件产生的事件到达通知更高级别的组件。

(1) 在相应 ASIC 的光模块检测到载波丢失后,将向相关驱动程序发送通知,后者又将此信息传递给 syncd。

(2) Syncd 调用适当的通知处理程序并将端口关闭事件发送到 ASIC_DB。

(3) Orchagent 利用其通知线程(专用于此任务)从 ASIC_DB 收集新状态,并执行“port-state-change”处理程序以:

a.  Generate an update to APPL\_DB to alert applications relying on
    this state for their operation (e.g. CLI -- "show interface
    status").

b.  Invoke sairedis APIs to alert syncd of the need to update the
    kernel state associated to the host-interface of the port being
    brought down. Again, orchagent delivers this request to syncd
    through the usual ASIC\_DB interface.

(4) Syncd 通过ASIC_DB 接收到这个新请求,并准备调用满足orchagent 请求所需的SAI API。

(5) Syncd 使用 SAI APIs + ASIC SDK 来更新内核与受影响主机接口的最新操作状态 (DOWN)。

(6) 在 portsyncd 处接收到与上一步相关联的 netlink 消息,由于所有 SONiC 组件现在完全知道端口关闭事件,因此该消息被静默丢弃。

https://imagehost-cdn.frytea.com/images/2021/06/08/20210608171807dc16592980fabb79.png

参考文献