KubeVirt with DPDK

news2024/11/23 20:53:25

发布于2022-11-25 15:52:32阅读 1020

Kubernetes优秀的架构设计,借助multus cni + intel userspace cni 可以屏蔽了DPDK底层的复杂,让KubeVirt 支持DPDK变得比较容易。

因为 e2e验证 等原因,KubeVirt社区至今未加入对DPDK支持,本篇试着在最新版的KubeVirt v0.53加入DPDK功能。

network Phase1 & Phase2

Phase1

Phase2

权限

大(privileged networking configuration)

小(unprivileged networking configuration)

发生场所

the virt-handler process

virt-launcher process

主要功能

第一步是决定改使用哪种 BindMechanism,一旦 BindMechanism 确定,则依次调用以下方法:1. discoverPodNetworkInterface:获取 Pod 网卡设备相关的信息,包括 IP 地址,路由,网关等信息2. preparePodNetworkInterfaces:基于前面获取的信息,配置网络3. setCachedInterface:缓存接口信息在内存中4. setCachedVIF:在文件系统中持久化 VIF 对象

和Phase1一样Phase2 也会选择正确的 BindMechanism,然后取得 Phase1 的配置信息(by loading cached VIF object)。基于 VIF 信息, proceed to decorate the domain xml configuration of the VM it will encapsulate

DPDK不需要Phase1做任何事情,因为不需要获取Pod的网络信息,也不需要缓存Pod的网络和配置vm的网络。

Phase2是基于Phase1的后续,所以DPDK也不需要Phase2做任何事情。

func (l *podNIC) PlugPhase1() error {

	...
	if l.vmiSpecIface.Vhostuser != nil {
		return nil
	}
	...language-go复制代码

复制

func (l *podNIC) PlugPhase2(domain *api.Domain) error {

	...
	if l.vmiSpecIface.Vhostuser != nil {
		return nil
	}
	...language-go复制代码

复制

准备Pod with DPDK mainifest

pkg/virt-controller/services/template.go

const VhostuserSocketDir = "/var/lib/cni/usrcni/"const OvsRunDirDefault = "/var/run/openvswitch/"const PodNetInfoDefault = "/etc/podnetinfo"func addVhostuserVolumes(volumeMounts *[]k8sv1.VolumeMount, volumes *[]k8sv1.Volume) {
	// "shared-dir" volume name will be used by userspace cni to place the vhostuser socket file`
	*volumeMounts = append(*volumeMounts, k8sv1.VolumeMount{
		Name:      "shared-dir",
		MountPath: VhostuserSocketDir,
	})

	*volumes = append(*volumes, k8sv1.Volume{
		Name: "shared-dir",
		VolumeSource: k8sv1.VolumeSource{
			EmptyDir: &k8sv1.EmptyDirVolumeSource{
				Medium: k8sv1.StorageMediumDefault,
			},
		},
	})

	// Libvirt uses ovs-vsctl commands to get interface stats
	*volumeMounts = append(*volumeMounts, k8sv1.VolumeMount{
		Name:      "ovs-run-dir",
		MountPath: OvsRunDirDefault,
	})
	*volumes = append(*volumes, k8sv1.Volume{
		Name: "ovs-run-dir",
		VolumeSource: k8sv1.VolumeSource{
			HostPath: &k8sv1.HostPathVolumeSource{
				Path: OvsRunDirDefault,
			},
		},
	})}func addPodInfoVolumes(volumeMounts *[]k8sv1.VolumeMount, volumes *[]k8sv1.Volume) {
	// userspace cni will set the vhostuser socket details in annotations, app-netutil helper
	// will parse annotations from /etc/podnetinfo to get the interface details of
	// vhostuser socket (which will be added to VM xml)
	*volumeMounts = append(*volumeMounts, k8sv1.VolumeMount{
		Name: "podinfo",
		// TODO: (skramaja): app-netutil expects path to be /etc/podnetinfo, make it customizable
		MountPath: PodNetInfoDefault,
	})
	*volumes = append(*volumes, k8sv1.Volume{
		Name: "podinfo",
		VolumeSource: k8sv1.VolumeSource{
			DownwardAPI: &k8sv1.DownwardAPIVolumeSource{
				Items: []k8sv1.DownwardAPIVolumeFile{
					{
						Path: "labels",
						FieldRef: &k8sv1.ObjectFieldSelector{
							APIVersion: "v1",
							FieldPath:  "metadata.labels",
						},
					},
					{
						Path: "annotations",
						FieldRef: &k8sv1.ObjectFieldSelector{
							APIVersion: "v1",
							FieldPath:  "metadata.annotations",
						},
					},
				},
			},
		},
	})}func (t *templateService) renderLaunchManifest(vmi *v1.VirtualMachineInstance, imageIDs map[string]string, tempPod bool) (*k8sv1.Pod, error) {
	...
	if util.IsVhostuserVmi(vmi) {
		...
		addVhostuserVolumes(&volumeMounts, &volumes)
		addPodInfoVolumes(&volumeMounts, &volumes)
		...
	}
	...language-go复制代码

复制

生成类似下面的pod yaml

volumes:
  - name: vhostuser-sockets
    emptyDir: {}containers:
  - name: vm
    image: vm-vhostuser:latest
    volumeMounts:
      - name: vhostuser-sockets
        mountPath: /var/run/vmlanguage-yaml复制代码

复制

准备libvirt xml define

pkg/virt-launcher/virtwrap/converter/network.go

...} else if iface.Vhostuser != nil {
	networks := map[string]*v1.Network{}
	cniNetworks := map[string]int{}
	multusNetworkIndex := 1
	for _, network := range vmi.Spec.Networks {
		numberOfSources := 0
		if network.Pod != nil {
			numberOfSources++
		}
		if network.Multus != nil {
			if network.Multus.Default {
				// default network is eth0
				cniNetworks[network.Name] = 0
			} else {
				cniNetworks[network.Name] = multusNetworkIndex
				multusNetworkIndex++
			}
			numberOfSources++
		}
		if numberOfSources == 0 {
			return nil, fmt.Errorf("fail network %s must have a network type", network.Name)
		} else if numberOfSources > 1 {
			return nil, fmt.Errorf("fail network %s must have only one network type", network.Name)
		}
		networks[network.Name] = network.DeepCopy()
	}

	domainIface.Type = "vhostuser"
	interfaceName := GetPodInterfaceName(networks, cniNetworks, iface.Name)
	vhostPath, vhostMode, err := getVhostuserInfo(interfaceName, c)
	if err != nil {
		log.Log.Errorf("Failed to get vhostuser interface info: %v", err)
		return nil, err	}
	vhostPathParts := strings.Split(vhostPath, "/")
	vhostDevice := vhostPathParts[len(vhostPathParts)-1]
	domainIface.Source = api.InterfaceSource{
		Type: "unix",
		Path: vhostPath,
		Mode: vhostMode,
	}
	domainIface.Target = &api.InterfaceTarget{
		Device: vhostDevice,
	}
	//var vhostuserQueueSize uint32 = 1024
	domainIface.Driver = &api.InterfaceDriver{
		//RxQueueSize: &vhostuserQueueSize,
		//TxQueueSize: &vhostuserQueueSize,
	}}language-go复制代码

复制

func getVhostuserInfo(ifaceName string, c *ConverterContext) (string, string, error) {
	if c.PodNetInterfaces == nil {
		err := fmt.Errorf("PodNetInterfaces cannot be nil for vhostuser interface")
		return "", "", err	}
	for _, iface := range c.PodNetInterfaces.Interface {
		if iface.DeviceType == "vhost" && iface.NetworkStatus.Interface == ifaceName {
			return iface.NetworkStatus.DeviceInfo.VhostUser.Path, iface.NetworkStatus.DeviceInfo.VhostUser.Mode, nil
		}
	}
	err := fmt.Errorf("Unable to get vhostuser interface info for %s", ifaceName)
	return "", "", err}func GetPodInterfaceName(networks map[string]*v1.Network, cniNetworks map[string]int, ifaceName string) string {
	if networks[ifaceName].Multus != nil && !networks[ifaceName].Multus.Default {
		// multus pod interfaces named netX
		return fmt.Sprintf("net%d", cniNetworks[ifaceName])
	} else {
		return PodInterfaceNameDefault	}}language-go复制代码

复制

生成类似下面的libvrit xml define

<interface type='vhostuser'>
  <mac address='00:00:00:0A:30:89'/>
  <source type='unix' path='/var/run/vm/sock' mode='server'/>
   <model type='virtio'/>
  <driver queues='2'>
    <host mrg_rxbuf='off'/>
  </driver></interface>language-markup复制代码

复制

变更KubeVirt API

staging/src/kubevirt.io/api/core/v1/schema.go

type InterfaceBindingMethod struct {
	Bridge     *InterfaceBridge     `json:"bridge,omitempty"`
	Slirp      *InterfaceSlirp      `json:"slirp,omitempty"`
	Masquerade *InterfaceMasquerade `json:"masquerade,omitempty"`
	SRIOV      *InterfaceSRIOV      `json:"sriov,omitempty"`
	Vhostuser  *InterfaceVhostuser  `json:"vhostuser,omitempty"`
	Macvtap    *InterfaceMacvtap    `json:"macvtap,omitempty"`}language-go复制代码

复制

type InterfaceVhostuser struct{}language-go复制代码

复制

支持类似下面的CRD

apiVersion: kubevirt.io/v1kind: VirtualMachineInstancemetadata:
  name: vm-trex-1spec:
  domain:
    devices:
      interfaces:
      - name: default
        masquerade: {}
      - name: vhost-user-net-1
        vhostuser: {}
  networks:
  - name: default
    pod: {}
  - name: vhost-user-net-1
    multus:
      networkName: userspace-ovs-net-1language-yaml复制代码

复制

感觉KubeVirt增加DPDK不是很复杂,只是在KubeVirt自动化流程上多加点DPDK相关的pod yaml部分,vm define xml部分。关键是对DPDK功能的验证,今后再开一篇补上验证相关的内容。

附:DPDK vHost User Ports

Open vSwitch 提供两种类型的 vHost User ports:

  • vhost-user (dpdkvhostuser)
  • vhost-user-client (dpdkvhostuserclient)

vHost User采用客户端服务端模式。服务端 creates/manages/destroys the vHost User sockets。客户端连接到服务端。

For vhost-user ports, Open vSwitch acts as the server and QEMU the client.

ovs-vsctl add-port br0 vhost-user-1 -- set Interface vhost-user-1 type=dpdkvhostuser复制代码

复制

libvirt xml define

<interface type='vhostuser'>
  <mac address='00:00:00:00:00:01'/>
  <source type='unix' path='/usr/local/var/run/openvswitch/dpdkvhostuser0' mode='client'/>
   <model type='virtio'/>
  <driver queues='2'>
    <host mrg_rxbuf='off'/>
  </driver>
</interface>复制代码

复制

For vhost-user-client ports, Open vSwitch acts as the client and QEMU the server.

VHOST_USER_SOCKET_PATH=/path/to/socket
ovs-vsctl add-port br0 vhost-client-1 -- set Interface vhost-client-1 type=dpdkvhostuserclient options:vhost-server-path=$VHOST_USER_SOCKET_PATH复制代码

复制

libvirt xml define

    <interface type='vhostuser'>
      <mac address='00:00:00:0A:30:89'/>
      <source type='unix' path='/var/run/vm/sock' mode='server'/>
       <model type='virtio'/>
      <driver queues='2'>
        <host mrg_rxbuf='off'/>
      </driver>
    </interface>复制代码

复制

由于在QEMU中不支持动态重新连接中,因此在OVS-DPDK中已弃用了dpdkvhostuser模式。dpdkvhostuser模式重新启动OVS需要重新启动VM。 在 DPDK vhostuserclient 模式下,QEMU充当服务器,QEMU创建了Vhostuser套接字文件,此时OVS充当客户端。即使重新启动OVS也不会影响VM,因为OVS可以在重新启动后连接到socket,OVS支持重启后自动重新连接。 由于旧模式 DPDK VHostuser 被弃用,KubeVirt仅实现了DPDK VHostUserClient模式。 OVS-DPDK 借助vhostuser socket绕过内核空间,增强了应用程序的数据包处理性能,它需要启用大页内存,在host和guest间共享数据包处理。 userspace CNI 借助 multus CNI增加一个额外的DPDK网络,在 OVS-DPDK 和 kubevirt (Qemu) 间共享 vhostuser socket。

原文链接:https://cloud.tencent.com/developer/article/2175858

(免费订阅,永久学习)学习地址: Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂

更多DPDK相关学习资料有需要的可以自行报名学习,免费订阅,永久学习,或点击这里加qun免费
领取,关注我持续更新哦! ! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/43499.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

有用的CSS代码块

文章目录调试 DOM 元素的 border通用的网页样式调试 DOM 元素的 border 显示所有DOM元素的border&#xff0c;方便调试网页元素的相对布局。 * {outline: auto; }如何用javascript设置某个网页的style(复制以下代码到浏览器控制台执行即可)&#xff1f; // wuyujin1997 var …

PIC单片机5——串口 中断

//10M晶振 波特率9600 BRG8位波特率发生器 异步模式 #include "p18f458.h" #include "mydelay.h" #pragma config OSCHS,WDTOFF,LVPOFF,DEBUGON void PIC18F_High_isr(void);/*中断服务函数声明*/ void PIC18F_Low_isr(void); void usart_tx(unsigned ch…

基于javaweb房屋租赁管理系统的设计与实现

摘要 当今社会不管房屋出租、出售买卖是必不可少的&#xff0c;人们不管走到哪里都需要有一个温馨的家&#xff0c;有一个落脚之地&#xff0c;所以房源出租市场也是非常火爆&#xff01;不管是房源出租公司或者是个人都需要一套完整的管理系统来掌握整个市场信息。针对这一需求…

观察者模式在spring中的应用

作者&#xff1a;王子源 1 观察者模式简介 1.1 定义 指多个对象间存在一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式&#xff0c;它是对象行为型模式。 …

离散卡尔曼滤波实现

离散卡尔曼滤波基本理论 卡尔曼预报器、平滑器可以参考之前的博客&#xff1a;(2条消息) 卡尔曼滤波器_KPer_Yang的博客-CSDN博客 下面贴上一张图1&#xff0c;很直观&#xff1a;分成时间更新和测量更新两步&#xff0c;其中的KKK和PPP有可能随着时间推移变成常数&#xff0…

STM32实战总结:HAL之RTC

RTC基础知识参考&#xff1a; 51单片机内部外设&#xff1a;实时时钟(SPI)_路溪非溪的博客-CSDN博客 STM32中的RTC 51单片机通常是外置的RTC芯片如DS1302&#xff0c;那么STM32的RTC是什么情况呢&#xff1f; STM32芯片自带RTC&#xff0c;因此不须像其他MCU需外接RTC模块。 先…

年末盘点Android 过去一年与未来的一个走势~

随着Android的发展&#xff0c;有些人对Android未来感到茫然&#xff0c;不少人可能会产生这样的疑惑&#xff1a;“从事Android是不是没有前途&#xff0c;Android开发还有什么值得学&#xff1f;“这类话题一直让大家争论不休&#xff0c;它并没有一个确切、唯一的标准答案&a…

中介者模式

思考中介者模式 当多个类&#xff08;对象&#xff09;耦合严重时&#xff0c;通过中介者模式创建一个中介者&#xff0c;多个类不直接交互了&#xff0c;变成和中介者进行交互&#xff0c;松散耦合 1.中介者模式的本质 中介者模式的本质:封装交互。 中介者模式的目的&#xff…

关于无感刷新Token,我是这样子做的

本文正在参加「金石计划 . 瓜分6万现金大奖」 什么是JWT JWT是全称是JSON WEB TOKEN&#xff0c;是一个开放标准&#xff0c;用于将各方数据信息作为JSON格式进行对象传递&#xff0c;可以对数据进行可选的数字加密&#xff0c;可使用RSA或ECDSA进行公钥/私钥签名。 使用场景…

WPSpell将拼写检查添加到VCL应用程序

WPSpell将拼写检查添加到VCL应用程序 WPSpell包括键入功能时的拼写。拼写错误的单词带有下划线&#xff0c;可以使用上下文菜单进行更正。它还包括一个传统的拼写检查对话框&#xff0c;并支持多个词典。WPSpell特别适合与WPTools一起使用。 WPSpell功能 键入时进行拼写检查。 …

1-FreeRTOS入门指南

本专栏是根据官方提供的文档进行FreeRTOS的各个功能函数的说明&#xff0c;以及函数的使用 本专栏不涉及动手操作&#xff0c;只是对原理进行说明&#xff0c;FreeRTOS基础知识篇更新完成会对如何在开发板上进行上手实战操作。 这里不会对比其他RTOS的优缺点&#xff0c;因为每…

2、Redis中简单动态字符串的简介,也就是Redis中的键和值的字符串底层表达

简介 首先在Redis中&#xff0c;没有直接使用C语言传统字符串表示(以空字符结尾的字符数组,以下简称C字符串)&#xff0c;而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型(可以简单的理解为Java中的String 类)&#xff0c;并且将SDS用作Redis的默…

动态规划算法(1)

认识动态规划 动态规划的求解思路&#xff1a; 1. 把一个问题分解成若干个子问题 2. 将中间结果保存以避免重复计算 基本步骤&#xff1a; 1. 找出最优解的性质&#xff0c;然后刻画结构特征 &#xff08;找规律&#xff09; 2. 最优解(最好的解决方案 定义) 循环(递归) 3. 以…

我与梅西粉丝们的世界杯观球日常

世界杯 ⚽️ 期间&#xff0c;我与其他的梅西粉丝在某 APP 里建了个梅粉聊天群&#xff0c;群内人数上万人&#xff0c;大家一起讨论赛事热点&#xff0c;可谓热火朝天&#xff0c;此起彼伏&#xff0c;这是四年一度的狂欢&#xff0c;虽值冬季&#xff0c;但热情不减。 “阿根…

配置设备远程管理—eNSP

案例&#xff1a;给路由器配置远程管理&#xff0c;使一台路由器远程管理另一台。 所需设备&#xff1a;两台路由器&#xff0c;一根网线 图示 一、给两台设备配置IP地址 AR1&#xff08;以下命令&#xff09; a. sy b. int g0/0/0 c. ip add 1.1.1.1 24AR2 a. sy b. int g0/0…

十分钟学完简单工厂,普通工厂,抽象工厂

快速学习简单工厂&#xff0c;普通工厂&#xff0c;抽象工厂前言&#xff1a;产品等级和产品族工厂模式作用简单工厂模式uml代码优缺点普通工厂模式uml代码优缺点抽象工厂模式uml代码优缺点前言&#xff1a;产品等级和产品族 在学习工厂模式之前&#xff0c;先得了解一下产品等…

Redis实践

一、持久化 Redis 的数据 全部存储 在 内存 中&#xff0c;如果 突然宕机&#xff0c;数据就会全部丢失&#xff0c;因此必须有一套机制来保证 Redis 的数据不会因为故障而丢失&#xff0c;这种机制就是 Redis 的 持久化机制&#xff0c;它会将内存中的数据库状态 保存到磁盘 …

Spring——AOP原理及流程详解

AOP原理及流程详解一、AOP结构介绍Pointcut通知原理连接点拦截器二、Bean介入点EnableAspectJAutoProxyAspectJAutoProxyRegistrarAnnotationAwareAspectJAutoProxyCreatorAbstractAutoProxyCreator实例前执行初始化后执行循环依赖会调用总结三、处理切面获取所有切面其下通知方…

国内饮料行业数据浅析

大家好&#xff0c;这里是小安说网控。 饮料一直深得年轻人的宠爱&#xff0c;主要消费品类为饮用水、碳酸饮料、奶制品、气泡水等。刚刚过去的十月份&#xff0c;我国饮料产量当期值1199.6万吨&#xff0c;同比下降6.1%&#xff1b;今年1-10月份&#xff0c;饮料产量累计值157…

这几个点让我买了Watch Ultra

01.凑够Apple 全家桶 MacBook ProiPhoneAirPodsiPad 02.可以解锁iPhone手机&#xff0c;MacBook,iPad 03.当iPhone 来电话&#xff0c;不方便接听&#xff0c;可以使用Watch接听(虽然这种情况挺少) 04.可以连接AirPods 听音乐 05.花10元钱开卡&#xff0c;iPhone和Watch 可以…